From 441c8f408cf451f41e784171335c0e59d5972d8e Mon Sep 17 00:00:00 2001 From: qianyj Date: Mon, 1 Aug 2022 13:50:25 +0800 Subject: [PATCH] update TF code --- .../Accuracy_Validation/README.md | 28 + .../ResNet50_Official/.gitmodules | 3 - .../ResNet50_Official/AUTHORS | 10 - .../ResNet50_Official/CODEOWNERS | 67 - .../ResNet50_Official/CONTRIBUTING.md | 23 - .../ResNet50_Official/ISSUE_TEMPLATE.md | 37 - .../ResNet50_Official/README.md | 23 - .../ResNet50_Official/README_origin.md | 19 - .../ResNet50_Official/WORKSPACE | 0 .../ResNet50_Official/official/.gitignore | 2 - .../ResNet50_Official/official/Dockerfile.cpu | 17 - .../ResNet50_Official/official/Dockerfile.gpu | 18 - .../ResNet50_Official/official/README.md | 76 - .../official/benchmark/benchmark_uploader.py | 157 - .../benchmark/benchmark_uploader_main.py | 66 - .../benchmark/benchmark_uploader_test.py | 123 - .../datastore/schema/benchmark_metric.json | 56 - .../datastore/schema/benchmark_run.json | 368 -- .../schema/benchmark_run_status.json | 14 - .../official/boosted_trees/README.md | 112 - .../official/boosted_trees/data_download.py | 97 - .../official/boosted_trees/train_higgs.py | 296 -- .../boosted_trees/train_higgs_test.csv | 20 - .../boosted_trees/train_higgs_test.py | 155 - .../official/datasets/movielens.py | 308 -- .../keras_application_models/README.md | 39 - .../benchmark_main.py | 227 -- .../keras_application_models/dataset.py | 74 - .../model_callbacks.py | 165 - .../official/mnist/README.md | 81 - .../official/mnist/dataset.py | 117 - .../official/mnist/example3.png | Bin 368 -> 0 bytes .../official/mnist/example5.png | Bin 367 -> 0 bytes .../official/mnist/examples.npy | Bin 12624 -> 0 bytes .../ResNet50_Official/official/mnist/mnist.py | 236 -- .../official/mnist/mnist_eager.py | 209 - .../official/mnist/mnist_eager_test.py | 80 - .../official/mnist/mnist_test.py | 135 - .../official/mnist/mnist_tpu.py | 198 - .../official/recommendation/README.md | 71 - .../official/recommendation/constants.py | 79 - .../official/recommendation/data_pipeline.py | 873 ---- .../recommendation/data_preprocessing.py | 232 -- .../official/recommendation/data_test.py | 345 -- .../official/recommendation/ncf_main.py | 444 --- .../official/recommendation/ncf_test.py | 205 - .../official/recommendation/neumf_model.py | 412 -- .../official/recommendation/popen_helper.py | 60 - .../official/recommendation/run.sh | 91 - .../official/recommendation/run_tpu.sh | 22 - .../official/recommendation/stat_utils.py | 92 - .../official/requirements.txt | 11 - .../official/resnet/README.md | 139 - .../resnet/cifar10_download_and_extract.py | 63 - .../official/resnet/cifar10_main.py | 278 -- .../official/resnet/cifar10_test.py | 170 - .../resnet/estimator_cifar_benchmark.py | 155 - .../official/resnet/imagenet_main.py | 357 -- .../official/resnet/imagenet_preprocessing.py | 260 -- .../official/resnet/imagenet_test.py | 309 -- .../official/resnet/keras/__init__.py | 0 .../official/resnet/keras/keras_benchmark.py | 132 - .../resnet/keras/keras_cifar_benchmark.py | 217 - .../official/resnet/keras/keras_cifar_main.py | 200 - .../official/resnet/keras/keras_common.py | 265 -- .../resnet/keras/keras_common_test.py | 113 - .../resnet/keras/keras_imagenet_benchmark.py | 196 - .../resnet/keras/keras_imagenet_main.py | 193 - .../resnet/keras/resnet_cifar_model.py | 287 -- .../official/resnet/keras/resnet_model.py | 241 -- .../official/resnet/layer_test.py | 204 - .../official/resnet/resnet_model.py | 546 --- .../official/resnet/resnet_run_loop.py | 645 --- .../official/transformer/README.md | 369 -- .../official/transformer/__init__.py | 0 .../official/transformer/compute_bleu.py | 137 - .../official/transformer/compute_bleu_test.py | 64 - .../official/transformer/data_download.py | 421 -- .../official/transformer/model/__init__.py | 0 .../transformer/model/attention_layer.py | 148 - .../official/transformer/model/beam_search.py | 541 --- .../transformer/model/beam_search_test.py | 101 - .../transformer/model/embedding_layer.py | 109 - .../official/transformer/model/ffn_layer.py | 89 - .../transformer/model/model_params.py | 96 - .../official/transformer/model/model_utils.py | 110 - .../transformer/model/model_utils_test.py | 68 - .../official/transformer/model/transformer.py | 417 -- .../transformer/test_data/newstest2014.de | 3003 -------------- .../transformer/test_data/newstest2014.en | 3003 -------------- .../official/transformer/transformer_main.py | 636 --- .../official/transformer/translate.py | 236 -- .../official/transformer/utils/__init__.py | 0 .../official/transformer/utils/dataset.py | 283 -- .../official/transformer/utils/metrics.py | 490 --- .../official/transformer/utils/schedule.py | 130 - .../transformer/utils/schedule_test.py | 84 - .../official/transformer/utils/tokenizer.py | 611 --- .../transformer/utils/tokenizer_test.py | 182 - .../official/utils/__init__.py | 0 .../official/utils/accelerator/__init__.py | 0 .../official/utils/accelerator/tpu.py | 115 - .../official/utils/accelerator/tpu_test.py | 108 - .../official/utils/data/__init__.py | 0 .../official/utils/data/file_io.py | 203 - .../official/utils/data/file_io_test.py | 193 - .../official/utils/export/__init__.py | 0 .../official/utils/export/export.py | 49 - .../official/utils/export/export_test.py | 63 - .../official/utils/flags/README.md | 97 - .../official/utils/flags/__init__.py | 0 .../official/utils/flags/_base.py | 141 - .../official/utils/flags/_benchmark.py | 99 - .../official/utils/flags/_conventions.py | 46 - .../official/utils/flags/_device.py | 85 - .../official/utils/flags/_misc.py | 50 - .../official/utils/flags/_performance.py | 172 - .../official/utils/flags/core.py | 88 - .../official/utils/flags/flags_test.py | 100 - .../official/utils/flags/guidelines.md | 64 - .../official/utils/logs/__init__.py | 0 .../official/utils/logs/cloud_lib.py | 34 - .../official/utils/logs/guidelines.md | 58 - .../official/utils/logs/hooks.py | 127 - .../official/utils/logs/hooks_helper.py | 163 - .../official/utils/logs/hooks_helper_test.py | 67 - .../official/utils/logs/hooks_test.py | 157 - .../official/utils/logs/logger.py | 441 -- .../official/utils/logs/logger_test.py | 364 -- .../official/utils/logs/metric_hook.py | 97 - .../official/utils/logs/metric_hook_test.py | 217 - .../official/utils/logs/mlperf_helper.py | 192 - .../official/utils/misc/__init__.py | 0 .../official/utils/misc/distribution_utils.py | 98 - .../utils/misc/distribution_utils_test.py | 65 - .../official/utils/misc/model_helpers.py | 93 - .../official/utils/misc/model_helpers_test.py | 121 - .../official/utils/testing/__init__.py | 0 .../official/utils/testing/integration.py | 65 - .../official/utils/testing/pylint.rcfile | 169 - .../official/utils/testing/reference_data.py | 334 -- .../reference_data_test/dense/expected_graph | Bin 5996 -> 0 bytes .../dense/model.ckpt.data-00000-of-00001 | Bin 76 -> 0 bytes .../dense/model.ckpt.index | Bin 254 -> 0 bytes .../reference_data_test/dense/results.json | 1 - .../reference_data_test/dense/tf_version.json | 1 - .../uniform_random/expected_graph | Bin 893 -> 0 bytes .../model.ckpt.data-00000-of-00001 | 1 - .../uniform_random/model.ckpt.index | Bin 136 -> 0 bytes .../uniform_random/results.json | 1 - .../uniform_random/tf_version.json | 1 - .../expected_graph | Bin 27274 -> 0 bytes .../model.ckpt.data-00000-of-00001 | Bin 33456 -> 0 bytes .../model.ckpt.index | Bin 824 -> 0 bytes .../results.json | 1 - .../tf_version.json | 1 - .../expected_graph | Bin 22479 -> 0 bytes .../model.ckpt.data-00000-of-00001 | Bin 33264 -> 0 bytes .../model.ckpt.index | Bin 680 -> 0 bytes .../results.json | 1 - .../tf_version.json | 1 - .../expected_graph | Bin 20424 -> 0 bytes .../model.ckpt.data-00000-of-00001 | Bin 32932 -> 0 bytes .../model.ckpt.index | Bin 628 -> 0 bytes .../results.json | 1 - .../tf_version.json | 1 - .../expected_graph | Bin 20432 -> 0 bytes .../model.ckpt.data-00000-of-00001 | Bin 32932 -> 0 bytes .../model.ckpt.index | Bin 628 -> 0 bytes .../results.json | 1 - .../tf_version.json | 1 - .../expected_graph | Bin 20709 -> 0 bytes .../model.ckpt.data-00000-of-00001 | Bin 36736 -> 0 bytes .../model.ckpt.index | Bin 641 -> 0 bytes .../results.json | 1 - .../tf_version.json | 1 - .../expected_graph | Bin 15914 -> 0 bytes .../model.ckpt.data-00000-of-00001 | Bin 36544 -> 0 bytes .../model.ckpt.index | Bin 521 -> 0 bytes .../results.json | 1 - .../tf_version.json | 1 - .../expected_graph | Bin 13864 -> 0 bytes .../model.ckpt.data-00000-of-00001 | Bin 34048 -> 0 bytes .../model.ckpt.index | Bin 477 -> 0 bytes .../results.json | 1 - .../tf_version.json | 1 - .../expected_graph | Bin 13867 -> 0 bytes .../model.ckpt.data-00000-of-00001 | Bin 34048 -> 0 bytes .../model.ckpt.index | Bin 477 -> 0 bytes .../results.json | 1 - .../tf_version.json | 1 - .../resnet/batch_norm/expected_graph | Bin 5513 -> 0 bytes .../batch_norm/model.ckpt.data-00000-of-00001 | Bin 98352 -> 0 bytes .../resnet/batch_norm/model.ckpt.index | Bin 275 -> 0 bytes .../resnet/batch_norm/results.json | 1 - .../resnet/batch_norm/tf_version.json | 1 - .../utils/testing/reference_data_test.py | 133 - .../utils/testing/scripts/presubmit.sh | 97 - .../official/wide_deep/README.md | 95 - .../official/wide_deep/__init__.py | 0 .../official/wide_deep/census_dataset.py | 204 - .../official/wide_deep/census_main.py | 116 - .../official/wide_deep/census_test.csv | 30 - .../official/wide_deep/census_test.py | 161 - .../official/wide_deep/movielens_dataset.py | 163 - .../official/wide_deep/movielens_main.py | 115 - .../official/wide_deep/movielens_test.py | 117 - .../official/wide_deep/wide_deep_run_loop.py | 131 - .../.gitignore | 4 +- .../LICENSE | 6 +- .../benchmarks-master/perfzero/README.md | 482 +++ .../docker/Dockerfile_ubuntu_1804_build | 97 + .../Dockerfile_ubuntu_1804_s4tf_cuda10.0 | 124 + .../Dockerfile_ubuntu_1804_s4tf_cuda10.1 | 133 + .../Dockerfile_ubuntu_1804_s4tf_cuda11.0 | 128 + .../Dockerfile_ubuntu_1804_testing_apiclient | 94 + .../docker/Dockerfile_ubuntu_1804_tf_cuda_11 | 109 + .../Dockerfile_ubuntu_1804_tf_cuda_11_0 | 94 + .../Dockerfile_ubuntu_1804_tf_cuda_11_py36 | 90 + ...Dockerfile_ubuntu_1804_tf_cuda_11_rollback | 110 + ...ckerfile_ubuntu_1804_tf_cuda_11_rollback_2 | 110 + .../Dockerfile_ubuntu_1804_tf_cuda_testing | 94 + .../Dockerfile_ubuntu_1804_tf_custom_pip | 88 + .../docker/Dockerfile_ubuntu_1804_tf_v1 | 88 + .../docker/Dockerfile_ubuntu_1804_tf_v2 | 85 + .../docker/Dockerfile_ubuntu_1804_tf_v2_1 | 96 + .../docker/Dockerfile_ubuntu_1804_tfx | 262 ++ .../docker/Dockerfile_ubuntu_cuda11_8_0_0_180 | 95 + .../Dockerfile_ubuntu_experimental_cuda11 | 109 + .../perfzero/dockertest/cuda_diff.sh | 105 + .../perfzero/dockertest/diff_benchmarks.py | 117 + .../perfzero/dockertest/requirements_temp.txt | 27 + .../perfzero/dockertest/resnet50_synth.sh | 82 + .../dockertest/run_single_benchmark.sh | 74 + .../perfzero/lib}/__init__.py | 0 .../perfzero/lib/benchmark.py | 193 + .../perfzero/lib/benchmark_test.py | 57 + .../perfzero/lib/cloud_manager.py | 431 ++ .../perfzero/lib/perfzero}/__init__.py | 0 .../lib/perfzero/benchmark_method_runner.py | 187 + .../perfzero/lib/perfzero/device_utils.py | 86 + .../perfzero/lib/perfzero/perfzero_config.py | 367 ++ .../lib/perfzero/perfzero_config_test.py | 54 + .../lib/perfzero/process_info_tracker.py | 93 + .../perfzero/lib/perfzero/report_utils.py | 237 ++ .../lib/perfzero/tensorflow_profiler.py | 128 + .../example_nvidia-smi_no_processes.txt | 40 + .../example_nvidia-smi_processes.txt | 43 + .../perfzero/test_files/nvme_device_log.txt | 15 + .../lib/perfzero/tpu_runtime_utils.py | 89 + .../perfzero/lib/perfzero/utils.py | 546 +++ .../perfzero/lib/perfzero/utils_test.py | 219 + .../benchmarks-master/perfzero/lib/setup.py | 197 + .../screenshots/profiling_overview.png | Bin 0 -> 373190 bytes .../screenshots/profiling_trace_view.png | Bin 0 -> 279800 bytes .../perfzero/scripts/create_big_table.txt | 43 + .../scripts/generate-readme-header.sh | 275 ++ .../perfzero/scripts/plot_process_info.py | 74 + .../scripts/tf_cnn_benchmarks/README.md | 88 + .../tf_cnn_benchmarks/all_reduce_benchmark.py | 290 ++ .../all_reduce_benchmark_test.py | 52 + .../scripts/tf_cnn_benchmarks/allreduce.py | 649 +++ .../tf_cnn_benchmarks/allreduce_test.py | 448 +++ .../tf_cnn_benchmarks/batch_allreduce.py | 628 +++ .../tf_cnn_benchmarks/benchmark_cnn.py | 3548 +++++++++++++++++ .../benchmark_cnn_distributed_test.py | 493 +++ .../benchmark_cnn_distributed_test_runner.py | 122 + .../tf_cnn_benchmarks/benchmark_cnn_test.py | 1493 +++++++ .../scripts/tf_cnn_benchmarks/cnn_util.py | 253 ++ .../tf_cnn_benchmarks/cnn_util_test.py | 129 + .../scripts/tf_cnn_benchmarks/coco_metric.py | 198 + .../scripts/tf_cnn_benchmarks/constants.py | 67 + .../tf_cnn_benchmarks/convnet_builder.py | 498 +++ .../scripts/tf_cnn_benchmarks/datasets.py | 251 ++ .../scripts/tf_cnn_benchmarks/flags.py | 93 + .../leading_indicators_test.py | 1003 +++++ .../scripts/tf_cnn_benchmarks/mlperf.py | 260 ++ .../scripts/tf_cnn_benchmarks/mlperf_test.py | 189 + .../tf_cnn_benchmarks/models}/__init__.py | 0 .../tf_cnn_benchmarks/models/alexnet_model.py | 93 + .../models/densenet_model.py | 100 + .../models/experimental}/__init__.py | 0 .../models/experimental/deepspeech.py | 449 +++ .../models/experimental/official_ncf_model.py | 172 + .../models/googlenet_model.py | 63 + .../models/inception_model.py | 213 + .../tf_cnn_benchmarks/models/lenet_model.py} | 44 +- .../scripts/tf_cnn_benchmarks/models/model.py | 340 ++ .../tf_cnn_benchmarks/models/model_config.py | 181 + .../models/official_resnet_model.py | 77 + .../models/overfeat_model.py | 53 + .../tf_cnn_benchmarks/models/resnet_model.py | 480 +++ .../models/resnet_model_test.py | 80 + .../models/tf1_only}/__init__.py | 0 .../models/tf1_only/mobilenet.py | 467 +++ .../models/tf1_only/mobilenet_conv_blocks.py | 360 ++ .../models/tf1_only/mobilenet_test.py | 191 + .../models/tf1_only/mobilenet_v2.py | 198 + .../models/tf1_only/nasnet_model.py | 582 +++ .../models/tf1_only/nasnet_test.py | 289 ++ .../models/tf1_only/nasnet_utils.py | 492 +++ .../models/tf1_only/ssd_model.py | 683 ++++ .../tf_cnn_benchmarks/models/trivial_model.py | 73 + .../tf_cnn_benchmarks/models/vgg_model.py | 83 + .../tf_cnn_benchmarks/platforms}/__init__.py | 0 .../platforms/default}/__init__.py | 0 .../platforms/default/util.py | 90 + .../tf_cnn_benchmarks/platforms/util.py} | 28 +- .../tf_cnn_benchmarks/preprocessing.py | 1339 +++++++ .../scripts/tf_cnn_benchmarks/run.sh | 18 + .../scripts/tf_cnn_benchmarks/run_tests.py | 107 + .../tf_cnn_benchmarks/ssd_constants.py | 118 + .../tf_cnn_benchmarks/ssd_dataloader.py | 405 ++ .../tf_cnn_benchmarks/test_data}/__init__.py | 0 .../fake_tf_record_data/train-00000-of-00008 | Bin 0 -> 101327 bytes .../fake_tf_record_data/train-00001-of-00008 | Bin 0 -> 101943 bytes .../fake_tf_record_data/train-00002-of-00008 | Bin 0 -> 97476 bytes .../fake_tf_record_data/train-00003-of-00008 | Bin 0 -> 109017 bytes .../fake_tf_record_data/train-00004-of-00008 | Bin 0 -> 94955 bytes .../fake_tf_record_data/train-00005-of-00008 | Bin 0 -> 98266 bytes .../fake_tf_record_data/train-00006-of-00008 | Bin 0 -> 100921 bytes .../fake_tf_record_data/train-00007-of-00008 | Bin 0 -> 99400 bytes .../validation-00000-of-00002 | Bin 0 -> 97205 bytes .../validation-00001-of-00002 | Bin 0 -> 97782 bytes .../test_data/images/black_image.jpg | Bin 0 -> 825 bytes .../test_data/images/white_image.jpg | Bin 0 -> 823 bytes .../test_data/tfrecord_image_generator.py | 226 ++ .../scripts/tf_cnn_benchmarks/test_util.py | 532 +++ .../tf_cnn_benchmarks/tf_cnn_benchmarks.py | 73 + .../scripts/tf_cnn_benchmarks/variable_mgr.py | 839 ++++ .../tf_cnn_benchmarks/variable_mgr_util.py | 676 ++++ .../variable_mgr_util_test.py | 153 + .../scripts-run/single_process.sh | 38 + .../Classification/.README.md.swp | Bin 12288 -> 0 bytes .../ComputeVision/Classification/README.md | 34 +- .../scripts-run/single_process.sh | 9 +- .../ResNet50_Official/README.md | 38 +- .../ResNet50_Official/README_ori.md | 47 - .../scripts-run/single_process.sh | 33 + .../ComputeVision/Classification/README.md | 31 +- .../scripts-run/single_process.sh | 7 +- 341 files changed, 27511 insertions(+), 28933 deletions(-) create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/README.md delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/.gitmodules delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/AUTHORS delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/CODEOWNERS delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/CONTRIBUTING.md delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/ISSUE_TEMPLATE.md delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/README.md delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/README_origin.md delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/WORKSPACE delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/.gitignore delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/Dockerfile.cpu delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/Dockerfile.gpu delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/README.md delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/benchmark_uploader.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/benchmark_uploader_main.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/benchmark_uploader_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/datastore/schema/benchmark_metric.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/datastore/schema/benchmark_run.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/datastore/schema/benchmark_run_status.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/README.md delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/data_download.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/train_higgs.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/train_higgs_test.csv delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/train_higgs_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/datasets/movielens.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/keras_application_models/README.md delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/keras_application_models/benchmark_main.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/keras_application_models/dataset.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/keras_application_models/model_callbacks.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/README.md delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/dataset.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/example3.png delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/example5.png delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/examples.npy delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/mnist.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/mnist_eager.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/mnist_eager_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/mnist_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/mnist_tpu.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/README.md delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/constants.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/data_pipeline.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/data_preprocessing.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/data_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/ncf_main.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/ncf_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/neumf_model.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/popen_helper.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/run.sh delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/run_tpu.sh delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/stat_utils.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/requirements.txt delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/README.md delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/cifar10_download_and_extract.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/cifar10_main.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/cifar10_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/estimator_cifar_benchmark.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/imagenet_main.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/imagenet_preprocessing.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/imagenet_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/__init__.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_benchmark.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_cifar_benchmark.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_cifar_main.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_common.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_common_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_imagenet_benchmark.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_imagenet_main.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/resnet_cifar_model.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/resnet_model.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/layer_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/resnet_model.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/resnet_run_loop.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/README.md delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/__init__.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/compute_bleu.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/compute_bleu_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/data_download.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/__init__.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/attention_layer.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/beam_search.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/beam_search_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/embedding_layer.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/ffn_layer.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/model_params.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/model_utils.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/model_utils_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/transformer.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/test_data/newstest2014.de delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/test_data/newstest2014.en delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/transformer_main.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/translate.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/__init__.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/dataset.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/metrics.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/schedule.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/schedule_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/tokenizer.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/tokenizer_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/__init__.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/accelerator/__init__.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/accelerator/tpu.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/accelerator/tpu_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/data/__init__.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/data/file_io.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/data/file_io_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/export/__init__.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/export/export.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/export/export_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/README.md delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/__init__.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_base.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_benchmark.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_conventions.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_device.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_misc.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_performance.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/core.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/flags_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/guidelines.md delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/__init__.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/cloud_lib.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/guidelines.md delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/hooks.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/hooks_helper.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/hooks_helper_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/hooks_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/logger.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/logger_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/metric_hook.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/metric_hook_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/mlperf_helper.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/misc/__init__.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/misc/distribution_utils.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/misc/distribution_utils_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/misc/model_helpers.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/misc/model_helpers_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/__init__.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/integration.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/pylint.rcfile delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/dense/expected_graph delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/dense/model.ckpt.data-00000-of-00001 delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/dense/model.ckpt.index delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/dense/results.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/dense/tf_version.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/uniform_random/expected_graph delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/uniform_random/model.ckpt.data-00000-of-00001 delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/uniform_random/model.ckpt.index delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/uniform_random/results.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/uniform_random/tf_version.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_projection_version-1_width-8_channels-4/expected_graph delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_projection_version-1_width-8_channels-4/model.ckpt.data-00000-of-00001 delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_projection_version-1_width-8_channels-4/model.ckpt.index delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_projection_version-1_width-8_channels-4/results.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_projection_version-1_width-8_channels-4/tf_version.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_projection_version-2_width-8_channels-4/expected_graph delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_projection_version-2_width-8_channels-4/model.ckpt.data-00000-of-00001 delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_projection_version-2_width-8_channels-4/model.ckpt.index delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_projection_version-2_width-8_channels-4/results.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_projection_version-2_width-8_channels-4/tf_version.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-1_width-8_channels-4/expected_graph delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-1_width-8_channels-4/model.ckpt.data-00000-of-00001 delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-1_width-8_channels-4/model.ckpt.index delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-1_width-8_channels-4/results.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-1_width-8_channels-4/tf_version.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-2_width-8_channels-4/expected_graph delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-2_width-8_channels-4/model.ckpt.data-00000-of-00001 delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-2_width-8_channels-4/model.ckpt.index delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-2_width-8_channels-4/results.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-2_width-8_channels-4/tf_version.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_projection_version-1_width-8_channels-4/expected_graph delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_projection_version-1_width-8_channels-4/model.ckpt.data-00000-of-00001 delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_projection_version-1_width-8_channels-4/model.ckpt.index delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_projection_version-1_width-8_channels-4/results.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_projection_version-1_width-8_channels-4/tf_version.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_projection_version-2_width-8_channels-4/expected_graph delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_projection_version-2_width-8_channels-4/model.ckpt.data-00000-of-00001 delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_projection_version-2_width-8_channels-4/model.ckpt.index delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_projection_version-2_width-8_channels-4/results.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_projection_version-2_width-8_channels-4/tf_version.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_version-1_width-8_channels-4/expected_graph delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_version-1_width-8_channels-4/model.ckpt.data-00000-of-00001 delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_version-1_width-8_channels-4/model.ckpt.index delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_version-1_width-8_channels-4/results.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_version-1_width-8_channels-4/tf_version.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_version-2_width-8_channels-4/expected_graph delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_version-2_width-8_channels-4/model.ckpt.data-00000-of-00001 delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_version-2_width-8_channels-4/model.ckpt.index delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_version-2_width-8_channels-4/results.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_version-2_width-8_channels-4/tf_version.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch_norm/expected_graph delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch_norm/model.ckpt.data-00000-of-00001 delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch_norm/model.ckpt.index delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch_norm/results.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch_norm/tf_version.json delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/scripts/presubmit.sh delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/README.md delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/__init__.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/census_dataset.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/census_main.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/census_test.csv delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/census_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/movielens_dataset.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/movielens_main.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/movielens_test.py delete mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/wide_deep_run_loop.py rename TensorFlow/ComputeVision/Accuracy_Validation/{ResNet50_Official => benchmarks-master}/.gitignore (92%) rename TensorFlow/ComputeVision/Accuracy_Validation/{ResNet50_Official => benchmarks-master}/LICENSE (99%) create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/README.md create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_build create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_s4tf_cuda10.0 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_s4tf_cuda10.1 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_s4tf_cuda11.0 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_testing_apiclient create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_11 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_11_0 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_11_py36 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_11_rollback create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_11_rollback_2 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_testing create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_custom_pip create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_v1 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_v2 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_v2_1 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tfx create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_cuda11_8_0_0_180 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_experimental_cuda11 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/dockertest/cuda_diff.sh create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/dockertest/diff_benchmarks.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/dockertest/requirements_temp.txt create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/dockertest/resnet50_synth.sh create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/dockertest/run_single_benchmark.sh rename TensorFlow/ComputeVision/Accuracy_Validation/{ResNet50_Official/official => benchmarks-master/perfzero/lib}/__init__.py (100%) create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/benchmark.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/benchmark_test.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/cloud_manager.py rename TensorFlow/ComputeVision/Accuracy_Validation/{ResNet50_Official/official/benchmark => benchmarks-master/perfzero/lib/perfzero}/__init__.py (100%) create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/benchmark_method_runner.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/device_utils.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/perfzero_config.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/perfzero_config_test.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/process_info_tracker.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/report_utils.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/tensorflow_profiler.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/test_files/example_nvidia-smi_no_processes.txt create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/test_files/example_nvidia-smi_processes.txt create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/test_files/nvme_device_log.txt create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/tpu_runtime_utils.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/utils.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/utils_test.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/setup.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/screenshots/profiling_overview.png create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/screenshots/profiling_trace_view.png create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/scripts/create_big_table.txt create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/scripts/generate-readme-header.sh create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/scripts/plot_process_info.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/README.md create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/all_reduce_benchmark.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/all_reduce_benchmark_test.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/allreduce.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/allreduce_test.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/batch_allreduce.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/benchmark_cnn.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/benchmark_cnn_distributed_test.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/benchmark_cnn_distributed_test_runner.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/benchmark_cnn_test.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/cnn_util.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/cnn_util_test.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/coco_metric.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/constants.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/convnet_builder.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/datasets.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/flags.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/leading_indicators_test.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/mlperf.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/mlperf_test.py rename TensorFlow/ComputeVision/Accuracy_Validation/{ResNet50_Official/official/boosted_trees => benchmarks-master/scripts/tf_cnn_benchmarks/models}/__init__.py (100%) create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/alexnet_model.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/densenet_model.py rename TensorFlow/ComputeVision/Accuracy_Validation/{ResNet50_Official/official/datasets => benchmarks-master/scripts/tf_cnn_benchmarks/models/experimental}/__init__.py (100%) create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/experimental/deepspeech.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/experimental/official_ncf_model.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/googlenet_model.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/inception_model.py rename TensorFlow/ComputeVision/Accuracy_Validation/{ResNet50_Official/official/utils/logs/cloud_lib_test.py => benchmarks-master/scripts/tf_cnn_benchmarks/models/lenet_model.py} (55%) create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/model.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/model_config.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/official_resnet_model.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/overfeat_model.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/resnet_model.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/resnet_model_test.py rename TensorFlow/ComputeVision/Accuracy_Validation/{ResNet50_Official/official/keras_application_models => benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only}/__init__.py (100%) create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/mobilenet.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/mobilenet_conv_blocks.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/mobilenet_test.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/mobilenet_v2.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/nasnet_model.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/nasnet_test.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/nasnet_utils.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/ssd_model.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/trivial_model.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/vgg_model.py rename TensorFlow/ComputeVision/Accuracy_Validation/{ResNet50_Official/official/mnist => benchmarks-master/scripts/tf_cnn_benchmarks/platforms}/__init__.py (100%) rename TensorFlow/ComputeVision/Accuracy_Validation/{ResNet50_Official/official/recommendation => benchmarks-master/scripts/tf_cnn_benchmarks/platforms/default}/__init__.py (100%) create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/platforms/default/util.py rename TensorFlow/ComputeVision/Accuracy_Validation/{ResNet50_Official/official/utils/testing/mock_lib.py => benchmarks-master/scripts/tf_cnn_benchmarks/platforms/util.py} (58%) create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/preprocessing.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/run.sh create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/run_tests.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/ssd_constants.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/ssd_dataloader.py rename TensorFlow/ComputeVision/Accuracy_Validation/{ResNet50_Official/official/resnet => benchmarks-master/scripts/tf_cnn_benchmarks/test_data}/__init__.py (100%) create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/fake_tf_record_data/train-00000-of-00008 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/fake_tf_record_data/train-00001-of-00008 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/fake_tf_record_data/train-00002-of-00008 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/fake_tf_record_data/train-00003-of-00008 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/fake_tf_record_data/train-00004-of-00008 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/fake_tf_record_data/train-00005-of-00008 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/fake_tf_record_data/train-00006-of-00008 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/fake_tf_record_data/train-00007-of-00008 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/fake_tf_record_data/validation-00000-of-00002 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/fake_tf_record_data/validation-00001-of-00002 create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/images/black_image.jpg create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/images/white_image.jpg create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/tfrecord_image_generator.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_util.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/tf_cnn_benchmarks.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/variable_mgr.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/variable_mgr_util.py create mode 100644 TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/variable_mgr_util_test.py create mode 100755 TensorFlow/ComputeVision/Accuracy_Validation/scripts-run/single_process.sh delete mode 100644 TensorFlow/ComputeVision/Classification/.README.md.swp delete mode 100644 TensorFlow2x/Accuracy_Validation/ResNet50_Official/README_ori.md create mode 100644 TensorFlow2x/Accuracy_Validation/ResNet50_Official/scripts-run/single_process.sh diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/README.md b/TensorFlow/ComputeVision/Accuracy_Validation/README.md new file mode 100644 index 00000000..78062a5b --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/README.md @@ -0,0 +1,28 @@ +# 简介 + +该测试用例可用于ResNet50/Vgg16等网络的性能测试及精度验证。 + + +# 单卡测试 (单精度) + +## 运行 + + export HIP_VISIBLE_DEVICES=0 + python3 ./benchmarks-master/scripts/tf_cnn_benchmarks/tf_cnn_benchmarks.py --data_format=NCHW --batch_size=128 --model=resnet50 --optimizer=momentum --variable_update=parameter_server --print_training_accuracy=true --nodistortions --num_gpus=1 --num_epochs=90 --weight_decay=1e-4 --data_dir=$data_dir_path --use_fp16=False --data_name=imagenet --train_dir=$save_checkpoint_path + +# 单卡测试 (混合精度) + +## 运行 + + python3 ./benchmarks-master/scripts/tf_cnn_benchmarks/tf_cnn_benchmarks.py --data_format=NCHW --batch_size=128 --model=resnet50 --optimizer=momentum --variable_update=parameter_server --print_training_accuracy=true --nodistortions --num_gpus=4 --num_epochs=90 --weight_decay=1e-4 --data_dir=$data_dir_path --use_fp16=True --data_name=imagenet --train_dir=$save_checkpoint_path + +# 多卡测试 (单精度) + +## 运行 + + mpirun -np 4 --hostfile hostfile --bind-to none scripts-run/single_process.shhi + +# 参考资料 +[https://github.com/tensorflow/benchmarks/tree/master/scripts/tf_cnn_benchmarks] +[https://github.com/horovod/horovod] + diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/.gitmodules b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/.gitmodules deleted file mode 100644 index 7f5b5324..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "tensorflow"] - path = research/syntaxnet/tensorflow - url = https://github.com/tensorflow/tensorflow.git diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/AUTHORS b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/AUTHORS deleted file mode 100644 index 0fa85c98..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/AUTHORS +++ /dev/null @@ -1,10 +0,0 @@ -# This is the official list of authors for copyright purposes. -# This file is distinct from the CONTRIBUTORS files. -# See the latter for an explanation. - -# Names should be added to this file as: -# Name or Organization -# The email address is not required for organizations. - -Google Inc. -David Dao diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/CODEOWNERS b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/CODEOWNERS deleted file mode 100644 index 08671562..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/CODEOWNERS +++ /dev/null @@ -1,67 +0,0 @@ -* @tensorflow/tf-garden-team -/official/ @tensorflow/tf-garden-team @karmel -/research/adversarial_crypto/ @dave-andersen -/research/adversarial_logit_pairing/ @AlexeyKurakin -/research/adversarial_text/ @rsepassi @a-dai -/research/adv_imagenet_models/ @AlexeyKurakin -/research/attention_ocr/ @alexgorban -/research/audioset/ @plakal @dpwe -/research/autoaugment/* @barretzoph -/research/autoencoders/ @snurkabill -/research/brain_coder/ @danabo -/research/cognitive_mapping_and_planning/ @s-gupta -/research/compression/ @nmjohn -/research/cvt_text/ @clarkkev @lmthang -/research/deep_contextual_bandits/ @rikel -/research/deeplab/ @aquariusjay @yknzhu @gpapan -/research/delf/ @andrefaraujo -/research/differential_privacy/ @ilyamironov @ananthr -/research/domain_adaptation/ @bousmalis @dmrd -/research/efficient-hrl/ @ofirnachum -/research/gan/ @joel-shor -/research/global_objectives/ @mackeya-google -/research/im2txt/ @cshallue -/research/inception/ @shlens @vincentvanhoucke -/research/keypointnet/ @mnorouzi -/research/learned_optimizer/ @olganw @nirum -/research/learning_to_remember_rare_events/ @lukaszkaiser @ofirnachum -/research/learning_unsupervised_learning/ @lukemetz @nirum -/research/lexnet_nc/ @vered1986 @waterson -/research/lfads/ @jazcollins @susillo -/research/lm_1b/ @oriolvinyals @panyx0718 -/research/lm_commonsense/ @thtrieu -/research/lstm_object_detection/ @dreamdragon @masonliuw @yinxiaoli -/research/marco/ @vincentvanhoucke -/research/maskgan/ @a-dai -/research/morph_net/ @gariel-google -/research/namignizer/ @knathanieltucker -/research/neural_gpu/ @lukaszkaiser -/research/neural_programmer/ @arvind2505 -/research/next_frame_prediction/ @panyx0718 -/research/object_detection/ @jch1 @tombstone @derekjchow @jesu9 @dreamdragon @pkulzc -/research/pcl_rl/ @ofirnachum -/research/ptn/ @xcyan @arkanath @hellojas @honglaklee -/research/real_nvp/ @laurent-dinh -/research/rebar/ @gjtucker -/research/resnet/ @panyx0718 -/research/seq2species/ @apbusia @depristo -/research/skip_thoughts/ @cshallue -/research/slim/ @sguada @nathansilberman -/research/steve/ @buckman-google -/research/street/ @theraysmith -/research/struct2depth/ @aneliaangelova -/research/swivel/ @waterson -/research/syntaxnet/ @calberti @andorardo @bogatyy @markomernick -/research/tcn/ @coreylynch @sermanet -/research/tensorrt/ @karmel -/research/textsum/ @panyx0718 @peterjliu -/research/transformer/ @daviddao -/research/vid2depth/ @rezama -/research/video_prediction/ @cbfinn -/research/fivo/ @dieterichlawson -/samples/ @MarkDaoust @lamberta -/samples/languages/java/ @asimshankar -/tutorials/embedding/ @zffchen78 @a-dai -/tutorials/image/ @sherrym @shlens -/tutorials/image/cifar10_estimator/ @tfboyd @protoget -/tutorials/rnn/ @lukaszkaiser @ebrevdo diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/CONTRIBUTING.md b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/CONTRIBUTING.md deleted file mode 100644 index 6053119c..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/CONTRIBUTING.md +++ /dev/null @@ -1,23 +0,0 @@ -# Contributing guidelines - -If you have created a model and would like to publish it here, please send us a -pull request. For those just getting started with pull requests, GitHub has a -[howto](https://help.github.com/articles/using-pull-requests/). - -The code for any model in this repository is licensed under the Apache License -2.0. - -In order to accept our code, we have to make sure that we can publish your code: -You have to sign a Contributor License Agreement (CLA). - -### Contributor License Agreements - -Please fill out either the individual or corporate Contributor License Agreement (CLA). - - * If you are an individual writing original source code and you're sure you own the intellectual property, then you'll need to sign an [individual CLA](http://code.google.com/legal/individual-cla-v1.0.html). - * If you work for a company that wants to allow you to contribute your work, then you'll need to sign a [corporate CLA](http://code.google.com/legal/corporate-cla-v1.0.html). - -Follow either of the two links above to access the appropriate CLA and instructions for how to sign and return it. Once we receive it, we'll be able to accept your pull requests. - -***NOTE***: Only original source code from you and other people that have signed the CLA can be accepted into the repository. - diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/ISSUE_TEMPLATE.md b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/ISSUE_TEMPLATE.md deleted file mode 100644 index 021d5aa8..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,37 +0,0 @@ -Please go to Stack Overflow for help and support: - -http://stackoverflow.com/questions/tagged/tensorflow - -Also, please understand that many of the models included in this repository are experimental and research-style code. If you open a GitHub issue, here is our policy: - -1. It must be a bug, a feature request, or a significant problem with documentation (for small docs fixes please send a PR instead). -2. The form below must be filled out. - -**Here's why we have that policy**: TensorFlow developers respond to issues. We want to focus on work that benefits the whole community, e.g., fixing bugs and adding features. Support only helps individuals. GitHub also notifies thousands of people when issues are filed. We want them to see you communicating an interesting problem, rather than being redirected to Stack Overflow. - ------------------------- - -### System information -- **What is the top-level directory of the model you are using**: -- **Have I written custom code (as opposed to using a stock example script provided in TensorFlow)**: -- **OS Platform and Distribution (e.g., Linux Ubuntu 16.04)**: -- **TensorFlow installed from (source or binary)**: -- **TensorFlow version (use command below)**: -- **Bazel version (if compiling from source)**: -- **CUDA/cuDNN version**: -- **GPU model and memory**: -- **Exact command to reproduce**: - -You can collect some of this information using our environment capture script: - -https://github.com/tensorflow/tensorflow/tree/master/tools/tf_env_collect.sh - -You can obtain the TensorFlow version with - -python -c "import tensorflow as tf; print(tf.GIT_VERSION, tf.VERSION)" - -### Describe the problem -Describe the problem clearly here. Be sure to convey here why it's a bug in TensorFlow or a feature request. - -### Source code / logs -Include any logs or source code that would be helpful to diagnose the problem. If including tracebacks, please include the full traceback. Large logs and files should be attached. Try to provide a reproducible test case that is the bare minimum necessary to generate the problem. diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/README.md b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/README.md deleted file mode 100644 index 07a79880..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# 介绍 -本测试用例用于图像分类ResNet50模型在ROCm平台的精度验证,测试流程如下。 -# 测试流程 -## 加载环境变量 -下载tensorflow官方github中的[model](https://github.com/tensorflow/models) -设置python变量: - - export PYTHONPATH=$PYTHONPATH:/path/to/tensorflow/model -ROCm平台使用MIOpen进行加速,以下变量设置可以参考使用: - - export MIOPEN_DEBUG_DISABLE_FIND_DB=1 - - export MIOPEN_USER_DB_PATH=/path/to/{miopen_save_dir} - - export LD_LIBRARY_PATH=/path/to/devtoolset7:$LD_LIBRARY_PATH -## 运行示例 -可以使用单卡或多卡运行,4卡运行指令如下: - - cd official/resnet - - python3 imagenet_main.py --data_dir=/path/to/{ImageNet-tensorflow_data_dir} --model_dir=/path/to/{model_save_dir} --batch_size=512 --num_gpus=4 -# 参考 -[https://github.com/tensorflow/models/tree/r1.13.0/official/resnet](https://github.com/tensorflow/models/tree/r1.13.0/official/resnet) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/README_origin.md b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/README_origin.md deleted file mode 100644 index c2821cc7..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/README_origin.md +++ /dev/null @@ -1,19 +0,0 @@ -# TensorFlow Models - -This repository contains a number of different models implemented in [TensorFlow](https://www.tensorflow.org): - -The [official models](official) are a collection of example models that use TensorFlow's high-level APIs. They are intended to be well-maintained, tested, and kept up to date with the latest stable TensorFlow API. They should also be reasonably optimized for fast performance while still being easy to read. We especially recommend newer TensorFlow users to start here. - -The [research models](https://github.com/tensorflow/models/tree/master/research) are a large collection of models implemented in TensorFlow by researchers. They are not officially supported or available in release branches; it is up to the individual researchers to maintain the models and/or provide support on issues and pull requests. - -The [samples folder](samples) contains code snippets and smaller models that demonstrate features of TensorFlow, including code presented in various blog posts. - -The [tutorials folder](tutorials) is a collection of models described in the [TensorFlow tutorials](https://www.tensorflow.org/tutorials/). - -## Contribution guidelines - -If you want to contribute to models, be sure to review the [contribution guidelines](CONTRIBUTING.md). - -## License - -[Apache License 2.0](LICENSE) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/WORKSPACE b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/WORKSPACE deleted file mode 100644 index e69de29b..00000000 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/.gitignore b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/.gitignore deleted file mode 100644 index 2b837bcc..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -MNIST-data -labels.txt diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/Dockerfile.cpu b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/Dockerfile.cpu deleted file mode 100644 index e40755c5..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/Dockerfile.cpu +++ /dev/null @@ -1,17 +0,0 @@ -# Docker image for running examples in Tensorflow models. -# base_image depends on whether we are running on GPUs or non-GPUs -FROM ubuntu:latest - -RUN apt-get update && apt-get install -y --no-install-recommends \ - ca-certificates \ - build-essential \ - git \ - python \ - python-pip \ - python-setuptools - -RUN pip install tf-nightly - -# Checkout tensorflow/models at HEAD -RUN git clone https://github.com/tensorflow/models.git /tensorflow_models - diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/Dockerfile.gpu b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/Dockerfile.gpu deleted file mode 100644 index 012b49cb..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/Dockerfile.gpu +++ /dev/null @@ -1,18 +0,0 @@ -# Docker image for running examples in Tensorflow models. -# base_image depends on whether we are running on GPUs or non-GPUs -FROM nvidia/cuda:9.0-cudnn7-runtime-ubuntu16.04 - -RUN apt-get update && apt-get install -y --no-install-recommends \ - ca-certificates \ - build-essential \ - git \ - python \ - python-pip \ - python-setuptools - -RUN pip install tf-nightly-gpu - -# Checkout tensorflow/models at HEAD -RUN git clone https://github.com/tensorflow/models.git /tensorflow_models - - diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/README.md b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/README.md deleted file mode 100644 index 08957298..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/README.md +++ /dev/null @@ -1,76 +0,0 @@ -# TensorFlow Official Models - -The TensorFlow official models are a collection of example models that use TensorFlow's high-level APIs. They are intended to be well-maintained, tested, and kept up to date with the latest TensorFlow API. They should also be reasonably optimized for fast performance while still being easy to read. - -These models are used as end-to-end tests, ensuring that the models run with the same speed and performance with each new TensorFlow build. - -## Tensorflow releases -The master branch of the models are **in development**, and they target the [nightly binaries](https://github.com/tensorflow/tensorflow#installation) built from the [master branch of TensorFlow](https://github.com/tensorflow/tensorflow/tree/master). We aim to keep them backwards compatible with the latest release when possible (currently TensorFlow 1.5), but we cannot always guarantee compatibility. - -**Stable versions** of the official models targeting releases of TensorFlow are available as tagged branches or [downloadable releases](https://github.com/tensorflow/models/releases). Model repository version numbers match the target TensorFlow release, such that [branch r1.4.0](https://github.com/tensorflow/models/tree/r1.4.0) and [release v1.4.0](https://github.com/tensorflow/models/releases/tag/v1.4.0) are compatible with [TensorFlow v1.4.0](https://github.com/tensorflow/tensorflow/releases/tag/v1.4.0). - -If you are on a version of TensorFlow earlier than 1.4, please [update your installation](https://www.tensorflow.org/install/). - -## Requirements -Please follow the below steps before running models in this repo: - - -1. TensorFlow [nightly binaries](https://github.com/tensorflow/tensorflow#installation) - -2. Add the top-level ***/models*** folder to the Python path with the command: - ``` - export PYTHONPATH="$PYTHONPATH:/path/to/models" - ``` - - Using Colab: - ``` - import os - os.environ['PYTHONPATH'] += ":/path/to/models" - ``` - -3. Install dependencies: - ``` - pip3 install --user -r official/requirements.txt - ``` - or - ``` - pip install --user -r official/requirements.txt - ``` - - -To make Official Models easier to use, we are planning to create a pip installable Official Models package. This is being tracked in [#917](https://github.com/tensorflow/models/issues/917). - - -## Available models - -**NOTE:** Please make sure to follow the steps in the [Requirements](#requirements) section. - -* [boosted_trees](boosted_trees): A Gradient Boosted Trees model to classify higgs boson process from HIGGS Data Set. -* [mnist](mnist): A basic model to classify digits from the MNIST dataset. -* [resnet](resnet): A deep residual network that can be used to classify both CIFAR-10 and ImageNet's dataset of 1000 classes. -* [transformer](transformer): A transformer model to translate the WMT English to German dataset. -* [wide_deep](wide_deep): A model that combines a wide model and deep network to classify census income data. -* More models to come! - -If you would like to make any fixes or improvements to the models, please [submit a pull request](https://github.com/tensorflow/models/compare). - -## New Models - -The team is actively working to add new models to the repository. Every model should follow the following guidelines, to uphold the -our objectives of readable, usable, and maintainable code. - -**General guidelines** -* Code should be well documented and tested. -* Runnable from a blank environment with relative ease. -* Trainable on: single GPU/CPU (baseline), multiple GPUs, TPU -* Compatible with Python 2 and 3 (using [six](https://pythonhosted.org/six/) when necessary) -* Conform to [Google Python Style Guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md) - -**Implementation guidelines** - -These guidelines exist so the model implementations are consistent for better readability and maintainability. - -* Use [common utility functions](utils) -* Export SavedModel at the end of training. -* Consistent flags and flag-parsing library ([read more here](utils/flags/guidelines.md)) -* Produce benchmarks and logs ([read more here](utils/logs/guidelines.md)) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/benchmark_uploader.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/benchmark_uploader.py deleted file mode 100644 index 70c07fcf..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/benchmark_uploader.py +++ /dev/null @@ -1,157 +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. -# ============================================================================== - -"""Library to upload benchmark generated by BenchmarkLogger to remote repo. - -This library require google cloud bigquery lib as dependency, which can be -installed with: - > pip install --upgrade google-cloud-bigquery -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import json - -from google.cloud import bigquery -from google.cloud import exceptions - -import tensorflow as tf - - -class BigQueryUploader(object): - """Upload the benchmark and metric info from JSON input to BigQuery. """ - - def __init__(self, gcp_project=None, credentials=None): - """Initialized BigQueryUploader with proper setting. - - Args: - gcp_project: string, the name of the GCP project that the log will be - uploaded to. The default project name will be detected from local - environment if no value is provided. - credentials: google.auth.credentials. The credential to access the - BigQuery service. The default service account credential will be - detected from local environment if no value is provided. Please use - google.oauth2.service_account.Credentials to load credential from local - file for the case that the test is run out side of GCP. - """ - self._bq_client = bigquery.Client( - project=gcp_project, credentials=credentials) - - def upload_benchmark_run_json( - self, dataset_name, table_name, run_id, run_json): - """Upload benchmark run information to Bigquery. - - Args: - dataset_name: string, the name of bigquery dataset where the data will be - uploaded. - table_name: string, the name of bigquery table under the dataset where - the data will be uploaded. - run_id: string, a unique ID that will be attached to the data, usually - this is a UUID4 format. - run_json: dict, the JSON data that contains the benchmark run info. - """ - run_json["model_id"] = run_id - self._upload_json(dataset_name, table_name, [run_json]) - - def upload_benchmark_metric_json( - self, dataset_name, table_name, run_id, metric_json_list): - """Upload metric information to Bigquery. - - Args: - dataset_name: string, the name of bigquery dataset where the data will be - uploaded. - table_name: string, the name of bigquery table under the dataset where - the metric data will be uploaded. This is different from the - benchmark_run table. - run_id: string, a unique ID that will be attached to the data, usually - this is a UUID4 format. This should be the same as the benchmark run_id. - metric_json_list: list, a list of JSON object that record the metric info. - """ - for m in metric_json_list: - m["run_id"] = run_id - self._upload_json(dataset_name, table_name, metric_json_list) - - def upload_benchmark_run_file( - self, dataset_name, table_name, run_id, run_json_file): - """Upload benchmark run information to Bigquery from input json file. - - Args: - dataset_name: string, the name of bigquery dataset where the data will be - uploaded. - table_name: string, the name of bigquery table under the dataset where - the data will be uploaded. - run_id: string, a unique ID that will be attached to the data, usually - this is a UUID4 format. - run_json_file: string, the file path that contains the run JSON data. - """ - with tf.gfile.GFile(run_json_file) as f: - benchmark_json = json.load(f) - self.upload_benchmark_run_json( - dataset_name, table_name, run_id, benchmark_json) - - def upload_metric_file( - self, dataset_name, table_name, run_id, metric_json_file): - """Upload metric information to Bigquery from input json file. - - Args: - dataset_name: string, the name of bigquery dataset where the data will be - uploaded. - table_name: string, the name of bigquery table under the dataset where - the metric data will be uploaded. This is different from the - benchmark_run table. - run_id: string, a unique ID that will be attached to the data, usually - this is a UUID4 format. This should be the same as the benchmark run_id. - metric_json_file: string, the file path that contains the metric JSON - data. - """ - with tf.gfile.GFile(metric_json_file) as f: - metrics = [] - for line in f: - metrics.append(json.loads(line.strip())) - self.upload_benchmark_metric_json( - dataset_name, table_name, run_id, metrics) - - def _upload_json(self, dataset_name, table_name, json_list): - # Find the unique table reference based on dataset and table name, so that - # the data can be inserted to it. - table_ref = self._bq_client.dataset(dataset_name).table(table_name) - errors = self._bq_client.insert_rows_json(table_ref, json_list) - if errors: - tf.logging.error( - "Failed to upload benchmark info to bigquery: {}".format(errors)) - - def insert_run_status(self, dataset_name, table_name, run_id, run_status): - """Insert the run status in to Bigquery run status table.""" - query = ("INSERT {ds}.{tb} " - "(run_id, status) " - "VALUES('{rid}', '{status}')").format( - ds=dataset_name, tb=table_name, rid=run_id, status=run_status) - try: - self._bq_client.query(query=query).result() - except exceptions.GoogleCloudError as e: - tf.logging.error("Failed to insert run status: %s", e) - - def update_run_status(self, dataset_name, table_name, run_id, run_status): - """Update the run status in in Bigquery run status table.""" - query = ("UPDATE {ds}.{tb} " - "SET status = '{status}' " - "WHERE run_id = '{rid}'").format( - ds=dataset_name, tb=table_name, status=run_status, rid=run_id) - try: - self._bq_client.query(query=query).result() - except exceptions.GoogleCloudError as e: - tf.logging.error("Failed to update run status: %s", e) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/benchmark_uploader_main.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/benchmark_uploader_main.py deleted file mode 100644 index e0150512..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/benchmark_uploader_main.py +++ /dev/null @@ -1,66 +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. -# ============================================================================== - -"""Binary to upload benchmark generated by BenchmarkLogger to remote repo. - -This library require google cloud bigquery lib as dependency, which can be -installed with: - > pip install --upgrade google-cloud-bigquery -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os -import sys -import uuid - -from absl import app as absl_app -from absl import flags - -from official.benchmark import benchmark_uploader -from official.utils.flags import core as flags_core -from official.utils.logs import logger - -def main(_): - if not flags.FLAGS.benchmark_log_dir: - print("Usage: benchmark_uploader.py --benchmark_log_dir=/some/dir") - sys.exit(1) - - uploader = benchmark_uploader.BigQueryUploader( - gcp_project=flags.FLAGS.gcp_project) - run_id = str(uuid.uuid4()) - run_json_file = os.path.join( - flags.FLAGS.benchmark_log_dir, logger.BENCHMARK_RUN_LOG_FILE_NAME) - metric_json_file = os.path.join( - flags.FLAGS.benchmark_log_dir, logger.METRIC_LOG_FILE_NAME) - - uploader.upload_benchmark_run_file( - flags.FLAGS.bigquery_data_set, flags.FLAGS.bigquery_run_table, run_id, - run_json_file) - uploader.upload_metric_file( - flags.FLAGS.bigquery_data_set, flags.FLAGS.bigquery_metric_table, run_id, - metric_json_file) - # Assume the run finished successfully before user invoke the upload script. - uploader.insert_run_status( - flags.FLAGS.bigquery_data_set, flags.FLAGS.bigquery_run_status_table, - run_id, logger.RUN_STATUS_SUCCESS) - - -if __name__ == "__main__": - flags_core.define_benchmark() - flags.adopt_module_key_flags(flags_core) - absl_app.run(main=main) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/benchmark_uploader_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/benchmark_uploader_test.py deleted file mode 100644 index df379682..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/benchmark_uploader_test.py +++ /dev/null @@ -1,123 +0,0 @@ -# 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. -# ============================================================================== - -"""Tests for benchmark_uploader.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import json -import os -import tempfile -import unittest -from mock import MagicMock -from mock import patch - -import tensorflow as tf # pylint: disable=g-bad-import-order - -try: - from google.cloud import bigquery - from official.benchmark import benchmark_uploader -except ImportError: - bigquery = None - benchmark_uploader = None - - -@unittest.skipIf(bigquery is None, "Bigquery dependency is not installed.") -class BigQueryUploaderTest(tf.test.TestCase): - - @patch.object(bigquery, "Client") - def setUp(self, mock_bigquery): - self.mock_client = mock_bigquery.return_value - self.mock_dataset = MagicMock(name="dataset") - self.mock_table = MagicMock(name="table") - self.mock_client.dataset.return_value = self.mock_dataset - self.mock_dataset.table.return_value = self.mock_table - self.mock_client.insert_rows_json.return_value = [] - - self.benchmark_uploader = benchmark_uploader.BigQueryUploader() - self.benchmark_uploader._bq_client = self.mock_client - - self.log_dir = tempfile.mkdtemp(dir=self.get_temp_dir()) - with open(os.path.join(self.log_dir, "metric.log"), "a") as f: - json.dump({"name": "accuracy", "value": 1.0}, f) - f.write("\n") - json.dump({"name": "loss", "value": 0.5}, f) - f.write("\n") - with open(os.path.join(self.log_dir, "run.log"), "w") as f: - json.dump({"model_name": "value"}, f) - - def tearDown(self): - tf.gfile.DeleteRecursively(self.get_temp_dir()) - - def test_upload_benchmark_run_json(self): - self.benchmark_uploader.upload_benchmark_run_json( - "dataset", "table", "run_id", {"model_name": "value"}) - - self.mock_client.insert_rows_json.assert_called_once_with( - self.mock_table, [{"model_name": "value", "model_id": "run_id"}]) - - def test_upload_benchmark_metric_json(self): - metric_json_list = [ - {"name": "accuracy", "value": 1.0}, - {"name": "loss", "value": 0.5} - ] - expected_params = [ - {"run_id": "run_id", "name": "accuracy", "value": 1.0}, - {"run_id": "run_id", "name": "loss", "value": 0.5} - ] - self.benchmark_uploader.upload_benchmark_metric_json( - "dataset", "table", "run_id", metric_json_list) - self.mock_client.insert_rows_json.assert_called_once_with( - self.mock_table, expected_params) - - def test_upload_benchmark_run_file(self): - self.benchmark_uploader.upload_benchmark_run_file( - "dataset", "table", "run_id", os.path.join(self.log_dir, "run.log")) - - self.mock_client.insert_rows_json.assert_called_once_with( - self.mock_table, [{"model_name": "value", "model_id": "run_id"}]) - - def test_upload_metric_file(self): - self.benchmark_uploader.upload_metric_file( - "dataset", "table", "run_id", - os.path.join(self.log_dir, "metric.log")) - expected_params = [ - {"run_id": "run_id", "name": "accuracy", "value": 1.0}, - {"run_id": "run_id", "name": "loss", "value": 0.5} - ] - self.mock_client.insert_rows_json.assert_called_once_with( - self.mock_table, expected_params) - - def test_insert_run_status(self): - self.benchmark_uploader.insert_run_status( - "dataset", "table", "run_id", "status") - expected_query = ("INSERT dataset.table " - "(run_id, status) " - "VALUES('run_id', 'status')") - self.mock_client.query.assert_called_once_with(query=expected_query) - - def test_update_run_status(self): - self.benchmark_uploader.update_run_status( - "dataset", "table", "run_id", "status") - expected_query = ("UPDATE dataset.table " - "SET status = 'status' " - "WHERE run_id = 'run_id'") - self.mock_client.query.assert_called_once_with(query=expected_query) - - -if __name__ == "__main__": - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/datastore/schema/benchmark_metric.json b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/datastore/schema/benchmark_metric.json deleted file mode 100644 index cc571d48..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/datastore/schema/benchmark_metric.json +++ /dev/null @@ -1,56 +0,0 @@ -[ - { - "description": "The ID of the benchmark run, where this metric should tie to.", - "mode": "REQUIRED", - "name": "run_id", - "type": "STRING" - }, - { - "description": "The name of the metric, which should be descriptive. E.g. training_loss, accuracy.", - "mode": "REQUIRED", - "name": "name", - "type": "STRING" - }, - { - "description": "The unit of the metric. E.g. MB per sec.", - "mode": "NULLABLE", - "name": "unit", - "type": "STRING" - }, - { - "description": "The value of the metric.", - "mode": "NULLABLE", - "name": "value", - "type": "FLOAT" - }, - { - "description": "The timestamp when the metric is recorded.", - "mode": "REQUIRED", - "name": "timestamp", - "type": "TIMESTAMP" - }, - { - "description": "The global step when this metric is recorded.", - "mode": "NULLABLE", - "name": "global_step", - "type": "INTEGER" - }, - { - "description": "Free format metadata for the extra information about the metric.", - "mode": "REPEATED", - "name": "extras", - "type": "RECORD", - "fields": [ - { - "mode": "NULLABLE", - "name": "name", - "type": "STRING" - }, - { - "mode": "NULLABLE", - "name": "value", - "type": "STRING" - } - ] - } -] diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/datastore/schema/benchmark_run.json b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/datastore/schema/benchmark_run.json deleted file mode 100644 index 58e5ddca..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/datastore/schema/benchmark_run.json +++ /dev/null @@ -1,368 +0,0 @@ -[ - { - "description": "The UUID of the run for the benchmark.", - "mode": "REQUIRED", - "name": "model_id", - "type": "STRING" - }, - { - "description": "The name of the model, E.g ResNet50, LeNet-5 etc.", - "mode": "REQUIRED", - "name": "model_name", - "type": "STRING" - }, - { - "description": "The date when the test of the model is started", - "mode": "REQUIRED", - "name": "run_date", - "type": "TIMESTAMP" - }, - { - "description": "The unique name for a test by the combination of key parameters, eg batch size, num of GPU, etc. It is hardware independent.", - "mode": "NULLABLE", - "name": "test_id", - "type": "STRING" - }, - { - "description": "The tensorflow version information.", - "fields": [ - { - "description": "Version of the tensorflow. E.g. 1.7.0-rc0", - "mode": "REQUIRED", - "name": "version", - "type": "STRING" - }, - { - "description": "Git Hash of the tensorflow", - "mode": "NULLABLE", - "name": "git_hash", - "type": "STRING" - }, - { - "description": "The channel of the tensorflow binary, eg, nightly, RC, final, custom.", - "mode": "NULLABLE", - "name": "channel", - "type": "STRING" - }, - { - "description": "Identify anything special about the build, eg CUDA 10, NCCL, MKL, etc.", - "mode": "NULLABLE", - "name": "build_type", - "type": "STRING" - } - ], - "mode": "REQUIRED", - "name": "tensorflow_version", - "type": "RECORD" - }, - { - "description": "The arbitrary attribute of the model.", - "fields": [ - { - "description": "The name of the attribute.", - "mode": "REQUIRED", - "name": "name", - "type": "STRING" - }, - { - "description": "The value of the attribute.", - "mode": "NULLABLE", - "name": "value", - "type": "STRING" - } - ], - "mode": "REPEATED", - "name": "attribute", - "type": "RECORD" - }, - { - "description": "Environment variables when the benchmark run is executed.", - "fields": [ - { - "description": "The name of the variable.", - "mode": "REQUIRED", - "name": "name", - "type": "STRING" - }, - { - "description": "The value of the variable.", - "mode": "NULLABLE", - "name": "value", - "type": "STRING" - } - ], - "mode": "REPEATED", - "name": "environment_variable", - "type": "RECORD" - }, - { - "description": "TF Environment variables when the benchmark run is executed.", - "fields": [ - { - "description": "The name of the variable.", - "mode": "REQUIRED", - "name": "name", - "type": "STRING" - }, - { - "description": "The value of the variable.", - "mode": "NULLABLE", - "name": "value", - "type": "STRING" - } - ], - "mode": "REPEATED", - "name": "tensorflow_environment_variables", - "type": "RECORD" - }, - { - "description": "The list of parameters run with the model. It could contain hyperparameters or others.", - "fields": [ - { - "description": "The name of the parameter.", - "mode": "REQUIRED", - "name": "name", - "type": "STRING" - }, - { - "description": "The string value of the parameter.", - "mode": "NULLABLE", - "name": "string_value", - "type": "STRING" - }, - { - "description": "The bool value of the parameter.", - "mode": "NULLABLE", - "name": "bool_value", - "type": "STRING" - }, - { - "description": "The int/long value of the parameter.", - "mode": "NULLABLE", - "name": "long_value", - "type": "INTEGER" - }, - { - "description": "The double/float value of parameter.", - "mode": "NULLABLE", - "name": "float_value", - "type": "FLOAT" - } - ], - "mode": "REPEATED", - "name": "run_parameters", - "type": "RECORD" - }, - { - "description": "The dataset that run with the benchmark.", - "mode": "NULLABLE", - "name": "dataset", - "type": "RECORD", - "fields": [ - { - "description": "The name of the dataset that the model is trained/validated with. E.g ImageNet, mnist.", - "mode": "REQUIRED", - "name": "name", - "type": "STRING" - }, - { - "description": "The arbitrary attribute of the dataset.", - "fields": [ - { - "description": "The name of the attribute.", - "mode": "REQUIRED", - "name": "name", - "type": "STRING" - }, - { - "description": "The value of the attribute.", - "mode": "NULLABLE", - "name": "value", - "type": "STRING" - } - ], - "mode": "REPEATED", - "name": "attribute", - "type": "RECORD" - } - ] - }, - { - "description": "Used to differentiate from AWS, GCE or DGX-1 at a high level", - "mode": "NULLABLE", - "name": "test_environment", - "type": "STRING" - }, - { - "description": "The machine configuration of the benchmark run.", - "mode": "NULLABLE", - "name": "machine_config", - "type": "RECORD", - "fields": [ - { - "description": "The platform information of the benchmark run.", - "mode": "NULLABLE", - "name": "platform_info", - "type": "RECORD", - "fields": [ - { - "description": "Eg: 64bit.", - "mode": "NULLABLE", - "name": "bits", - "type": "STRING" - }, - { - "description": "Eg: ELF.", - "mode": "NULLABLE", - "name": "linkage", - "type": "STRING" - }, - { - "description": "Eg: i386.", - "mode": "NULLABLE", - "name": "machine", - "type": "STRING" - }, - { - "description": "Eg: 3.13.0-76-generic.", - "mode": "NULLABLE", - "name": "release", - "type": "STRING" - }, - { - "description": "Eg: Linux.", - "mode": "NULLABLE", - "name": "system", - "type": "STRING" - }, - { - "description": "Eg: #120-Ubuntu SMP Mon Jan 18 15:59:10 UTC 2016.", - "mode": "NULLABLE", - "name": "version", - "type": "STRING" - } - ] - }, - { - "description": "The CPU information of the benchmark run.", - "mode": "NULLABLE", - "name": "cpu_info", - "type": "RECORD", - "fields": [ - { - "mode": "NULLABLE", - "name": "num_cores", - "type": "INTEGER" - }, - { - "mode": "NULLABLE", - "name": "num_cores_allowed", - "type": "INTEGER" - }, - { - "description" : "How fast are those CPUs.", - "mode": "NULLABLE", - "name": "mhz_per_cpu", - "type": "FLOAT" - }, - { - "description" : "Additional CPU info, Eg: Intel Ivybridge with HyperThreading (24 cores).", - "mode": "NULLABLE", - "name": "cpu_info", - "type": "STRING" - }, - { - "description" : "What kind of cpu scaling is enabled on the host. Eg performance, ondemand, conservative, mixed.", - "mode": "NULLABLE", - "name": "cpu_governor", - "type": "STRING" - }, - { - "description": "Cache size of the CPUs.", - "mode": "NULLABLE", - "name": "cache_size", - "type": "RECORD", - "fields": [ - { - "mode": "NULLABLE", - "name": "level", - "type": "STRING" - }, - { - "mode": "NULLABLE", - "name": "size", - "type": "INTEGER" - } - ] - } - ] - }, - { - "mode": "NULLABLE", - "name": "gpu_info", - "type": "RECORD", - "fields": [ - { - "mode": "NULLABLE", - "name": "count", - "type": "INTEGER" - }, - { - "mode": "NULLABLE", - "name": "model", - "type": "STRING" - }, - { - "mode": "NULLABLE", - "name": "cuda_version", - "type": "STRING" - } - ] - }, - { - "description": "The cloud instance inforation if the benchmark run is executed on cloud", - "mode": "NULLABLE", - "name": "cloud_info", - "type": "RECORD", - "fields": [ - { - "description": "The instance type, E.g. n1-standard-4.", - "mode": "NULLABLE", - "name": "instance_type", - "type": "STRING" - }, - { - "description": "The arbitrary attribute of the cloud info.", - "fields": [ - { - "description": "The name of the attribute.", - "mode": "REQUIRED", - "name": "name", - "type": "STRING" - }, - { - "description": "The value of the attribute.", - "mode": "NULLABLE", - "name": "value", - "type": "STRING" - } - ], - "mode": "REPEATED", - "name": "attribute", - "type": "RECORD" - } - ] - }, - { - "mode": "NULLABLE", - "name": "memory_total", - "type": "INTEGER" - }, - { - "mode": "NULLABLE", - "name": "memory_available", - "type": "STRING" - } - ] - } -] diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/datastore/schema/benchmark_run_status.json b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/datastore/schema/benchmark_run_status.json deleted file mode 100644 index f7ac59eb..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/datastore/schema/benchmark_run_status.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "description": "The UUID of the run for the benchmark.", - "mode": "REQUIRED", - "name": "run_id", - "type": "STRING" - }, - { - "description": "The status of the run for the benchmark. Eg, running, failed, success", - "mode": "REQUIRED", - "name": "status", - "type": "STRING" - } -] \ No newline at end of file diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/README.md b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/README.md deleted file mode 100644 index d8c8fcca..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/README.md +++ /dev/null @@ -1,112 +0,0 @@ -# Classifying Higgs boson processes in the HIGGS Data Set -## Overview -The [HIGGS Data Set](https://archive.ics.uci.edu/ml/datasets/HIGGS) contains 11 million samples with 28 features, and is for the classification problem to distinguish between a signal process which produces Higgs bosons and a background process which does not. - -We use Gradient Boosted Trees algorithm to distinguish the two classes. - ---- - -The code sample uses the high level `tf.estimator.Estimator` and `tf.data.Dataset`. These APIs are great for fast iteration and quickly adapting models to your own datasets without major code overhauls. It allows you to move from single-worker training to distributed training, and makes it easy to export model binaries for prediction. Here, for further simplicity and faster execution, we use a utility function `tf.contrib.estimator.boosted_trees_classifier_train_in_memory`. This utility function is especially effective when the input is provided as in-memory data sets like numpy arrays. - -An input function for the `Estimator` typically uses `tf.data.Dataset` API, which can handle various data control like streaming, batching, transform and shuffling. However `boosted_trees_classifier_train_in_memory()` utility function requires that the entire data is provided as a single batch (i.e. without using `batch()` API). Thus in this practice, simply `Dataset.from_tensors()` is used to convert numpy arrays into structured tensors, and `Dataset.zip()` is used to put features and label together. -For further references of `Dataset`, [Read more here](https://www.tensorflow.org/guide/datasets). - -## Running the code -First make sure you've [added the models folder to your Python path](/official/#running-the-models); otherwise you may encounter an error like `ImportError: No module named official.boosted_trees`. - -### Setup -The [HIGGS Data Set](https://archive.ics.uci.edu/ml/datasets/HIGGS) that this sample uses for training is hosted by the [UC Irvine Machine Learning Repository](https://archive.ics.uci.edu/ml/datasets/). We have provided a script that downloads and cleans the necessary files. - -``` -python data_download.py -``` - -This will download a file and store the processed file under the directory designated by `--data_dir` (defaults to `/tmp/higgs_data/`). To change the target directory, set the `--data_dir` flag. The directory could be network storages that Tensorflow supports (like Google Cloud Storage, `gs:////`). -The file downloaded to the local temporary folder is about 2.8 GB, and the processed file is about 0.8 GB, so there should be enough storage to handle them. - - -### Training - -This example uses about 3 GB of RAM during training. -You can run the code locally as follows: - -``` -python train_higgs.py -``` - -The model is by default saved to `/tmp/higgs_model`, which can be changed using the `--model_dir` flag. -Note that the model_dir is cleaned up before every time training starts. - -Model parameters can be adjusted by flags, like `--n_trees`, `--max_depth`, `--learning_rate` and so on. Check out the code for details. - -The final accuracy will be around 74% and loss will be around 0.516 over the eval set, when trained with the default parameters. - -By default, the first 1 million examples among 11 millions are used for training, and the last 1 million examples are used for evaluation. -The training/evaluation data can be selected as index ranges by flags `--train_start`, `--train_count`, `--eval_start`, `--eval_count`, etc. - -### TensorBoard - -Run TensorBoard to inspect the details about the graph and training progression. - -``` -tensorboard --logdir=/tmp/higgs_model # set logdir as --model_dir set during training. -``` - -## Inference with SavedModel -You can export the model into Tensorflow [SavedModel](https://www.tensorflow.org/guide/saved_model) format by using the argument `--export_dir`: - -``` -python train_higgs.py --export_dir /tmp/higgs_boosted_trees_saved_model -``` - -After the model finishes training, use [`saved_model_cli`](https://www.tensorflow.org/guide/saved_model#cli_to_inspect_and_execute_savedmodel) to inspect and execute the SavedModel. - -Try the following commands to inspect the SavedModel: - -**Replace `${TIMESTAMP}` with the folder produced (e.g. 1524249124)** -``` -# List possible tag_sets. Only one metagraph is saved, so there will be one option. -saved_model_cli show --dir /tmp/higgs_boosted_trees_saved_model/${TIMESTAMP}/ - -# Show SignatureDefs for tag_set=serve. SignatureDefs define the outputs to show. -saved_model_cli show --dir /tmp/higgs_boosted_trees_saved_model/${TIMESTAMP}/ \ - --tag_set serve --all -``` - -### Inference -Let's use the model to predict the income group of two examples. -Note that this model exports SavedModel with the custom parsing module that accepts csv lines as features. (Each line is an example with 28 columns; be careful to not add a label column, unlike in the training data.) - -``` -saved_model_cli run --dir /tmp/boosted_trees_higgs_saved_model/${TIMESTAMP}/ \ - --tag_set serve --signature_def="predict" \ - --input_exprs='inputs=["0.869293,-0.635082,0.225690,0.327470,-0.689993,0.754202,-0.248573,-1.092064,0.0,1.374992,-0.653674,0.930349,1.107436,1.138904,-1.578198,-1.046985,0.0,0.657930,-0.010455,-0.045767,3.101961,1.353760,0.979563,0.978076,0.920005,0.721657,0.988751,0.876678", "1.595839,-0.607811,0.007075,1.818450,-0.111906,0.847550,-0.566437,1.581239,2.173076,0.755421,0.643110,1.426367,0.0,0.921661,-1.190432,-1.615589,0.0,0.651114,-0.654227,-1.274345,3.101961,0.823761,0.938191,0.971758,0.789176,0.430553,0.961357,0.957818"]' -``` - -This will print out the predicted classes and class probabilities. Something like: - -``` -Result for output key class_ids: -[[1] - [0]] -Result for output key classes: -[['1'] - ['0']] -Result for output key logistic: -[[0.6440273 ] - [0.10902369]] -Result for output key logits: -[[ 0.59288704] - [-2.1007526 ]] -Result for output key probabilities: -[[0.3559727 0.6440273] - [0.8909763 0.1090237]] -``` - -Please note that "predict" signature_def gives out different (more detailed) results than "classification" or "serving_default". - -## Additional Links - -If you are interested in distributed training, take a look at [Distributed TensorFlow](https://www.tensorflow.org/deploy/distributed). - -You can also [train models on Cloud ML Engine](https://cloud.google.com/ml-engine/docs/getting-started-training-prediction), which provides [hyperparameter tuning](https://cloud.google.com/ml-engine/docs/getting-started-training-prediction#hyperparameter_tuning) to maximize your model's results and enables [deploying your model for prediction](https://cloud.google.com/ml-engine/docs/getting-started-training-prediction#deploy_a_model_to_support_prediction). diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/data_download.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/data_download.py deleted file mode 100644 index 1b6fc050..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/data_download.py +++ /dev/null @@ -1,97 +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. -# ============================================================================== -"""Downloads the UCI HIGGS Dataset and prepares train data. - -The details on the dataset are in https://archive.ics.uci.edu/ml/datasets/HIGGS - -It takes a while as it needs to download 2.8 GB over the network, process, then -store it into the specified location as a compressed numpy file. - -Usage: -$ python data_download.py --data_dir=/tmp/higgs_data -""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import gzip -import os -import tempfile - -# pylint: disable=g-bad-import-order -import numpy as np -import pandas as pd -from six.moves import urllib -from absl import app as absl_app -from absl import flags -import tensorflow as tf - -from official.utils.flags import core as flags_core - -URL_ROOT = "https://archive.ics.uci.edu/ml/machine-learning-databases/00280" -INPUT_FILE = "HIGGS.csv.gz" -NPZ_FILE = "HIGGS.csv.gz.npz" # numpy compressed file to contain "data" array. - - -def _download_higgs_data_and_save_npz(data_dir): - """Download higgs data and store as a numpy compressed file.""" - input_url = URL_ROOT + "/" + INPUT_FILE - np_filename = os.path.join(data_dir, NPZ_FILE) - if tf.gfile.Exists(np_filename): - raise ValueError("data_dir already has the processed data file: {}".format( - np_filename)) - if not tf.gfile.Exists(data_dir): - tf.gfile.MkDir(data_dir) - # 2.8 GB to download. - try: - tf.logging.info("Data downloading...") - temp_filename, _ = urllib.request.urlretrieve(input_url) - # Reading and parsing 11 million csv lines takes 2~3 minutes. - tf.logging.info("Data processing... taking multiple minutes...") - with gzip.open(temp_filename, "rb") as csv_file: - data = pd.read_csv( - csv_file, - dtype=np.float32, - names=["c%02d" % i for i in range(29)] # label + 28 features. - ).as_matrix() - finally: - tf.gfile.Remove(temp_filename) - - # Writing to temporary location then copy to the data_dir (0.8 GB). - f = tempfile.NamedTemporaryFile() - np.savez_compressed(f, data=data) - tf.gfile.Copy(f.name, np_filename) - tf.logging.info("Data saved to: {}".format(np_filename)) - - -def main(unused_argv): - if not tf.gfile.Exists(FLAGS.data_dir): - tf.gfile.MkDir(FLAGS.data_dir) - _download_higgs_data_and_save_npz(FLAGS.data_dir) - - -def define_data_download_flags(): - """Add flags specifying data download arguments.""" - flags.DEFINE_string( - name="data_dir", default="/tmp/higgs_data", - help=flags_core.help_wrap( - "Directory to download higgs dataset and store training/eval data.")) - - -if __name__ == "__main__": - tf.logging.set_verbosity(tf.logging.INFO) - define_data_download_flags() - FLAGS = flags.FLAGS - absl_app.run(main) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/train_higgs.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/train_higgs.py deleted file mode 100644 index 56e54316..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/train_higgs.py +++ /dev/null @@ -1,296 +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. -# ============================================================================== -r"""A script that builds boosted trees over higgs data. - -If you haven't, please run data_download.py beforehand to prepare the data. - -For some more details on this example, please refer to README.md as well. - -Note that the model_dir is cleaned up before starting the training. - -Usage: -$ python train_higgs.py --n_trees=100 --max_depth=6 --learning_rate=0.1 \ - --model_dir=/tmp/higgs_model - -Note that BoostedTreesClassifier is available since Tensorflow 1.8.0. -So you need to install recent enough version of Tensorflow to use this example. - -The training data is by default the first million examples out of 11M examples, -and eval data is by default the last million examples. -They are controlled by --train_start, --train_count, --eval_start, --eval_count. -e.g. to train over the first 10 million examples instead of 1 million: -$ python train_higgs.py --n_trees=100 --max_depth=6 --learning_rate=0.1 \ - --model_dir=/tmp/higgs_model --train_count=10000000 - -Training history and metrics can be inspected using tensorboard. -Set --logdir as the --model_dir set by flag when training -(or the default /tmp/higgs_model). -$ tensorboard --logdir=/tmp/higgs_model -""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os - -# pylint: disable=g-bad-import-order -import numpy as np -from absl import app as absl_app -from absl import flags -import tensorflow as tf -# pylint: enable=g-bad-import-order - -from official.utils.flags import core as flags_core -from official.utils.flags._conventions import help_wrap -from official.utils.logs import logger - -NPZ_FILE = "HIGGS.csv.gz.npz" # numpy compressed file containing "data" array - - -def read_higgs_data(data_dir, train_start, train_count, eval_start, eval_count): - """Reads higgs data from csv and returns train and eval data. - - Args: - data_dir: A string, the directory of higgs dataset. - train_start: An integer, the start index of train examples within the data. - train_count: An integer, the number of train examples within the data. - eval_start: An integer, the start index of eval examples within the data. - eval_count: An integer, the number of eval examples within the data. - - Returns: - Numpy array of train data and eval data. - """ - npz_filename = os.path.join(data_dir, NPZ_FILE) - try: - # gfile allows numpy to read data from network data sources as well. - with tf.gfile.Open(npz_filename, "rb") as npz_file: - with np.load(npz_file) as npz: - data = npz["data"] - except tf.errors.NotFoundError as e: - raise RuntimeError( - "Error loading data; use data_download.py to prepare the data.\n{}: {}" - .format(type(e).__name__, e)) - return (data[train_start:train_start+train_count], - data[eval_start:eval_start+eval_count]) - - -# This showcases how to make input_fn when the input data is available in the -# form of numpy arrays. -def make_inputs_from_np_arrays(features_np, label_np): - """Makes and returns input_fn and feature_columns from numpy arrays. - - The generated input_fn will return tf.data.Dataset of feature dictionary and a - label, and feature_columns will consist of the list of - tf.feature_column.BucketizedColumn. - - Note, for in-memory training, tf.data.Dataset should contain the whole data - as a single tensor. Don't use batch. - - Args: - features_np: A numpy ndarray (shape=[batch_size, num_features]) for - float32 features. - label_np: A numpy ndarray (shape=[batch_size, 1]) for labels. - - Returns: - input_fn: A function returning a Dataset of feature dict and label. - feature_names: A list of feature names. - feature_column: A list of tf.feature_column.BucketizedColumn. - """ - num_features = features_np.shape[1] - features_np_list = np.split(features_np, num_features, axis=1) - # 1-based feature names. - feature_names = ["feature_%02d" % (i + 1) for i in range(num_features)] - - # Create source feature_columns and bucketized_columns. - def get_bucket_boundaries(feature): - """Returns bucket boundaries for feature by percentiles.""" - return np.unique(np.percentile(feature, range(0, 100))).tolist() - source_columns = [ - tf.feature_column.numeric_column( - feature_name, dtype=tf.float32, - # Although higgs data have no missing values, in general, default - # could be set as 0 or some reasonable value for missing values. - default_value=0.0) - for feature_name in feature_names - ] - bucketized_columns = [ - tf.feature_column.bucketized_column( - source_columns[i], - boundaries=get_bucket_boundaries(features_np_list[i])) - for i in range(num_features) - ] - - # Make an input_fn that extracts source features. - def input_fn(): - """Returns features as a dictionary of numpy arrays, and a label.""" - features = { - feature_name: tf.constant(features_np_list[i]) - for i, feature_name in enumerate(feature_names) - } - return tf.data.Dataset.zip((tf.data.Dataset.from_tensors(features), - tf.data.Dataset.from_tensors(label_np),)) - - return input_fn, feature_names, bucketized_columns - - -def make_eval_inputs_from_np_arrays(features_np, label_np): - """Makes eval input as streaming batches.""" - num_features = features_np.shape[1] - features_np_list = np.split(features_np, num_features, axis=1) - # 1-based feature names. - feature_names = ["feature_%02d" % (i + 1) for i in range(num_features)] - - def input_fn(): - features = { - feature_name: tf.constant(features_np_list[i]) - for i, feature_name in enumerate(feature_names) - } - return tf.data.Dataset.zip(( - tf.data.Dataset.from_tensor_slices(features), - tf.data.Dataset.from_tensor_slices(label_np),)).batch(1000) - - return input_fn - - -def _make_csv_serving_input_receiver_fn(column_names, column_defaults): - """Returns serving_input_receiver_fn for csv. - - The input arguments are relevant to `tf.decode_csv()`. - - Args: - column_names: a list of column names in the order within input csv. - column_defaults: a list of default values with the same size of - column_names. Each entity must be either a list of one scalar, or an - empty list to denote the corresponding column is required. - e.g. [[""], [2.5], []] indicates the third column is required while - the first column must be string and the second must be float/double. - - Returns: - a serving_input_receiver_fn that handles csv for serving. - """ - def serving_input_receiver_fn(): - csv = tf.placeholder(dtype=tf.string, shape=[None], name="csv") - features = dict(zip(column_names, tf.decode_csv(csv, column_defaults))) - receiver_tensors = {"inputs": csv} - return tf.estimator.export.ServingInputReceiver(features, receiver_tensors) - - return serving_input_receiver_fn - - -def train_boosted_trees(flags_obj): - """Train boosted_trees estimator on HIGGS data. - - Args: - flags_obj: An object containing parsed flag values. - """ - # Clean up the model directory if present. - if tf.gfile.Exists(flags_obj.model_dir): - tf.gfile.DeleteRecursively(flags_obj.model_dir) - tf.logging.info("## Data loading...") - train_data, eval_data = read_higgs_data( - flags_obj.data_dir, flags_obj.train_start, flags_obj.train_count, - flags_obj.eval_start, flags_obj.eval_count) - tf.logging.info("## Data loaded; train: {}{}, eval: {}{}".format( - train_data.dtype, train_data.shape, eval_data.dtype, eval_data.shape)) - # Data consists of one label column followed by 28 feature columns. - train_input_fn, feature_names, feature_columns = make_inputs_from_np_arrays( - features_np=train_data[:, 1:], label_np=train_data[:, 0:1]) - eval_input_fn = make_eval_inputs_from_np_arrays( - features_np=eval_data[:, 1:], label_np=eval_data[:, 0:1]) - tf.logging.info("## Features prepared. Training starts...") - - # Create benchmark logger to log info about the training and metric values - run_params = { - "train_start": flags_obj.train_start, - "train_count": flags_obj.train_count, - "eval_start": flags_obj.eval_start, - "eval_count": flags_obj.eval_count, - "n_trees": flags_obj.n_trees, - "max_depth": flags_obj.max_depth, - } - benchmark_logger = logger.config_benchmark_logger(flags_obj) - benchmark_logger.log_run_info( - model_name="boosted_trees", - dataset_name="higgs", - run_params=run_params, - test_id=flags_obj.benchmark_test_id) - - # Though BoostedTreesClassifier is under tf.estimator, faster in-memory - # training is yet provided as a contrib library. - classifier = tf.contrib.estimator.boosted_trees_classifier_train_in_memory( - train_input_fn, - feature_columns, - model_dir=flags_obj.model_dir or None, - n_trees=flags_obj.n_trees, - max_depth=flags_obj.max_depth, - learning_rate=flags_obj.learning_rate) - - # Evaluation. - eval_results = classifier.evaluate(eval_input_fn) - # Benchmark the evaluation results - benchmark_logger.log_evaluation_result(eval_results) - - # Exporting the savedmodel with csv parsing. - if flags_obj.export_dir is not None: - classifier.export_savedmodel( - flags_obj.export_dir, - _make_csv_serving_input_receiver_fn( - column_names=feature_names, - # columns are all floats. - column_defaults=[[0.0]] * len(feature_names)), - strip_default_attrs=True) - - -def main(_): - train_boosted_trees(flags.FLAGS) - - -def define_train_higgs_flags(): - """Add tree related flags as well as training/eval configuration.""" - flags_core.define_base(clean=False, stop_threshold=False, batch_size=False, - num_gpu=False) - flags_core.define_benchmark() - flags.adopt_module_key_flags(flags_core) - - flags.DEFINE_integer( - name="train_start", default=0, - help=help_wrap("Start index of train examples within the data.")) - flags.DEFINE_integer( - name="train_count", default=1000000, - help=help_wrap("Number of train examples within the data.")) - flags.DEFINE_integer( - name="eval_start", default=10000000, - help=help_wrap("Start index of eval examples within the data.")) - flags.DEFINE_integer( - name="eval_count", default=1000000, - help=help_wrap("Number of eval examples within the data.")) - - flags.DEFINE_integer( - "n_trees", default=100, help=help_wrap("Number of trees to build.")) - flags.DEFINE_integer( - "max_depth", default=6, help=help_wrap("Maximum depths of each tree.")) - flags.DEFINE_float( - "learning_rate", default=0.1, - help=help_wrap("The learning rate.")) - - flags_core.set_defaults(data_dir="/tmp/higgs_data", - model_dir="/tmp/higgs_model") - - -if __name__ == "__main__": - # Training progress and eval results are shown as logging.INFO; so enables it. - tf.logging.set_verbosity(tf.logging.INFO) - define_train_higgs_flags() - absl_app.run(main) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/train_higgs_test.csv b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/train_higgs_test.csv deleted file mode 100644 index 5cb03393..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/train_higgs_test.csv +++ /dev/null @@ -1,20 +0,0 @@ -1.000000000000000000e+00,8.692932128906250000e-01,-6.350818276405334473e-01,2.256902605295181274e-01,3.274700641632080078e-01,-6.899932026863098145e-01,7.542022466659545898e-01,-2.485731393098831177e-01,-1.092063903808593750e+00,0.000000000000000000e+00,1.374992132186889648e+00,-6.536741852760314941e-01,9.303491115570068359e-01,1.107436060905456543e+00,1.138904333114624023e+00,-1.578198313713073730e+00,-1.046985387802124023e+00,0.000000000000000000e+00,6.579295396804809570e-01,-1.045456994324922562e-02,-4.576716944575309753e-02,3.101961374282836914e+00,1.353760004043579102e+00,9.795631170272827148e-01,9.780761599540710449e-01,9.200048446655273438e-01,7.216574549674987793e-01,9.887509346008300781e-01,8.766783475875854492e-01 -1.000000000000000000e+00,9.075421094894409180e-01,3.291472792625427246e-01,3.594118654727935791e-01,1.497969865798950195e+00,-3.130095303058624268e-01,1.095530629158020020e+00,-5.575249195098876953e-01,-1.588229775428771973e+00,2.173076152801513672e+00,8.125811815261840820e-01,-2.136419266462326050e-01,1.271014571189880371e+00,2.214872121810913086e+00,4.999939501285552979e-01,-1.261431813240051270e+00,7.321561574935913086e-01,0.000000000000000000e+00,3.987008929252624512e-01,-1.138930082321166992e+00,-8.191101951524615288e-04,0.000000000000000000e+00,3.022198975086212158e-01,8.330481648445129395e-01,9.856996536254882812e-01,9.780983924865722656e-01,7.797321677207946777e-01,9.923557639122009277e-01,7.983425855636596680e-01 -1.000000000000000000e+00,7.988347411155700684e-01,1.470638751983642578e+00,-1.635974764823913574e+00,4.537731707096099854e-01,4.256291687488555908e-01,1.104874610900878906e+00,1.282322287559509277e+00,1.381664276123046875e+00,0.000000000000000000e+00,8.517372012138366699e-01,1.540658950805664062e+00,-8.196895122528076172e-01,2.214872121810913086e+00,9.934899210929870605e-01,3.560801148414611816e-01,-2.087775468826293945e-01,2.548224449157714844e+00,1.256954550743103027e+00,1.128847599029541016e+00,9.004608392715454102e-01,0.000000000000000000e+00,9.097532629966735840e-01,1.108330488204956055e+00,9.856922030448913574e-01,9.513312578201293945e-01,8.032515048980712891e-01,8.659244179725646973e-01,7.801175713539123535e-01 -0.000000000000000000e+00,1.344384789466857910e+00,-8.766260147094726562e-01,9.359127283096313477e-01,1.992050051689147949e+00,8.824543952941894531e-01,1.786065936088562012e+00,-1.646777749061584473e+00,-9.423825144767761230e-01,0.000000000000000000e+00,2.423264741897583008e+00,-6.760157942771911621e-01,7.361586689949035645e-01,2.214872121810913086e+00,1.298719763755798340e+00,-1.430738091468811035e+00,-3.646581768989562988e-01,0.000000000000000000e+00,7.453126907348632812e-01,-6.783788204193115234e-01,-1.360356330871582031e+00,0.000000000000000000e+00,9.466524720191955566e-01,1.028703689575195312e+00,9.986560940742492676e-01,7.282806038856506348e-01,8.692002296447753906e-01,1.026736497879028320e+00,9.579039812088012695e-01 -1.000000000000000000e+00,1.105008959770202637e+00,3.213555514812469482e-01,1.522401213645935059e+00,8.828076124191284180e-01,-1.205349326133728027e+00,6.814661026000976562e-01,-1.070463895797729492e+00,-9.218706488609313965e-01,0.000000000000000000e+00,8.008721470832824707e-01,1.020974040031433105e+00,9.714065194129943848e-01,2.214872121810913086e+00,5.967612862586975098e-01,-3.502728641033172607e-01,6.311942934989929199e-01,0.000000000000000000e+00,4.799988865852355957e-01,-3.735655248165130615e-01,1.130406111478805542e-01,0.000000000000000000e+00,7.558564543724060059e-01,1.361057043075561523e+00,9.866096973419189453e-01,8.380846381187438965e-01,1.133295178413391113e+00,8.722448945045471191e-01,8.084865212440490723e-01 -0.000000000000000000e+00,1.595839262008666992e+00,-6.078106760978698730e-01,7.074915803968906403e-03,1.818449616432189941e+00,-1.119059920310974121e-01,8.475499153137207031e-01,-5.664370059967041016e-01,1.581239342689514160e+00,2.173076152801513672e+00,7.554209828376770020e-01,6.431096196174621582e-01,1.426366806030273438e+00,0.000000000000000000e+00,9.216607809066772461e-01,-1.190432429313659668e+00,-1.615589022636413574e+00,0.000000000000000000e+00,6.511141061782836914e-01,-6.542269587516784668e-01,-1.274344921112060547e+00,3.101961374282836914e+00,8.237605690956115723e-01,9.381914138793945312e-01,9.717581868171691895e-01,7.891763448715209961e-01,4.305532872676849365e-01,9.613569378852844238e-01,9.578179121017456055e-01 -1.000000000000000000e+00,4.093913435935974121e-01,-1.884683609008789062e+00,-1.027292013168334961e+00,1.672451734542846680e+00,-1.604598283767700195e+00,1.338014960289001465e+00,5.542744323611259460e-02,1.346588134765625000e-02,2.173076152801513672e+00,5.097832679748535156e-01,-1.038338065147399902e+00,7.078623175621032715e-01,0.000000000000000000e+00,7.469175457954406738e-01,-3.584651052951812744e-01,-1.646654248237609863e+00,0.000000000000000000e+00,3.670579791069030762e-01,6.949646025896072388e-02,1.377130270004272461e+00,3.101961374282836914e+00,8.694183826446533203e-01,1.222082972526550293e+00,1.000627398490905762e+00,5.450449585914611816e-01,6.986525058746337891e-01,9.773144721984863281e-01,8.287860751152038574e-01 -1.000000000000000000e+00,9.338953495025634766e-01,6.291297078132629395e-01,5.275348424911499023e-01,2.380327433347702026e-01,-9.665691256523132324e-01,5.478111505508422852e-01,-5.943922698497772217e-02,-1.706866145133972168e+00,2.173076152801513672e+00,9.410027265548706055e-01,-2.653732776641845703e+00,-1.572199910879135132e-01,0.000000000000000000e+00,1.030370354652404785e+00,-1.755051016807556152e-01,5.230209231376647949e-01,2.548224449157714844e+00,1.373546600341796875e+00,1.291248083114624023e+00,-1.467454433441162109e+00,0.000000000000000000e+00,9.018372893333435059e-01,1.083671212196350098e+00,9.796960949897766113e-01,7.833003997802734375e-01,8.491951823234558105e-01,8.943563103675842285e-01,7.748793959617614746e-01 -1.000000000000000000e+00,1.405143737792968750e+00,5.366026163101196289e-01,6.895543336868286133e-01,1.179567337036132812e+00,-1.100611537694931030e-01,3.202404975891113281e+00,-1.526960015296936035e+00,-1.576033473014831543e+00,0.000000000000000000e+00,2.931536912918090820e+00,5.673424601554870605e-01,-1.300333440303802490e-01,2.214872121810913086e+00,1.787122726440429688e+00,8.994985818862915039e-01,5.851513147354125977e-01,2.548224449157714844e+00,4.018652141094207764e-01,-1.512016952037811279e-01,1.163489103317260742e+00,0.000000000000000000e+00,1.667070508003234863e+00,4.039272785186767578e+00,1.175828456878662109e+00,1.045351743698120117e+00,1.542971968650817871e+00,3.534826755523681641e+00,2.740753889083862305e+00 -1.000000000000000000e+00,1.176565527915954590e+00,1.041605025529861450e-01,1.397002458572387695e+00,4.797213077545166016e-01,2.655133903026580811e-01,1.135563015937805176e+00,1.534830927848815918e+00,-2.532912194728851318e-01,0.000000000000000000e+00,1.027246594429016113e+00,5.343157649040222168e-01,1.180022358894348145e+00,0.000000000000000000e+00,2.405661106109619141e+00,8.755676448345184326e-02,-9.765340685844421387e-01,2.548224449157714844e+00,1.250382542610168457e+00,2.685412168502807617e-01,5.303344726562500000e-01,0.000000000000000000e+00,8.331748843193054199e-01,7.739681005477905273e-01,9.857499599456787109e-01,1.103696346282958984e+00,8.491398692131042480e-01,9.371039867401123047e-01,8.123638033866882324e-01 -1.000000000000000000e+00,9.459739923477172852e-01,1.111244320869445801e+00,1.218337059020996094e+00,9.076390862464904785e-01,8.215369582176208496e-01,1.153243303298950195e+00,-3.654202818870544434e-01,-1.566054821014404297e+00,0.000000000000000000e+00,7.447192072868347168e-01,7.208195328712463379e-01,-3.758229315280914307e-01,2.214872121810913086e+00,6.088791489601135254e-01,3.078369498252868652e-01,-1.281638383865356445e+00,0.000000000000000000e+00,1.597967982292175293e+00,-4.510180354118347168e-01,6.365344673395156860e-02,3.101961374282836914e+00,8.290241360664367676e-01,9.806482791900634766e-01,9.943597912788391113e-01,9.082478284835815430e-01,7.758789062500000000e-01,7.833113670349121094e-01,7.251217961311340332e-01 -0.000000000000000000e+00,7.393567562103271484e-01,-1.782904267311096191e-01,8.299342393875122070e-01,5.045390725135803223e-01,-1.302167475223541260e-01,9.610513448715209961e-01,-3.555179834365844727e-01,-1.717399358749389648e+00,2.173076152801513672e+00,6.209560632705688477e-01,-4.817410409450531006e-01,-1.199193239212036133e+00,0.000000000000000000e+00,9.826014041900634766e-01,8.118502795696258545e-02,-2.903236448764801025e-01,0.000000000000000000e+00,1.064662933349609375e+00,7.740649580955505371e-01,3.988203406333923340e-01,3.101961374282836914e+00,9.445360302925109863e-01,1.026260614395141602e+00,9.821967482566833496e-01,5.421146750450134277e-01,1.250978946685791016e+00,8.300446271896362305e-01,7.613079547882080078e-01 -1.000000000000000000e+00,1.384097695350646973e+00,1.168220937252044678e-01,-1.179878950119018555e+00,7.629125714302062988e-01,-7.978226989507675171e-02,1.019863128662109375e+00,8.773182630538940430e-01,1.276887178421020508e+00,2.173076152801513672e+00,3.312520980834960938e-01,1.409523487091064453e+00,-1.474388837814331055e+00,0.000000000000000000e+00,1.282738208770751953e+00,7.374743819236755371e-01,-2.254196107387542725e-01,0.000000000000000000e+00,1.559753060340881348e+00,8.465205430984497070e-01,5.048085451126098633e-01,3.101961374282836914e+00,9.593246579170227051e-01,8.073760271072387695e-01,1.191813588142395020e+00,1.221210360527038574e+00,8.611412644386291504e-01,9.293408989906311035e-01,8.383023738861083984e-01 -1.000000000000000000e+00,1.383548736572265625e+00,8.891792893409729004e-01,6.185320615768432617e-01,1.081547021865844727e+00,3.446055650711059570e-01,9.563793540000915527e-01,8.545429706573486328e-01,-1.129207015037536621e+00,2.173076152801513672e+00,5.456657409667968750e-01,-3.078651726245880127e-01,-6.232798099517822266e-01,2.214872121810913086e+00,3.482571244239807129e-01,1.024202585220336914e+00,1.840776652097702026e-01,0.000000000000000000e+00,7.813369035720825195e-01,-1.636125564575195312e+00,1.144067287445068359e+00,0.000000000000000000e+00,5.222384929656982422e-01,7.376385331153869629e-01,9.861995577812194824e-01,1.349615693092346191e+00,8.127878904342651367e-01,9.534064531326293945e-01,7.797226309776306152e-01 -1.000000000000000000e+00,1.343652725219726562e+00,8.385329246520996094e-01,-1.061138510704040527e+00,2.472015142440795898e+00,-5.726317167282104492e-01,1.512709975242614746e+00,1.143690109252929688e+00,8.555619716644287109e-01,0.000000000000000000e+00,8.842203021049499512e-01,1.474605560302734375e+00,-1.360648751258850098e+00,1.107436060905456543e+00,1.587265610694885254e+00,2.234833478927612305e+00,7.756848633289337158e-02,0.000000000000000000e+00,1.609408140182495117e+00,2.396404743194580078e+00,7.572935223579406738e-01,0.000000000000000000e+00,9.340201020240783691e-01,8.447072505950927734e-01,1.077844023704528809e+00,1.400183677673339844e+00,9.477745294570922852e-01,1.007614254951477051e+00,9.010174870491027832e-01 -0.000000000000000000e+00,5.470141768455505371e-01,-3.497089445590972900e-01,-6.466571688652038574e-01,2.040462255477905273e+00,2.764569818973541260e-01,5.446965098381042480e-01,8.386992812156677246e-01,1.728703141212463379e+00,0.000000000000000000e+00,6.528096199035644531e-01,1.471691370010375977e+00,1.243273019790649414e+00,0.000000000000000000e+00,7.857298851013183594e-01,-4.442929103970527649e-02,-1.019803404808044434e+00,2.548224449157714844e+00,4.191471040248870850e-01,-6.292421817779541016e-01,1.570794582366943359e+00,3.101961374282836914e+00,6.894335746765136719e-01,8.672295808792114258e-01,1.082487821578979492e+00,6.641419529914855957e-01,3.541145622730255127e-01,5.799450278282165527e-01,8.172734379768371582e-01 -1.000000000000000000e+00,1.484203696250915527e+00,1.699521422386169434e+00,-1.059473991394042969e+00,2.700195550918579102e+00,-1.055963873863220215e+00,2.409452915191650391e+00,4.574607908725738525e-01,3.449823260307312012e-01,0.000000000000000000e+00,1.414903521537780762e+00,1.114225864410400391e+00,-1.448866605758666992e+00,0.000000000000000000e+00,1.012983918190002441e+00,-2.056988954544067383e+00,1.131010890007019043e+00,0.000000000000000000e+00,9.054746031761169434e-01,2.182368993759155273e+00,1.043073177337646484e+00,0.000000000000000000e+00,1.653626322746276855e+00,9.935762286186218262e-01,9.833217859268188477e-01,7.413797974586486816e-01,1.633816361427307129e-01,5.923243165016174316e-01,7.451378703117370605e-01 -0.000000000000000000e+00,1.057975649833679199e+00,-1.607590019702911377e-01,-1.949972510337829590e-01,2.705023050308227539e+00,-7.514767050743103027e-01,1.909918904304504395e+00,-1.031844973564147949e+00,8.649863600730895996e-01,0.000000000000000000e+00,1.300834894180297852e+00,1.467376798391342163e-01,-1.118742942810058594e+00,1.107436060905456543e+00,9.669710993766784668e-01,-3.666573464870452881e-01,1.108266711235046387e+00,0.000000000000000000e+00,5.547249317169189453e-01,-7.141901850700378418e-01,1.505314946174621582e+00,3.101961374282836914e+00,9.544943571090698242e-01,6.510385870933532715e-01,1.124949693679809570e+00,8.940010070800781250e-01,6.721734404563903809e-01,1.182358264923095703e+00,1.316304087638854980e+00 -0.000000000000000000e+00,6.753035783767700195e-01,1.120983958244323730e+00,-2.804459035396575928e-01,1.539554953575134277e+00,7.345175743103027344e-01,6.146844029426574707e-01,-5.070231556892395020e-01,7.945806980133056641e-01,2.173076152801513672e+00,2.188202738761901855e-01,-1.894118309020996094e+00,-5.805578827857971191e-01,0.000000000000000000e+00,1.245682120323181152e+00,-3.475421071052551270e-01,-8.561564683914184570e-01,2.548224449157714844e+00,7.531017661094665527e-01,-1.145592689514160156e+00,-1.374783992767333984e+00,0.000000000000000000e+00,9.069401025772094727e-01,8.983390927314758301e-01,1.119651079177856445e+00,1.269073486328125000e+00,1.088765859603881836e+00,1.015413045883178711e+00,9.146358966827392578e-01 -1.000000000000000000e+00,6.427279114723205566e-01,-1.429840326309204102e+00,1.519071936607360840e+00,9.409985542297363281e-01,8.872274160385131836e-01,1.615126848220825195e+00,-1.336835741996765137e+00,-2.665962278842926025e-01,1.086538076400756836e+00,1.667088270187377930e+00,6.557375192642211914e-01,-1.588128924369812012e+00,0.000000000000000000e+00,8.282302021980285645e-01,1.836144566535949707e+00,4.081907570362091064e-01,0.000000000000000000e+00,1.708718180656433105e+00,-3.469151556491851807e-01,-1.182784557342529297e+00,3.101961374282836914e+00,9.210902452468872070e-01,1.373361706733703613e+00,9.849172830581665039e-01,1.422878146171569824e+00,1.546551108360290527e+00,1.782585501670837402e+00,1.438173770904541016e+00 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/train_higgs_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/train_higgs_test.py deleted file mode 100644 index 03b7e200..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/train_higgs_test.py +++ /dev/null @@ -1,155 +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. -# ============================================================================== -"""Tests for boosted_tree.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os -import tempfile - -import numpy as np -import pandas as pd -import tensorflow as tf - -# pylint: disable=g-bad-import-order -from official.boosted_trees import train_higgs -from official.utils.testing import integration - -TEST_CSV = os.path.join(os.path.dirname(__file__), "train_higgs_test.csv") - -tf.logging.set_verbosity(tf.logging.ERROR) - - -class BaseTest(tf.test.TestCase): - """Tests for Wide Deep model.""" - - @classmethod - def setUpClass(cls): # pylint: disable=invalid-name - super(BaseTest, cls).setUpClass() - train_higgs.define_train_higgs_flags() - - def setUp(self): - # Create temporary CSV file - self.data_dir = self.get_temp_dir() - data = pd.read_csv( - TEST_CSV, dtype=np.float32, names=["c%02d" % i for i in range(29)] - ).as_matrix() - self.input_npz = os.path.join(self.data_dir, train_higgs.NPZ_FILE) - # numpy.savez doesn't take gfile.Gfile, so need to write down and copy. - tmpfile = tempfile.NamedTemporaryFile() - np.savez_compressed(tmpfile, data=data) - tf.gfile.Copy(tmpfile.name, self.input_npz) - - def test_read_higgs_data(self): - """Tests read_higgs_data() function.""" - # Error when a wrong data_dir is given. - with self.assertRaisesRegexp(RuntimeError, "Error loading data.*"): - train_data, eval_data = train_higgs.read_higgs_data( - self.data_dir + "non-existing-path", - train_start=0, train_count=15, eval_start=15, eval_count=5) - - # Loading fine with the correct data_dir. - train_data, eval_data = train_higgs.read_higgs_data( - self.data_dir, - train_start=0, train_count=15, eval_start=15, eval_count=5) - self.assertEqual((15, 29), train_data.shape) - self.assertEqual((5, 29), eval_data.shape) - - def test_make_inputs_from_np_arrays(self): - """Tests make_inputs_from_np_arrays() function.""" - train_data, _ = train_higgs.read_higgs_data( - self.data_dir, - train_start=0, train_count=15, eval_start=15, eval_count=5) - (input_fn, feature_names, - feature_columns) = train_higgs.make_inputs_from_np_arrays( - features_np=train_data[:, 1:], label_np=train_data[:, 0:1]) - - # Check feature_names. - self.assertAllEqual(feature_names, - ["feature_%02d" % (i+1) for i in range(28)]) - - # Check feature columns. - self.assertEqual(28, len(feature_columns)) - bucketized_column_type = type( - tf.feature_column.bucketized_column( - tf.feature_column.numeric_column("feature_01"), - boundaries=[0, 1, 2])) # dummy boundaries. - for feature_column in feature_columns: - self.assertIsInstance(feature_column, bucketized_column_type) - # At least 2 boundaries. - self.assertGreaterEqual(len(feature_column.boundaries), 2) - # Tests that the source column names of the bucketized columns match. - self.assertAllEqual(feature_names, - [col.source_column.name for col in feature_columns]) - - # Check features. - features, labels = input_fn().make_one_shot_iterator().get_next() - with tf.Session() as sess: - features, labels = sess.run((features, labels)) - self.assertIsInstance(features, dict) - self.assertAllEqual(feature_names, sorted(features.keys())) - self.assertAllEqual([[15, 1]] * 28, - [features[name].shape for name in feature_names]) - # Validate actual values of some features. - self.assertAllClose( - [0.869293, 0.907542, 0.798834, 1.344384, 1.105009, 1.595839, - 0.409391, 0.933895, 1.405143, 1.176565, 0.945974, 0.739356, - 1.384097, 1.383548, 1.343652], - np.squeeze(features[feature_names[0]], 1)) - self.assertAllClose( - [-0.653674, -0.213641, 1.540659, -0.676015, 1.020974, 0.643109, - -1.038338, -2.653732, 0.567342, 0.534315, 0.720819, -0.481741, - 1.409523, -0.307865, 1.474605], - np.squeeze(features[feature_names[10]], 1)) - - def test_end_to_end(self): - """Tests end-to-end running.""" - model_dir = os.path.join(self.get_temp_dir(), "model") - integration.run_synthetic( - main=train_higgs.main, tmp_root=self.get_temp_dir(), extra_flags=[ - "--data_dir", self.data_dir, - "--model_dir", model_dir, - "--n_trees", "5", - "--train_start", "0", - "--train_count", "12", - "--eval_start", "12", - "--eval_count", "8", - ], - synth=False, max_train=None) - self.assertTrue(tf.gfile.Exists(os.path.join(model_dir, "checkpoint"))) - - def test_end_to_end_with_export(self): - """Tests end-to-end running.""" - model_dir = os.path.join(self.get_temp_dir(), "model") - export_dir = os.path.join(self.get_temp_dir(), "export") - integration.run_synthetic( - main=train_higgs.main, tmp_root=self.get_temp_dir(), extra_flags=[ - "--data_dir", self.data_dir, - "--model_dir", model_dir, - "--export_dir", export_dir, - "--n_trees", "5", - "--train_start", "0", - "--train_count", "12", - "--eval_start", "12", - "--eval_count", "8", - ], - synth=False, max_train=None) - self.assertTrue(tf.gfile.Exists(os.path.join(model_dir, "checkpoint"))) - self.assertTrue(tf.gfile.Exists(os.path.join(export_dir))) - - -if __name__ == "__main__": - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/datasets/movielens.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/datasets/movielens.py deleted file mode 100644 index 23b684e3..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/datasets/movielens.py +++ /dev/null @@ -1,308 +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. -# ============================================================================== -"""Download and extract the MovieLens dataset from GroupLens website. - -Download the dataset, and perform basic preprocessing. -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os -import sys -import tempfile -import zipfile - -# pylint: disable=g-bad-import-order -import numpy as np -import pandas as pd -import six -from six.moves import urllib # pylint: disable=redefined-builtin -from absl import app as absl_app -from absl import flags -import tensorflow as tf -# pylint: enable=g-bad-import-order - -from official.utils.flags import core as flags_core - - -ML_1M = "ml-1m" -ML_20M = "ml-20m" -DATASETS = [ML_1M, ML_20M] - -RATINGS_FILE = "ratings.csv" -MOVIES_FILE = "movies.csv" - -# URL to download dataset -_DATA_URL = "http://files.grouplens.org/datasets/movielens/" - -GENRE_COLUMN = "genres" -ITEM_COLUMN = "item_id" # movies -RATING_COLUMN = "rating" -TIMESTAMP_COLUMN = "timestamp" -TITLE_COLUMN = "titles" -USER_COLUMN = "user_id" - -GENRES = [ - 'Action', 'Adventure', 'Animation', "Children", 'Comedy', 'Crime', - 'Documentary', 'Drama', 'Fantasy', 'Film-Noir', 'Horror', "IMAX", 'Musical', - 'Mystery', 'Romance', 'Sci-Fi', 'Thriller', 'War', 'Western' -] -N_GENRE = len(GENRES) - -RATING_COLUMNS = [USER_COLUMN, ITEM_COLUMN, RATING_COLUMN, TIMESTAMP_COLUMN] -MOVIE_COLUMNS = [ITEM_COLUMN, TITLE_COLUMN, GENRE_COLUMN] - -# Note: Users are indexed [1, k], not [0, k-1] -NUM_USER_IDS = { - ML_1M: 6040, - ML_20M: 138493, -} - -# Note: Movies are indexed [1, k], not [0, k-1] -# Both the 1m and 20m datasets use the same movie set. -NUM_ITEM_IDS = 3952 - -MAX_RATING = 5 - -NUM_RATINGS = { - ML_1M: 1000209, - ML_20M: 20000263 -} - - -def _download_and_clean(dataset, data_dir): - """Download MovieLens dataset in a standard format. - - This function downloads the specified MovieLens format and coerces it into a - standard format. The only difference between the ml-1m and ml-20m datasets - after this point (other than size, of course) is that the 1m dataset uses - whole number ratings while the 20m dataset allows half integer ratings. - """ - if dataset not in DATASETS: - raise ValueError("dataset {} is not in {{{}}}".format( - dataset, ",".join(DATASETS))) - - data_subdir = os.path.join(data_dir, dataset) - - expected_files = ["{}.zip".format(dataset), RATINGS_FILE, MOVIES_FILE] - - tf.gfile.MakeDirs(data_subdir) - if set(expected_files).intersection( - tf.gfile.ListDirectory(data_subdir)) == set(expected_files): - tf.logging.info("Dataset {} has already been downloaded".format(dataset)) - return - - url = "{}{}.zip".format(_DATA_URL, dataset) - - temp_dir = tempfile.mkdtemp() - try: - zip_path = os.path.join(temp_dir, "{}.zip".format(dataset)) - zip_path, _ = urllib.request.urlretrieve(url, zip_path) - statinfo = os.stat(zip_path) - # A new line to clear the carriage return from download progress - # tf.logging.info is not applicable here - print() - tf.logging.info( - "Successfully downloaded {} {} bytes".format( - zip_path, statinfo.st_size)) - - zipfile.ZipFile(zip_path, "r").extractall(temp_dir) - - if dataset == ML_1M: - _regularize_1m_dataset(temp_dir) - else: - _regularize_20m_dataset(temp_dir) - - for fname in tf.gfile.ListDirectory(temp_dir): - if not tf.gfile.Exists(os.path.join(data_subdir, fname)): - tf.gfile.Copy(os.path.join(temp_dir, fname), - os.path.join(data_subdir, fname)) - else: - tf.logging.info("Skipping copy of {}, as it already exists in the " - "destination folder.".format(fname)) - - finally: - tf.gfile.DeleteRecursively(temp_dir) - - -def _transform_csv(input_path, output_path, names, skip_first, separator=","): - """Transform csv to a regularized format. - - Args: - input_path: The path of the raw csv. - output_path: The path of the cleaned csv. - names: The csv column names. - skip_first: Boolean of whether to skip the first line of the raw csv. - separator: Character used to separate fields in the raw csv. - """ - if six.PY2: - names = [n.decode("utf-8") for n in names] - - with tf.gfile.Open(output_path, "wb") as f_out, \ - tf.gfile.Open(input_path, "rb") as f_in: - - # Write column names to the csv. - f_out.write(",".join(names).encode("utf-8")) - f_out.write(b"\n") - for i, line in enumerate(f_in): - if i == 0 and skip_first: - continue # ignore existing labels in the csv - - line = line.decode("utf-8", errors="ignore") - fields = line.split(separator) - if separator != ",": - fields = ['"{}"'.format(field) if "," in field else field - for field in fields] - f_out.write(",".join(fields).encode("utf-8")) - - -def _regularize_1m_dataset(temp_dir): - """ - ratings.dat - The file has no header row, and each line is in the following format: - UserID::MovieID::Rating::Timestamp - - UserIDs range from 1 and 6040 - - MovieIDs range from 1 and 3952 - - Ratings are made on a 5-star scale (whole-star ratings only) - - Timestamp is represented in seconds since midnight Coordinated Universal - Time (UTC) of January 1, 1970. - - Each user has at least 20 ratings - - movies.dat - Each line has the following format: - MovieID::Title::Genres - - MovieIDs range from 1 and 3952 - """ - working_dir = os.path.join(temp_dir, ML_1M) - - _transform_csv( - input_path=os.path.join(working_dir, "ratings.dat"), - output_path=os.path.join(temp_dir, RATINGS_FILE), - names=RATING_COLUMNS, skip_first=False, separator="::") - - _transform_csv( - input_path=os.path.join(working_dir, "movies.dat"), - output_path=os.path.join(temp_dir, MOVIES_FILE), - names=MOVIE_COLUMNS, skip_first=False, separator="::") - - tf.gfile.DeleteRecursively(working_dir) - - -def _regularize_20m_dataset(temp_dir): - """ - ratings.csv - Each line of this file after the header row represents one rating of one - movie by one user, and has the following format: - userId,movieId,rating,timestamp - - The lines within this file are ordered first by userId, then, within user, - by movieId. - - Ratings are made on a 5-star scale, with half-star increments - (0.5 stars - 5.0 stars). - - Timestamps represent seconds since midnight Coordinated Universal Time - (UTC) of January 1, 1970. - - All the users had rated at least 20 movies. - - movies.csv - Each line has the following format: - MovieID,Title,Genres - - MovieIDs range from 1 and 3952 - """ - working_dir = os.path.join(temp_dir, ML_20M) - - _transform_csv( - input_path=os.path.join(working_dir, "ratings.csv"), - output_path=os.path.join(temp_dir, RATINGS_FILE), - names=RATING_COLUMNS, skip_first=True, separator=",") - - _transform_csv( - input_path=os.path.join(working_dir, "movies.csv"), - output_path=os.path.join(temp_dir, MOVIES_FILE), - names=MOVIE_COLUMNS, skip_first=True, separator=",") - - tf.gfile.DeleteRecursively(working_dir) - - -def download(dataset, data_dir): - if dataset: - _download_and_clean(dataset, data_dir) - else: - _ = [_download_and_clean(d, data_dir) for d in DATASETS] - - -def ratings_csv_to_dataframe(data_dir, dataset): - with tf.gfile.Open(os.path.join(data_dir, dataset, RATINGS_FILE)) as f: - return pd.read_csv(f, encoding="utf-8") - - -def csv_to_joint_dataframe(data_dir, dataset): - ratings = ratings_csv_to_dataframe(data_dir, dataset) - - with tf.gfile.Open(os.path.join(data_dir, dataset, MOVIES_FILE)) as f: - movies = pd.read_csv(f, encoding="utf-8") - - df = ratings.merge(movies, on=ITEM_COLUMN) - df[RATING_COLUMN] = df[RATING_COLUMN].astype(np.float32) - - return df - - -def integerize_genres(dataframe): - """Replace genre string with a binary vector. - - Args: - dataframe: a pandas dataframe of movie data. - - Returns: - The transformed dataframe. - """ - def _map_fn(entry): - entry.replace("Children's", "Children") # naming difference. - movie_genres = entry.split("|") - output = np.zeros((len(GENRES),), dtype=np.int64) - for i, genre in enumerate(GENRES): - if genre in movie_genres: - output[i] = 1 - return output - - dataframe[GENRE_COLUMN] = dataframe[GENRE_COLUMN].apply(_map_fn) - - return dataframe - - -def define_data_download_flags(): - """Add flags specifying data download arguments.""" - flags.DEFINE_string( - name="data_dir", default="/tmp/movielens-data/", - help=flags_core.help_wrap( - "Directory to download and extract data.")) - - flags.DEFINE_enum( - name="dataset", default=None, - enum_values=DATASETS, case_sensitive=False, - help=flags_core.help_wrap("Dataset to be trained and evaluated.")) - - -def main(_): - """Download and extract the data from GroupLens website.""" - download(flags.FLAGS.dataset, flags.FLAGS.data_dir) - - -if __name__ == "__main__": - tf.logging.set_verbosity(tf.logging.INFO) - define_data_download_flags() - FLAGS = flags.FLAGS - absl_app.run(main) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/keras_application_models/README.md b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/keras_application_models/README.md deleted file mode 100644 index b3cd525e..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/keras_application_models/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# Keras Application Models Benchmark -## Overview -This provides a single scaffold to benchmark the Keras built-in application [models](https://keras.io/applications/). All the models are for image classification applications, and include: - - - Xception - - VGG16 - - VGG19 - - ResNet50 - - InceptionV3 - - InceptionResNetV2 - - MobileNet - - DenseNet - - NASNet - -## Dataset -Synthetic dataset is used for the benchmark. - -## Callbacks -Two custom callbacks are provided for model benchmarking: ExamplesPerSecondCallback and LoggingMetricCallback. For each callback, `epoch_based` and `batch_based` options are available to set the benchmark level. Check [model_callbacks.py](model_callbacks.py) for more details. - -## Running Code -To benchmark a model, use `--model` to specify the model name. To perform the benchmark with eager execution, issue the following command: -``` -python benchmark_main.py --model resnet50 --eager -``` -Note that, if eager execution is enabled, only one GPU is utilized even if multiple GPUs are provided and multi_gpu_model is used. - - -To use distribution strategy in the benchmark, run the following: -``` -python benchmark_main.py --model resnet50 --dist_strat -``` -Currently, only one of the --eager and --dist_strat arguments can be defined, as DistributionStrategy is not supported in Eager execution now. - -Arguments: - * `--model`: Which model to be benchmarked. The model name is defined as the keys of `MODELS` in [benchmark_main.py](benchmark_main.py). - * `--callbacks`: To specify a list of callbacks. - -Use the `--help` or `-h` flag to get a full list of possible arguments. diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/keras_application_models/benchmark_main.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/keras_application_models/benchmark_main.py deleted file mode 100644 index 906a420c..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/keras_application_models/benchmark_main.py +++ /dev/null @@ -1,227 +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. -# ============================================================================== -"""Benchmark on the keras built-in application models.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -# pylint: disable=g-bad-import-order -import numpy as np -from absl import app as absl_app -from absl import flags -import tensorflow as tf -# pylint: enable=g-bad-import-order - -from official.keras_application_models import dataset -from official.keras_application_models import model_callbacks -from official.utils.flags import core as flags_core -from official.utils.logs import logger -from official.utils.misc import distribution_utils - -# Define a dictionary that maps model names to their model classes inside Keras -MODELS = { - "vgg16": tf.keras.applications.VGG16, - "vgg19": tf.keras.applications.VGG19, - "inceptionv3": tf.keras.applications.InceptionV3, - "xception": tf.keras.applications.Xception, - "resnet50": tf.keras.applications.ResNet50, - "inceptionresnetv2": tf.keras.applications.InceptionResNetV2, - "mobilenet": tf.keras.applications.MobileNet, - "densenet121": tf.keras.applications.DenseNet121, - "densenet169": tf.keras.applications.DenseNet169, - "densenet201": tf.keras.applications.DenseNet201, - "nasnetlarge": tf.keras.applications.NASNetLarge, - "nasnetmobile": tf.keras.applications.NASNetMobile, -} - - -def run_keras_model_benchmark(_): - """Run the benchmark on keras model.""" - # Ensure a valid model name was supplied via command line argument - if FLAGS.model not in MODELS.keys(): - raise AssertionError("The --model command line argument should " - "be a key in the `MODELS` dictionary.") - - # Check if eager execution is enabled - if FLAGS.eager: - tf.logging.info("Eager execution is enabled...") - tf.enable_eager_execution() - - # Load the model - tf.logging.info("Benchmark on {} model...".format(FLAGS.model)) - keras_model = MODELS[FLAGS.model] - - # Get dataset - dataset_name = "ImageNet" - if FLAGS.use_synthetic_data: - tf.logging.info("Using synthetic dataset...") - dataset_name += "_Synthetic" - train_dataset = dataset.generate_synthetic_input_dataset( - FLAGS.model, FLAGS.batch_size) - val_dataset = dataset.generate_synthetic_input_dataset( - FLAGS.model, FLAGS.batch_size) - model = keras_model(weights=None) - else: - tf.logging.info("Using CIFAR-10 dataset...") - dataset_name = "CIFAR-10" - ds = dataset.Cifar10Dataset(FLAGS.batch_size) - train_dataset = ds.train_dataset - val_dataset = ds.test_dataset - model = keras_model( - weights=None, input_shape=ds.input_shape, classes=ds.num_classes) - - num_gpus = flags_core.get_num_gpus(FLAGS) - - distribution = None - # Use distribution strategy - if FLAGS.dist_strat: - distribution = distribution_utils.get_distribution_strategy( - num_gpus=num_gpus) - elif num_gpus > 1: - # Run with multi_gpu_model - # If eager execution is enabled, only one GPU is utilized even if multiple - # GPUs are provided. - if FLAGS.eager: - tf.logging.warning( - "{} GPUs are provided, but only one GPU is utilized as " - "eager execution is enabled.".format(num_gpus)) - model = tf.keras.utils.multi_gpu_model(model, gpus=num_gpus) - - # Adam optimizer and some other optimizers doesn't work well with - # distribution strategy (b/113076709) - # Use GradientDescentOptimizer here - optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.001) - model.compile(loss="categorical_crossentropy", - optimizer=optimizer, - metrics=["accuracy"], - distribute=distribution) - - # Create benchmark logger for benchmark logging - run_params = { - "batch_size": FLAGS.batch_size, - "synthetic_data": FLAGS.use_synthetic_data, - "train_epochs": FLAGS.train_epochs, - "num_train_images": FLAGS.num_train_images, - "num_eval_images": FLAGS.num_eval_images, - } - - benchmark_logger = logger.get_benchmark_logger() - benchmark_logger.log_run_info( - model_name=FLAGS.model, - dataset_name=dataset_name, - run_params=run_params, - test_id=FLAGS.benchmark_test_id) - - # Create callbacks that log metric values about the training and evaluation - callbacks = model_callbacks.get_model_callbacks( - FLAGS.callbacks, - batch_size=FLAGS.batch_size, - metric_logger=benchmark_logger) - # Train and evaluate the model - history = model.fit( - train_dataset, - epochs=FLAGS.train_epochs, - callbacks=callbacks, - validation_data=val_dataset, - steps_per_epoch=int(np.ceil(FLAGS.num_train_images / FLAGS.batch_size)), - validation_steps=int(np.ceil(FLAGS.num_eval_images / FLAGS.batch_size)) - ) - - tf.logging.info("Logging the evaluation results...") - for epoch in range(FLAGS.train_epochs): - eval_results = { - "accuracy": history.history["val_acc"][epoch], - "loss": history.history["val_loss"][epoch], - tf.GraphKeys.GLOBAL_STEP: (epoch + 1) * np.ceil( - FLAGS.num_eval_images/FLAGS.batch_size) - } - benchmark_logger.log_evaluation_result(eval_results) - - # Clear the session explicitly to avoid session delete error - tf.keras.backend.clear_session() - - -def define_keras_benchmark_flags(): - """Add flags for keras built-in application models.""" - flags_core.define_base(hooks=False) - flags_core.define_performance() - flags_core.define_image() - flags_core.define_benchmark() - flags.adopt_module_key_flags(flags_core) - - flags_core.set_defaults( - data_format="channels_last", - use_synthetic_data=True, - batch_size=32, - train_epochs=2) - - flags.DEFINE_enum( - name="model", default=None, - enum_values=MODELS.keys(), case_sensitive=False, - help=flags_core.help_wrap( - "Model to be benchmarked.")) - - flags.DEFINE_integer( - name="num_train_images", default=1000, - help=flags_core.help_wrap( - "The number of synthetic images for training. The default value is " - "1000.")) - - flags.DEFINE_integer( - name="num_eval_images", default=50, - help=flags_core.help_wrap( - "The number of synthetic images for evaluation. The default value is " - "50.")) - - flags.DEFINE_boolean( - name="eager", default=False, help=flags_core.help_wrap( - "To enable eager execution. Note that if eager execution is enabled, " - "only one GPU is utilized even if multiple GPUs are provided and " - "multi_gpu_model is used.")) - - flags.DEFINE_boolean( - name="dist_strat", default=False, help=flags_core.help_wrap( - "To enable distribution strategy for model training and evaluation. " - "Number of GPUs used for distribution strategy can be set by the " - "argument --num_gpus.")) - - flags.DEFINE_list( - name="callbacks", - default=["ExamplesPerSecondCallback", "LoggingMetricCallback"], - help=flags_core.help_wrap( - "A list of (case insensitive) strings to specify the names of " - "callbacks. For example: `--callbacks ExamplesPerSecondCallback," - "LoggingMetricCallback`")) - - @flags.multi_flags_validator( - ["eager", "dist_strat"], - message="Both --eager and --dist_strat were set. Only one can be " - "defined, as DistributionStrategy is not supported in Eager " - "execution currently.") - # pylint: disable=unused-variable - def _check_eager_dist_strat(flag_dict): - return not(flag_dict["eager"] and flag_dict["dist_strat"]) - - -def main(_): - with logger.benchmark_context(FLAGS): - run_keras_model_benchmark(FLAGS) - - -if __name__ == "__main__": - tf.logging.set_verbosity(tf.logging.INFO) - define_keras_benchmark_flags() - FLAGS = flags.FLAGS - absl_app.run(main) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/keras_application_models/dataset.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/keras_application_models/dataset.py deleted file mode 100644 index 1a35caeb..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/keras_application_models/dataset.py +++ /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. -# ============================================================================== -"""Prepare dataset for keras model benchmark.""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import numpy as np -import tensorflow as tf -from official.utils.misc import model_helpers # pylint: disable=g-bad-import-order - -# Default values for dataset. -_NUM_CHANNELS = 3 -_NUM_CLASSES = 1000 - - -def _get_default_image_size(model): - """Provide default image size for each model.""" - image_size = (224, 224) - if model in ["inceptionv3", "xception", "inceptionresnetv2"]: - image_size = (299, 299) - elif model in ["nasnetlarge"]: - image_size = (331, 331) - return image_size - - -def generate_synthetic_input_dataset(model, batch_size): - """Generate synthetic dataset.""" - image_size = _get_default_image_size(model) - image_shape = (batch_size,) + image_size + (_NUM_CHANNELS,) - label_shape = (batch_size, _NUM_CLASSES) - - dataset = model_helpers.generate_synthetic_data( - input_shape=tf.TensorShape(image_shape), - label_shape=tf.TensorShape(label_shape), - ) - return dataset - - -class Cifar10Dataset(object): - """CIFAR10 dataset, including train and test set. - - Each sample consists of a 32x32 color image, and label is from 10 classes. - """ - - def __init__(self, batch_size): - """Initializes train/test datasets. - - Args: - batch_size: int, the number of batch size. - """ - self.input_shape = (32, 32, 3) - self.num_classes = 10 - (x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data() - x_train, x_test = x_train / 255.0, x_test / 255.0 - y_train, y_test = y_train.astype(np.int64), y_test.astype(np.int64) - y_train = tf.keras.utils.to_categorical(y_train, self.num_classes) - y_test = tf.keras.utils.to_categorical(y_test, self.num_classes) - self.train_dataset = tf.data.Dataset.from_tensor_slices( - (x_train, y_train)).shuffle(2000).batch(batch_size).repeat() - self.test_dataset = tf.data.Dataset.from_tensor_slices( - (x_test, y_test)).shuffle(2000).batch(batch_size).repeat() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/keras_application_models/model_callbacks.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/keras_application_models/model_callbacks.py deleted file mode 100644 index 8d75fc12..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/keras_application_models/model_callbacks.py +++ /dev/null @@ -1,165 +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. -# ============================================================================== -"""Callbacks for Keras built-in application models. - -Note that, in the callbacks, the global_step is initialized in the __init__ of -each callback rather than on_train_begin. As on_train_begin gets called in -the fit_loop, and it will be reset with each call to fit(). To keep the -global_step persistent across all training sessions, it should be initialized in -the __init__. -""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import time - -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.utils.logs import logger - -# Metrics to log after each batch and epoch -_PER_BATCH_METRICS = { - "loss": "train_loss", - "acc": "train_accuracy", -} -_PER_EPOCH_METRICS = { - "loss": "train_loss", - "acc": "train_accuracy", - "val_loss": "loss", - "val_acc": "accuracy" -} - - -class ExamplesPerSecondCallback(tf.keras.callbacks.Callback): - """ExamplesPerSecond callback. - - This callback records the average_examples_per_sec and - current_examples_per_sec during training. - """ - - def __init__(self, batch_size, every_n_steps=1, metric_logger=None): - self._batch_size = batch_size - self._every_n_steps = every_n_steps - self._logger = metric_logger or logger.BaseBenchmarkLogger() - self._global_step = 0 # Initialize it in __init__ - super(ExamplesPerSecondCallback, self).__init__() - - def on_train_begin(self, logs=None): - self._train_start_time = time.time() - self._last_recorded_time = time.time() - - def on_batch_end(self, batch, logs=None): - """Log the examples_per_sec metric every_n_steps.""" - self._global_step += 1 - current_time = time.time() - - if self._global_step % self._every_n_steps == 0: - average_examples_per_sec = self._batch_size * ( - self._global_step / (current_time - self._train_start_time)) - self._logger.log_metric( - "average_examples_per_sec", average_examples_per_sec, - global_step=self._global_step) - - current_examples_per_sec = self._batch_size * ( - self._every_n_steps / (current_time - self._last_recorded_time)) - self._logger.log_metric( - "current_examples_per_sec", current_examples_per_sec, - global_step=self._global_step) - self._last_recorded_time = current_time # Update last_recorded_time - - -class LoggingMetricCallback(tf.keras.callbacks.Callback): - """LoggingMetric callback. - - Log the predefined _PER_BATCH_METRICS after each batch, and log the predefined - _PER_EPOCH_METRICS after each epoch. - """ - - def __init__(self, metric_logger=None): - self._logger = metric_logger or logger.BaseBenchmarkLogger() - self._per_batch_metrics = _PER_BATCH_METRICS - self._per_epoch_metrics = _PER_EPOCH_METRICS - self._global_step = 0 # Initialize it in __init__ - super(LoggingMetricCallback, self).__init__() - - def on_batch_end(self, batch, logs=None): - """Log metrics after each batch.""" - self._global_step += 1 - for metric in _PER_BATCH_METRICS: - self._logger.log_metric( - _PER_BATCH_METRICS[metric], - logs.get(metric), - global_step=self._global_step) - - def on_epoch_end(self, epoch, logs=None): - """Log metrics after each epoch.""" - for metric in _PER_EPOCH_METRICS: - self._logger.log_metric( - _PER_EPOCH_METRICS[metric], - logs.get(metric), - global_step=self._global_step) - - -def get_model_callbacks(name_list, **kwargs): - """Factory for getting a list of TensorFlow hooks for training by name. - - Args: - name_list: a list of strings to name desired callback classes. Allowed: - ExamplesPerSecondCallback, LoggingMetricCallback, which are defined - as keys in CALLBACKS. - **kwargs: a dictionary of arguments to the callbacks. - - Returns: - list of instantiated callbacks, ready to be used in a classifier.train call. - - Raises: - ValueError: if an unrecognized name is passed. - """ - - if not name_list: - return [] - - callbacks = [] - for name in name_list: - callback_name = CALLBACKS.get(name.strip().lower()) - if callback_name is None: - raise ValueError( - "Unrecognized training callback requested: {}".format(name)) - else: - callbacks.append(callback_name(**kwargs)) - - return callbacks - - -def get_examples_per_second_callback( - every_n_steps=1, batch_size=32, metric_logger=None, **kwargs): # pylint: disable=unused-argument - """Function to get ExamplesPerSecondCallback.""" - return ExamplesPerSecondCallback( - batch_size=batch_size, every_n_steps=every_n_steps, - metric_logger=metric_logger or logger.get_benchmark_logger()) - - -def get_logging_metric_callback(metric_logger=None, **kwargs): # pylint: disable=unused-argument - """Function to get LoggingMetricCallback.""" - return LoggingMetricCallback( - metric_logger=metric_logger or logger.get_benchmark_logger()) - - -# A dictionary to map the callback name and its corresponding function -CALLBACKS = { - "examplespersecondcallback": get_examples_per_second_callback, - "loggingmetriccallback": get_logging_metric_callback, -} diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/README.md b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/README.md deleted file mode 100644 index 751c972a..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# MNIST in TensorFlow - -This directory builds a convolutional neural net to classify the [MNIST -dataset](http://yann.lecun.com/exdb/mnist/) using the -[tf.data](https://www.tensorflow.org/api_docs/python/tf/data), -[tf.estimator.Estimator](https://www.tensorflow.org/api_docs/python/tf/estimator/Estimator), -and -[tf.layers](https://www.tensorflow.org/api_docs/python/tf/layers) -APIs. - - -## Setup - -To begin, you'll simply need the latest version of TensorFlow installed. -First make sure you've [added the models folder to your Python path](/official/#running-the-models); otherwise you may encounter an error like `ImportError: No module named official.mnist`. - -Then to train the model, run the following: - -``` -python mnist.py -``` - -The model will begin training and will automatically evaluate itself on the -validation data. - -Illustrative unit tests and benchmarks can be run with: - -``` -python mnist_test.py -python mnist_test.py --benchmarks=. -``` - -## Exporting the model - -You can export the model into Tensorflow [SavedModel](https://www.tensorflow.org/guide/saved_model) format by using the argument `--export_dir`: - -``` -python mnist.py --export_dir /tmp/mnist_saved_model -``` - -The SavedModel will be saved in a timestamped directory under `/tmp/mnist_saved_model/` (e.g. `/tmp/mnist_saved_model/1513630966/`). - -**Getting predictions with SavedModel** -Use [`saved_model_cli`](https://www.tensorflow.org/guide/saved_model#cli_to_inspect_and_execute_savedmodel) to inspect and execute the SavedModel. - -``` -saved_model_cli run --dir /tmp/mnist_saved_model/TIMESTAMP --tag_set serve --signature_def classify --inputs image=examples.npy -``` - -`examples.npy` contains the data from `example5.png` and `example3.png` in a numpy array, in that order. The array values are normalized to values between 0 and 1. - -The output should look similar to below: -``` -Result for output key classes: -[5 3] -Result for output key probabilities: -[[ 1.53558474e-07 1.95694142e-13 1.31193523e-09 5.47467265e-03 - 5.85711526e-22 9.94520664e-01 3.48423509e-06 2.65365645e-17 - 9.78631419e-07 3.15522470e-08] - [ 1.22413359e-04 5.87615965e-08 1.72251271e-06 9.39960718e-01 - 3.30306928e-11 2.87386645e-02 2.82353517e-02 8.21146413e-18 - 2.52568233e-03 4.15460236e-04]] -``` - -## Experimental: Eager Execution - -[Eager execution](https://research.googleblog.com/2017/10/eager-execution-imperative-define-by.html) -(an preview feature in TensorFlow 1.5) is an imperative interface to TensorFlow. -The exact same model defined in `mnist.py` can be trained without creating a -TensorFlow graph using: - -``` -python mnist_eager.py -``` - -## Experimental: TPU Acceleration - -`mnist.py` (and `mnist_eager.py`) demonstrate training a neural network to -classify digits on CPUs and GPUs. `mnist_tpu.py` can be used to train the -same model using TPUs for hardware acceleration. More information in -the [tensorflow/tpu](https://github.com/tensorflow/tpu) repository. diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/dataset.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/dataset.py deleted file mode 100644 index 0ba7c1a8..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/dataset.py +++ /dev/null @@ -1,117 +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. -"""tf.data.Dataset interface to the MNIST dataset.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import gzip -import os -import shutil -import tempfile - -import numpy as np -from six.moves import urllib -import tensorflow as tf - - -def read32(bytestream): - """Read 4 bytes from bytestream as an unsigned 32-bit integer.""" - dt = np.dtype(np.uint32).newbyteorder('>') - return np.frombuffer(bytestream.read(4), dtype=dt)[0] - - -def check_image_file_header(filename): - """Validate that filename corresponds to images for the MNIST dataset.""" - with tf.gfile.Open(filename, 'rb') as f: - magic = read32(f) - read32(f) # num_images, unused - rows = read32(f) - cols = read32(f) - if magic != 2051: - raise ValueError('Invalid magic number %d in MNIST file %s' % (magic, - f.name)) - if rows != 28 or cols != 28: - raise ValueError( - 'Invalid MNIST file %s: Expected 28x28 images, found %dx%d' % - (f.name, rows, cols)) - - -def check_labels_file_header(filename): - """Validate that filename corresponds to labels for the MNIST dataset.""" - with tf.gfile.Open(filename, 'rb') as f: - magic = read32(f) - read32(f) # num_items, unused - if magic != 2049: - raise ValueError('Invalid magic number %d in MNIST file %s' % (magic, - f.name)) - - -def download(directory, filename): - """Download (and unzip) a file from the MNIST dataset if not already done.""" - filepath = os.path.join(directory, filename) - if tf.gfile.Exists(filepath): - return filepath - if not tf.gfile.Exists(directory): - tf.gfile.MakeDirs(directory) - # CVDF mirror of http://yann.lecun.com/exdb/mnist/ - url = 'https://storage.googleapis.com/cvdf-datasets/mnist/' + filename + '.gz' - _, zipped_filepath = tempfile.mkstemp(suffix='.gz') - print('Downloading %s to %s' % (url, zipped_filepath)) - urllib.request.urlretrieve(url, zipped_filepath) - with gzip.open(zipped_filepath, 'rb') as f_in, \ - tf.gfile.Open(filepath, 'wb') as f_out: - shutil.copyfileobj(f_in, f_out) - os.remove(zipped_filepath) - return filepath - - -def dataset(directory, images_file, labels_file): - """Download and parse MNIST dataset.""" - - images_file = download(directory, images_file) - labels_file = download(directory, labels_file) - - check_image_file_header(images_file) - check_labels_file_header(labels_file) - - def decode_image(image): - # Normalize from [0, 255] to [0.0, 1.0] - image = tf.decode_raw(image, tf.uint8) - image = tf.cast(image, tf.float32) - image = tf.reshape(image, [784]) - return image / 255.0 - - def decode_label(label): - label = tf.decode_raw(label, tf.uint8) # tf.string -> [tf.uint8] - label = tf.reshape(label, []) # label is a scalar - return tf.to_int32(label) - - images = tf.data.FixedLengthRecordDataset( - images_file, 28 * 28, header_bytes=16).map(decode_image) - labels = tf.data.FixedLengthRecordDataset( - labels_file, 1, header_bytes=8).map(decode_label) - return tf.data.Dataset.zip((images, labels)) - - -def train(directory): - """tf.data.Dataset object for MNIST training data.""" - return dataset(directory, 'train-images-idx3-ubyte', - 'train-labels-idx1-ubyte') - - -def test(directory): - """tf.data.Dataset object for MNIST test data.""" - return dataset(directory, 't10k-images-idx3-ubyte', 't10k-labels-idx1-ubyte') diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/example3.png b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/example3.png deleted file mode 100644 index bb7f5b8842d2e61a3878dc0aade03773cdc6aceb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 368 zcmeAS@N?(olHy`uVBq!ia0vp^G9b*s1SJ3FdmIK*jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85m^SL71`s>Bm%{23AiO#}E(iw^KF>HXHD`#u_oHJ~m)dRgDe+ zvXTv?RDYKLI?1WhqIh!YNo@k~B{4m9;sTcjp4dZoCQlbMD2eXs-TYOE zQKs5uXQD*Fol@E3xAz$W)s|%>UpDEfO7VPNIcqP_~7i_ z;OnR7Jak&jlAC^_Ad+jvndx4OABP3Z)Nl3U+K{>m}N44$rj JF6*2UngExClBNIv diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/example5.png b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/example5.png deleted file mode 100644 index 68496bcce524dda1b51eb5197ba2653ce7ea9475..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 367 zcmeAS@N?(olHy`uVBq!ia0vp^G9b*s1SJ3FdmIK*jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85m^SL71`s>Bm%{1{O~j#}E(iw^I~_nhgY8Cwiz%TC6b9qp#r! z8;FpQ;p}v|WdCG(V@vELKWovq!ahP~`IFwLUHrxP?@~fr;)%~P@!w?kEctA+J$H6$ zWQjuGk`*-mjNm6`2Ym;6G~x67-AWAf=ucUBAa zF4E``i8gE&n{~y&S2)`Lfoafzg~s25#8Wy?m3s#Idrp#AT=MtulCVWfUdtgTe~ HDWM4fUe}C_ diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/examples.npy b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/examples.npy deleted file mode 100644 index 85d78b1b6dadb1df44128ca173426aff9866c2c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12624 zcmeHMy>8S%5H>AU1Sp9Rg*y}Js#Fm|a+eN7LqUa3dUF?)v*?XJ>XU&+p#9bML{WXft}2Y>g(5 z#>uTXxxKxS4B}*acl>lbeDZL2yfqU2o5P*SDDR&<9`23u_RaM{yuKm+)&}u@EaY+| zB}+poG!XOKI(_Fsy z?*D%O67q$OzloFDx3VncZSTAYQ7jl|xg#*e;uY`%@ii%L+V3I7ysKA)ysqg7cH8&< zncttD3i-;{yB5!(9kYZV^RLwXipdLY|9BxTkR86L*{5#r{_x}a7gzEJvLL&DxxB7R zdCxz;5`g{tT$~s;ue_-ApPHYUKVIPGmfZUZtyh1#$_xDU_*=){)>HZo$My@nLMb%R z|M>~~5cVP1;s1LM_4{S@yDs7;-uvbUxc4yP6-B=Aos}2(p(LL8ox>fww&icQht0;{ z9e!=U&x6 - # can then be used to see the recorded summaries. - train_dir = os.path.join(flags_obj.output_dir, 'train') - test_dir = os.path.join(flags_obj.output_dir, 'eval') - tf.gfile.MakeDirs(flags_obj.output_dir) - else: - train_dir = None - test_dir = None - summary_writer = tf.contrib.summary.create_file_writer( - train_dir, flush_millis=10000) - test_summary_writer = tf.contrib.summary.create_file_writer( - test_dir, flush_millis=10000, name='test') - - # Create and restore checkpoint (if one exists on the path) - checkpoint_prefix = os.path.join(flags_obj.model_dir, 'ckpt') - step_counter = tf.train.get_or_create_global_step() - checkpoint = tf.train.Checkpoint( - model=model, optimizer=optimizer, step_counter=step_counter) - # Restore variables on creation if a checkpoint exists. - checkpoint.restore(tf.train.latest_checkpoint(flags_obj.model_dir)) - - # Train and evaluate for a set number of epochs. - with tf.device(device): - for _ in range(flags_obj.train_epochs): - start = time.time() - with summary_writer.as_default(): - train(model, optimizer, train_ds, step_counter, - flags_obj.log_interval) - end = time.time() - print('\nTrain time for epoch #%d (%d total steps): %f' % - (checkpoint.save_counter.numpy() + 1, - step_counter.numpy(), - end - start)) - with test_summary_writer.as_default(): - test(model, test_ds) - checkpoint.save(checkpoint_prefix) - - -def define_mnist_eager_flags(): - """Defined flags and defaults for MNIST in eager mode.""" - flags_core.define_base_eager() - flags_core.define_image() - flags.adopt_module_key_flags(flags_core) - - flags.DEFINE_integer( - name='log_interval', short_name='li', default=10, - help=flags_core.help_wrap('batches between logging training status')) - - flags.DEFINE_string( - name='output_dir', short_name='od', default=None, - help=flags_core.help_wrap('Directory to write TensorBoard summaries')) - - flags.DEFINE_float(name='learning_rate', short_name='lr', default=0.01, - help=flags_core.help_wrap('Learning rate.')) - - flags.DEFINE_float(name='momentum', short_name='m', default=0.5, - help=flags_core.help_wrap('SGD momentum.')) - - flags.DEFINE_bool(name='no_gpu', short_name='nogpu', default=False, - help=flags_core.help_wrap( - 'disables GPU usage even if a GPU is available')) - - flags_core.set_defaults( - data_dir='/tmp/tensorflow/mnist/input_data', - model_dir='/tmp/tensorflow/mnist/checkpoints/', - batch_size=100, - train_epochs=10, - ) - - -def main(_): - run_mnist_eager(flags.FLAGS) - - -if __name__ == '__main__': - define_mnist_eager_flags() - absl_app.run(main=main) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/mnist_eager_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/mnist_eager_test.py deleted file mode 100644 index b58a9bcf..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/mnist_eager_test.py +++ /dev/null @@ -1,80 +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. -# ============================================================================== - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import tensorflow as tf # pylint: disable=g-bad-import-order -import tensorflow.contrib.eager as tfe # pylint: disable=g-bad-import-order - -from official.mnist import mnist -from official.mnist import mnist_eager - - -def device(): - return "/device:GPU:0" if tfe.num_gpus() else "/device:CPU:0" - - -def data_format(): - return "channels_first" if tfe.num_gpus() else "channels_last" - - -def random_dataset(): - batch_size = 64 - images = tf.random_normal([batch_size, 784]) - labels = tf.random_uniform([batch_size], minval=0, maxval=10, dtype=tf.int32) - return tf.data.Dataset.from_tensors((images, labels)) - - -def train(defun=False): - model = mnist.create_model(data_format()) - if defun: - model.call = tfe.defun(model.call) - optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01) - dataset = random_dataset() - with tf.device(device()): - mnist_eager.train(model, optimizer, dataset, - step_counter=tf.train.get_or_create_global_step()) - - -def evaluate(defun=False): - model = mnist.create_model(data_format()) - dataset = random_dataset() - if defun: - model.call = tfe.defun(model.call) - with tf.device(device()): - mnist_eager.test(model, dataset) - - -class MNISTTest(tf.test.TestCase): - """Run tests for MNIST eager loop.""" - - def test_train(self): - train(defun=False) - - def test_evaluate(self): - evaluate(defun=False) - - def test_train_with_defun(self): - train(defun=True) - - def test_evaluate_with_defun(self): - evaluate(defun=True) - - -if __name__ == "__main__": - tfe.enable_eager_execution() - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/mnist_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/mnist_test.py deleted file mode 100644 index f66e5253..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/mnist_test.py +++ /dev/null @@ -1,135 +0,0 @@ -# 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. -# ============================================================================== - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import time - -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.mnist import mnist - -BATCH_SIZE = 100 - - -def dummy_input_fn(): - image = tf.random_uniform([BATCH_SIZE, 784]) - labels = tf.random_uniform([BATCH_SIZE, 1], maxval=9, dtype=tf.int32) - return image, labels - - -def make_estimator(): - data_format = 'channels_last' - if tf.test.is_built_with_cuda(): - data_format = 'channels_first' - return tf.estimator.Estimator( - model_fn=mnist.model_fn, params={ - 'data_format': data_format - }) - - -class Tests(tf.test.TestCase): - """Run tests for MNIST model.""" - - def test_mnist(self): - classifier = make_estimator() - classifier.train(input_fn=dummy_input_fn, steps=2) - eval_results = classifier.evaluate(input_fn=dummy_input_fn, steps=1) - - loss = eval_results['loss'] - global_step = eval_results['global_step'] - accuracy = eval_results['accuracy'] - self.assertEqual(loss.shape, ()) - self.assertEqual(2, global_step) - self.assertEqual(accuracy.shape, ()) - - input_fn = lambda: tf.random_uniform([3, 784]) - predictions_generator = classifier.predict(input_fn) - for _ in range(3): - predictions = next(predictions_generator) - self.assertEqual(predictions['probabilities'].shape, (10,)) - self.assertEqual(predictions['classes'].shape, ()) - - def mnist_model_fn_helper(self, mode, multi_gpu=False): - features, labels = dummy_input_fn() - image_count = features.shape[0] - spec = mnist.model_fn(features, labels, mode, { - 'data_format': 'channels_last', - 'multi_gpu': multi_gpu - }) - - if mode == tf.estimator.ModeKeys.PREDICT: - predictions = spec.predictions - self.assertAllEqual(predictions['probabilities'].shape, (image_count, 10)) - self.assertEqual(predictions['probabilities'].dtype, tf.float32) - self.assertAllEqual(predictions['classes'].shape, (image_count,)) - self.assertEqual(predictions['classes'].dtype, tf.int64) - - if mode != tf.estimator.ModeKeys.PREDICT: - loss = spec.loss - self.assertAllEqual(loss.shape, ()) - self.assertEqual(loss.dtype, tf.float32) - - if mode == tf.estimator.ModeKeys.EVAL: - eval_metric_ops = spec.eval_metric_ops - self.assertAllEqual(eval_metric_ops['accuracy'][0].shape, ()) - self.assertAllEqual(eval_metric_ops['accuracy'][1].shape, ()) - self.assertEqual(eval_metric_ops['accuracy'][0].dtype, tf.float32) - self.assertEqual(eval_metric_ops['accuracy'][1].dtype, tf.float32) - - def test_mnist_model_fn_train_mode(self): - self.mnist_model_fn_helper(tf.estimator.ModeKeys.TRAIN) - - def test_mnist_model_fn_train_mode_multi_gpu(self): - self.mnist_model_fn_helper(tf.estimator.ModeKeys.TRAIN, multi_gpu=True) - - def test_mnist_model_fn_eval_mode(self): - self.mnist_model_fn_helper(tf.estimator.ModeKeys.EVAL) - - def test_mnist_model_fn_predict_mode(self): - self.mnist_model_fn_helper(tf.estimator.ModeKeys.PREDICT) - - -class Benchmarks(tf.test.Benchmark): - """Simple speed benchmarking for MNIST.""" - - def benchmark_train_step_time(self): - classifier = make_estimator() - # Run one step to warmup any use of the GPU. - classifier.train(input_fn=dummy_input_fn, steps=1) - - have_gpu = tf.test.is_gpu_available() - num_steps = 1000 if have_gpu else 100 - name = 'train_step_time_%s' % ('gpu' if have_gpu else 'cpu') - - start = time.time() - classifier.train(input_fn=dummy_input_fn, steps=num_steps) - end = time.time() - - wall_time = (end - start) / num_steps - self.report_benchmark( - iters=num_steps, - wall_time=wall_time, - name=name, - extras={ - 'examples_per_sec': BATCH_SIZE / wall_time - }) - - -if __name__ == '__main__': - tf.logging.set_verbosity(tf.logging.ERROR) - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/mnist_tpu.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/mnist_tpu.py deleted file mode 100644 index 6902f767..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/mnist_tpu.py +++ /dev/null @@ -1,198 +0,0 @@ -# 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. -# ============================================================================== -"""MNIST model training using TPUs. - -This program demonstrates training of the convolutional neural network model -defined in mnist.py on Google Cloud TPUs (https://cloud.google.com/tpu/). - -If you are not interested in TPUs, you should ignore this file. -""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os -import sys - -import tensorflow as tf # pylint: disable=g-bad-import-order - -# For open source environment, add grandparent directory for import -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(sys.path[0])))) - -from official.mnist import dataset # pylint: disable=wrong-import-position -from official.mnist import mnist # pylint: disable=wrong-import-position - -# Cloud TPU Cluster Resolver flags -tf.flags.DEFINE_string( - "tpu", default=None, - help="The Cloud TPU 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.") -tf.flags.DEFINE_string( - "tpu_zone", default=None, - help="[Optional] GCE zone where the Cloud TPU is located in. If not " - "specified, we will attempt to automatically detect the GCE project from " - "metadata.") -tf.flags.DEFINE_string( - "gcp_project", default=None, - help="[Optional] Project name for the Cloud TPU-enabled project. If not " - "specified, we will attempt to automatically detect the GCE project from " - "metadata.") - -# Model specific parameters -tf.flags.DEFINE_string("data_dir", "", - "Path to directory containing the MNIST dataset") -tf.flags.DEFINE_string("model_dir", None, "Estimator model_dir") -tf.flags.DEFINE_integer("batch_size", 1024, - "Mini-batch size for the training. Note that this " - "is the global batch size and not the per-shard batch.") -tf.flags.DEFINE_integer("train_steps", 1000, "Total number of training steps.") -tf.flags.DEFINE_integer("eval_steps", 0, - "Total number of evaluation steps. If `0`, evaluation " - "after training is skipped.") -tf.flags.DEFINE_float("learning_rate", 0.05, "Learning rate.") - -tf.flags.DEFINE_bool("use_tpu", True, "Use TPUs rather than plain CPUs") -tf.flags.DEFINE_bool("enable_predict", True, "Do some predictions at the end") -tf.flags.DEFINE_integer("iterations", 50, - "Number of iterations per TPU training loop.") -tf.flags.DEFINE_integer("num_shards", 8, "Number of shards (TPU chips).") - -FLAGS = tf.flags.FLAGS - - -def metric_fn(labels, logits): - accuracy = tf.metrics.accuracy( - labels=labels, predictions=tf.argmax(logits, axis=1)) - return {"accuracy": accuracy} - - -def model_fn(features, labels, mode, params): - """model_fn constructs the ML model used to predict handwritten digits.""" - - del params - image = features - if isinstance(image, dict): - image = features["image"] - - model = mnist.create_model("channels_last") - - if mode == tf.estimator.ModeKeys.PREDICT: - logits = model(image, training=False) - predictions = { - 'class_ids': tf.argmax(logits, axis=1), - 'probabilities': tf.nn.softmax(logits), - } - return tf.contrib.tpu.TPUEstimatorSpec(mode, predictions=predictions) - - logits = model(image, training=(mode == tf.estimator.ModeKeys.TRAIN)) - loss = tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits) - - if mode == tf.estimator.ModeKeys.TRAIN: - learning_rate = tf.train.exponential_decay( - FLAGS.learning_rate, - tf.train.get_global_step(), - decay_steps=100000, - decay_rate=0.96) - optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate) - if FLAGS.use_tpu: - optimizer = tf.contrib.tpu.CrossShardOptimizer(optimizer) - return tf.contrib.tpu.TPUEstimatorSpec( - mode=mode, - loss=loss, - train_op=optimizer.minimize(loss, tf.train.get_global_step())) - - if mode == tf.estimator.ModeKeys.EVAL: - return tf.contrib.tpu.TPUEstimatorSpec( - mode=mode, loss=loss, eval_metrics=(metric_fn, [labels, logits])) - - -def train_input_fn(params): - """train_input_fn defines the input pipeline used for training.""" - batch_size = params["batch_size"] - data_dir = params["data_dir"] - # Retrieves the batch size for the current shard. The # of shards is - # computed according to the input pipeline deployment. See - # `tf.contrib.tpu.RunConfig` for details. - ds = dataset.train(data_dir).cache().repeat().shuffle( - buffer_size=50000).batch(batch_size, drop_remainder=True) - return ds - - -def eval_input_fn(params): - batch_size = params["batch_size"] - data_dir = params["data_dir"] - ds = dataset.test(data_dir).batch(batch_size, drop_remainder=True) - return ds - - -def predict_input_fn(params): - batch_size = params["batch_size"] - data_dir = params["data_dir"] - # Take out top 10 samples from test data to make the predictions. - ds = dataset.test(data_dir).take(10).batch(batch_size) - return ds - - -def main(argv): - del argv # Unused. - tf.logging.set_verbosity(tf.logging.INFO) - - tpu_cluster_resolver = tf.contrib.cluster_resolver.TPUClusterResolver( - FLAGS.tpu, - zone=FLAGS.tpu_zone, - project=FLAGS.gcp_project - ) - - run_config = tf.contrib.tpu.RunConfig( - cluster=tpu_cluster_resolver, - model_dir=FLAGS.model_dir, - session_config=tf.ConfigProto( - allow_soft_placement=True, log_device_placement=True), - tpu_config=tf.contrib.tpu.TPUConfig(FLAGS.iterations, FLAGS.num_shards), - ) - - estimator = tf.contrib.tpu.TPUEstimator( - model_fn=model_fn, - use_tpu=FLAGS.use_tpu, - train_batch_size=FLAGS.batch_size, - eval_batch_size=FLAGS.batch_size, - predict_batch_size=FLAGS.batch_size, - params={"data_dir": FLAGS.data_dir}, - config=run_config) - # TPUEstimator.train *requires* a max_steps argument. - estimator.train(input_fn=train_input_fn, max_steps=FLAGS.train_steps) - # TPUEstimator.evaluate *requires* a steps argument. - # Note that the number of examples used during evaluation is - # --eval_steps * --batch_size. - # So if you change --batch_size then change --eval_steps too. - if FLAGS.eval_steps: - estimator.evaluate(input_fn=eval_input_fn, steps=FLAGS.eval_steps) - - # Run prediction on top few samples of test data. - if FLAGS.enable_predict: - predictions = estimator.predict(input_fn=predict_input_fn) - - for pred_dict in predictions: - template = ('Prediction is "{}" ({:.1f}%).') - - class_id = pred_dict['class_ids'] - probability = pred_dict['probabilities'][class_id] - - print(template.format(class_id, 100 * probability)) - - -if __name__ == "__main__": - tf.app.run() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/README.md b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/README.md deleted file mode 100644 index 28b699fa..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/README.md +++ /dev/null @@ -1,71 +0,0 @@ -# Recommendation Model -## Overview -This is an implementation of the Neural Collaborative Filtering (NCF) framework with Neural Matrix Factorization (NeuMF) model as described in the [Neural Collaborative Filtering](https://arxiv.org/abs/1708.05031) paper. Current implementation is based on the code from the authors' [NCF code](https://github.com/hexiangnan/neural_collaborative_filtering) and the Stanford implementation in the [MLPerf Repo](https://github.com/mlperf/reference/tree/master/recommendation/pytorch). - -NCF is a general framework for collaborative filtering of recommendations in which a neural network architecture is used to model user-item interactions. Unlike traditional models, NCF does not resort to Matrix Factorization (MF) with an inner product on latent features of users and items. It replaces the inner product with a multi-layer perceptron that can learn an arbitrary function from data. - -Two instantiations of NCF are Generalized Matrix Factorization (GMF) and Multi-Layer Perceptron (MLP). GMF applies a linear kernel to model the latent feature interactions, and and MLP uses a nonlinear kernel to learn the interaction function from data. NeuMF is a fused model of GMF and MLP to better model the complex user-item interactions, and unifies the strengths of linearity of MF and non-linearity of MLP for modeling the user-item latent structures. NeuMF allows GMF and MLP to learn separate embeddings, and combines the two models by concatenating their last hidden layer. [neumf_model.py](neumf_model.py) defines the architecture details. - -Some abbreviations used the code base include: - - NCF: Neural Collaborative Filtering - - NeuMF: Neural Matrix Factorization - - GMF: Generalized Matrix Factorization - - MLP: Multi-Layer Perceptron - - HR: Hit Ratio (HR) - - NDCG: Normalized Discounted Cumulative Gain - - ml-1m: MovieLens 1 million dataset - - ml-20m: MovieLens 20 million dataset - -## Dataset -The [MovieLens datasets](http://files.grouplens.org/datasets/movielens/) are used for model training and evaluation. Specifically, we use two datasets: **ml-1m** (short for MovieLens 1 million) and **ml-20m** (short for MovieLens 20 million). - -### ml-1m -ml-1m dataset contains 1,000,209 anonymous ratings of approximately 3,706 movies made by 6,040 users who joined MovieLens in 2000. All ratings are contained in the file "ratings.dat" without header row, and are in the following format: -``` - UserID::MovieID::Rating::Timestamp -``` - - UserIDs range between 1 and 6040. - - MovieIDs range between 1 and 3952. - - Ratings are made on a 5-star scale (whole-star ratings only). - -### ml-20m -ml-20m dataset contains 20,000,263 ratings of 26,744 movies by 138493 users. All ratings are contained in the file "ratings.csv". Each line of this file after the header row represents one rating of one movie by one user, and has the following format: -``` -userId,movieId,rating,timestamp -``` - - The lines within this file are ordered first by userId, then, within user, by movieId. - - Ratings are made on a 5-star scale, with half-star increments (0.5 stars - 5.0 stars). - -In both datasets, the timestamp is represented in seconds since midnight Coordinated Universal Time (UTC) of January 1, 1970. Each user has at least 20 ratings. - -## Running Code - -### Download and preprocess dataset -To download the dataset, please install Pandas package first. Then issue the following command: -``` -python ../datasets/movielens.py -``` -Arguments: - * `--data_dir`: Directory where to download and save the preprocessed data. By default, it is `/tmp/movielens-data/`. - * `--dataset`: The dataset name to be downloaded and preprocessed. By default, it is `ml-1m`. - -Use the `--help` or `-h` flag to get a full list of possible arguments. - -Note the ml-20m dataset is large (the rating file is ~500 MB), and it may take several minutes (~2 mins) for data preprocessing. -Both the ml-1m and ml-20m datasets will be coerced into a common format when downloaded. - -### Train and evaluate model -To train and evaluate the model, issue the following command: -``` -python ncf_main.py -``` -Arguments: - * `--model_dir`: Directory to save model training checkpoints. By default, it is `/tmp/ncf/`. - * `--data_dir`: This should be set to the same directory given to the `data_download`'s `data_dir` argument. - * `--dataset`: The dataset name to be downloaded and preprocessed. By default, it is `ml-1m`. - -There are other arguments about models and training process. Use the `--help` or `-h` flag to get a full list of possible arguments with detailed descriptions. - -## Benchmarks (TODO) -### Training times -### Evaluation results diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/constants.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/constants.py deleted file mode 100644 index ad7b9e3b..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/constants.py +++ /dev/null @@ -1,79 +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. -# ============================================================================== -"""Central location for NCF specific values.""" - -import sys - -import numpy as np - -from official.datasets import movielens - -# ============================================================================== -# == Main Thread Data Processing =============================================== -# ============================================================================== - -# Keys for data shards -TRAIN_USER_KEY = "train_{}".format(movielens.USER_COLUMN) -TRAIN_ITEM_KEY = "train_{}".format(movielens.ITEM_COLUMN) -TRAIN_LABEL_KEY = "train_labels" -MASK_START_INDEX = "mask_start_index" -VALID_POINT_MASK = "valid_point_mask" -EVAL_USER_KEY = "eval_{}".format(movielens.USER_COLUMN) -EVAL_ITEM_KEY = "eval_{}".format(movielens.ITEM_COLUMN) - -USER_MAP = "user_map" -ITEM_MAP = "item_map" - -USER_DTYPE = np.int32 -ITEM_DTYPE = np.int32 - -# In both datasets, each user has at least 20 ratings. -MIN_NUM_RATINGS = 20 - -# The number of negative examples attached with a positive example -# when performing evaluation. -NUM_EVAL_NEGATIVES = 999 - -# keys for evaluation metrics -TOP_K = 10 # Top-k list for evaluation -HR_KEY = "HR" -NDCG_KEY = "NDCG" -DUPLICATE_MASK = "duplicate_mask" - -# Metric names -HR_METRIC_NAME = "HR_METRIC" -NDCG_METRIC_NAME = "NDCG_METRIC" - -# Trying to load a cache created in py2 when running in py3 will cause an -# error due to differences in unicode handling. -RAW_CACHE_FILE = "raw_data_cache_py{}.pickle".format(sys.version_info[0]) -CACHE_INVALIDATION_SEC = 3600 * 24 - -# ============================================================================== -# == Data Generation =========================================================== -# ============================================================================== -CYCLES_TO_BUFFER = 3 # The number of train cycles worth of data to "run ahead" - # of the main training loop. - -# Number of batches to run per epoch when using synthetic data. At high batch -# sizes, we run for more batches than with real data, which is good since -# running more batches reduces noise when measuring the average batches/second. -SYNTHETIC_BATCHES_PER_EPOCH = 2000 - -# Only used when StreamingFilesDataset is used. -NUM_FILE_SHARDS = 16 -TRAIN_FOLDER_TEMPLATE = "training_cycle_{}" -EVAL_FOLDER = "eval_data" -SHARD_TEMPLATE = "shard_{}.tfrecords" diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/data_pipeline.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/data_pipeline.py deleted file mode 100644 index e50110d4..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/data_pipeline.py +++ /dev/null @@ -1,873 +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. -# ============================================================================== -"""Asynchronous data producer for the NCF pipeline.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import atexit -import functools -import os -import sys -import tempfile -import threading -import time -import timeit -import traceback -import typing - -import numpy as np -import six -from six.moves import queue -import tensorflow as tf -from tensorflow.contrib.tpu.python.tpu.datasets import StreamingFilesDataset - -from official.datasets import movielens -from official.recommendation import constants as rconst -from official.recommendation import popen_helper -from official.recommendation import stat_utils - - -SUMMARY_TEMPLATE = """General: -{spacer}Num users: {num_users} -{spacer}Num items: {num_items} - -Training: -{spacer}Positive count: {train_pos_ct} -{spacer}Batch size: {train_batch_size} {multiplier} -{spacer}Batch count per epoch: {train_batch_ct} - -Eval: -{spacer}Positive count: {eval_pos_ct} -{spacer}Batch size: {eval_batch_size} {multiplier} -{spacer}Batch count per epoch: {eval_batch_ct}""" - - -_TRAIN_FEATURE_MAP = { - movielens.USER_COLUMN: tf.FixedLenFeature([], dtype=tf.string), - movielens.ITEM_COLUMN: tf.FixedLenFeature([], dtype=tf.string), - rconst.MASK_START_INDEX: tf.FixedLenFeature([1], dtype=tf.string), - "labels": tf.FixedLenFeature([], dtype=tf.string), -} - - -_EVAL_FEATURE_MAP = { - movielens.USER_COLUMN: tf.FixedLenFeature([], dtype=tf.string), - movielens.ITEM_COLUMN: tf.FixedLenFeature([], dtype=tf.string), - rconst.DUPLICATE_MASK: tf.FixedLenFeature([], dtype=tf.string) -} - - -class DatasetManager(object): - """Helper class for handling TensorFlow specific data tasks. - - This class takes the (relatively) framework agnostic work done by the data - constructor classes and handles the TensorFlow specific portions (TFRecord - management, tf.Dataset creation, etc.). - """ - def __init__(self, is_training, stream_files, batches_per_epoch, - shard_root=None, deterministic=False): - # type: (bool, bool, int, typing.Optional[str], bool) -> None - """Constructs a `DatasetManager` instance. - Args: - is_training: Boolean of whether the data provided is training or - evaluation data. This determines whether to reuse the data - (if is_training=False) and the exact structure to use when storing and - yielding data. - stream_files: Boolean indicating whether data should be serialized and - written to file shards. - batches_per_epoch: The number of batches in a single epoch. - shard_root: The base directory to be used when stream_files=True. - deterministic: Forgo non-deterministic speedups. (i.e. sloppy=True) - """ - self._is_training = is_training - self._deterministic = deterministic - self._stream_files = stream_files - self._writers = [] - self._write_locks = [threading.RLock() for _ in - range(rconst.NUM_FILE_SHARDS)] if stream_files else [] - self._batches_per_epoch = batches_per_epoch - self._epochs_completed = 0 - self._epochs_requested = 0 - self._shard_root = shard_root - - self._result_queue = queue.Queue() - self._result_reuse = [] - - @property - def current_data_root(self): - subdir = (rconst.TRAIN_FOLDER_TEMPLATE.format(self._epochs_completed) - if self._is_training else rconst.EVAL_FOLDER) - return os.path.join(self._shard_root, subdir) - - def buffer_reached(self): - # Only applicable for training. - return (self._epochs_completed - self._epochs_requested >= - rconst.CYCLES_TO_BUFFER and self._is_training) - - @staticmethod - def _serialize(data): - """Convert NumPy arrays into a TFRecords entry.""" - - feature_dict = { - k: tf.train.Feature(bytes_list=tf.train.BytesList( - value=[memoryview(v).tobytes()])) for k, v in data.items()} - - return tf.train.Example( - features=tf.train.Features(feature=feature_dict)).SerializeToString() - - def _deserialize(self, serialized_data, batch_size): - """Convert serialized TFRecords into tensors. - - Args: - serialized_data: A tensor containing serialized records. - batch_size: The data arrives pre-batched, so batch size is needed to - deserialize the data. - """ - feature_map = _TRAIN_FEATURE_MAP if self._is_training else _EVAL_FEATURE_MAP - features = tf.parse_single_example(serialized_data, feature_map) - - users = tf.reshape(tf.decode_raw( - features[movielens.USER_COLUMN], rconst.USER_DTYPE), (batch_size,)) - items = tf.reshape(tf.decode_raw( - features[movielens.ITEM_COLUMN], rconst.ITEM_DTYPE), (batch_size,)) - - def decode_binary(data_bytes): - # tf.decode_raw does not support bool as a decode type. As a result it is - # necessary to decode to int8 (7 of the bits will be ignored) and then - # cast to bool. - return tf.reshape(tf.cast(tf.decode_raw(data_bytes, tf.int8), tf.bool), - (batch_size,)) - - if self._is_training: - mask_start_index = tf.decode_raw( - features[rconst.MASK_START_INDEX], tf.int32)[0] - valid_point_mask = tf.less(tf.range(batch_size), mask_start_index) - - return { - movielens.USER_COLUMN: users, - movielens.ITEM_COLUMN: items, - rconst.VALID_POINT_MASK: valid_point_mask, - }, decode_binary(features["labels"]) - - return { - movielens.USER_COLUMN: users, - movielens.ITEM_COLUMN: items, - rconst.DUPLICATE_MASK: decode_binary(features[rconst.DUPLICATE_MASK]), - } - - def put(self, index, data): - # type: (int, dict) -> None - """Store data for later consumption. - - Because there are several paths for storing and yielding data (queues, - lists, files) the data producer simply provides the data in a standard - format at which point the dataset manager handles storing it in the correct - form. - - Args: - index: Used to select shards when writing to files. - data: A dict of the data to be stored. This method mutates data, and - therefore expects to be the only consumer. - """ - - if self._stream_files: - example_bytes = self._serialize(data) - with self._write_locks[index % rconst.NUM_FILE_SHARDS]: - self._writers[index % rconst.NUM_FILE_SHARDS].write(example_bytes) - - else: - if self._is_training: - mask_start_index = data.pop(rconst.MASK_START_INDEX) - batch_size = data[movielens.ITEM_COLUMN].shape[0] - data[rconst.VALID_POINT_MASK] = np.less(np.arange(batch_size), - mask_start_index) - data = (data, data.pop("labels")) - self._result_queue.put(data) - - def start_construction(self): - if self._stream_files: - tf.gfile.MakeDirs(self.current_data_root) - template = os.path.join(self.current_data_root, rconst.SHARD_TEMPLATE) - self._writers = [tf.io.TFRecordWriter(template.format(i)) - for i in range(rconst.NUM_FILE_SHARDS)] - - def end_construction(self): - if self._stream_files: - [writer.close() for writer in self._writers] - self._writers = [] - self._result_queue.put(self.current_data_root) - - self._epochs_completed += 1 - - def data_generator(self, epochs_between_evals): - """Yields examples during local training.""" - assert not self._stream_files - assert self._is_training or epochs_between_evals == 1 - - if self._is_training: - for _ in range(self._batches_per_epoch * epochs_between_evals): - yield self._result_queue.get(timeout=300) - - else: - if self._result_reuse: - assert len(self._result_reuse) == self._batches_per_epoch - - for i in self._result_reuse: - yield i - else: - # First epoch. - for _ in range(self._batches_per_epoch * epochs_between_evals): - result = self._result_queue.get(timeout=300) - self._result_reuse.append(result) - yield result - - - def get_dataset(self, batch_size, epochs_between_evals): - """Construct the dataset to be used for training and eval. - - For local training, data is provided through Dataset.from_generator. For - remote training (TPUs) the data is first serialized to files and then sent - to the TPU through a StreamingFilesDataset. - - Args: - batch_size: The per-device batch size of the dataset. - epochs_between_evals: How many epochs worth of data to yield. - (Generator mode only.) - """ - self._epochs_requested += 1 - if self._stream_files: - if epochs_between_evals > 1: - raise ValueError("epochs_between_evals > 1 not supported for file " - "based dataset.") - epoch_data_dir = self._result_queue.get(timeout=300) - if not self._is_training: - self._result_queue.put(epoch_data_dir) # Eval data is reused. - - file_pattern = os.path.join( - epoch_data_dir, rconst.SHARD_TEMPLATE.format("*")) - dataset = StreamingFilesDataset( - files=file_pattern, worker_job="worker", - num_parallel_reads=rconst.NUM_FILE_SHARDS, num_epochs=1, - sloppy=not self._deterministic) - map_fn = functools.partial(self._deserialize, batch_size=batch_size) - dataset = dataset.map(map_fn, num_parallel_calls=16) - - else: - types = {movielens.USER_COLUMN: rconst.USER_DTYPE, - movielens.ITEM_COLUMN: rconst.ITEM_DTYPE} - shapes = {movielens.USER_COLUMN: tf.TensorShape([batch_size]), - movielens.ITEM_COLUMN: tf.TensorShape([batch_size])} - - if self._is_training: - types[rconst.VALID_POINT_MASK] = np.bool - shapes[rconst.VALID_POINT_MASK] = tf.TensorShape([batch_size]) - - types = (types, np.bool) - shapes = (shapes, tf.TensorShape([batch_size])) - - else: - types[rconst.DUPLICATE_MASK] = np.bool - shapes[rconst.DUPLICATE_MASK] = tf.TensorShape([batch_size]) - - data_generator = functools.partial( - self.data_generator, epochs_between_evals=epochs_between_evals) - dataset = tf.data.Dataset.from_generator( - generator=data_generator, output_types=types, - output_shapes=shapes) - - return dataset.prefetch(16) - - def make_input_fn(self, batch_size): - """Create an input_fn which checks for batch size consistency.""" - - def input_fn(params): - param_batch_size = (params["batch_size"] if self._is_training else - params["eval_batch_size"]) - if batch_size != param_batch_size: - raise ValueError("producer batch size ({}) differs from params batch " - "size ({})".format(batch_size, param_batch_size)) - - epochs_between_evals = (params.get("epochs_between_evals", 1) - if self._is_training else 1) - return self.get_dataset(batch_size=batch_size, - epochs_between_evals=epochs_between_evals) - - return input_fn - - -class BaseDataConstructor(threading.Thread): - """Data constructor base class. - - This class manages the control flow for constructing data. It is not meant - to be used directly, but instead subclasses should implement the following - two methods: - - self.construct_lookup_variables - self.lookup_negative_items - - """ - def __init__(self, - maximum_number_epochs, # type: int - num_users, # type: int - num_items, # type: int - user_map, # type: dict - item_map, # type: dict - train_pos_users, # type: np.ndarray - train_pos_items, # type: np.ndarray - train_batch_size, # type: int - batches_per_train_step, # type: int - num_train_negatives, # type: int - eval_pos_users, # type: np.ndarray - eval_pos_items, # type: np.ndarray - eval_batch_size, # type: int - batches_per_eval_step, # type: int - stream_files, # type: bool - deterministic=False # type: bool - ): - # General constants - self._maximum_number_epochs = maximum_number_epochs - self._num_users = num_users - self._num_items = num_items - self.user_map = user_map - self.item_map = item_map - self._train_pos_users = train_pos_users - self._train_pos_items = train_pos_items - self.train_batch_size = train_batch_size - self._num_train_negatives = num_train_negatives - self._batches_per_train_step = batches_per_train_step - self._eval_pos_users = eval_pos_users - self._eval_pos_items = eval_pos_items - self.eval_batch_size = eval_batch_size - - # Training - if self._train_pos_users.shape != self._train_pos_items.shape: - raise ValueError( - "User positives ({}) is different from item positives ({})".format( - self._train_pos_users.shape, self._train_pos_items.shape)) - - (self._train_pos_count,) = self._train_pos_users.shape - self._elements_in_epoch = (1 + num_train_negatives) * self._train_pos_count - self.train_batches_per_epoch = self._count_batches( - self._elements_in_epoch, train_batch_size, batches_per_train_step) - - # Evaluation - if eval_batch_size % (1 + rconst.NUM_EVAL_NEGATIVES): - raise ValueError("Eval batch size {} is not divisible by {}".format( - eval_batch_size, 1 + rconst.NUM_EVAL_NEGATIVES)) - self._eval_users_per_batch = int( - eval_batch_size // (1 + rconst.NUM_EVAL_NEGATIVES)) - self._eval_elements_in_epoch = num_users * (1 + rconst.NUM_EVAL_NEGATIVES) - self.eval_batches_per_epoch = self._count_batches( - self._eval_elements_in_epoch, eval_batch_size, batches_per_eval_step) - - # Intermediate artifacts - self._current_epoch_order = np.empty(shape=(0,)) - self._shuffle_iterator = None - - self._shuffle_with_forkpool = not stream_files - if stream_files: - self._shard_root = tempfile.mkdtemp(prefix="ncf_") - atexit.register(tf.gfile.DeleteRecursively, dirname=self._shard_root) - else: - self._shard_root = None - - self._train_dataset = DatasetManager( - True, stream_files, self.train_batches_per_epoch, self._shard_root, - deterministic) - self._eval_dataset = DatasetManager( - False, stream_files, self.eval_batches_per_epoch, self._shard_root, - deterministic) - - # Threading details - super(BaseDataConstructor, self).__init__() - self.daemon = True - self._stop_loop = False - self._fatal_exception = None - self.deterministic = deterministic - - def __str__(self): - multiplier = ("(x{} devices)".format(self._batches_per_train_step) - if self._batches_per_train_step > 1 else "") - summary = SUMMARY_TEMPLATE.format( - spacer=" ", num_users=self._num_users, num_items=self._num_items, - train_pos_ct=self._train_pos_count, - train_batch_size=self.train_batch_size, - train_batch_ct=self.train_batches_per_epoch, - eval_pos_ct=self._num_users, eval_batch_size=self.eval_batch_size, - eval_batch_ct=self.eval_batches_per_epoch, multiplier=multiplier) - return super(BaseDataConstructor, self).__str__() + "\n" + summary - - @staticmethod - def _count_batches(example_count, batch_size, batches_per_step): - """Determine the number of batches, rounding up to fill all devices.""" - x = (example_count + batch_size - 1) // batch_size - return (x + batches_per_step - 1) // batches_per_step * batches_per_step - - def stop_loop(self): - self._stop_loop = True - - def construct_lookup_variables(self): - """Perform any one time pre-compute work.""" - raise NotImplementedError - - def lookup_negative_items(self, **kwargs): - """Randomly sample negative items for given users.""" - raise NotImplementedError - - def _run(self): - atexit.register(self.stop_loop) - self._start_shuffle_iterator() - self.construct_lookup_variables() - self._construct_training_epoch() - self._construct_eval_epoch() - for _ in range(self._maximum_number_epochs - 1): - self._construct_training_epoch() - self.stop_loop() - - def run(self): - try: - self._run() - except Exception as e: - # The Thread base class swallows stack traces, so unfortunately it is - # necessary to catch and re-raise to get debug output - traceback.print_exc() - self._fatal_exception = e - sys.stderr.flush() - raise - - def _start_shuffle_iterator(self): - if self._shuffle_with_forkpool: - pool = popen_helper.get_forkpool(3, closing=False) - else: - pool = popen_helper.get_threadpool(1, closing=False) - atexit.register(pool.close) - args = [(self._elements_in_epoch, stat_utils.random_int32()) - for _ in range(self._maximum_number_epochs)] - imap = pool.imap if self.deterministic else pool.imap_unordered - self._shuffle_iterator = imap(stat_utils.permutation, args) - - def _get_training_batch(self, i): - """Construct a single batch of training data. - - Args: - i: The index of the batch. This is used when stream_files=True to assign - data to file shards. - """ - batch_indices = self._current_epoch_order[i * self.train_batch_size: - (i + 1) * self.train_batch_size] - (mask_start_index,) = batch_indices.shape - - batch_ind_mod = np.mod(batch_indices, self._train_pos_count) - users = self._train_pos_users[batch_ind_mod] - - negative_indices = np.greater_equal(batch_indices, self._train_pos_count) - negative_users = users[negative_indices] - - negative_items = self.lookup_negative_items(negative_users=negative_users) - - items = self._train_pos_items[batch_ind_mod] - items[negative_indices] = negative_items - - labels = np.logical_not(negative_indices) - - # Pad last partial batch - pad_length = self.train_batch_size - mask_start_index - if pad_length: - # We pad with arange rather than zeros because the network will still - # compute logits for padded examples, and padding with zeros would create - # a very "hot" embedding key which can have performance implications. - user_pad = np.arange(pad_length, dtype=users.dtype) % self._num_users - item_pad = np.arange(pad_length, dtype=items.dtype) % self._num_items - label_pad = np.zeros(shape=(pad_length,), dtype=labels.dtype) - users = np.concatenate([users, user_pad]) - items = np.concatenate([items, item_pad]) - labels = np.concatenate([labels, label_pad]) - - self._train_dataset.put(i, { - movielens.USER_COLUMN: users, - movielens.ITEM_COLUMN: items, - rconst.MASK_START_INDEX: np.array(mask_start_index, dtype=np.int32), - "labels": labels, - }) - - def _wait_to_construct_train_epoch(self): - count = 0 - while self._train_dataset.buffer_reached() and not self._stop_loop: - time.sleep(0.01) - count += 1 - if count >= 100 and np.log10(count) == np.round(np.log10(count)): - tf.logging.info( - "Waited {} times for training data to be consumed".format(count)) - - def _construct_training_epoch(self): - """Loop to construct a batch of training data.""" - self._wait_to_construct_train_epoch() - start_time = timeit.default_timer() - if self._stop_loop: - return - - self._train_dataset.start_construction() - map_args = list(range(self.train_batches_per_epoch)) - self._current_epoch_order = next(self._shuffle_iterator) - - get_pool = (popen_helper.get_fauxpool if self.deterministic else - popen_helper.get_threadpool) - with get_pool(6) as pool: - pool.map(self._get_training_batch, map_args) - self._train_dataset.end_construction() - - tf.logging.info("Epoch construction complete. Time: {:.1f} seconds".format( - timeit.default_timer() - start_time)) - - @staticmethod - def _assemble_eval_batch(users, positive_items, negative_items, - users_per_batch): - """Construct duplicate_mask and structure data accordingly. - - The positive items should be last so that they lose ties. However, they - should not be masked out if the true eval positive happens to be - selected as a negative. So instead, the positive is placed in the first - position, and then switched with the last element after the duplicate - mask has been computed. - - Args: - users: An array of users in a batch. (should be identical along axis 1) - positive_items: An array (batch_size x 1) of positive item indices. - negative_items: An array of negative item indices. - users_per_batch: How many users should be in the batch. This is passed - as an argument so that ncf_test.py can use this method. - - Returns: - User, item, and duplicate_mask arrays. - """ - items = np.concatenate([positive_items, negative_items], axis=1) - - # We pad the users and items here so that the duplicate mask calculation - # will include padding. The metric function relies on all padded elements - # except the positive being marked as duplicate to mask out padded points. - if users.shape[0] < users_per_batch: - pad_rows = users_per_batch - users.shape[0] - padding = np.zeros(shape=(pad_rows, users.shape[1]), dtype=np.int32) - users = np.concatenate([users, padding.astype(users.dtype)], axis=0) - items = np.concatenate([items, padding.astype(items.dtype)], axis=0) - - duplicate_mask = stat_utils.mask_duplicates(items, axis=1).astype(np.bool) - - items[:, (0, -1)] = items[:, (-1, 0)] - duplicate_mask[:, (0, -1)] = duplicate_mask[:, (-1, 0)] - - assert users.shape == items.shape == duplicate_mask.shape - return users, items, duplicate_mask - - def _get_eval_batch(self, i): - """Construct a single batch of evaluation data. - - Args: - i: The index of the batch. - """ - low_index = i * self._eval_users_per_batch - high_index = (i + 1) * self._eval_users_per_batch - users = np.repeat(self._eval_pos_users[low_index:high_index, np.newaxis], - 1 + rconst.NUM_EVAL_NEGATIVES, axis=1) - positive_items = self._eval_pos_items[low_index:high_index, np.newaxis] - negative_items = (self.lookup_negative_items(negative_users=users[:, :-1]) - .reshape(-1, rconst.NUM_EVAL_NEGATIVES)) - - users, items, duplicate_mask = self._assemble_eval_batch( - users, positive_items, negative_items, self._eval_users_per_batch) - - self._eval_dataset.put(i, { - movielens.USER_COLUMN: users.flatten(), - movielens.ITEM_COLUMN: items.flatten(), - rconst.DUPLICATE_MASK: duplicate_mask.flatten(), - }) - - def _construct_eval_epoch(self): - """Loop to construct data for evaluation.""" - if self._stop_loop: - return - - start_time = timeit.default_timer() - - self._eval_dataset.start_construction() - map_args = [i for i in range(self.eval_batches_per_epoch)] - - get_pool = (popen_helper.get_fauxpool if self.deterministic else - popen_helper.get_threadpool) - with get_pool(6) as pool: - pool.map(self._get_eval_batch, map_args) - self._eval_dataset.end_construction() - - tf.logging.info("Eval construction complete. Time: {:.1f} seconds".format( - timeit.default_timer() - start_time)) - - def make_input_fn(self, is_training): - # It isn't feasible to provide a foolproof check, so this is designed to - # catch most failures rather than provide an exhaustive guard. - if self._fatal_exception is not None: - raise ValueError("Fatal exception in the data production loop: {}" - .format(self._fatal_exception)) - - return ( - self._train_dataset.make_input_fn(self.train_batch_size) if is_training - else self._eval_dataset.make_input_fn(self.eval_batch_size)) - - -class DummyConstructor(threading.Thread): - """Class for running with synthetic data.""" - def run(self): - pass - - def stop_loop(self): - pass - - @staticmethod - def make_input_fn(is_training): - """Construct training input_fn that uses synthetic data.""" - - def input_fn(params): - """Generated input_fn for the given epoch.""" - batch_size = (params["batch_size"] if is_training else - params["eval_batch_size"]) - num_users = params["num_users"] - num_items = params["num_items"] - - users = tf.random_uniform([batch_size], dtype=tf.int32, minval=0, - maxval=num_users) - items = tf.random_uniform([batch_size], dtype=tf.int32, minval=0, - maxval=num_items) - - if is_training: - valid_point_mask = tf.cast(tf.random_uniform( - [batch_size], dtype=tf.int32, minval=0, maxval=2), tf.bool) - labels = tf.cast(tf.random_uniform( - [batch_size], dtype=tf.int32, minval=0, maxval=2), tf.bool) - data = { - movielens.USER_COLUMN: users, - movielens.ITEM_COLUMN: items, - rconst.VALID_POINT_MASK: valid_point_mask, - }, labels - else: - dupe_mask = tf.cast(tf.random_uniform([batch_size], dtype=tf.int32, - minval=0, maxval=2), tf.bool) - data = { - movielens.USER_COLUMN: users, - movielens.ITEM_COLUMN: items, - rconst.DUPLICATE_MASK: dupe_mask, - } - - dataset = tf.data.Dataset.from_tensors(data).repeat( - rconst.SYNTHETIC_BATCHES_PER_EPOCH * params["batches_per_step"]) - dataset = dataset.prefetch(32) - return dataset - - return input_fn - - -class MaterializedDataConstructor(BaseDataConstructor): - """Materialize a table of negative examples for fast negative generation. - - This class creates a table (num_users x num_items) containing all of the - negative examples for each user. This table is conceptually ragged; that is to - say the items dimension will have a number of unused elements at the end equal - to the number of positive elements for a given user. For instance: - - num_users = 3 - num_items = 5 - positives = [[1, 3], [0], [1, 2, 3, 4]] - - will generate a negative table: - [ - [0 2 4 int32max int32max], - [1 2 3 4 int32max], - [0 int32max int32max int32max int32max], - ] - - and a vector of per-user negative counts, which in this case would be: - [3, 4, 1] - - When sampling negatives, integers are (nearly) uniformly selected from the - range [0, per_user_neg_count[user]) which gives a column_index, at which - point the negative can be selected as: - negative_table[user, column_index] - - This technique will not scale; however MovieLens is small enough that even - a pre-compute which is quadratic in problem size will still fit in memory. A - more scalable lookup method is in the works. - """ - def __init__(self, *args, **kwargs): - super(MaterializedDataConstructor, self).__init__(*args, **kwargs) - self._negative_table = None - self._per_user_neg_count = None - - def construct_lookup_variables(self): - # Materialize negatives for fast lookup sampling. - start_time = timeit.default_timer() - inner_bounds = np.argwhere(self._train_pos_users[1:] - - self._train_pos_users[:-1])[:, 0] + 1 - (upper_bound,) = self._train_pos_users.shape - index_bounds = [0] + inner_bounds.tolist() + [upper_bound] - self._negative_table = np.zeros(shape=(self._num_users, self._num_items), - dtype=rconst.ITEM_DTYPE) - - # Set the table to the max value to make sure the embedding lookup will fail - # if we go out of bounds, rather than just overloading item zero. - self._negative_table += np.iinfo(rconst.ITEM_DTYPE).max - assert self._num_items < np.iinfo(rconst.ITEM_DTYPE).max - - # Reuse arange during generation. np.delete will make a copy. - full_set = np.arange(self._num_items, dtype=rconst.ITEM_DTYPE) - - self._per_user_neg_count = np.zeros( - shape=(self._num_users,), dtype=np.int32) - - # Threading does not improve this loop. For some reason, the np.delete - # call does not parallelize well. Multiprocessing incurs too much - # serialization overhead to be worthwhile. - for i in range(self._num_users): - positives = self._train_pos_items[index_bounds[i]:index_bounds[i+1]] - negatives = np.delete(full_set, positives) - self._per_user_neg_count[i] = self._num_items - positives.shape[0] - self._negative_table[i, :self._per_user_neg_count[i]] = negatives - - tf.logging.info("Negative sample table built. Time: {:.1f} seconds".format( - timeit.default_timer() - start_time)) - - def lookup_negative_items(self, negative_users, **kwargs): - negative_item_choice = stat_utils.very_slightly_biased_randint( - self._per_user_neg_count[negative_users]) - return self._negative_table[negative_users, negative_item_choice] - - -class BisectionDataConstructor(BaseDataConstructor): - """Use bisection to index within positive examples. - - This class tallies the number of negative items which appear before each - positive item for a user. This means that in order to select the ith negative - item for a user, it only needs to determine which two positive items bound - it at which point the item id for the ith negative is a simply algebraic - expression. - """ - def __init__(self, *args, **kwargs): - super(BisectionDataConstructor, self).__init__(*args, **kwargs) - self.index_bounds = None - self._sorted_train_pos_items = None - self._total_negatives = None - - def _index_segment(self, user): - lower, upper = self.index_bounds[user:user+2] - items = self._sorted_train_pos_items[lower:upper] - - negatives_since_last_positive = np.concatenate( - [items[0][np.newaxis], items[1:] - items[:-1] - 1]) - - return np.cumsum(negatives_since_last_positive) - - def construct_lookup_variables(self): - start_time = timeit.default_timer() - inner_bounds = np.argwhere(self._train_pos_users[1:] - - self._train_pos_users[:-1])[:, 0] + 1 - (upper_bound,) = self._train_pos_users.shape - self.index_bounds = np.array([0] + inner_bounds.tolist() + [upper_bound]) - - # Later logic will assume that the users are in sequential ascending order. - assert np.array_equal(self._train_pos_users[self.index_bounds[:-1]], - np.arange(self._num_users)) - - self._sorted_train_pos_items = self._train_pos_items.copy() - - for i in range(self._num_users): - lower, upper = self.index_bounds[i:i+2] - self._sorted_train_pos_items[lower:upper].sort() - - self._total_negatives = np.concatenate([ - self._index_segment(i) for i in range(self._num_users)]) - - tf.logging.info("Negative total vector built. Time: {:.1f} seconds".format( - timeit.default_timer() - start_time)) - - def lookup_negative_items(self, negative_users, **kwargs): - output = np.zeros(shape=negative_users.shape, dtype=rconst.ITEM_DTYPE) - 1 - - left_index = self.index_bounds[negative_users] - right_index = self.index_bounds[negative_users + 1] - 1 - - num_positives = right_index - left_index + 1 - num_negatives = self._num_items - num_positives - neg_item_choice = stat_utils.very_slightly_biased_randint(num_negatives) - - # Shortcuts: - # For points where the negative is greater than or equal to the tally before - # the last positive point there is no need to bisect. Instead the item id - # corresponding to the negative item choice is simply: - # last_postive_index + 1 + (neg_choice - last_negative_tally) - # Similarly, if the selection is less than the tally at the first positive - # then the item_id is simply the selection. - # - # Because MovieLens organizes popular movies into low integers (which is - # preserved through the preprocessing), the first shortcut is very - # efficient, allowing ~60% of samples to bypass the bisection. For the same - # reason, the second shortcut is rarely triggered (<0.02%) and is therefore - # not worth implementing. - use_shortcut = neg_item_choice >= self._total_negatives[right_index] - output[use_shortcut] = ( - self._sorted_train_pos_items[right_index] + 1 + - (neg_item_choice - self._total_negatives[right_index]) - )[use_shortcut] - - if np.all(use_shortcut): - # The bisection code is ill-posed when there are no elements. - return output - - not_use_shortcut = np.logical_not(use_shortcut) - left_index = left_index[not_use_shortcut] - right_index = right_index[not_use_shortcut] - neg_item_choice = neg_item_choice[not_use_shortcut] - - num_loops = np.max( - np.ceil(np.log2(num_positives[not_use_shortcut])).astype(np.int32)) - - for i in range(num_loops): - mid_index = (left_index + right_index) // 2 - right_criteria = self._total_negatives[mid_index] > neg_item_choice - left_criteria = np.logical_not(right_criteria) - - right_index[right_criteria] = mid_index[right_criteria] - left_index[left_criteria] = mid_index[left_criteria] - - # Expected state after bisection pass: - # The right index is the smallest index whose tally is greater than the - # negative item choice index. - - assert np.all((right_index - left_index) <= 1) - - output[not_use_shortcut] = ( - self._sorted_train_pos_items[right_index] - - (self._total_negatives[right_index] - neg_item_choice) - ) - - assert np.all(output >= 0) - - return output - - -def get_constructor(name): - if name == "bisection": - return BisectionDataConstructor - if name == "materialized": - return MaterializedDataConstructor - raise ValueError("Unrecognized constructor: {}".format(name)) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/data_preprocessing.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/data_preprocessing.py deleted file mode 100644 index 56a1e615..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/data_preprocessing.py +++ /dev/null @@ -1,232 +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. -# ============================================================================== -"""Preprocess dataset and construct any necessary artifacts.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os -import pickle -import time -import timeit -import typing - -# pylint: disable=wrong-import-order -import numpy as np -import pandas as pd -import tensorflow as tf -# pylint: enable=wrong-import-order - -from official.datasets import movielens -from official.recommendation import constants as rconst -from official.recommendation import data_pipeline -from official.utils.logs import mlperf_helper - - -DATASET_TO_NUM_USERS_AND_ITEMS = { - "ml-1m": (6040, 3706), - "ml-20m": (138493, 26744) -} - - -_EXPECTED_CACHE_KEYS = ( - rconst.TRAIN_USER_KEY, rconst.TRAIN_ITEM_KEY, rconst.EVAL_USER_KEY, - rconst.EVAL_ITEM_KEY, rconst.USER_MAP, rconst.ITEM_MAP) - - -def _filter_index_sort(raw_rating_path, cache_path): - # type: (str, str, bool) -> (dict, bool) - """Read in data CSV, and output structured data. - - This function reads in the raw CSV of positive items, and performs three - preprocessing transformations: - - 1) Filter out all users who have not rated at least a certain number - of items. (Typically 20 items) - - 2) Zero index the users and items such that the largest user_id is - `num_users - 1` and the largest item_id is `num_items - 1` - - 3) Sort the dataframe by user_id, with timestamp as a secondary sort key. - This allows the dataframe to be sliced by user in-place, and for the last - item to be selected simply by calling the `-1` index of a user's slice. - - While all of these transformations are performed by Pandas (and are therefore - single-threaded), they only take ~2 minutes, and the overhead to apply a - MapReduce pattern to parallel process the dataset adds significant complexity - for no computational gain. For a larger dataset parallelizing this - preprocessing could yield speedups. (Also, this preprocessing step is only - performed once for an entire run. - - Args: - raw_rating_path: The path to the CSV which contains the raw dataset. - cache_path: The path to the file where results of this function are saved. - - Returns: - A filtered, zero-index remapped, sorted dataframe, a dict mapping raw user - IDs to regularized user IDs, and a dict mapping raw item IDs to regularized - item IDs. - """ - valid_cache = tf.gfile.Exists(cache_path) - if valid_cache: - with tf.gfile.Open(cache_path, "rb") as f: - cached_data = pickle.load(f) - - cache_age = time.time() - cached_data.get("create_time", 0) - if cache_age > rconst.CACHE_INVALIDATION_SEC: - valid_cache = False - - for key in _EXPECTED_CACHE_KEYS: - if key not in cached_data: - valid_cache = False - - if not valid_cache: - tf.logging.info("Removing stale raw data cache file.") - tf.gfile.Remove(cache_path) - - if valid_cache: - data = cached_data - else: - with tf.gfile.Open(raw_rating_path) as f: - df = pd.read_csv(f) - - # Get the info of users who have more than 20 ratings on items - grouped = df.groupby(movielens.USER_COLUMN) - df = grouped.filter( - lambda x: len(x) >= rconst.MIN_NUM_RATINGS) # type: pd.DataFrame - - original_users = df[movielens.USER_COLUMN].unique() - original_items = df[movielens.ITEM_COLUMN].unique() - - # Map the ids of user and item to 0 based index for following processing - tf.logging.info("Generating user_map and item_map...") - user_map = {user: index for index, user in enumerate(original_users)} - item_map = {item: index for index, item in enumerate(original_items)} - - df[movielens.USER_COLUMN] = df[movielens.USER_COLUMN].apply( - lambda user: user_map[user]) - df[movielens.ITEM_COLUMN] = df[movielens.ITEM_COLUMN].apply( - lambda item: item_map[item]) - - num_users = len(original_users) - num_items = len(original_items) - - mlperf_helper.ncf_print(key=mlperf_helper.TAGS.PREPROC_HP_NUM_EVAL, - value=rconst.NUM_EVAL_NEGATIVES) - - assert num_users <= np.iinfo(rconst.USER_DTYPE).max - assert num_items <= np.iinfo(rconst.ITEM_DTYPE).max - assert df[movielens.USER_COLUMN].max() == num_users - 1 - assert df[movielens.ITEM_COLUMN].max() == num_items - 1 - - # This sort is used to shard the dataframe by user, and later to select - # the last item for a user to be used in validation. - tf.logging.info("Sorting by user, timestamp...") - - # This sort is equivalent to - # df.sort_values([movielens.USER_COLUMN, movielens.TIMESTAMP_COLUMN], - # inplace=True) - # except that the order of items with the same user and timestamp are - # sometimes different. For some reason, this sort results in a better - # hit-rate during evaluation, matching the performance of the MLPerf - # reference implementation. - df.sort_values(by=movielens.TIMESTAMP_COLUMN, inplace=True) - df.sort_values([movielens.USER_COLUMN, movielens.TIMESTAMP_COLUMN], - inplace=True, kind="mergesort") - - df = df.reset_index() # The dataframe does not reconstruct indices in the - # sort or filter steps. - - grouped = df.groupby(movielens.USER_COLUMN, group_keys=False) - eval_df, train_df = grouped.tail(1), grouped.apply(lambda x: x.iloc[:-1]) - - data = { - rconst.TRAIN_USER_KEY: train_df[movielens.USER_COLUMN] - .values.astype(rconst.USER_DTYPE), - rconst.TRAIN_ITEM_KEY: train_df[movielens.ITEM_COLUMN] - .values.astype(rconst.ITEM_DTYPE), - rconst.EVAL_USER_KEY: eval_df[movielens.USER_COLUMN] - .values.astype(rconst.USER_DTYPE), - rconst.EVAL_ITEM_KEY: eval_df[movielens.ITEM_COLUMN] - .values.astype(rconst.ITEM_DTYPE), - rconst.USER_MAP: user_map, - rconst.ITEM_MAP: item_map, - "create_time": time.time(), - } - - tf.logging.info("Writing raw data cache.") - with tf.gfile.Open(cache_path, "wb") as f: - pickle.dump(data, f, protocol=pickle.HIGHEST_PROTOCOL) - - # TODO(robieta): MLPerf cache clear. - return data, valid_cache - - -def instantiate_pipeline(dataset, data_dir, params, constructor_type=None, - deterministic=False): - # type: (str, str, dict, typing.Optional[str], bool) -> (NCFDataset, typing.Callable) - """Load and digest data CSV into a usable form. - - Args: - dataset: The name of the dataset to be used. - data_dir: The root directory of the dataset. - params: dict of parameters for the run. - constructor_type: The name of the constructor subclass that should be used - for the input pipeline. - deterministic: Tell the data constructor to produce deterministically. - """ - tf.logging.info("Beginning data preprocessing.") - - st = timeit.default_timer() - raw_rating_path = os.path.join(data_dir, dataset, movielens.RATINGS_FILE) - cache_path = os.path.join(data_dir, dataset, rconst.RAW_CACHE_FILE) - - raw_data, _ = _filter_index_sort(raw_rating_path, cache_path) - user_map, item_map = raw_data["user_map"], raw_data["item_map"] - num_users, num_items = DATASET_TO_NUM_USERS_AND_ITEMS[dataset] - - if num_users != len(user_map): - raise ValueError("Expected to find {} users, but found {}".format( - num_users, len(user_map))) - if num_items != len(item_map): - raise ValueError("Expected to find {} items, but found {}".format( - num_items, len(item_map))) - - producer = data_pipeline.get_constructor(constructor_type or "materialized")( - maximum_number_epochs=params["train_epochs"], - num_users=num_users, - num_items=num_items, - user_map=user_map, - item_map=item_map, - train_pos_users=raw_data[rconst.TRAIN_USER_KEY], - train_pos_items=raw_data[rconst.TRAIN_ITEM_KEY], - train_batch_size=params["batch_size"], - batches_per_train_step=params["batches_per_step"], - num_train_negatives=params["num_neg"], - eval_pos_users=raw_data[rconst.EVAL_USER_KEY], - eval_pos_items=raw_data[rconst.EVAL_ITEM_KEY], - eval_batch_size=params["eval_batch_size"], - batches_per_eval_step=params["batches_per_step"], - stream_files=params["use_tpu"], - deterministic=deterministic - ) - - run_time = timeit.default_timer() - st - tf.logging.info("Data preprocessing complete. Time: {:.1f} sec." - .format(run_time)) - - print(producer) - return num_users, num_items, producer diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/data_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/data_test.py deleted file mode 100644 index 05667b89..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/data_test.py +++ /dev/null @@ -1,345 +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. -# ============================================================================== -"""Test NCF data pipeline.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -from collections import defaultdict -import hashlib -import os - -import mock -import numpy as np -import scipy.stats -import tensorflow as tf - -from official.datasets import movielens -from official.recommendation import constants as rconst -from official.recommendation import data_preprocessing -from official.recommendation import popen_helper - - -DATASET = "ml-test" -NUM_USERS = 1000 -NUM_ITEMS = 2000 -NUM_PTS = 50000 -BATCH_SIZE = 2048 -EVAL_BATCH_SIZE = 4000 -NUM_NEG = 4 - - -END_TO_END_TRAIN_MD5 = "b218738e915e825d03939c5e305a2698" -END_TO_END_EVAL_MD5 = "d753d0f3186831466d6e218163a9501e" -FRESH_RANDOMNESS_MD5 = "63d0dff73c0e5f1048fbdc8c65021e22" - - -def mock_download(*args, **kwargs): - return - -# The forkpool used by data producers interacts badly with the threading -# used by TestCase. Without this patch tests will hang, and no amount -# of diligent closing and joining within the producer will prevent it. -@mock.patch.object(popen_helper, "get_forkpool", popen_helper.get_fauxpool) -class BaseTest(tf.test.TestCase): - def setUp(self): - self.temp_data_dir = self.get_temp_dir() - ratings_folder = os.path.join(self.temp_data_dir, DATASET) - tf.gfile.MakeDirs(ratings_folder) - np.random.seed(0) - raw_user_ids = np.arange(NUM_USERS * 3) - np.random.shuffle(raw_user_ids) - raw_user_ids = raw_user_ids[:NUM_USERS] - - raw_item_ids = np.arange(NUM_ITEMS * 3) - np.random.shuffle(raw_item_ids) - raw_item_ids = raw_item_ids[:NUM_ITEMS] - - users = np.random.choice(raw_user_ids, NUM_PTS) - items = np.random.choice(raw_item_ids, NUM_PTS) - scores = np.random.randint(low=0, high=5, size=NUM_PTS) - times = np.random.randint(low=1000000000, high=1200000000, size=NUM_PTS) - - self.rating_file = os.path.join(ratings_folder, movielens.RATINGS_FILE) - self.seen_pairs = set() - self.holdout = {} - with tf.gfile.Open(self.rating_file, "w") as f: - f.write("user_id,item_id,rating,timestamp\n") - for usr, itm, scr, ts in zip(users, items, scores, times): - pair = (usr, itm) - if pair in self.seen_pairs: - continue - self.seen_pairs.add(pair) - if usr not in self.holdout or (ts, itm) > self.holdout[usr]: - self.holdout[usr] = (ts, itm) - - f.write("{},{},{},{}\n".format(usr, itm, scr, ts)) - - movielens.download = mock_download - movielens.NUM_RATINGS[DATASET] = NUM_PTS - data_preprocessing.DATASET_TO_NUM_USERS_AND_ITEMS[DATASET] = (NUM_USERS, - NUM_ITEMS) - - def make_params(self, train_epochs=1): - return { - "train_epochs": train_epochs, - "batches_per_step": 1, - "use_seed": False, - "batch_size": BATCH_SIZE, - "eval_batch_size": EVAL_BATCH_SIZE, - "num_neg": NUM_NEG, - "match_mlperf": True, - "use_tpu": False, - "use_xla_for_gpu": False, - } - - def test_preprocessing(self): - # For the most part the necessary checks are performed within - # _filter_index_sort() - - cache_path = os.path.join(self.temp_data_dir, "test_cache.pickle") - data, valid_cache = data_preprocessing._filter_index_sort( - self.rating_file, cache_path=cache_path) - - assert len(data[rconst.USER_MAP]) == NUM_USERS - assert len(data[rconst.ITEM_MAP]) == NUM_ITEMS - - def drain_dataset(self, dataset, g): - # type: (tf.data.Dataset, tf.Graph) -> list - with self.test_session(graph=g) as sess: - with g.as_default(): - batch = dataset.make_one_shot_iterator().get_next() - output = [] - while True: - try: - output.append(sess.run(batch)) - except tf.errors.OutOfRangeError: - break - return output - - def _test_end_to_end(self, constructor_type): - params = self.make_params(train_epochs=1) - _, _, producer = data_preprocessing.instantiate_pipeline( - dataset=DATASET, data_dir=self.temp_data_dir, params=params, - constructor_type=constructor_type, deterministic=True) - - producer.start() - producer.join() - assert producer._fatal_exception is None - - user_inv_map = {v: k for k, v in producer.user_map.items()} - item_inv_map = {v: k for k, v in producer.item_map.items()} - - # ========================================================================== - # == Training Data ========================================================= - # ========================================================================== - g = tf.Graph() - with g.as_default(): - input_fn = producer.make_input_fn(is_training=True) - dataset = input_fn(params) - - first_epoch = self.drain_dataset(dataset=dataset, g=g) - - counts = defaultdict(int) - train_examples = { - True: set(), - False: set(), - } - - md5 = hashlib.md5() - for features, labels in first_epoch: - data_list = [ - features[movielens.USER_COLUMN], features[movielens.ITEM_COLUMN], - features[rconst.VALID_POINT_MASK], labels] - for i in data_list: - md5.update(i.tobytes()) - - for u, i, v, l in zip(*data_list): - if not v: - continue # ignore padding - - u_raw = user_inv_map[u] - i_raw = item_inv_map[i] - if ((u_raw, i_raw) in self.seen_pairs) != l: - # The evaluation item is not considered during false negative - # generation, so it will occasionally appear as a negative example - # during training. - assert not l - self.assertEqual(i_raw, self.holdout[u_raw][1]) - train_examples[l].add((u_raw, i_raw)) - counts[(u_raw, i_raw)] += 1 - - self.assertRegexpMatches(md5.hexdigest(), END_TO_END_TRAIN_MD5) - - num_positives_seen = len(train_examples[True]) - self.assertEqual(producer._train_pos_users.shape[0], num_positives_seen) - - # This check is more heuristic because negatives are sampled with - # replacement. It only checks that negative generation is reasonably random. - self.assertGreater( - len(train_examples[False]) / NUM_NEG / num_positives_seen, 0.9) - - # This checks that the samples produced are independent by checking the - # number of duplicate entries. If workers are not properly independent there - # will be lots of repeated pairs. - self.assertLess(np.mean(list(counts.values())), 1.1) - - # ========================================================================== - # == Eval Data ============================================================= - # ========================================================================== - with g.as_default(): - input_fn = producer.make_input_fn(is_training=False) - dataset = input_fn(params) - - eval_data = self.drain_dataset(dataset=dataset, g=g) - - current_user = None - md5 = hashlib.md5() - for features in eval_data: - data_list = [ - features[movielens.USER_COLUMN], features[movielens.ITEM_COLUMN], - features[rconst.DUPLICATE_MASK]] - for i in data_list: - md5.update(i.tobytes()) - - for idx, (u, i, d) in enumerate(zip(*data_list)): - u_raw = user_inv_map[u] - i_raw = item_inv_map[i] - if current_user is None: - current_user = u - - # Ensure that users appear in blocks, as the evaluation logic expects - # this structure. - self.assertEqual(u, current_user) - - # The structure of evaluation data is 999 negative examples followed - # by the holdout positive. - if not (idx + 1) % (rconst.NUM_EVAL_NEGATIVES + 1): - # Check that the last element in each chunk is the holdout item. - self.assertEqual(i_raw, self.holdout[u_raw][1]) - current_user = None - - elif i_raw == self.holdout[u_raw][1]: - # Because the holdout item is not given to the negative generation - # process, it can appear as a negative. In that case, it should be - # masked out as a duplicate. (Since the true positive is placed at - # the end and would therefore lose the tie.) - assert d - - else: - # Otherwise check that the other 999 points for a user are selected - # from the negatives. - assert (u_raw, i_raw) not in self.seen_pairs - - self.assertRegexpMatches(md5.hexdigest(), END_TO_END_EVAL_MD5) - - def _test_fresh_randomness(self, constructor_type): - train_epochs = 5 - params = self.make_params(train_epochs=train_epochs) - _, _, producer = data_preprocessing.instantiate_pipeline( - dataset=DATASET, data_dir=self.temp_data_dir, params=params, - constructor_type=constructor_type, deterministic=True) - - producer.start() - - results = [] - g = tf.Graph() - with g.as_default(): - for _ in range(train_epochs): - input_fn = producer.make_input_fn(is_training=True) - dataset = input_fn(params) - results.extend(self.drain_dataset(dataset=dataset, g=g)) - - producer.join() - assert producer._fatal_exception is None - - positive_counts, negative_counts = defaultdict(int), defaultdict(int) - md5 = hashlib.md5() - for features, labels in results: - data_list = [ - features[movielens.USER_COLUMN], features[movielens.ITEM_COLUMN], - features[rconst.VALID_POINT_MASK], labels] - for i in data_list: - md5.update(i.tobytes()) - - for u, i, v, l in zip(*data_list): - if not v: - continue # ignore padding - - if l: - positive_counts[(u, i)] += 1 - else: - negative_counts[(u, i)] += 1 - - self.assertRegexpMatches(md5.hexdigest(), FRESH_RANDOMNESS_MD5) - - # The positive examples should appear exactly once each epoch - self.assertAllEqual(list(positive_counts.values()), - [train_epochs for _ in positive_counts]) - - # The threshold for the negatives is heuristic, but in general repeats are - # expected, but should not appear too frequently. - - pair_cardinality = NUM_USERS * NUM_ITEMS - neg_pair_cardinality = pair_cardinality - len(self.seen_pairs) - - # Approximation for the expectation number of times that a particular - # negative will appear in a given epoch. Implicit in this calculation is the - # treatment of all negative pairs as equally likely. Normally is not - # necessarily reasonable; however the generation in self.setUp() will - # approximate this behavior sufficiently for heuristic testing. - e_sample = len(self.seen_pairs) * NUM_NEG / neg_pair_cardinality - - # The frequency of occurance of a given negative pair should follow an - # approximately binomial distribution in the limit that the cardinality of - # the negative pair set >> number of samples per epoch. - approx_pdf = scipy.stats.binom.pmf(k=np.arange(train_epochs+1), - n=train_epochs, p=e_sample) - - # Tally the actual observed counts. - count_distribution = [0 for _ in range(train_epochs + 1)] - for i in negative_counts.values(): - i = min([i, train_epochs]) # round down tail for simplicity. - count_distribution[i] += 1 - count_distribution[0] = neg_pair_cardinality - sum(count_distribution[1:]) - - # Check that the frequency of negative pairs is approximately binomial. - for i in range(train_epochs + 1): - if approx_pdf[i] < 0.05: - continue # Variance will be high at the tails. - - observed_fraction = count_distribution[i] / neg_pair_cardinality - deviation = (2 * abs(observed_fraction - approx_pdf[i]) / - (observed_fraction + approx_pdf[i])) - - self.assertLess(deviation, 0.2) - - def test_end_to_end_materialized(self): - self._test_end_to_end("materialized") - - def test_end_to_end_bisection(self): - self._test_end_to_end("bisection") - - def test_fresh_randomness_materialized(self): - self._test_fresh_randomness("materialized") - - def test_fresh_randomness_bisection(self): - self._test_fresh_randomness("bisection") - - -if __name__ == "__main__": - tf.logging.set_verbosity(tf.logging.INFO) - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/ncf_main.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/ncf_main.py deleted file mode 100644 index 32b41f3b..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/ncf_main.py +++ /dev/null @@ -1,444 +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. -# ============================================================================== -"""NCF framework to train and evaluate the NeuMF model. - -The NeuMF model assembles both MF and MLP models under the NCF framework. Check -`neumf_model.py` for more details about the models. -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import contextlib -import heapq -import json -import logging -import math -import multiprocessing -import os -import signal -import typing - -# pylint: disable=g-bad-import-order -import numpy as np -from absl import app as absl_app -from absl import flags -import tensorflow as tf -# pylint: enable=g-bad-import-order - -from tensorflow.contrib.compiler import xla -from official.datasets import movielens -from official.recommendation import constants as rconst -from official.recommendation import data_pipeline -from official.recommendation import data_preprocessing -from official.recommendation import neumf_model -from official.utils.flags import core as flags_core -from official.utils.logs import hooks_helper -from official.utils.logs import logger -from official.utils.logs import mlperf_helper -from official.utils.misc import distribution_utils -from official.utils.misc import model_helpers - - -FLAGS = flags.FLAGS - - -def construct_estimator(model_dir, params): - """Construct either an Estimator or TPUEstimator for NCF. - - Args: - model_dir: The model directory for the estimator - params: The params dict for the estimator - - Returns: - An Estimator or TPUEstimator. - """ - - if params["use_tpu"]: - # Some of the networking libraries are quite chatty. - for name in ["googleapiclient.discovery", "googleapiclient.discovery_cache", - "oauth2client.transport"]: - logging.getLogger(name).setLevel(logging.ERROR) - - tpu_cluster_resolver = tf.contrib.cluster_resolver.TPUClusterResolver( - tpu=params["tpu"], - zone=params["tpu_zone"], - project=params["tpu_gcp_project"], - coordinator_name="coordinator" - ) - - tf.logging.info("Issuing reset command to TPU to ensure a clean state.") - tf.Session.reset(tpu_cluster_resolver.get_master()) - - # Estimator looks at the master it connects to for MonitoredTrainingSession - # by reading the `TF_CONFIG` environment variable, and the coordinator - # is used by StreamingFilesDataset. - tf_config_env = { - "session_master": tpu_cluster_resolver.get_master(), - "eval_session_master": tpu_cluster_resolver.get_master(), - "coordinator": tpu_cluster_resolver.cluster_spec() - .as_dict()["coordinator"] - } - os.environ['TF_CONFIG'] = json.dumps(tf_config_env) - - distribution = tf.contrib.distribute.TPUStrategy( - tpu_cluster_resolver, steps_per_run=100) - - else: - distribution = distribution_utils.get_distribution_strategy( - num_gpus=params["num_gpus"]) - - run_config = tf.estimator.RunConfig(train_distribute=distribution, - eval_distribute=distribution) - - model_fn = neumf_model.neumf_model_fn - if params["use_xla_for_gpu"]: - tf.logging.info("Using XLA for GPU for training and evaluation.") - model_fn = xla.estimator_model_fn(model_fn) - estimator = tf.estimator.Estimator(model_fn=model_fn, model_dir=model_dir, - config=run_config, params=params) - return estimator - - -def log_and_get_hooks(eval_batch_size): - """Convenience function for hook and logger creation.""" - # Create hooks that log information about the training and metric values - train_hooks = hooks_helper.get_train_hooks( - FLAGS.hooks, - model_dir=FLAGS.model_dir, - batch_size=FLAGS.batch_size, # for ExamplesPerSecondHook - tensors_to_log={"cross_entropy": "cross_entropy"} - ) - run_params = { - "batch_size": FLAGS.batch_size, - "eval_batch_size": eval_batch_size, - "number_factors": FLAGS.num_factors, - "hr_threshold": FLAGS.hr_threshold, - "train_epochs": FLAGS.train_epochs, - } - benchmark_logger = logger.get_benchmark_logger() - benchmark_logger.log_run_info( - model_name="recommendation", - dataset_name=FLAGS.dataset, - run_params=run_params, - test_id=FLAGS.benchmark_test_id) - - return benchmark_logger, train_hooks - - -def parse_flags(flags_obj): - """Convenience function to turn flags into params.""" - num_gpus = flags_core.get_num_gpus(flags_obj) - num_devices = FLAGS.num_tpu_shards if FLAGS.tpu else num_gpus or 1 - - batch_size = (flags_obj.batch_size + num_devices - 1) // num_devices - - eval_divisor = (rconst.NUM_EVAL_NEGATIVES + 1) * num_devices - eval_batch_size = flags_obj.eval_batch_size or flags_obj.batch_size - eval_batch_size = ((eval_batch_size + eval_divisor - 1) // - eval_divisor * eval_divisor // num_devices) - - return { - "train_epochs": flags_obj.train_epochs, - "batches_per_step": num_devices, - "use_seed": flags_obj.seed is not None, - "batch_size": batch_size, - "eval_batch_size": eval_batch_size, - "learning_rate": flags_obj.learning_rate, - "mf_dim": flags_obj.num_factors, - "model_layers": [int(layer) for layer in flags_obj.layers], - "mf_regularization": flags_obj.mf_regularization, - "mlp_reg_layers": [float(reg) for reg in flags_obj.mlp_regularization], - "num_neg": flags_obj.num_neg, - "num_gpus": num_gpus, - "use_tpu": flags_obj.tpu is not None, - "tpu": flags_obj.tpu, - "tpu_zone": flags_obj.tpu_zone, - "tpu_gcp_project": flags_obj.tpu_gcp_project, - "beta1": flags_obj.beta1, - "beta2": flags_obj.beta2, - "epsilon": flags_obj.epsilon, - "match_mlperf": flags_obj.ml_perf, - "use_xla_for_gpu": flags_obj.use_xla_for_gpu, - "epochs_between_evals": FLAGS.epochs_between_evals, - } - - -def main(_): - with logger.benchmark_context(FLAGS), \ - mlperf_helper.LOGGER(FLAGS.output_ml_perf_compliance_logging): - mlperf_helper.set_ncf_root(os.path.split(os.path.abspath(__file__))[0]) - run_ncf(FLAGS) - - -def run_ncf(_): - """Run NCF training and eval loop.""" - if FLAGS.download_if_missing and not FLAGS.use_synthetic_data: - movielens.download(FLAGS.dataset, FLAGS.data_dir) - - if FLAGS.seed is not None: - np.random.seed(FLAGS.seed) - - params = parse_flags(FLAGS) - total_training_cycle = FLAGS.train_epochs // FLAGS.epochs_between_evals - - if FLAGS.use_synthetic_data: - producer = data_pipeline.DummyConstructor() - num_users, num_items = data_preprocessing.DATASET_TO_NUM_USERS_AND_ITEMS[ - FLAGS.dataset] - num_train_steps = rconst.SYNTHETIC_BATCHES_PER_EPOCH - num_eval_steps = rconst.SYNTHETIC_BATCHES_PER_EPOCH - else: - num_users, num_items, producer = data_preprocessing.instantiate_pipeline( - dataset=FLAGS.dataset, data_dir=FLAGS.data_dir, params=params, - constructor_type=FLAGS.constructor_type, - deterministic=FLAGS.seed is not None) - - num_train_steps = (producer.train_batches_per_epoch // - params["batches_per_step"]) - num_eval_steps = (producer.eval_batches_per_epoch // - params["batches_per_step"]) - assert not producer.train_batches_per_epoch % params["batches_per_step"] - assert not producer.eval_batches_per_epoch % params["batches_per_step"] - producer.start() - - params["num_users"], params["num_items"] = num_users, num_items - model_helpers.apply_clean(flags.FLAGS) - - estimator = construct_estimator(model_dir=FLAGS.model_dir, params=params) - - benchmark_logger, train_hooks = log_and_get_hooks(params["eval_batch_size"]) - - target_reached = False - mlperf_helper.ncf_print(key=mlperf_helper.TAGS.TRAIN_LOOP) - for cycle_index in range(total_training_cycle): - assert FLAGS.epochs_between_evals == 1 or not mlperf_helper.LOGGER.enabled - tf.logging.info("Starting a training cycle: {}/{}".format( - cycle_index + 1, total_training_cycle)) - - mlperf_helper.ncf_print(key=mlperf_helper.TAGS.TRAIN_EPOCH, - value=cycle_index) - - train_input_fn = producer.make_input_fn(is_training=True) - estimator.train(input_fn=train_input_fn, hooks=train_hooks, - steps=num_train_steps) - - tf.logging.info("Beginning evaluation.") - eval_input_fn = producer.make_input_fn(is_training=False) - - mlperf_helper.ncf_print(key=mlperf_helper.TAGS.EVAL_START, - value=cycle_index) - eval_results = estimator.evaluate(eval_input_fn, steps=num_eval_steps) - tf.logging.info("Evaluation complete.") - - hr = float(eval_results[rconst.HR_KEY]) - ndcg = float(eval_results[rconst.NDCG_KEY]) - loss = float(eval_results["loss"]) - - mlperf_helper.ncf_print( - key=mlperf_helper.TAGS.EVAL_TARGET, - value={"epoch": cycle_index, "value": FLAGS.hr_threshold}) - mlperf_helper.ncf_print(key=mlperf_helper.TAGS.EVAL_ACCURACY, - value={"epoch": cycle_index, "value": hr}) - mlperf_helper.ncf_print( - key=mlperf_helper.TAGS.EVAL_HP_NUM_NEG, - value={"epoch": cycle_index, "value": rconst.NUM_EVAL_NEGATIVES}) - - mlperf_helper.ncf_print(key=mlperf_helper.TAGS.EVAL_STOP, value=cycle_index) - - # Benchmark the evaluation results - benchmark_logger.log_evaluation_result(eval_results) - # Log the HR and NDCG results. - tf.logging.info( - "Iteration {}: HR = {:.4f}, NDCG = {:.4f}, Loss = {:.4f}".format( - cycle_index + 1, hr, ndcg, loss)) - - # If some evaluation threshold is met - if model_helpers.past_stop_threshold(FLAGS.hr_threshold, hr): - target_reached = True - break - - mlperf_helper.ncf_print(key=mlperf_helper.TAGS.RUN_STOP, - value={"success": target_reached}) - producer.stop_loop() - producer.join() - - # Clear the session explicitly to avoid session delete error - tf.keras.backend.clear_session() - - mlperf_helper.ncf_print(key=mlperf_helper.TAGS.RUN_FINAL) - - -def define_ncf_flags(): - """Add flags for running ncf_main.""" - # Add common flags - flags_core.define_base(export_dir=False) - flags_core.define_performance( - num_parallel_calls=False, - inter_op=False, - intra_op=False, - synthetic_data=True, - max_train_steps=False, - dtype=False, - all_reduce_alg=False - ) - flags_core.define_device(tpu=True) - flags_core.define_benchmark() - - flags.adopt_module_key_flags(flags_core) - - flags_core.set_defaults( - model_dir="/tmp/ncf/", - data_dir="/tmp/movielens-data/", - train_epochs=2, - batch_size=256, - hooks="ProfilerHook", - tpu=None - ) - - # Add ncf-specific flags - flags.DEFINE_enum( - name="dataset", default="ml-1m", - enum_values=["ml-1m", "ml-20m"], case_sensitive=False, - help=flags_core.help_wrap( - "Dataset to be trained and evaluated.")) - - flags.DEFINE_boolean( - name="download_if_missing", default=True, help=flags_core.help_wrap( - "Download data to data_dir if it is not already present.")) - - flags.DEFINE_integer( - name="eval_batch_size", default=None, help=flags_core.help_wrap( - "The batch size used for evaluation. This should generally be larger" - "than the training batch size as the lack of back propagation during" - "evaluation can allow for larger batch sizes to fit in memory. If not" - "specified, the training batch size (--batch_size) will be used.")) - - flags.DEFINE_integer( - name="num_factors", default=8, - help=flags_core.help_wrap("The Embedding size of MF model.")) - - # Set the default as a list of strings to be consistent with input arguments - flags.DEFINE_list( - name="layers", default=["64", "32", "16", "8"], - help=flags_core.help_wrap( - "The sizes of hidden layers for MLP. Example " - "to specify different sizes of MLP layers: --layers=32,16,8,4")) - - flags.DEFINE_float( - name="mf_regularization", default=0., - help=flags_core.help_wrap( - "The regularization factor for MF embeddings. The factor is used by " - "regularizer which allows to apply penalties on layer parameters or " - "layer activity during optimization.")) - - flags.DEFINE_list( - name="mlp_regularization", default=["0.", "0.", "0.", "0."], - help=flags_core.help_wrap( - "The regularization factor for each MLP layer. See mf_regularization " - "help for more info about regularization factor.")) - - flags.DEFINE_integer( - name="num_neg", default=4, - help=flags_core.help_wrap( - "The Number of negative instances to pair with a positive instance.")) - - flags.DEFINE_float( - name="learning_rate", default=0.001, - help=flags_core.help_wrap("The learning rate.")) - - flags.DEFINE_float( - name="beta1", default=0.9, - help=flags_core.help_wrap("beta1 hyperparameter for the Adam optimizer.")) - - flags.DEFINE_float( - name="beta2", default=0.999, - help=flags_core.help_wrap("beta2 hyperparameter for the Adam optimizer.")) - - flags.DEFINE_float( - name="epsilon", default=1e-8, - help=flags_core.help_wrap("epsilon hyperparameter for the Adam " - "optimizer.")) - - flags.DEFINE_float( - name="hr_threshold", default=None, - help=flags_core.help_wrap( - "If passed, training will stop when the evaluation metric HR is " - "greater than or equal to hr_threshold. For dataset ml-1m, the " - "desired hr_threshold is 0.68 which is the result from the paper; " - "For dataset ml-20m, the threshold can be set as 0.95 which is " - "achieved by MLPerf implementation.")) - - flags.DEFINE_enum( - name="constructor_type", default="bisection", - enum_values=["bisection", "materialized"], case_sensitive=False, - help=flags_core.help_wrap( - "Strategy to use for generating false negatives. materialized has a" - "precompute that scales badly, but a faster per-epoch construction" - "time and can be faster on very large systems.")) - - flags.DEFINE_bool( - name="ml_perf", default=False, - help=flags_core.help_wrap( - "If set, changes the behavior of the model slightly to match the " - "MLPerf reference implementations here: \n" - "https://github.com/mlperf/reference/tree/master/recommendation/" - "pytorch\n" - "The two changes are:\n" - "1. When computing the HR and NDCG during evaluation, remove " - "duplicate user-item pairs before the computation. This results in " - "better HRs and NDCGs.\n" - "2. Use a different soring algorithm when sorting the input data, " - "which performs better due to the fact the sorting algorithms are " - "not stable.")) - - flags.DEFINE_bool( - name="output_ml_perf_compliance_logging", default=False, - help=flags_core.help_wrap( - "If set, output the MLPerf compliance logging. This is only useful " - "if one is running the model for MLPerf. See " - "https://github.com/mlperf/policies/blob/master/training_rules.adoc" - "#submission-compliance-logs for details. This uses sudo and so may " - "ask for your password, as root access is needed to clear the system " - "caches, which is required for MLPerf compliance." - ) - ) - - flags.DEFINE_integer( - name="seed", default=None, help=flags_core.help_wrap( - "This value will be used to seed both NumPy and TensorFlow.")) - - @flags.validator("eval_batch_size", "eval_batch_size must be at least {}" - .format(rconst.NUM_EVAL_NEGATIVES + 1)) - def eval_size_check(eval_batch_size): - return (eval_batch_size is None or - int(eval_batch_size) > rconst.NUM_EVAL_NEGATIVES) - - flags.DEFINE_bool( - name="use_xla_for_gpu", default=False, help=flags_core.help_wrap( - "If True, use XLA for the model function. Only works when using a " - "GPU. On TPUs, XLA is always used")) - - xla_message = "--use_xla_for_gpu is incompatible with --tpu" - @flags.multi_flags_validator(["use_xla_for_gpu", "tpu"], message=xla_message) - def xla_validator(flag_dict): - return not flag_dict["use_xla_for_gpu"] or not flag_dict["tpu"] - - -if __name__ == "__main__": - tf.logging.set_verbosity(tf.logging.INFO) - define_ncf_flags() - absl_app.run(main) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/ncf_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/ncf_test.py deleted file mode 100644 index a3a498d7..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/ncf_test.py +++ /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. -# ============================================================================== -"""Tests NCF.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import math -import mock - -import numpy as np -import tensorflow as tf - -from absl.testing import flagsaver -from official.recommendation import constants as rconst -from official.recommendation import data_pipeline -from official.recommendation import neumf_model -from official.recommendation import ncf_main - - -NUM_TRAIN_NEG = 4 - - -class NcfTest(tf.test.TestCase): - - @classmethod - def setUpClass(cls): # pylint: disable=invalid-name - super(NcfTest, cls).setUpClass() - ncf_main.define_ncf_flags() - - def setUp(self): - self.top_k_old = rconst.TOP_K - self.num_eval_negatives_old = rconst.NUM_EVAL_NEGATIVES - rconst.NUM_EVAL_NEGATIVES = 2 - - def tearDown(self): - rconst.NUM_EVAL_NEGATIVES = self.num_eval_negatives_old - rconst.TOP_K = self.top_k_old - - def get_hit_rate_and_ndcg(self, predicted_scores_by_user, items_by_user, - top_k=rconst.TOP_K, match_mlperf=False): - rconst.TOP_K = top_k - rconst.NUM_EVAL_NEGATIVES = predicted_scores_by_user.shape[1] - 1 - batch_size = items_by_user.shape[0] - - users = np.repeat(np.arange(batch_size)[:, np.newaxis], - rconst.NUM_EVAL_NEGATIVES + 1, axis=1) - users, items, duplicate_mask = \ - data_pipeline.BaseDataConstructor._assemble_eval_batch( - users, items_by_user[:, -1:], items_by_user[:, :-1], batch_size) - - g = tf.Graph() - with g.as_default(): - logits = tf.convert_to_tensor( - predicted_scores_by_user.reshape((-1, 1)), tf.float32) - softmax_logits = tf.concat([tf.zeros(logits.shape, dtype=logits.dtype), - logits], axis=1) - duplicate_mask = tf.convert_to_tensor(duplicate_mask, tf.float32) - - metric_ops = neumf_model.compute_eval_loss_and_metrics( - logits=logits, softmax_logits=softmax_logits, - duplicate_mask=duplicate_mask, num_training_neg=NUM_TRAIN_NEG, - match_mlperf=match_mlperf).eval_metric_ops - - hr = metric_ops[rconst.HR_KEY] - ndcg = metric_ops[rconst.NDCG_KEY] - - init = [tf.global_variables_initializer(), - tf.local_variables_initializer()] - - with self.test_session(graph=g) as sess: - sess.run(init) - return sess.run([hr[1], ndcg[1]]) - - def test_hit_rate_and_ndcg(self): - # Test with no duplicate items - predictions = np.array([ - [2., 0., 1.], # In top 2 - [1., 0., 2.], # In top 1 - [2., 1., 0.], # In top 3 - [3., 4., 2.] # In top 3 - ]) - items = np.array([ - [2, 3, 1], - [3, 1, 2], - [2, 1, 3], - [1, 3, 2], - ]) - - hr, ndcg = self.get_hit_rate_and_ndcg(predictions, items, 1) - self.assertAlmostEqual(hr, 1 / 4) - self.assertAlmostEqual(ndcg, 1 / 4) - - hr, ndcg = self.get_hit_rate_and_ndcg(predictions, items, 2) - self.assertAlmostEqual(hr, 2 / 4) - self.assertAlmostEqual(ndcg, (1 + math.log(2) / math.log(3)) / 4) - - hr, ndcg = self.get_hit_rate_and_ndcg(predictions, items, 3) - self.assertAlmostEqual(hr, 4 / 4) - self.assertAlmostEqual(ndcg, (1 + math.log(2) / math.log(3) + - 2 * math.log(2) / math.log(4)) / 4) - - hr, ndcg = self.get_hit_rate_and_ndcg(predictions, items, 1, - match_mlperf=True) - self.assertAlmostEqual(hr, 1 / 4) - self.assertAlmostEqual(ndcg, 1 / 4) - - hr, ndcg = self.get_hit_rate_and_ndcg(predictions, items, 2, - match_mlperf=True) - self.assertAlmostEqual(hr, 2 / 4) - self.assertAlmostEqual(ndcg, (1 + math.log(2) / math.log(3)) / 4) - - hr, ndcg = self.get_hit_rate_and_ndcg(predictions, items, 3, - match_mlperf=True) - self.assertAlmostEqual(hr, 4 / 4) - self.assertAlmostEqual(ndcg, (1 + math.log(2) / math.log(3) + - 2 * math.log(2) / math.log(4)) / 4) - - # Test with duplicate items. In the MLPerf case, we treat the duplicates as - # a single item. Otherwise, we treat the duplicates as separate items. - predictions = np.array([ - [2., 2., 3., 1.], # In top 4. MLPerf: In top 3 - [1., 0., 2., 3.], # In top 1. MLPerf: In top 1 - [2., 3., 2., 0.], # In top 4. MLPerf: In top 3 - [2., 4., 2., 3.] # In top 2. MLPerf: In top 2 - ]) - items = np.array([ - [2, 2, 3, 1], - [2, 3, 4, 1], - [2, 3, 2, 1], - [3, 2, 1, 4], - ]) - hr, ndcg = self.get_hit_rate_and_ndcg(predictions, items, 1) - self.assertAlmostEqual(hr, 1 / 4) - self.assertAlmostEqual(ndcg, 1 / 4) - - hr, ndcg = self.get_hit_rate_and_ndcg(predictions, items, 2) - self.assertAlmostEqual(hr, 2 / 4) - self.assertAlmostEqual(ndcg, (1 + math.log(2) / math.log(3)) / 4) - - hr, ndcg = self.get_hit_rate_and_ndcg(predictions, items, 3) - self.assertAlmostEqual(hr, 2 / 4) - self.assertAlmostEqual(ndcg, (1 + math.log(2) / math.log(3)) / 4) - - hr, ndcg = self.get_hit_rate_and_ndcg(predictions, items, 4) - self.assertAlmostEqual(hr, 4 / 4) - self.assertAlmostEqual(ndcg, (1 + math.log(2) / math.log(3) + - 2 * math.log(2) / math.log(5)) / 4) - - hr, ndcg = self.get_hit_rate_and_ndcg(predictions, items, 1, - match_mlperf=True) - self.assertAlmostEqual(hr, 1 / 4) - self.assertAlmostEqual(ndcg, 1 / 4) - - hr, ndcg = self.get_hit_rate_and_ndcg(predictions, items, 2, - match_mlperf=True) - self.assertAlmostEqual(hr, 2 / 4) - self.assertAlmostEqual(ndcg, (1 + math.log(2) / math.log(3)) / 4) - - hr, ndcg = self.get_hit_rate_and_ndcg(predictions, items, 3, - match_mlperf=True) - self.assertAlmostEqual(hr, 4 / 4) - self.assertAlmostEqual(ndcg, (1 + math.log(2) / math.log(3) + - 2 * math.log(2) / math.log(4)) / 4) - - hr, ndcg = self.get_hit_rate_and_ndcg(predictions, items, 4, - match_mlperf=True) - self.assertAlmostEqual(hr, 4 / 4) - self.assertAlmostEqual(ndcg, (1 + math.log(2) / math.log(3) + - 2 * math.log(2) / math.log(4)) / 4) - - - _BASE_END_TO_END_FLAGS = { - "batch_size": 1024, - "train_epochs": 1, - "use_synthetic_data": True - } - - @flagsaver.flagsaver(**_BASE_END_TO_END_FLAGS) - @mock.patch.object(rconst, "SYNTHETIC_BATCHES_PER_EPOCH", 100) - def test_end_to_end(self): - ncf_main.main(None) - - @flagsaver.flagsaver(ml_perf=True, **_BASE_END_TO_END_FLAGS) - @mock.patch.object(rconst, "SYNTHETIC_BATCHES_PER_EPOCH", 100) - def test_end_to_end_mlperf(self): - ncf_main.main(None) - - -if __name__ == "__main__": - tf.logging.set_verbosity(tf.logging.INFO) - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/neumf_model.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/neumf_model.py deleted file mode 100644 index 98f74a3f..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/neumf_model.py +++ /dev/null @@ -1,412 +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. -# ============================================================================== -"""Defines NeuMF model for NCF framework. - -Some abbreviations used in the code base: -NeuMF: Neural Matrix Factorization -NCF: Neural Collaborative Filtering -GMF: Generalized Matrix Factorization -MLP: Multi-Layer Perceptron - -GMF applies a linear kernel to model the latent feature interactions, and MLP -uses a nonlinear kernel to learn the interaction function from data. NeuMF model -is a fused model of GMF and MLP to better model the complex user-item -interactions, and unifies the strengths of linearity of MF and non-linearity of -MLP for modeling the user-item latent structures. - -In NeuMF model, it allows GMF and MLP to learn separate embeddings, and combine -the two models by concatenating their last hidden layer. -""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import sys -import typing - -from six.moves import xrange # pylint: disable=redefined-builtin -import tensorflow as tf - -from official.datasets import movielens # pylint: disable=g-bad-import-order -from official.recommendation import constants as rconst -from official.recommendation import stat_utils -from official.utils.logs import mlperf_helper - - -def _sparse_to_dense_grads(grads_and_vars): - """Convert sparse gradients to dense gradients. - - All sparse gradients, which are represented as instances of tf.IndexedSlices, - are converted to dense Tensors. Dense gradients, which are represents as - Tensors, are unchanged. - - The purpose of this conversion is that for small embeddings, which are used by - this model, applying dense gradients with the AdamOptimizer is faster than - applying sparse gradients. - - Args - grads_and_vars: A list of (gradient, variable) tuples. Each gradient can - be a Tensor or an IndexedSlices. Tensors are unchanged, and IndexedSlices - are converted to dense Tensors. - Returns: - The same list of (gradient, variable) as `grads_and_vars`, except each - IndexedSlices gradient is converted to a Tensor. - """ - - # Calling convert_to_tensor changes IndexedSlices into Tensors, and leaves - # Tensors unchanged. - return [(tf.convert_to_tensor(g), v) for g, v in grads_and_vars] - - -def neumf_model_fn(features, labels, mode, params): - """Model Function for NeuMF estimator.""" - if params.get("use_seed"): - tf.set_random_seed(stat_utils.random_int32()) - - users = features[movielens.USER_COLUMN] - items = features[movielens.ITEM_COLUMN] - - logits = construct_model(users, items, params).output - - # Softmax with the first column of zeros is equivalent to sigmoid. - softmax_logits = tf.concat([tf.zeros(logits.shape, dtype=logits.dtype), - logits], axis=1) - - if mode == tf.estimator.ModeKeys.EVAL: - duplicate_mask = tf.cast(features[rconst.DUPLICATE_MASK], tf.float32) - return compute_eval_loss_and_metrics( - logits, softmax_logits, duplicate_mask, params["num_neg"], - params["match_mlperf"], - use_tpu_spec=params["use_xla_for_gpu"]) - - elif mode == tf.estimator.ModeKeys.TRAIN: - labels = tf.cast(labels, tf.int32) - valid_pt_mask = features[rconst.VALID_POINT_MASK] - - mlperf_helper.ncf_print(key=mlperf_helper.TAGS.OPT_NAME, value="adam") - mlperf_helper.ncf_print(key=mlperf_helper.TAGS.OPT_LR, - value=params["learning_rate"]) - mlperf_helper.ncf_print(key=mlperf_helper.TAGS.OPT_HP_ADAM_BETA1, - value=params["beta1"]) - mlperf_helper.ncf_print(key=mlperf_helper.TAGS.OPT_HP_ADAM_BETA2, - value=params["beta2"]) - mlperf_helper.ncf_print(key=mlperf_helper.TAGS.OPT_HP_ADAM_EPSILON, - value=params["epsilon"]) - - optimizer = tf.train.AdamOptimizer( - learning_rate=params["learning_rate"], beta1=params["beta1"], - beta2=params["beta2"], epsilon=params["epsilon"]) - if params["use_tpu"]: - optimizer = tf.contrib.tpu.CrossShardOptimizer(optimizer) - - mlperf_helper.ncf_print(key=mlperf_helper.TAGS.MODEL_HP_LOSS_FN, - value=mlperf_helper.TAGS.BCE) - loss = tf.losses.sparse_softmax_cross_entropy( - labels=labels, - logits=softmax_logits, - weights=tf.cast(valid_pt_mask, tf.float32) - ) - - # This tensor is used by logging hooks. - tf.identity(loss, name="cross_entropy") - - global_step = tf.train.get_global_step() - tvars = tf.trainable_variables() - gradients = optimizer.compute_gradients( - loss, tvars, colocate_gradients_with_ops=True) - gradients = _sparse_to_dense_grads(gradients) - minimize_op = optimizer.apply_gradients( - gradients, global_step=global_step, name="train") - update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) - train_op = tf.group(minimize_op, update_ops) - - return tf.estimator.EstimatorSpec(mode=mode, loss=loss, train_op=train_op) - - else: - raise NotImplementedError - - -def construct_model(users, items, params): - # type: (tf.Tensor, tf.Tensor, dict) -> tf.keras.Model - """Initialize NeuMF model. - - Args: - users: Tensor of user ids. - items: Tensor of item ids. - params: Dict of hyperparameters. - Raises: - ValueError: if the first model layer is not even. - Returns: - model: a keras Model for computing the logits - """ - num_users = params["num_users"] - num_items = params["num_items"] - - model_layers = params["model_layers"] - - mf_regularization = params["mf_regularization"] - mlp_reg_layers = params["mlp_reg_layers"] - - mf_dim = params["mf_dim"] - - mlperf_helper.ncf_print(key=mlperf_helper.TAGS.MODEL_HP_MF_DIM, value=mf_dim) - mlperf_helper.ncf_print(key=mlperf_helper.TAGS.MODEL_HP_MLP_LAYER_SIZES, - value=model_layers) - - if model_layers[0] % 2 != 0: - raise ValueError("The first layer size should be multiple of 2!") - - # Input variables - user_input = tf.keras.layers.Input(tensor=users, name="user_input") - item_input = tf.keras.layers.Input(tensor=items, name="item_input") - - # Initializer for embedding layers - embedding_initializer = "glorot_uniform" - - # It turns out to be significantly more effecient to store the MF and MLP - # embedding portions in the same table, and then slice as needed. - mf_slice_fn = lambda x: x[:, :mf_dim] - mlp_slice_fn = lambda x: x[:, mf_dim:] - embedding_user = tf.keras.layers.Embedding( - num_users, mf_dim + model_layers[0] // 2, - embeddings_initializer=embedding_initializer, - embeddings_regularizer=tf.keras.regularizers.l2(mf_regularization), - input_length=1, name="embedding_user")(user_input) - - embedding_item = tf.keras.layers.Embedding( - num_items, mf_dim + model_layers[0] // 2, - embeddings_initializer=embedding_initializer, - embeddings_regularizer=tf.keras.regularizers.l2(mf_regularization), - input_length=1, name="embedding_item")(item_input) - - # GMF part - mf_user_latent = tf.keras.layers.Lambda( - mf_slice_fn, name="embedding_user_mf")(embedding_user) - mf_item_latent = tf.keras.layers.Lambda( - mf_slice_fn, name="embedding_item_mf")(embedding_item) - - # MLP part - mlp_user_latent = tf.keras.layers.Lambda( - mlp_slice_fn, name="embedding_user_mlp")(embedding_user) - mlp_item_latent = tf.keras.layers.Lambda( - mlp_slice_fn, name="embedding_item_mlp")(embedding_item) - - # Element-wise multiply - mf_vector = tf.keras.layers.multiply([mf_user_latent, mf_item_latent]) - - # Concatenation of two latent features - mlp_vector = tf.keras.layers.concatenate([mlp_user_latent, mlp_item_latent]) - - num_layer = len(model_layers) # Number of layers in the MLP - for layer in xrange(1, num_layer): - model_layer = tf.keras.layers.Dense( - model_layers[layer], - kernel_regularizer=tf.keras.regularizers.l2(mlp_reg_layers[layer]), - activation="relu") - mlp_vector = model_layer(mlp_vector) - - # Concatenate GMF and MLP parts - predict_vector = tf.keras.layers.concatenate([mf_vector, mlp_vector]) - - # Final prediction layer - logits = tf.keras.layers.Dense( - 1, activation=None, kernel_initializer="lecun_uniform", - name=movielens.RATING_COLUMN)(predict_vector) - - # Print model topology. - model = tf.keras.models.Model([user_input, item_input], logits) - model.summary() - sys.stdout.flush() - - return model - - -def compute_eval_loss_and_metrics(logits, # type: tf.Tensor - softmax_logits, # type: tf.Tensor - duplicate_mask, # type: tf.Tensor - num_training_neg, # type: int - match_mlperf=False, # type: bool - use_tpu_spec=False # type: bool - ): - # type: (...) -> tf.estimator.EstimatorSpec - """Model evaluation with HR and NDCG metrics. - - The evaluation protocol is to rank the test interacted item (truth items) - among the randomly chosen 999 items that are not interacted by the user. - The performance of the ranked list is judged by Hit Ratio (HR) and Normalized - Discounted Cumulative Gain (NDCG). - - For evaluation, the ranked list is truncated at 10 for both metrics. As such, - the HR intuitively measures whether the test item is present on the top-10 - list, and the NDCG accounts for the position of the hit by assigning higher - scores to hits at top ranks. Both metrics are calculated for each test user, - and the average scores are reported. - - If `match_mlperf` is True, then the HR and NDCG computations are done in a - slightly unusual way to match the MLPerf reference implementation. - Specifically, if the evaluation negatives contain duplicate items, it will be - treated as if the item only appeared once. Effectively, for duplicate items in - a row, the predicted score for all but one of the items will be set to - -infinity - - For example, suppose we have that following inputs: - logits_by_user: [[ 2, 3, 3], - [ 5, 4, 4]] - - items_by_user: [[10, 20, 20], - [30, 40, 40]] - - # Note: items_by_user is not explicitly present. Instead the relevant \ - information is contained within `duplicate_mask` - - top_k: 2 - - Then with match_mlperf=True, the HR would be 2/2 = 1.0. With - match_mlperf=False, the HR would be 1/2 = 0.5. This is because each user has - predicted scores for only 2 unique items: 10 and 20 for the first user, and 30 - and 40 for the second. Therefore, with match_mlperf=True, it's guaranteed the - first item's score is in the top 2. With match_mlperf=False, this function - would compute the first user's first item is not in the top 2, because item 20 - has a higher score, and item 20 occurs twice. - - Args: - logits: A tensor containing the predicted logits for each user. The shape - of logits is (num_users_per_batch * (1 + NUM_EVAL_NEGATIVES),) Logits - for a user are grouped, and the last element of the group is the true - element. - - softmax_logits: The same tensor, but with zeros left-appended. - - duplicate_mask: A vector with the same shape as logits, with a value of 1 - if the item corresponding to the logit at that position has already - appeared for that user. - - num_training_neg: The number of negatives per positive during training. - - match_mlperf: Use the MLPerf reference convention for computing rank. - - use_tpu_spec: Should a TPUEstimatorSpec be returned instead of an - EstimatorSpec. Required for TPUs and if XLA is done on a GPU. Despite its - name, TPUEstimatorSpecs work with GPUs - - Returns: - An EstimatorSpec for evaluation. - """ - in_top_k, ndcg, metric_weights, logits_by_user = compute_top_k_and_ndcg( - logits, duplicate_mask, match_mlperf) - - # Examples are provided by the eval Dataset in a structured format, so eval - # labels can be reconstructed on the fly. - eval_labels = tf.reshape(shape=(-1,), tensor=tf.one_hot( - tf.zeros(shape=(logits_by_user.shape[0],), dtype=tf.int32) + - rconst.NUM_EVAL_NEGATIVES, logits_by_user.shape[1], dtype=tf.int32)) - - eval_labels_float = tf.cast(eval_labels, tf.float32) - - # During evaluation, the ratio of negatives to positives is much higher - # than during training. (Typically 999 to 1 vs. 4 to 1) By adjusting the - # weights for the negative examples we compute a loss which is consistent with - # the training data. (And provides apples-to-apples comparison) - negative_scale_factor = num_training_neg / rconst.NUM_EVAL_NEGATIVES - example_weights = ( - (eval_labels_float + (1 - eval_labels_float) * negative_scale_factor) * - (1 + rconst.NUM_EVAL_NEGATIVES) / (1 + num_training_neg)) - - # Tile metric weights back to logit dimensions - expanded_metric_weights = tf.reshape(tf.tile( - metric_weights[:, tf.newaxis], (1, rconst.NUM_EVAL_NEGATIVES + 1)), (-1,)) - - # ignore padded examples - example_weights *= tf.cast(expanded_metric_weights, tf.float32) - - cross_entropy = tf.losses.sparse_softmax_cross_entropy( - logits=softmax_logits, labels=eval_labels, weights=example_weights) - - def metric_fn(top_k_tensor, ndcg_tensor, weight_tensor): - return { - rconst.HR_KEY: tf.metrics.mean(top_k_tensor, weights=weight_tensor, - name=rconst.HR_METRIC_NAME), - rconst.NDCG_KEY: tf.metrics.mean(ndcg_tensor, weights=weight_tensor, - name=rconst.NDCG_METRIC_NAME), - } - - if use_tpu_spec: - return tf.contrib.tpu.TPUEstimatorSpec( - mode=tf.estimator.ModeKeys.EVAL, loss=cross_entropy, - eval_metrics=(metric_fn, [in_top_k, ndcg, metric_weights])) - - return tf.estimator.EstimatorSpec( - mode=tf.estimator.ModeKeys.EVAL, - loss=cross_entropy, - eval_metric_ops=metric_fn(in_top_k, ndcg, metric_weights) - ) - - -def compute_top_k_and_ndcg(logits, # type: tf.Tensor - duplicate_mask, # type: tf.Tensor - match_mlperf=False # type: bool - ): - """Compute inputs of metric calculation. - - Args: - logits: A tensor containing the predicted logits for each user. The shape - of logits is (num_users_per_batch * (1 + NUM_EVAL_NEGATIVES),) Logits - for a user are grouped, and the first element of the group is the true - element. - duplicate_mask: A vector with the same shape as logits, with a value of 1 - if the item corresponding to the logit at that position has already - appeared for that user. - match_mlperf: Use the MLPerf reference convention for computing rank. - - Returns: - is_top_k, ndcg and weights, all of which has size (num_users_in_batch,), and - logits_by_user which has size - (num_users_in_batch, (rconst.NUM_EVAL_NEGATIVES + 1)). - """ - logits_by_user = tf.reshape(logits, (-1, rconst.NUM_EVAL_NEGATIVES + 1)) - duplicate_mask_by_user = tf.reshape(duplicate_mask, - (-1, rconst.NUM_EVAL_NEGATIVES + 1)) - - if match_mlperf: - # Set duplicate logits to the min value for that dtype. The MLPerf - # reference dedupes during evaluation. - logits_by_user *= (1 - duplicate_mask_by_user) - logits_by_user += duplicate_mask_by_user * logits_by_user.dtype.min - - # Determine the location of the first element in each row after the elements - # are sorted. - sort_indices = tf.contrib.framework.argsort( - logits_by_user, axis=1, direction="DESCENDING") - - # Use matrix multiplication to extract the position of the true item from the - # tensor of sorted indices. This approach is chosen because both GPUs and TPUs - # perform matrix multiplications very quickly. This is similar to np.argwhere. - # However this is a special case because the target will only appear in - # sort_indices once. - one_hot_position = tf.cast(tf.equal(sort_indices, rconst.NUM_EVAL_NEGATIVES), - tf.int32) - sparse_positions = tf.multiply( - one_hot_position, tf.range(logits_by_user.shape[1])[tf.newaxis, :]) - position_vector = tf.reduce_sum(sparse_positions, axis=1) - - in_top_k = tf.cast(tf.less(position_vector, rconst.TOP_K), tf.float32) - ndcg = tf.log(2.) / tf.log(tf.cast(position_vector, tf.float32) + 2) - ndcg *= in_top_k - - # If a row is a padded row, all but the first element will be a duplicate. - metric_weights = tf.not_equal(tf.reduce_sum(duplicate_mask_by_user, axis=1), - rconst.NUM_EVAL_NEGATIVES) - - return in_top_k, ndcg, metric_weights, logits_by_user diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/popen_helper.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/popen_helper.py deleted file mode 100644 index 89ee4519..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/popen_helper.py +++ /dev/null @@ -1,60 +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. -# ============================================================================== -"""Helper file for running the async data generation process in OSS.""" - -import contextlib -import multiprocessing -import multiprocessing.pool - - -def get_forkpool(num_workers, init_worker=None, closing=True): - pool = multiprocessing.Pool(processes=num_workers, initializer=init_worker) - return contextlib.closing(pool) if closing else pool - - -def get_threadpool(num_workers, init_worker=None, closing=True): - pool = multiprocessing.pool.ThreadPool(processes=num_workers, - initializer=init_worker) - return contextlib.closing(pool) if closing else pool - - -class FauxPool(object): - """Mimic a pool using for loops. - - This class is used in place of proper pools when true determinism is desired - for testing or debugging. - """ - def __init__(self, *args, **kwargs): - pass - - def map(self, func, iterable, chunksize=None): - return [func(i) for i in iterable] - - def imap(self, func, iterable, chunksize=1): - for i in iterable: - yield func(i) - - def close(self): - pass - - def terminate(self): - pass - - def join(self): - pass - -def get_fauxpool(num_workers, init_worker=None, closing=True): - pool = FauxPool(processes=num_workers, initializer=init_worker) - return contextlib.closing(pool) if closing else pool diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/run.sh b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/run.sh deleted file mode 100644 index 56acf0ff..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/run.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/bash -set -e - -if [ `id -u` != 0 ]; then - echo "Calling sudo to gain root for this shell. (Needed to clear caches.)" - sudo echo "Success" -fi - -SCRIPT_DIR=`dirname "$BASH_SOURCE"` -export PYTHONPATH="${SCRIPT_DIR}/../../" - -DATASET="ml-20m" - -BUCKET=${BUCKET:-""} -ROOT_DIR="${BUCKET:-/tmp}/MLPerf_NCF" -echo "Root directory: ${ROOT_DIR}" - -if [[ -z ${BUCKET} ]]; then - LOCAL_ROOT=${ROOT_DIR} -else - LOCAL_ROOT="/tmp/MLPerf_NCF" - mkdir -p ${LOCAL_ROOT} - echo "Local root (for files which cannot use GCS): ${LOCAL_ROOT}" -fi - -DATE=$(date '+%Y-%m-%d_%H:%M:%S') -TEST_DIR="${ROOT_DIR}/${DATE}" -LOCAL_TEST_DIR="${LOCAL_ROOT}/${DATE}" -mkdir -p ${LOCAL_TEST_DIR} - -TPU=${TPU:-""} -if [[ -z ${TPU} ]]; then - DEVICE_FLAG="--num_gpus -1" # --use_xla_for_gpu" -else - DEVICE_FLAG="--tpu ${TPU} --num_gpus 0" -fi - -DATA_DIR="${ROOT_DIR}/movielens_data" -python "${SCRIPT_DIR}/../datasets/movielens.py" --data_dir ${DATA_DIR} --dataset ${DATASET} - -{ - -for i in `seq 0 4`; -do - START_TIME=$(date +%s) - MODEL_DIR="${TEST_DIR}/model_dir_${i}" - - RUN_LOG="${LOCAL_TEST_DIR}/run_${i}.log" - export COMPLIANCE_FILE="${LOCAL_TEST_DIR}/run_${i}_compliance_raw.log" - export STITCHED_COMPLIANCE_FILE="${LOCAL_TEST_DIR}/run_${i}_compliance_submission.log" - echo "" - echo "Beginning run ${i}" - echo " Complete output logs are in ${RUN_LOG}" - echo " Compliance logs: (submission log is created after run.)" - echo " ${COMPLIANCE_FILE}" - echo " ${STITCHED_COMPLIANCE_FILE}" - - # To reduce variation set the seed flag: - # --seed ${i} - - python -u "${SCRIPT_DIR}/ncf_main.py" \ - --model_dir ${MODEL_DIR} \ - --data_dir ${DATA_DIR} \ - --dataset ${DATASET} --hooks "" \ - ${DEVICE_FLAG} \ - --clean \ - --train_epochs 14 \ - --batch_size 98304 \ - --eval_batch_size 160000 \ - --learning_rate 0.00382059 \ - --beta1 0.783529 \ - --beta2 0.909003 \ - --epsilon 1.45439e-07 \ - --layers 256,256,128,64 --num_factors 64 \ - --hr_threshold 0.635 \ - --ml_perf \ - |& tee ${RUN_LOG} \ - | grep --line-buffered -E --regexp="(Iteration [0-9]+: HR = [0-9\.]+, NDCG = [0-9\.]+, Loss = [0-9\.]+)|(pipeline_hash)|(MLPerf time:)" - - END_TIME=$(date +%s) - echo "Run ${i} complete: $(( $END_TIME - $START_TIME )) seconds." - - # Don't fill up the local hard drive. - if [[ -z ${BUCKET} ]]; then - echo "Removing model directory to save space." - rm -r ${MODEL_DIR} - fi - -done - -} |& tee "${LOCAL_TEST_DIR}/summary.log" diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/run_tpu.sh b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/run_tpu.sh deleted file mode 100644 index bcbb1929..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/run_tpu.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -set -e - -# Example settings: -# export TPU="taylorrobie-tpu-0" -# export BUCKET="gs://taylorrobie-tpu-test-bucket-2" - -# Remove IDE "not assigned" warning highlights. -TPU=${TPU:-""} -BUCKET=${BUCKET:-""} - -if [[ -z ${TPU} ]]; then - echo "Please set 'TPU' to the name of the TPU to be used." - exit 1 -fi - -if [[ -z ${BUCKET} ]]; then - echo "Please set 'BUCKET' to the GCS bucket to be used." - exit 1 -fi - -./run.sh diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/stat_utils.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/stat_utils.py deleted file mode 100644 index 658a2721..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/stat_utils.py +++ /dev/null @@ -1,92 +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. -# ============================================================================== -"""Statistics utility functions of NCF.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os - -import numpy as np - - -def random_int32(): - return np.random.randint(low=0, high=np.iinfo(np.int32).max, dtype=np.int32) - - -def permutation(args): - """Fork safe permutation function. - - This function can be called within a multiprocessing worker and give - appropriately random results. - - Args: - args: A size two tuple that will unpacked into the size of the permutation - and the random seed. This form is used because starmap is not universally - available. - - returns: - A NumPy array containing a random permutation. - """ - x, seed = args - - # If seed is None NumPy will seed randomly. - state = np.random.RandomState(seed=seed) # pylint: disable=no-member - output = np.arange(x, dtype=np.int32) - state.shuffle(output) - return output - - -def very_slightly_biased_randint(max_val_vector): - sample_dtype = np.uint64 - out_dtype = max_val_vector.dtype - samples = np.random.randint(low=0, high=np.iinfo(sample_dtype).max, - size=max_val_vector.shape, dtype=sample_dtype) - return np.mod(samples, max_val_vector.astype(sample_dtype)).astype(out_dtype) - - -def mask_duplicates(x, axis=1): # type: (np.ndarray, int) -> np.ndarray - """Identify duplicates from sampling with replacement. - - Args: - x: A 2D NumPy array of samples - axis: The axis along which to de-dupe. - - Returns: - A NumPy array with the same shape as x with one if an element appeared - previously along axis 1, else zero. - """ - if axis != 1: - raise NotImplementedError - - x_sort_ind = np.argsort(x, axis=1, kind="mergesort") - sorted_x = x[np.arange(x.shape[0])[:, np.newaxis], x_sort_ind] - - # compute the indices needed to map values back to their original position. - inv_x_sort_ind = np.argsort(x_sort_ind, axis=1, kind="mergesort") - - # Compute the difference of adjacent sorted elements. - diffs = sorted_x[:, :-1] - sorted_x[:, 1:] - - # We are only interested in whether an element is zero. Therefore left padding - # with ones to restore the original shape is sufficient. - diffs = np.concatenate( - [np.ones((diffs.shape[0], 1), dtype=diffs.dtype), diffs], axis=1) - - # Duplicate values will have a difference of zero. By definition the first - # element is never a duplicate. - return np.where(diffs[np.arange(x.shape[0])[:, np.newaxis], - inv_x_sort_ind], 0, 1) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/requirements.txt b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/requirements.txt deleted file mode 100644 index 1c8a05f9..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/requirements.txt +++ /dev/null @@ -1,11 +0,0 @@ -google-api-python-client>=1.6.7 -google-cloud-bigquery>=0.31.0 -kaggle>=1.3.9 -mlperf_compliance==0.0.10 -numpy>=1.15.4 -oauth2client>=4.1.2 -pandas>=0.22.0 -psutil>=5.4.3 -py-cpuinfo>=3.3.0 -scipy>=0.19.1 -typing diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/README.md b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/README.md deleted file mode 100644 index b6a13fc6..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/README.md +++ /dev/null @@ -1,139 +0,0 @@ -# ResNet in TensorFlow - -Deep residual networks, or ResNets for short, provided the breakthrough idea of -identity mappings in order to enable training of very deep convolutional neural -networks. This folder contains an implementation of ResNet for the ImageNet -dataset written in TensorFlow. - -See the following papers for more background: - -[1] [Deep Residual Learning for Image Recognition](https://arxiv.org/pdf/1512.03385.pdf) by Kaiming He, Xiangyu Zhang, Shaoqing Ren, and Jian Sun, Dec 2015. - -[2] [Identity Mappings in Deep Residual Networks](https://arxiv.org/pdf/1603.05027.pdf) by Kaiming He, Xiangyu Zhang, Shaoqing Ren, and Jian Sun, Jul 2016. - -In code, v1 refers to the ResNet defined in [1] but where a stride 2 is used on -the 3x3 conv rather than the first 1x1 in the bottleneck. This change results -in higher and more stable accuracy with less epochs than the original v1 and has -shown to scale to higher batch sizes with minimal degradation in accuracy. -There is no originating paper. The first mention we are aware of was in the -torch version of [ResNetv1](https://github.com/facebook/fb.resnet.torch). Most -popular v1 implementations are this implementation which we call ResNetv1.5. - -In testing we found v1.5 requires ~12% more compute to train and has 6% reduced -throughput for inference compared to ResNetv1. CIFAR-10 ResNet does not use the -bottleneck and is thus the same for v1 as v1.5. - -v2 refers to [2]. The principle difference between the two versions is that v1 -applies batch normalization and activation after convolution, while v2 applies -batch normalization, then activation, and finally convolution. A schematic -comparison is presented in Figure 1 (left) of [2]. - -Please proceed according to which dataset you would like to train/evaluate on: - - -## CIFAR-10 - -### Setup - -You simply need to have the latest version of TensorFlow installed. -First make sure you've [added the models folder to your Python path](/official/#running-the-models); otherwise you may encounter an error like `ImportError: No module named official.resnet`. - -Then download and extract the CIFAR-10 data from Alex's website, specifying the location with the `--data_dir` flag. Run the following: - -```bash -python cifar10_download_and_extract.py -# Then to train the model, run the following: -python cifar10_main.py - -``` - -Use `--data_dir` to specify the location of the CIFAR-10 data used in the previous step. There are more flag options as described in `cifar10_main.py`. - - -## ImageNet - -### Setup -To begin, you will need to download the ImageNet dataset and convert it to -TFRecord format. The following [script](https://github.com/tensorflow/tpu/blob/master/tools/datasets/imagenet_to_gcs.py) -and [README](https://github.com/tensorflow/tpu/tree/master/tools/datasets#imagenet_to_gcspy) -provide a few options. - -Once your dataset is ready, you can begin training the model as follows: - -```bash -python imagenet_main.py --data_dir=/path/to/imagenet -``` - -The model will begin training and will automatically evaluate itself on the -validation data roughly once per epoch. - -Note that there are a number of other options you can specify, including -`--model_dir` to choose where to store the model and `--resnet_size` to choose -the model size (options include ResNet-18 through ResNet-200). See -[`resnet_run_loop.py`](resnet_run_loop.py) for the full list of options. - - -## Compute Devices -Training is accomplished using the DistributionStrategies API. (https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/distribute/README.md) - -The appropriate distribution strategy is chosen based on the `--num_gpus` flag. -By default this flag is one if TensorFlow is compiled with CUDA, and zero -otherwise. - -num_gpus: -+ 0: Use OneDeviceStrategy and train on CPU. -+ 1: Use OneDeviceStrategy and train on GPU. -+ 2+: Use MirroredStrategy (data parallelism) to distribute a batch between devices. - -### Pre-trained model -You can download pre-trained versions of ResNet-50. Reported accuracies are top-1 single-crop accuracy for the ImageNet validation set. -Models are reported as both checkpoints produced by Estimator during training, and as SavedModels which are more portable. Checkpoints are fragile, -and these are not guaranteed to work with future versions of the code. Both ResNet v1 -and ResNet v2 have been trained in both fp16 and fp32 precision. (Here v1 refers to "v1.5". See the note above.) Furthermore, SavedModels -are generated to accept either tensor or JPG inputs, and with channels_first (NCHW) and channels_last (NHWC) convolutions. NCHW is generally -better for GPUs, while NHWC is generally better for CPUs. See the TensorFlow [performance guide](https://www.tensorflow.org/performance/performance_guide#data_formats) -for more details. - -ResNet-50 v2 (fp32, Accuracy 76.47%): -* [Checkpoint](http://download.tensorflow.org/models/official/20181001_resnet/checkpoints/resnet_imagenet_v2_fp32_20181001.tar.gz) -* SavedModel [(NCHW)](http://download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v2_fp32_savedmodel_NCHW.tar.gz), -[(NCHW, JPG)](http://download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v2_fp32_savedmodel_NCHW_jpg.tar.gz), -[(NHWC)](http://download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v2_fp32_savedmodel_NHWC.tar.gz), -[(NHWC, JPG)](http://download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v2_fp32_savedmodel_NHWC_jpg.tar.gz) - -ResNet-50 v2 (fp16, Accuracy 76.56%): -* [Checkpoint](http://download.tensorflow.org/models/official/20181001_resnet/checkpoints/resnet_imagenet_v2_fp16_20180928.tar.gz) -* SavedModel [(NCHW)](http://download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v2_fp16_savedmodel_NCHW.tar.gz), -[(NCHW, JPG)](http://download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v2_fp16_savedmodel_NCHW_jpg.tar.gz), -[(NHWC)](http://download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v2_fp16_savedmodel_NHWC.tar.gz), -[(NHWC, JPG)](http://download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v2_fp16_savedmodel_NHWC_jpg.tar.gz) - -ResNet-50 v1 (fp32, Accuracy 76.53%): -* [Checkpoint](http://download.tensorflow.org/models/official/20181001_resnet/checkpoints/resnet_imagenet_v1_fp32_20181001.tar.gz) -* SavedModel [(NCHW)](http://download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v1_fp32_savedmodel_NCHW.tar.gz), -[(NCHW, JPG)](http://download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v1_fp32_savedmodel_NCHW_jpg.tar.gz), -[(NHWC)](http://download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v1_fp32_savedmodel_NHWC.tar.gz), -[(NHWC, JPG)](http://download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v1_fp32_savedmodel_NHWC_jpg.tar.gz) - -ResNet-50 v1 (fp16, Accuracy 76.18%): -* [Checkpoint](http://download.tensorflow.org/models/official/20181001_resnet/checkpoints/resnet_imagenet_v1_fp16_20181001.tar.gz) -* SavedModel [(NCHW)](http://download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v1_fp16_savedmodel_NCHW.tar.gz), -[(NCHW, JPG)](http://download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v1_fp16_savedmodel_NCHW_jpg.tar.gz), -[(NHWC)](http://download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v1_fp16_savedmodel_NHWC.tar.gz), -[(NHWC, JPG)](http://download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v1_fp16_savedmodel_NHWC_jpg.tar.gz) - -### Transfer Learning -You can use a pretrained model to initialize a training process. In addition you are able to freeze all but the final fully connected layers to fine tune your model. Transfer Learning is useful when training on your own small datasets. For a brief look at transfer learning in the context of convolutional neural networks, we recommend reading these [short notes](http://cs231n.github.io/transfer-learning/). - - -To fine tune a pretrained resnet you must make three changes to your training procedure: - -1) Build the exact same model as previously except we change the number of labels in the final classification layer. - -2) Restore all weights from the pre-trained resnet except for the final classification layer; this will get randomly initialized instead. - -3) Freeze earlier layers of the network - -We can perform these three operations by specifying two flags: ```--pretrained_model_checkpoint_path``` and ```--fine_tune```. The first flag is a string that points to the path of a pre-trained resnet model. If this flag is specified, it will load all but the final classification layer. A key thing to note: if both ```--pretrained_model_checkpoint_path``` and a non empty ```model_dir``` directory are passed, the tensorflow estimator will load only the ```model_dir```. For more on this please see [WarmStartSettings](https://www.tensorflow.org/versions/master/api_docs/python/tf/estimator/WarmStartSettings) and [Estimators](https://www.tensorflow.org/guide/estimators). - -The second flag ```--fine_tune``` is a boolean that indicates whether earlier layers of the network should be frozen. You may set this flag to false if you wish to continue training a pre-trained model from a checkpoint. If you set this flag to true, you can train a new classification layer from scratch. diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/cifar10_download_and_extract.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/cifar10_download_and_extract.py deleted file mode 100644 index ee587efc..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/cifar10_download_and_extract.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright 2015 The TensorFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT 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 extracts the binary version of the CIFAR-10 dataset.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import argparse -import os -import sys -import tarfile - -from six.moves import urllib -import tensorflow as tf - -DATA_URL = 'https://www.cs.toronto.edu/~kriz/cifar-10-binary.tar.gz' - -parser = argparse.ArgumentParser() - -parser.add_argument( - '--data_dir', type=str, default='/tmp/cifar10_data', - help='Directory to download data and extract the tarball') - - -def main(_): - """Download and extract the tarball from Alex's website.""" - if not os.path.exists(FLAGS.data_dir): - os.makedirs(FLAGS.data_dir) - - filename = DATA_URL.split('/')[-1] - filepath = os.path.join(FLAGS.data_dir, filename) - - if not os.path.exists(filepath): - def _progress(count, block_size, total_size): - sys.stdout.write('\r>> Downloading %s %.1f%%' % ( - filename, 100.0 * count * block_size / total_size)) - sys.stdout.flush() - - filepath, _ = urllib.request.urlretrieve(DATA_URL, filepath, _progress) - print() - statinfo = os.stat(filepath) - print('Successfully downloaded', filename, statinfo.st_size, 'bytes.') - - tarfile.open(filepath, 'r:gz').extractall(FLAGS.data_dir) - - -if __name__ == '__main__': - FLAGS, unparsed = parser.parse_known_args() - tf.app.run(argv=[sys.argv[0]] + unparsed) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/cifar10_main.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/cifar10_main.py deleted file mode 100644 index e5e5008f..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/cifar10_main.py +++ /dev/null @@ -1,278 +0,0 @@ -# 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. -# ============================================================================== -"""Runs a ResNet model on the CIFAR-10 dataset.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os - -from absl import app as absl_app -from absl import flags -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.resnet import resnet_model -from official.resnet import resnet_run_loop -from official.utils.flags import core as flags_core -from official.utils.logs import logger - -HEIGHT = 32 -WIDTH = 32 -NUM_CHANNELS = 3 -_DEFAULT_IMAGE_BYTES = HEIGHT * WIDTH * NUM_CHANNELS -# The record is the image plus a one-byte label -_RECORD_BYTES = _DEFAULT_IMAGE_BYTES + 1 -NUM_CLASSES = 10 -_NUM_DATA_FILES = 5 - -# TODO(tobyboyd): Change to best practice 45K(train)/5K(val)/10K(test) splits. -NUM_IMAGES = { - 'train': 50000, - 'validation': 10000, -} - -DATASET_NAME = 'CIFAR-10' - - -############################################################################### -# Data processing -############################################################################### -def get_filenames(is_training, data_dir): - """Returns a list of filenames.""" - assert tf.gfile.Exists(data_dir), ( - 'Run cifar10_download_and_extract.py first to download and extract the ' - 'CIFAR-10 data.') - - if is_training: - return [ - os.path.join(data_dir, 'data_batch_%d.bin' % i) - for i in range(1, _NUM_DATA_FILES + 1) - ] - else: - return [os.path.join(data_dir, 'test_batch.bin')] - - -def parse_record(raw_record, is_training, dtype): - """Parse CIFAR-10 image and label from a raw record.""" - # Convert bytes to a vector of uint8 that is record_bytes long. - record_vector = tf.decode_raw(raw_record, tf.uint8) - - # The first byte represents the label, which we convert from uint8 to int32 - # and then to one-hot. - label = tf.cast(record_vector[0], tf.int32) - - # The remaining bytes after the label represent the image, which we reshape - # from [depth * height * width] to [depth, height, width]. - depth_major = tf.reshape(record_vector[1:_RECORD_BYTES], - [NUM_CHANNELS, HEIGHT, WIDTH]) - - # Convert from [depth, height, width] to [height, width, depth], and cast as - # float32. - image = tf.cast(tf.transpose(depth_major, [1, 2, 0]), tf.float32) - - image = preprocess_image(image, is_training) - image = tf.cast(image, dtype) - - return image, label - - -def preprocess_image(image, is_training): - """Preprocess a single image of layout [height, width, depth].""" - if is_training: - # Resize the image to add four extra pixels on each side. - image = tf.image.resize_image_with_crop_or_pad( - image, HEIGHT + 8, WIDTH + 8) - - # Randomly crop a [HEIGHT, WIDTH] section of the image. - image = tf.random_crop(image, [HEIGHT, WIDTH, NUM_CHANNELS]) - - # Randomly flip the image horizontally. - image = tf.image.random_flip_left_right(image) - - # Subtract off the mean and divide by the variance of the pixels. - image = tf.image.per_image_standardization(image) - return image - - -def input_fn(is_training, data_dir, batch_size, num_epochs=1, - dtype=tf.float32, datasets_num_private_threads=None, - num_parallel_batches=1, parse_record_fn=parse_record): - """Input function which provides batches for train or eval. - - Args: - is_training: A boolean denoting whether the input is for training. - data_dir: The directory containing the input data. - batch_size: The number of samples per batch. - num_epochs: The number of epochs to repeat the dataset. - dtype: Data type to use for images/features - datasets_num_private_threads: Number of private threads for tf.data. - num_parallel_batches: Number of parallel batches for tf.data. - parse_record_fn: Function to use for parsing the records. - - Returns: - A dataset that can be used for iteration. - """ - filenames = get_filenames(is_training, data_dir) - dataset = tf.data.FixedLengthRecordDataset(filenames, _RECORD_BYTES) - - return resnet_run_loop.process_record_dataset( - dataset=dataset, - is_training=is_training, - batch_size=batch_size, - shuffle_buffer=NUM_IMAGES['train'], - parse_record_fn=parse_record_fn, - num_epochs=num_epochs, - dtype=dtype, - datasets_num_private_threads=datasets_num_private_threads, - num_parallel_batches=num_parallel_batches - ) - - -def get_synth_input_fn(dtype): - return resnet_run_loop.get_synth_input_fn( - HEIGHT, WIDTH, NUM_CHANNELS, NUM_CLASSES, dtype=dtype) - - -############################################################################### -# Running the model -############################################################################### -class Cifar10Model(resnet_model.Model): - """Model class with appropriate defaults for CIFAR-10 data.""" - - def __init__(self, resnet_size, data_format=None, num_classes=NUM_CLASSES, - resnet_version=resnet_model.DEFAULT_VERSION, - dtype=resnet_model.DEFAULT_DTYPE): - """These are the parameters that work for CIFAR-10 data. - - Args: - resnet_size: The number of convolutional layers needed in the model. - data_format: Either 'channels_first' or 'channels_last', specifying which - data format to use when setting up the model. - num_classes: The number of output classes needed from the model. This - enables users to extend the same model to their own datasets. - resnet_version: Integer representing which version of the ResNet network - to use. See README for details. Valid values: [1, 2] - dtype: The TensorFlow dtype to use for calculations. - - Raises: - ValueError: if invalid resnet_size is chosen - """ - if resnet_size % 6 != 2: - raise ValueError('resnet_size must be 6n + 2:', resnet_size) - - num_blocks = (resnet_size - 2) // 6 - - super(Cifar10Model, self).__init__( - resnet_size=resnet_size, - bottleneck=False, - num_classes=num_classes, - num_filters=16, - kernel_size=3, - conv_stride=1, - first_pool_size=None, - first_pool_stride=None, - block_sizes=[num_blocks] * 3, - block_strides=[1, 2, 2], - resnet_version=resnet_version, - data_format=data_format, - dtype=dtype - ) - - -def cifar10_model_fn(features, labels, mode, params): - """Model function for CIFAR-10.""" - features = tf.reshape(features, [-1, HEIGHT, WIDTH, NUM_CHANNELS]) - # Learning rate schedule follows arXiv:1512.03385 for ResNet-56 and under. - learning_rate_fn = resnet_run_loop.learning_rate_with_decay( - batch_size=params['batch_size'], batch_denom=128, - num_images=NUM_IMAGES['train'], boundary_epochs=[91, 136, 182], - decay_rates=[1, 0.1, 0.01, 0.001]) - - # Weight decay of 2e-4 diverges from 1e-4 decay used in the ResNet paper - # and seems more stable in testing. The difference was nominal for ResNet-56. - weight_decay = 2e-4 - - # Empirical testing showed that including batch_normalization variables - # in the calculation of regularized loss helped validation accuracy - # for the CIFAR-10 dataset, perhaps because the regularization prevents - # overfitting on the small data set. We therefore include all vars when - # regularizing and computing loss during training. - def loss_filter_fn(_): - return True - - return resnet_run_loop.resnet_model_fn( - features=features, - labels=labels, - mode=mode, - model_class=Cifar10Model, - resnet_size=params['resnet_size'], - weight_decay=weight_decay, - learning_rate_fn=learning_rate_fn, - momentum=0.9, - data_format=params['data_format'], - resnet_version=params['resnet_version'], - loss_scale=params['loss_scale'], - loss_filter_fn=loss_filter_fn, - dtype=params['dtype'], - fine_tune=params['fine_tune'] - ) - - -def define_cifar_flags(): - resnet_run_loop.define_resnet_flags() - flags.adopt_module_key_flags(resnet_run_loop) - flags_core.set_defaults(data_dir='/tmp/cifar10_data/cifar-10-batches-bin', - model_dir='/tmp/cifar10_model', - resnet_size='56', - train_epochs=182, - epochs_between_evals=10, - batch_size=128, - image_bytes_as_serving_input=False) - - -def run_cifar(flags_obj): - """Run ResNet CIFAR-10 training and eval loop. - - Args: - flags_obj: An object containing parsed flag values. - - Returns: - Dictionary of results. Including final accuracy. - """ - if flags_obj.image_bytes_as_serving_input: - tf.logging.fatal('--image_bytes_as_serving_input cannot be set to True ' - 'for CIFAR. This flag is only applicable to ImageNet.') - return - - input_function = (flags_obj.use_synthetic_data and - get_synth_input_fn(flags_core.get_tf_dtype(flags_obj)) or - input_fn) - result = resnet_run_loop.resnet_main( - flags_obj, cifar10_model_fn, input_function, DATASET_NAME, - shape=[HEIGHT, WIDTH, NUM_CHANNELS]) - - return result - - -def main(_): - with logger.benchmark_context(flags.FLAGS): - run_cifar(flags.FLAGS) - - -if __name__ == '__main__': - tf.logging.set_verbosity(tf.logging.INFO) - define_cifar_flags() - absl_app.run(main) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/cifar10_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/cifar10_test.py deleted file mode 100644 index a736e10d..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/cifar10_test.py +++ /dev/null @@ -1,170 +0,0 @@ -# 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. -# ============================================================================== - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -from tempfile import mkstemp - -import numpy as np -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.resnet import cifar10_main -from official.utils.testing import integration - -tf.logging.set_verbosity(tf.logging.ERROR) - -_BATCH_SIZE = 128 -_HEIGHT = 32 -_WIDTH = 32 -_NUM_CHANNELS = 3 - - -class BaseTest(tf.test.TestCase): - """Tests for the Cifar10 version of Resnet. - """ - - @classmethod - def setUpClass(cls): # pylint: disable=invalid-name - super(BaseTest, cls).setUpClass() - cifar10_main.define_cifar_flags() - - def tearDown(self): - super(BaseTest, self).tearDown() - tf.gfile.DeleteRecursively(self.get_temp_dir()) - - def test_dataset_input_fn(self): - fake_data = bytearray() - fake_data.append(7) - for i in range(_NUM_CHANNELS): - for _ in range(_HEIGHT * _WIDTH): - fake_data.append(i) - - _, filename = mkstemp(dir=self.get_temp_dir()) - data_file = open(filename, 'wb') - data_file.write(fake_data) - data_file.close() - - fake_dataset = tf.data.FixedLengthRecordDataset( - filename, cifar10_main._RECORD_BYTES) # pylint: disable=protected-access - fake_dataset = fake_dataset.map( - lambda val: cifar10_main.parse_record(val, False, tf.float32)) - image, label = fake_dataset.make_one_shot_iterator().get_next() - - self.assertAllEqual(label.shape, ()) - self.assertAllEqual(image.shape, (_HEIGHT, _WIDTH, _NUM_CHANNELS)) - - with self.test_session() as sess: - image, label = sess.run([image, label]) - - self.assertEqual(label, 7) - - for row in image: - for pixel in row: - self.assertAllClose(pixel, np.array([-1.225, 0., 1.225]), rtol=1e-3) - - def cifar10_model_fn_helper(self, mode, resnet_version, dtype): - input_fn = cifar10_main.get_synth_input_fn(dtype) - dataset = input_fn(True, '', _BATCH_SIZE) - iterator = dataset.make_initializable_iterator() - features, labels = iterator.get_next() - spec = cifar10_main.cifar10_model_fn( - features, labels, mode, { - 'dtype': dtype, - 'resnet_size': 32, - 'data_format': 'channels_last', - 'batch_size': _BATCH_SIZE, - 'resnet_version': resnet_version, - 'loss_scale': 128 if dtype == tf.float16 else 1, - 'fine_tune': False, - }) - - predictions = spec.predictions - self.assertAllEqual(predictions['probabilities'].shape, - (_BATCH_SIZE, 10)) - self.assertEqual(predictions['probabilities'].dtype, tf.float32) - self.assertAllEqual(predictions['classes'].shape, (_BATCH_SIZE,)) - self.assertEqual(predictions['classes'].dtype, tf.int64) - - if mode != tf.estimator.ModeKeys.PREDICT: - loss = spec.loss - self.assertAllEqual(loss.shape, ()) - self.assertEqual(loss.dtype, tf.float32) - - if mode == tf.estimator.ModeKeys.EVAL: - eval_metric_ops = spec.eval_metric_ops - self.assertAllEqual(eval_metric_ops['accuracy'][0].shape, ()) - self.assertAllEqual(eval_metric_ops['accuracy'][1].shape, ()) - self.assertEqual(eval_metric_ops['accuracy'][0].dtype, tf.float32) - self.assertEqual(eval_metric_ops['accuracy'][1].dtype, tf.float32) - - def test_cifar10_model_fn_train_mode_v1(self): - self.cifar10_model_fn_helper(tf.estimator.ModeKeys.TRAIN, resnet_version=1, - dtype=tf.float32) - - def test_cifar10_model_fn_trainmode__v2(self): - self.cifar10_model_fn_helper(tf.estimator.ModeKeys.TRAIN, resnet_version=2, - dtype=tf.float32) - - def test_cifar10_model_fn_eval_mode_v1(self): - self.cifar10_model_fn_helper(tf.estimator.ModeKeys.EVAL, resnet_version=1, - dtype=tf.float32) - - def test_cifar10_model_fn_eval_mode_v2(self): - self.cifar10_model_fn_helper(tf.estimator.ModeKeys.EVAL, resnet_version=2, - dtype=tf.float32) - - def test_cifar10_model_fn_predict_mode_v1(self): - self.cifar10_model_fn_helper(tf.estimator.ModeKeys.PREDICT, - resnet_version=1, dtype=tf.float32) - - def test_cifar10_model_fn_predict_mode_v2(self): - self.cifar10_model_fn_helper(tf.estimator.ModeKeys.PREDICT, - resnet_version=2, dtype=tf.float32) - - def _test_cifar10model_shape(self, resnet_version): - batch_size = 135 - num_classes = 246 - - model = cifar10_main.Cifar10Model(32, data_format='channels_last', - num_classes=num_classes, - resnet_version=resnet_version) - fake_input = tf.random_uniform([batch_size, _HEIGHT, _WIDTH, _NUM_CHANNELS]) - output = model(fake_input, training=True) - - self.assertAllEqual(output.shape, (batch_size, num_classes)) - - def test_cifar10model_shape_v1(self): - self._test_cifar10model_shape(resnet_version=1) - - def test_cifar10model_shape_v2(self): - self._test_cifar10model_shape(resnet_version=2) - - def test_cifar10_end_to_end_synthetic_v1(self): - integration.run_synthetic( - main=cifar10_main.run_cifar, tmp_root=self.get_temp_dir(), - extra_flags=['-resnet_version', '1'] - ) - - def test_cifar10_end_to_end_synthetic_v2(self): - integration.run_synthetic( - main=cifar10_main.run_cifar, tmp_root=self.get_temp_dir(), - extra_flags=['-resnet_version', '2'] - ) - - -if __name__ == '__main__': - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/estimator_cifar_benchmark.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/estimator_cifar_benchmark.py deleted file mode 100644 index 46d46891..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/estimator_cifar_benchmark.py +++ /dev/null @@ -1,155 +0,0 @@ -# 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. -# ============================================================================== -"""Executes Estimator benchmarks and accuracy tests.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import json -import time -import os - -from absl import flags -from absl.testing import flagsaver -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.resnet import cifar10_main as cifar_main - -DATA_DIR = '/data/cifar10_data/cifar-10-batches-bin' - - -class EstimatorCifar10BenchmarkTests(tf.test.Benchmark): - """Benchmarks and accuracy tests for Estimator ResNet56.""" - - local_flags = None - - def __init__(self, output_dir=None): - self.output_dir = output_dir - - def resnet56_1_gpu(self): - """Test layers model with Estimator and distribution strategies.""" - self._setup() - flags.FLAGS.num_gpus = 1 - flags.FLAGS.data_dir = DATA_DIR - flags.FLAGS.batch_size = 128 - flags.FLAGS.train_epochs = 182 - flags.FLAGS.model_dir = self._get_model_dir('resnet56_1_gpu') - flags.FLAGS.resnet_size = 56 - flags.FLAGS.dtype = 'fp32' - self._run_and_report_benchmark() - - def resnet56_fp16_1_gpu(self): - """Test layers FP16 model with Estimator and distribution strategies.""" - self._setup() - flags.FLAGS.num_gpus = 1 - flags.FLAGS.data_dir = DATA_DIR - flags.FLAGS.batch_size = 128 - flags.FLAGS.train_epochs = 182 - flags.FLAGS.model_dir = self._get_model_dir('resnet56_fp16_1_gpu') - flags.FLAGS.resnet_size = 56 - flags.FLAGS.dtype = 'fp16' - self._run_and_report_benchmark() - - def resnet56_2_gpu(self): - """Test layers model with Estimator and dist_strat. 2 GPUs.""" - self._setup() - flags.FLAGS.num_gpus = 1 - flags.FLAGS.data_dir = DATA_DIR - flags.FLAGS.batch_size = 128 - flags.FLAGS.train_epochs = 182 - flags.FLAGS.model_dir = self._get_model_dir('resnet56_2_gpu') - flags.FLAGS.resnet_size = 56 - flags.FLAGS.dtype = 'fp32' - self._run_and_report_benchmark() - - def resnet56_fp16_2_gpu(self): - """Test layers FP16 model with Estimator and dist_strat. 2 GPUs.""" - self._setup() - flags.FLAGS.num_gpus = 2 - flags.FLAGS.data_dir = DATA_DIR - flags.FLAGS.batch_size = 128 - flags.FLAGS.train_epochs = 182 - flags.FLAGS.model_dir = self._get_model_dir('resnet56_fp16_2_gpu') - flags.FLAGS.resnet_size = 56 - flags.FLAGS.dtype = 'fp16' - self._run_and_report_benchmark() - - def unit_test(self): - """A lightweigth test that can finish quickly""" - self._setup() - flags.FLAGS.num_gpus = 1 - flags.FLAGS.data_dir = DATA_DIR - flags.FLAGS.batch_size = 128 - flags.FLAGS.train_epochs = 1 - flags.FLAGS.model_dir = self._get_model_dir('resnet56_1_gpu') - flags.FLAGS.resnet_size = 8 - flags.FLAGS.dtype = 'fp32' - self._run_and_report_benchmark() - - def _run_and_report_benchmark(self): - start_time_sec = time.time() - stats = cifar_main.run_cifar(flags.FLAGS) - wall_time_sec = time.time() - start_time_sec - - self.report_benchmark( - iters=stats['global_step'], - wall_time=wall_time_sec, - extras={ - 'accuracy': - self._json_description(stats['accuracy'].item(), priority=0), - 'accuracy_top_5': - self._json_description(stats['accuracy_top_5'].item()), - }) - - def _json_description(self, - value, - priority=None, - min_value=None, - max_value=None): - """Get a json-formatted string describing the attributes for a metric""" - - attributes = {} - attributes['value'] = value - if priority: - attributes['priority'] = priority - if min_value: - attributes['min_value'] = min_value - if max_value: - attributes['max_value'] = max_value - - if min_value or max_value: - succeeded = True - if min_value and value < min_value: - succeeded = False - if max_value and value > max_value: - succeeded = False - attributes['succeeded'] = succeeded - - return json.dumps(attributes) - - def _get_model_dir(self, folder_name): - return os.path.join(self.output_dir, folder_name) - - def _setup(self): - tf.logging.set_verbosity(tf.logging.DEBUG) - if EstimatorCifar10BenchmarkTests.local_flags is None: - cifar_main.define_cifar_flags() - # Loads flags to get defaults to then override. - flags.FLAGS(['foo']) - saved_flag_values = flagsaver.save_flag_values() - EstimatorCifar10BenchmarkTests.local_flags = saved_flag_values - return - flagsaver.restore_flag_values(EstimatorCifar10BenchmarkTests.local_flags) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/imagenet_main.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/imagenet_main.py deleted file mode 100644 index 01e7f0c0..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/imagenet_main.py +++ /dev/null @@ -1,357 +0,0 @@ -# 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. -# ============================================================================== -"""Runs a ResNet model on the ImageNet dataset.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os - -from absl import app as absl_app -from absl import flags -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.utils.flags import core as flags_core -from official.utils.logs import logger -from official.resnet import imagenet_preprocessing -from official.resnet import resnet_model -from official.resnet import resnet_run_loop - -DEFAULT_IMAGE_SIZE = 224 -NUM_CHANNELS = 3 -NUM_CLASSES = 1001 - -NUM_IMAGES = { - 'train': 1281167, - 'validation': 50000, -} - -_NUM_TRAIN_FILES = 1024 -_SHUFFLE_BUFFER = 10000 - -DATASET_NAME = 'ImageNet' - -############################################################################### -# Data processing -############################################################################### -def get_filenames(is_training, data_dir): - """Return filenames for dataset.""" - if is_training: - return [ - os.path.join(data_dir, 'train-%05d-of-01024' % i) - for i in range(_NUM_TRAIN_FILES)] - else: - return [ - os.path.join(data_dir, 'validation-%05d-of-00128' % i) - for i in range(128)] - - -def _parse_example_proto(example_serialized): - """Parses an Example proto containing a training example of an image. - - The output of the build_image_data.py image preprocessing script is a dataset - containing serialized Example protocol buffers. Each Example proto contains - the following fields (values are included as examples): - - image/height: 462 - image/width: 581 - image/colorspace: 'RGB' - image/channels: 3 - image/class/label: 615 - image/class/synset: 'n03623198' - image/class/text: 'knee pad' - image/object/bbox/xmin: 0.1 - image/object/bbox/xmax: 0.9 - image/object/bbox/ymin: 0.2 - image/object/bbox/ymax: 0.6 - image/object/bbox/label: 615 - image/format: 'JPEG' - image/filename: 'ILSVRC2012_val_00041207.JPEG' - image/encoded: - - Args: - example_serialized: scalar Tensor tf.string containing a serialized - Example protocol buffer. - - Returns: - image_buffer: Tensor tf.string containing the contents of a JPEG file. - label: Tensor tf.int32 containing the label. - bbox: 3-D float Tensor of bounding boxes arranged [1, num_boxes, coords] - where each coordinate is [0, 1) and the coordinates are arranged as - [ymin, xmin, ymax, xmax]. - """ - # Dense features in Example proto. - feature_map = { - 'image/encoded': tf.FixedLenFeature([], dtype=tf.string, - default_value=''), - 'image/class/label': tf.FixedLenFeature([], dtype=tf.int64, - default_value=-1), - 'image/class/text': tf.FixedLenFeature([], dtype=tf.string, - default_value=''), - } - sparse_float32 = tf.VarLenFeature(dtype=tf.float32) - # Sparse features in Example proto. - feature_map.update( - {k: sparse_float32 for k in ['image/object/bbox/xmin', - 'image/object/bbox/ymin', - 'image/object/bbox/xmax', - 'image/object/bbox/ymax']}) - - features = tf.parse_single_example(example_serialized, feature_map) - label = tf.cast(features['image/class/label'], dtype=tf.int32) - - xmin = tf.expand_dims(features['image/object/bbox/xmin'].values, 0) - ymin = tf.expand_dims(features['image/object/bbox/ymin'].values, 0) - xmax = tf.expand_dims(features['image/object/bbox/xmax'].values, 0) - ymax = tf.expand_dims(features['image/object/bbox/ymax'].values, 0) - - # Note that we impose an ordering of (y, x) just to make life difficult. - bbox = tf.concat([ymin, xmin, ymax, xmax], 0) - - # Force the variable number of bounding boxes into the shape - # [1, num_boxes, coords]. - bbox = tf.expand_dims(bbox, 0) - bbox = tf.transpose(bbox, [0, 2, 1]) - - return features['image/encoded'], label, bbox - - -def parse_record(raw_record, is_training, dtype): - """Parses a record containing a training example of an image. - - The input record is parsed into a label and image, and the image is passed - through preprocessing steps (cropping, flipping, and so on). - - Args: - raw_record: scalar Tensor tf.string containing a serialized - Example protocol buffer. - is_training: A boolean denoting whether the input is for training. - dtype: data type to use for images/features. - - Returns: - Tuple with processed image tensor and one-hot-encoded label tensor. - """ - image_buffer, label, bbox = _parse_example_proto(raw_record) - - image = imagenet_preprocessing.preprocess_image( - image_buffer=image_buffer, - bbox=bbox, - output_height=DEFAULT_IMAGE_SIZE, - output_width=DEFAULT_IMAGE_SIZE, - num_channels=NUM_CHANNELS, - is_training=is_training) - image = tf.cast(image, dtype) - - return image, label - - -def input_fn(is_training, data_dir, batch_size, num_epochs=1, - dtype=tf.float32, datasets_num_private_threads=None, - num_parallel_batches=1, parse_record_fn=parse_record): - """Input function which provides batches for train or eval. - - Args: - is_training: A boolean denoting whether the input is for training. - data_dir: The directory containing the input data. - batch_size: The number of samples per batch. - num_epochs: The number of epochs to repeat the dataset. - dtype: Data type to use for images/features - datasets_num_private_threads: Number of private threads for tf.data. - num_parallel_batches: Number of parallel batches for tf.data. - parse_record_fn: Function to use for parsing the records. - - Returns: - A dataset that can be used for iteration. - """ - filenames = get_filenames(is_training, data_dir) - dataset = tf.data.Dataset.from_tensor_slices(filenames) - - if is_training: - # Shuffle the input files - dataset = dataset.shuffle(buffer_size=_NUM_TRAIN_FILES) - - # Convert to individual records. - # cycle_length = 10 means 10 files will be read and deserialized in parallel. - # This number is low enough to not cause too much contention on small systems - # but high enough to provide the benefits of parallelization. You may want - # to increase this number if you have a large number of CPU cores. - dataset = dataset.apply(tf.contrib.data.parallel_interleave( - tf.data.TFRecordDataset, cycle_length=10)) - - return resnet_run_loop.process_record_dataset( - dataset=dataset, - is_training=is_training, - batch_size=batch_size, - shuffle_buffer=_SHUFFLE_BUFFER, - parse_record_fn=parse_record_fn, - num_epochs=num_epochs, - dtype=dtype, - datasets_num_private_threads=datasets_num_private_threads, - num_parallel_batches=num_parallel_batches - ) - - -def get_synth_input_fn(dtype): - return resnet_run_loop.get_synth_input_fn( - DEFAULT_IMAGE_SIZE, DEFAULT_IMAGE_SIZE, NUM_CHANNELS, NUM_CLASSES, - dtype=dtype) - - -############################################################################### -# Running the model -############################################################################### -class ImagenetModel(resnet_model.Model): - """Model class with appropriate defaults for Imagenet data.""" - - def __init__(self, resnet_size, data_format=None, num_classes=NUM_CLASSES, - resnet_version=resnet_model.DEFAULT_VERSION, - dtype=resnet_model.DEFAULT_DTYPE): - """These are the parameters that work for Imagenet data. - - Args: - resnet_size: The number of convolutional layers needed in the model. - data_format: Either 'channels_first' or 'channels_last', specifying which - data format to use when setting up the model. - num_classes: The number of output classes needed from the model. This - enables users to extend the same model to their own datasets. - resnet_version: Integer representing which version of the ResNet network - to use. See README for details. Valid values: [1, 2] - dtype: The TensorFlow dtype to use for calculations. - """ - - # For bigger models, we want to use "bottleneck" layers - if resnet_size < 50: - bottleneck = False - else: - bottleneck = True - - super(ImagenetModel, self).__init__( - resnet_size=resnet_size, - bottleneck=bottleneck, - num_classes=num_classes, - num_filters=64, - kernel_size=7, - conv_stride=2, - first_pool_size=3, - first_pool_stride=2, - block_sizes=_get_block_sizes(resnet_size), - block_strides=[1, 2, 2, 2], - resnet_version=resnet_version, - data_format=data_format, - dtype=dtype - ) - - -def _get_block_sizes(resnet_size): - """Retrieve the size of each block_layer in the ResNet model. - - The number of block layers used for the Resnet model varies according - to the size of the model. This helper grabs the layer set we want, throwing - an error if a non-standard size has been selected. - - Args: - resnet_size: The number of convolutional layers needed in the model. - - Returns: - A list of block sizes to use in building the model. - - Raises: - KeyError: if invalid resnet_size is received. - """ - choices = { - 18: [2, 2, 2, 2], - 34: [3, 4, 6, 3], - 50: [3, 4, 6, 3], - 101: [3, 4, 23, 3], - 152: [3, 8, 36, 3], - 200: [3, 24, 36, 3] - } - - try: - return choices[resnet_size] - except KeyError: - err = ('Could not find layers for selected Resnet size.\n' - 'Size received: {}; sizes allowed: {}.'.format( - resnet_size, choices.keys())) - raise ValueError(err) - - -def imagenet_model_fn(features, labels, mode, params): - """Our model_fn for ResNet to be used with our Estimator.""" - - # Warmup and higher lr may not be valid for fine tuning with small batches - # and smaller numbers of training images. - if params['fine_tune']: - warmup = False - base_lr = .1 - else: - warmup = True - base_lr = .128 - - learning_rate_fn = resnet_run_loop.learning_rate_with_decay( - batch_size=params['batch_size'], batch_denom=256, - num_images=NUM_IMAGES['train'], boundary_epochs=[30, 60, 80, 90], - decay_rates=[1, 0.1, 0.01, 0.001, 1e-4], warmup=warmup, base_lr=base_lr) - - return resnet_run_loop.resnet_model_fn( - features=features, - labels=labels, - mode=mode, - model_class=ImagenetModel, - resnet_size=params['resnet_size'], - weight_decay=1e-4, - learning_rate_fn=learning_rate_fn, - momentum=0.9, - data_format=params['data_format'], - resnet_version=params['resnet_version'], - loss_scale=params['loss_scale'], - loss_filter_fn=None, - dtype=params['dtype'], - fine_tune=params['fine_tune'] - ) - - -def define_imagenet_flags(): - resnet_run_loop.define_resnet_flags( - resnet_size_choices=['18', '34', '50', '101', '152', '200']) - flags.adopt_module_key_flags(resnet_run_loop) - flags_core.set_defaults(train_epochs=90) - - -def run_imagenet(flags_obj): - """Run ResNet ImageNet training and eval loop. - - Args: - flags_obj: An object containing parsed flag values. - """ - input_function = (flags_obj.use_synthetic_data and - get_synth_input_fn(flags_core.get_tf_dtype(flags_obj)) or - input_fn) - - resnet_run_loop.resnet_main( - flags_obj, imagenet_model_fn, input_function, DATASET_NAME, - shape=[DEFAULT_IMAGE_SIZE, DEFAULT_IMAGE_SIZE, NUM_CHANNELS]) - - -def main(_): - with logger.benchmark_context(flags.FLAGS): - run_imagenet(flags.FLAGS) - - -if __name__ == '__main__': - tf.logging.set_verbosity(tf.logging.INFO) - define_imagenet_flags() - absl_app.run(main) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/imagenet_preprocessing.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/imagenet_preprocessing.py deleted file mode 100644 index 01bcea44..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/imagenet_preprocessing.py +++ /dev/null @@ -1,260 +0,0 @@ -# Copyright 2016 The TensorFlow Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -"""Provides utilities to preprocess images. - -Training images are sampled using the provided bounding boxes, and subsequently -cropped to the sampled bounding box. Images are additionally flipped randomly, -then resized to the target output size (without aspect-ratio preservation). - -Images used during evaluation are resized (with aspect-ratio preservation) and -centrally cropped. - -All images undergo mean color subtraction. - -Note that these steps are colloquially referred to as "ResNet preprocessing," -and they differ from "VGG preprocessing," which does not use bounding boxes -and instead does an aspect-preserving resize followed by random crop during -training. (These both differ from "Inception preprocessing," which introduces -color distortion steps.) - -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import tensorflow as tf - -_R_MEAN = 123.68 -_G_MEAN = 116.78 -_B_MEAN = 103.94 -_CHANNEL_MEANS = [_R_MEAN, _G_MEAN, _B_MEAN] - -# The lower bound for the smallest side of the image for aspect-preserving -# resizing. For example, if an image is 500 x 1000, it will be resized to -# _RESIZE_MIN x (_RESIZE_MIN * 2). -_RESIZE_MIN = 256 - - -def _decode_crop_and_flip(image_buffer, bbox, num_channels): - """Crops the given image to a random part of the image, and randomly flips. - - We use the fused decode_and_crop op, which performs better than the two ops - used separately in series, but note that this requires that the image be - passed in as an un-decoded string Tensor. - - Args: - image_buffer: scalar string Tensor representing the raw JPEG image buffer. - bbox: 3-D float Tensor of bounding boxes arranged [1, num_boxes, coords] - where each coordinate is [0, 1) and the coordinates are arranged as - [ymin, xmin, ymax, xmax]. - num_channels: Integer depth of the image buffer for decoding. - - Returns: - 3-D tensor with cropped image. - - """ - # A large fraction of image datasets contain a human-annotated bounding box - # delineating the region of the image containing the object of interest. We - # choose to create a new bounding box for the object which is a randomly - # distorted version of the human-annotated bounding box that obeys an - # allowed range of aspect ratios, sizes and overlap with the human-annotated - # bounding box. If no box is supplied, then we assume the bounding box is - # the entire image. - sample_distorted_bounding_box = tf.image.sample_distorted_bounding_box( - tf.image.extract_jpeg_shape(image_buffer), - bounding_boxes=bbox, - min_object_covered=0.1, - aspect_ratio_range=[0.75, 1.33], - area_range=[0.05, 1.0], - max_attempts=100, - use_image_if_no_bounding_boxes=True) - bbox_begin, bbox_size, _ = sample_distorted_bounding_box - - # Reassemble the bounding box in the format the crop op requires. - offset_y, offset_x, _ = tf.unstack(bbox_begin) - target_height, target_width, _ = tf.unstack(bbox_size) - crop_window = tf.stack([offset_y, offset_x, target_height, target_width]) - - # Use the fused decode and crop op here, which is faster than each in series. - cropped = tf.image.decode_and_crop_jpeg( - image_buffer, crop_window, channels=num_channels) - - # Flip to add a little more random distortion in. - cropped = tf.image.random_flip_left_right(cropped) - return cropped - - -def _central_crop(image, crop_height, crop_width): - """Performs central crops of the given image list. - - Args: - image: a 3-D image tensor - crop_height: the height of the image following the crop. - crop_width: the width of the image following the crop. - - Returns: - 3-D tensor with cropped image. - """ - shape = tf.shape(image) - height, width = shape[0], shape[1] - - amount_to_be_cropped_h = (height - crop_height) - crop_top = amount_to_be_cropped_h // 2 - amount_to_be_cropped_w = (width - crop_width) - crop_left = amount_to_be_cropped_w // 2 - return tf.slice( - image, [crop_top, crop_left, 0], [crop_height, crop_width, -1]) - - -def _mean_image_subtraction(image, means, num_channels): - """Subtracts the given means from each image channel. - - For example: - means = [123.68, 116.779, 103.939] - image = _mean_image_subtraction(image, means) - - Note that the rank of `image` must be known. - - Args: - image: a tensor of size [height, width, C]. - means: a C-vector of values to subtract from each channel. - num_channels: number of color channels in the image that will be distorted. - - Returns: - the centered image. - - Raises: - ValueError: If the rank of `image` is unknown, if `image` has a rank other - than three or if the number of channels in `image` doesn't match the - number of values in `means`. - """ - if image.get_shape().ndims != 3: - raise ValueError('Input must be of size [height, width, C>0]') - - if len(means) != num_channels: - raise ValueError('len(means) must match the number of channels') - - # We have a 1-D tensor of means; convert to 3-D. - means = tf.expand_dims(tf.expand_dims(means, 0), 0) - - return image - means - - -def _smallest_size_at_least(height, width, resize_min): - """Computes new shape with the smallest side equal to `smallest_side`. - - Computes new shape with the smallest side equal to `smallest_side` while - preserving the original aspect ratio. - - Args: - height: an int32 scalar tensor indicating the current height. - width: an int32 scalar tensor indicating the current width. - resize_min: A python integer or scalar `Tensor` indicating the size of - the smallest side after resize. - - Returns: - new_height: an int32 scalar tensor indicating the new height. - new_width: an int32 scalar tensor indicating the new width. - """ - resize_min = tf.cast(resize_min, tf.float32) - - # Convert to floats to make subsequent calculations go smoothly. - height, width = tf.cast(height, tf.float32), tf.cast(width, tf.float32) - - smaller_dim = tf.minimum(height, width) - scale_ratio = resize_min / smaller_dim - - # Convert back to ints to make heights and widths that TF ops will accept. - new_height = tf.cast(height * scale_ratio, tf.int32) - new_width = tf.cast(width * scale_ratio, tf.int32) - - return new_height, new_width - - -def _aspect_preserving_resize(image, resize_min): - """Resize images preserving the original aspect ratio. - - Args: - image: A 3-D image `Tensor`. - resize_min: A python integer or scalar `Tensor` indicating the size of - the smallest side after resize. - - Returns: - resized_image: A 3-D tensor containing the resized image. - """ - shape = tf.shape(image) - height, width = shape[0], shape[1] - - new_height, new_width = _smallest_size_at_least(height, width, resize_min) - - return _resize_image(image, new_height, new_width) - - -def _resize_image(image, height, width): - """Simple wrapper around tf.resize_images. - - This is primarily to make sure we use the same `ResizeMethod` and other - details each time. - - Args: - image: A 3-D image `Tensor`. - height: The target height for the resized image. - width: The target width for the resized image. - - Returns: - resized_image: A 3-D tensor containing the resized image. The first two - dimensions have the shape [height, width]. - """ - return tf.image.resize_images( - image, [height, width], method=tf.image.ResizeMethod.BILINEAR, - align_corners=False) - - -def preprocess_image(image_buffer, bbox, output_height, output_width, - num_channels, is_training=False): - """Preprocesses the given image. - - Preprocessing includes decoding, cropping, and resizing for both training - and eval images. Training preprocessing, however, introduces some random - distortion of the image to improve accuracy. - - Args: - image_buffer: scalar string Tensor representing the raw JPEG image buffer. - bbox: 3-D float Tensor of bounding boxes arranged [1, num_boxes, coords] - where each coordinate is [0, 1) and the coordinates are arranged as - [ymin, xmin, ymax, xmax]. - output_height: The height of the image after preprocessing. - output_width: The width of the image after preprocessing. - num_channels: Integer depth of the image buffer for decoding. - is_training: `True` if we're preprocessing the image for training and - `False` otherwise. - - Returns: - A preprocessed image. - """ - if is_training: - # For training, we want to randomize some of the distortions. - image = _decode_crop_and_flip(image_buffer, bbox, num_channels) - image = _resize_image(image, output_height, output_width) - else: - # For validation, we want to decode, resize, then just crop the middle. - image = tf.image.decode_jpeg(image_buffer, channels=num_channels) - image = _aspect_preserving_resize(image, _RESIZE_MIN) - image = _central_crop(image, output_height, output_width) - - image.set_shape([output_height, output_width, num_channels]) - - return _mean_image_subtraction(image, _CHANNEL_MEANS, num_channels) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/imagenet_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/imagenet_test.py deleted file mode 100644 index 1eb2a992..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/imagenet_test.py +++ /dev/null @@ -1,309 +0,0 @@ -# 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. -# ============================================================================== - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import unittest - -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.resnet import imagenet_main -from official.utils.testing import integration - -tf.logging.set_verbosity(tf.logging.ERROR) - -_BATCH_SIZE = 32 -_LABEL_CLASSES = 1001 - - -class BaseTest(tf.test.TestCase): - - @classmethod - def setUpClass(cls): # pylint: disable=invalid-name - super(BaseTest, cls).setUpClass() - imagenet_main.define_imagenet_flags() - - def tearDown(self): - super(BaseTest, self).tearDown() - tf.gfile.DeleteRecursively(self.get_temp_dir()) - - def _tensor_shapes_helper(self, resnet_size, resnet_version, dtype, with_gpu): - """Checks the tensor shapes after each phase of the ResNet model.""" - def reshape(shape): - """Returns the expected dimensions depending on if a GPU is being used.""" - - # If a GPU is used for the test, the shape is returned (already in NCHW - # form). When GPU is not used, the shape is converted to NHWC. - if with_gpu: - return shape - return shape[0], shape[2], shape[3], shape[1] - - graph = tf.Graph() - - with graph.as_default(), self.test_session( - graph=graph, use_gpu=with_gpu, force_gpu=with_gpu): - model = imagenet_main.ImagenetModel( - resnet_size=resnet_size, - data_format='channels_first' if with_gpu else 'channels_last', - resnet_version=resnet_version, - dtype=dtype - ) - inputs = tf.random_uniform([1, 224, 224, 3]) - output = model(inputs, training=True) - - initial_conv = graph.get_tensor_by_name('resnet_model/initial_conv:0') - max_pool = graph.get_tensor_by_name('resnet_model/initial_max_pool:0') - block_layer1 = graph.get_tensor_by_name('resnet_model/block_layer1:0') - block_layer2 = graph.get_tensor_by_name('resnet_model/block_layer2:0') - block_layer3 = graph.get_tensor_by_name('resnet_model/block_layer3:0') - block_layer4 = graph.get_tensor_by_name('resnet_model/block_layer4:0') - reduce_mean = graph.get_tensor_by_name('resnet_model/final_reduce_mean:0') - dense = graph.get_tensor_by_name('resnet_model/final_dense:0') - - self.assertAllEqual(initial_conv.shape, reshape((1, 64, 112, 112))) - self.assertAllEqual(max_pool.shape, reshape((1, 64, 56, 56))) - - # The number of channels after each block depends on whether we're - # using the building_block or the bottleneck_block. - if resnet_size < 50: - self.assertAllEqual(block_layer1.shape, reshape((1, 64, 56, 56))) - self.assertAllEqual(block_layer2.shape, reshape((1, 128, 28, 28))) - self.assertAllEqual(block_layer3.shape, reshape((1, 256, 14, 14))) - self.assertAllEqual(block_layer4.shape, reshape((1, 512, 7, 7))) - self.assertAllEqual(reduce_mean.shape, reshape((1, 512, 1, 1))) - else: - self.assertAllEqual(block_layer1.shape, reshape((1, 256, 56, 56))) - self.assertAllEqual(block_layer2.shape, reshape((1, 512, 28, 28))) - self.assertAllEqual(block_layer3.shape, reshape((1, 1024, 14, 14))) - self.assertAllEqual(block_layer4.shape, reshape((1, 2048, 7, 7))) - self.assertAllEqual(reduce_mean.shape, reshape((1, 2048, 1, 1))) - - self.assertAllEqual(dense.shape, (1, _LABEL_CLASSES)) - self.assertAllEqual(output.shape, (1, _LABEL_CLASSES)) - - def tensor_shapes_helper(self, resnet_size, resnet_version, with_gpu=False): - self._tensor_shapes_helper(resnet_size=resnet_size, - resnet_version=resnet_version, - dtype=tf.float32, with_gpu=with_gpu) - self._tensor_shapes_helper(resnet_size=resnet_size, - resnet_version=resnet_version, - dtype=tf.float16, with_gpu=with_gpu) - - def test_tensor_shapes_resnet_18_v1(self): - self.tensor_shapes_helper(18, resnet_version=1) - - def test_tensor_shapes_resnet_18_v2(self): - self.tensor_shapes_helper(18, resnet_version=2) - - def test_tensor_shapes_resnet_34_v1(self): - self.tensor_shapes_helper(34, resnet_version=1) - - def test_tensor_shapes_resnet_34_v2(self): - self.tensor_shapes_helper(34, resnet_version=2) - - def test_tensor_shapes_resnet_50_v1(self): - self.tensor_shapes_helper(50, resnet_version=1) - - def test_tensor_shapes_resnet_50_v2(self): - self.tensor_shapes_helper(50, resnet_version=2) - - def test_tensor_shapes_resnet_101_v1(self): - self.tensor_shapes_helper(101, resnet_version=1) - - def test_tensor_shapes_resnet_101_v2(self): - self.tensor_shapes_helper(101, resnet_version=2) - - def test_tensor_shapes_resnet_152_v1(self): - self.tensor_shapes_helper(152, resnet_version=1) - - def test_tensor_shapes_resnet_152_v2(self): - self.tensor_shapes_helper(152, resnet_version=2) - - def test_tensor_shapes_resnet_200_v1(self): - self.tensor_shapes_helper(200, resnet_version=1) - - def test_tensor_shapes_resnet_200_v2(self): - self.tensor_shapes_helper(200, resnet_version=2) - - @unittest.skipUnless(tf.test.is_built_with_cuda(), 'requires GPU') - def test_tensor_shapes_resnet_18_with_gpu_v1(self): - self.tensor_shapes_helper(18, resnet_version=1, with_gpu=True) - - @unittest.skipUnless(tf.test.is_built_with_cuda(), 'requires GPU') - def test_tensor_shapes_resnet_18_with_gpu_v2(self): - self.tensor_shapes_helper(18, resnet_version=2, with_gpu=True) - - @unittest.skipUnless(tf.test.is_built_with_cuda(), 'requires GPU') - def test_tensor_shapes_resnet_34_with_gpu_v1(self): - self.tensor_shapes_helper(34, resnet_version=1, with_gpu=True) - - @unittest.skipUnless(tf.test.is_built_with_cuda(), 'requires GPU') - def test_tensor_shapes_resnet_34_with_gpu_v2(self): - self.tensor_shapes_helper(34, resnet_version=2, with_gpu=True) - - @unittest.skipUnless(tf.test.is_built_with_cuda(), 'requires GPU') - def test_tensor_shapes_resnet_50_with_gpu_v1(self): - self.tensor_shapes_helper(50, resnet_version=1, with_gpu=True) - - @unittest.skipUnless(tf.test.is_built_with_cuda(), 'requires GPU') - def test_tensor_shapes_resnet_50_with_gpu_v2(self): - self.tensor_shapes_helper(50, resnet_version=2, with_gpu=True) - - @unittest.skipUnless(tf.test.is_built_with_cuda(), 'requires GPU') - def test_tensor_shapes_resnet_101_with_gpu_v1(self): - self.tensor_shapes_helper(101, resnet_version=1, with_gpu=True) - - @unittest.skipUnless(tf.test.is_built_with_cuda(), 'requires GPU') - def test_tensor_shapes_resnet_101_with_gpu_v2(self): - self.tensor_shapes_helper(101, resnet_version=2, with_gpu=True) - - @unittest.skipUnless(tf.test.is_built_with_cuda(), 'requires GPU') - def test_tensor_shapes_resnet_152_with_gpu_v1(self): - self.tensor_shapes_helper(152, resnet_version=1, with_gpu=True) - - @unittest.skipUnless(tf.test.is_built_with_cuda(), 'requires GPU') - def test_tensor_shapes_resnet_152_with_gpu_v2(self): - self.tensor_shapes_helper(152, resnet_version=2, with_gpu=True) - - @unittest.skipUnless(tf.test.is_built_with_cuda(), 'requires GPU') - def test_tensor_shapes_resnet_200_with_gpu_v1(self): - self.tensor_shapes_helper(200, resnet_version=1, with_gpu=True) - - @unittest.skipUnless(tf.test.is_built_with_cuda(), 'requires GPU') - def test_tensor_shapes_resnet_200_with_gpu_v2(self): - self.tensor_shapes_helper(200, resnet_version=2, with_gpu=True) - - def resnet_model_fn_helper(self, mode, resnet_version, dtype): - """Tests that the EstimatorSpec is given the appropriate arguments.""" - tf.train.create_global_step() - - input_fn = imagenet_main.get_synth_input_fn(dtype) - dataset = input_fn(True, '', _BATCH_SIZE) - iterator = dataset.make_initializable_iterator() - features, labels = iterator.get_next() - spec = imagenet_main.imagenet_model_fn( - features, labels, mode, { - 'dtype': dtype, - 'resnet_size': 50, - 'data_format': 'channels_last', - 'batch_size': _BATCH_SIZE, - 'resnet_version': resnet_version, - 'loss_scale': 128 if dtype == tf.float16 else 1, - 'fine_tune': False, - }) - - predictions = spec.predictions - self.assertAllEqual(predictions['probabilities'].shape, - (_BATCH_SIZE, _LABEL_CLASSES)) - self.assertEqual(predictions['probabilities'].dtype, tf.float32) - self.assertAllEqual(predictions['classes'].shape, (_BATCH_SIZE,)) - self.assertEqual(predictions['classes'].dtype, tf.int64) - - if mode != tf.estimator.ModeKeys.PREDICT: - loss = spec.loss - self.assertAllEqual(loss.shape, ()) - self.assertEqual(loss.dtype, tf.float32) - - if mode == tf.estimator.ModeKeys.EVAL: - eval_metric_ops = spec.eval_metric_ops - self.assertAllEqual(eval_metric_ops['accuracy'][0].shape, ()) - self.assertAllEqual(eval_metric_ops['accuracy'][1].shape, ()) - self.assertEqual(eval_metric_ops['accuracy'][0].dtype, tf.float32) - self.assertEqual(eval_metric_ops['accuracy'][1].dtype, tf.float32) - - def test_resnet_model_fn_train_mode_v1(self): - self.resnet_model_fn_helper(tf.estimator.ModeKeys.TRAIN, resnet_version=1, - dtype=tf.float32) - - def test_resnet_model_fn_train_mode_v2(self): - self.resnet_model_fn_helper(tf.estimator.ModeKeys.TRAIN, resnet_version=2, - dtype=tf.float32) - - def test_resnet_model_fn_eval_mode_v1(self): - self.resnet_model_fn_helper(tf.estimator.ModeKeys.EVAL, resnet_version=1, - dtype=tf.float32) - - def test_resnet_model_fn_eval_mode_v2(self): - self.resnet_model_fn_helper(tf.estimator.ModeKeys.EVAL, resnet_version=2, - dtype=tf.float32) - - def test_resnet_model_fn_predict_mode_v1(self): - self.resnet_model_fn_helper(tf.estimator.ModeKeys.PREDICT, resnet_version=1, - dtype=tf.float32) - - def test_resnet_model_fn_predict_mode_v2(self): - self.resnet_model_fn_helper(tf.estimator.ModeKeys.PREDICT, resnet_version=2, - dtype=tf.float32) - - def _test_imagenetmodel_shape(self, resnet_version): - batch_size = 135 - num_classes = 246 - - model = imagenet_main.ImagenetModel( - 50, data_format='channels_last', num_classes=num_classes, - resnet_version=resnet_version) - - fake_input = tf.random_uniform([batch_size, 224, 224, 3]) - output = model(fake_input, training=True) - - self.assertAllEqual(output.shape, (batch_size, num_classes)) - - def test_imagenetmodel_shape_v1(self): - self._test_imagenetmodel_shape(resnet_version=1) - - def test_imagenetmodel_shape_v2(self): - self._test_imagenetmodel_shape(resnet_version=2) - - def test_imagenet_end_to_end_synthetic_v1(self): - integration.run_synthetic( - main=imagenet_main.run_imagenet, tmp_root=self.get_temp_dir(), - extra_flags=['-v', '1'] - ) - - def test_imagenet_end_to_end_synthetic_v2(self): - integration.run_synthetic( - main=imagenet_main.run_imagenet, tmp_root=self.get_temp_dir(), - extra_flags=['-v', '2'] - ) - - def test_imagenet_end_to_end_synthetic_v1_tiny(self): - integration.run_synthetic( - main=imagenet_main.run_imagenet, tmp_root=self.get_temp_dir(), - extra_flags=['-resnet_version', '1', '-resnet_size', '18'] - ) - - def test_imagenet_end_to_end_synthetic_v2_tiny(self): - integration.run_synthetic( - main=imagenet_main.run_imagenet, tmp_root=self.get_temp_dir(), - extra_flags=['-resnet_version', '2', '-resnet_size', '18'] - ) - - def test_imagenet_end_to_end_synthetic_v1_huge(self): - integration.run_synthetic( - main=imagenet_main.run_imagenet, tmp_root=self.get_temp_dir(), - extra_flags=['-resnet_version', '1', '-resnet_size', '200'] - ) - - def test_imagenet_end_to_end_synthetic_v2_huge(self): - integration.run_synthetic( - main=imagenet_main.run_imagenet, tmp_root=self.get_temp_dir(), - extra_flags=['-resnet_version', '2', '-resnet_size', '200'] - ) - - -if __name__ == '__main__': - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/__init__.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_benchmark.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_benchmark.py deleted file mode 100644 index 66a70ed2..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_benchmark.py +++ /dev/null @@ -1,132 +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. -# ============================================================================== -"""Executes Keras benchmarks and accuracy tests.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os -import time -import json - -from absl import flags -from absl.testing import flagsaver -import tensorflow as tf # pylint: disable=g-bad-import-order - -FLAGS = flags.FLAGS - - -class KerasBenchmark(tf.test.Benchmark): - """Base benchmark class with methods to simplify testing.""" - local_flags = None - - def __init__(self, output_dir=None, default_flags=None, flag_methods=None): - self.output_dir = output_dir - self.default_flags = default_flags or {} - self.flag_methods = flag_methods or {} - - def _get_model_dir(self, folder_name): - return os.path.join(self.output_dir, folder_name) - - def _setup(self): - """Sets up and resets flags before each test.""" - tf.logging.set_verbosity(tf.logging.DEBUG) - if KerasBenchmark.local_flags is None: - for flag_method in self.flag_methods: - flag_method() - # Loads flags to get defaults to then override. List cannot be empty. - flags.FLAGS(['foo']) - # Overrides flag values with defaults for the class of tests. - for k, v in self.default_flags.items(): - setattr(FLAGS, k, v) - saved_flag_values = flagsaver.save_flag_values() - KerasBenchmark.local_flags = saved_flag_values - else: - flagsaver.restore_flag_values(KerasBenchmark.local_flags) - - def _report_benchmark(self, - stats, - wall_time_sec, - top_1_max=None, - top_1_min=None, - log_steps=None, - total_batch_size=None, - warmup=1): - """Report benchmark results by writing to local protobuf file - - Args: - stats: dict returned from keras models with known entries. - wall_time_sec: the during of the benchmark execution in seconds - top_1_max: highest passing level for top_1 accuracy. - top_1_min: lowest passing level for top_1 accuracy. - log_steps: How often the log was created for stats['step_timestamp_log']. - total_batch_size: Global batch-size. - warmup: number of entries in stats['step_timestamp_log'] to ignore. - """ - - extras = {} - if 'accuracy_top_1' in stats: - extras['accuracy'] = self._json_description( - stats['accuracy_top_1'], - priority=0, - min_value=top_1_min, - max_value=top_1_max) - extras['top_1_train_accuracy'] = self._json_description( - stats['training_accuracy_top_1'], priority=1) - - if (warmup and 'step_timestamp_log' in stats and - len(stats['step_timestamp_log']) > warmup): - # first entry in the time_log is start of step 1. The rest of the - # entries are the end of each step recorded - time_log = stats['step_timestamp_log'] - elapsed = time_log[-1].timestamp - time_log[warmup].timestamp - num_examples = ( - total_batch_size * log_steps * (len(time_log) - warmup - 1)) - examples_per_sec = num_examples / elapsed - extras['exp_per_second'] = self._json_description( - examples_per_sec, priority=2) - - if 'avg_exp_per_second' in stats: - extras['avg_exp_per_second'] = self._json_description( - stats['avg_exp_per_second'], priority=3) - - self.report_benchmark(iters=-1, wall_time=wall_time_sec, extras=extras) - - def _json_description(self, - value, - priority=None, - min_value=None, - max_value=None): - """Get a json-formatted string describing the attributes for a metric""" - - attributes = {} - attributes['value'] = value - if priority: - attributes['priority'] = priority - if min_value: - attributes['min_value'] = min_value - if max_value: - attributes['max_value'] = max_value - - if min_value or max_value: - succeeded = True - if min_value and value < min_value: - succeeded = False - if max_value and value > max_value: - succeeded = False - attributes['succeeded'] = succeeded - - return json.dumps(attributes) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_cifar_benchmark.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_cifar_benchmark.py deleted file mode 100644 index 1d376374..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_cifar_benchmark.py +++ /dev/null @@ -1,217 +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. -# ============================================================================== -"""Executes Keras benchmarks and accuracy tests.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import time -from absl import flags - -from official.resnet import cifar10_main as cifar_main -from official.resnet.keras import keras_benchmark -from official.resnet.keras import keras_cifar_main -from official.resnet.keras import keras_common - -DATA_DIR = '/data/cifar10_data/cifar-10-batches-bin' -MIN_TOP_1_ACCURACY = 0.925 -MAX_TOP_1_ACCURACY = 0.938 - -FLAGS = flags.FLAGS - - -class Resnet56KerasAccuracy(keras_benchmark.KerasBenchmark): - """Accuracy tests for ResNet56 Keras CIFAR-10.""" - - def __init__(self, output_dir=None): - flag_methods = [ - keras_common.define_keras_flags, cifar_main.define_cifar_flags - ] - - super(Resnet56KerasAccuracy, self).__init__( - output_dir=output_dir, flag_methods=flag_methods) - - def benchmark_graph_1_gpu(self): - """Test keras based model with Keras fit and distribution strategies.""" - self._setup() - FLAGS.num_gpus = 1 - FLAGS.data_dir = DATA_DIR - FLAGS.batch_size = 128 - FLAGS.train_epochs = 182 - FLAGS.model_dir = self._get_model_dir('keras_resnet56_1_gpu') - FLAGS.dtype = 'fp32' - self._run_and_report_benchmark() - - def benchmark_1_gpu(self): - """Test keras based model with eager and distribution strategies.""" - self._setup() - FLAGS.num_gpus = 1 - FLAGS.data_dir = DATA_DIR - FLAGS.batch_size = 128 - FLAGS.train_epochs = 182 - FLAGS.model_dir = self._get_model_dir('keras_resnet56_eager_1_gpu') - FLAGS.dtype = 'fp32' - FLAGS.enable_eager = True - self._run_and_report_benchmark() - - def benchmark_2_gpu(self): - """Test keras based model with eager and distribution strategies.""" - self._setup() - FLAGS.num_gpus = 2 - FLAGS.data_dir = DATA_DIR - FLAGS.batch_size = 128 - FLAGS.train_epochs = 182 - FLAGS.model_dir = self._get_model_dir('keras_resnet56_eager_2_gpu') - FLAGS.dtype = 'fp32' - FLAGS.enable_eager = True - self._run_and_report_benchmark() - - def benchmark_graph_2_gpu(self): - """Test keras based model with Keras fit and distribution strategies.""" - self._setup() - FLAGS.num_gpus = 2 - FLAGS.data_dir = DATA_DIR - FLAGS.batch_size = 128 - FLAGS.train_epochs = 182 - FLAGS.model_dir = self._get_model_dir('keras_resnet56_2_gpu') - FLAGS.dtype = 'fp32' - self._run_and_report_benchmark() - - def benchmark_graph_1_gpu_no_dist_strat(self): - """Test keras based model with Keras fit but not distribution strategies.""" - self._setup() - FLAGS.turn_off_distribution_strategy = True - FLAGS.num_gpus = 1 - FLAGS.data_dir = DATA_DIR - FLAGS.batch_size = 128 - FLAGS.train_epochs = 182 - FLAGS.model_dir = self._get_model_dir('keras_resnet56_no_dist_strat_1_gpu') - FLAGS.dtype = 'fp32' - self._run_and_report_benchmark() - - def _run_and_report_benchmark(self): - start_time_sec = time.time() - stats = keras_cifar_main.run(FLAGS) - wall_time_sec = time.time() - start_time_sec - - super(Resnet56KerasAccuracy, self)._report_benchmark( - stats, - wall_time_sec, - top_1_min=MIN_TOP_1_ACCURACY, - top_1_max=MAX_TOP_1_ACCURACY, - total_batch_size=FLAGS.batch_size, - log_steps=100) - - -class Resnet56KerasBenchmarkBase(keras_benchmark.KerasBenchmark): - """Short performance tests for ResNet56 via Keras and CIFAR-10.""" - - def __init__(self, output_dir=None, default_flags=None): - flag_methods = [ - keras_common.define_keras_flags, cifar_main.define_cifar_flags - ] - - super(Resnet56KerasBenchmarkBase, self).__init__( - output_dir=output_dir, - flag_methods=flag_methods, - default_flags=default_flags) - - def _run_and_report_benchmark(self): - start_time_sec = time.time() - stats = keras_cifar_main.run(FLAGS) - wall_time_sec = time.time() - start_time_sec - - super(Resnet56KerasBenchmarkBase, self)._report_benchmark( - stats, - wall_time_sec, - total_batch_size=FLAGS.batch_size, - log_steps=FLAGS.log_steps) - - def benchmark_1_gpu_no_dist_strat(self): - self._setup() - FLAGS.num_gpus = 1 - FLAGS.enable_eager = True - FLAGS.turn_off_distribution_strategy = True - FLAGS.batch_size = 128 - self._run_and_report_benchmark() - - def benchmark_graph_1_gpu_no_dist_strat(self): - self._setup() - FLAGS.num_gpus = 1 - FLAGS.enable_eager = False - FLAGS.turn_off_distribution_strategy = True - FLAGS.batch_size = 128 - self._run_and_report_benchmark() - - def benchmark_1_gpu(self): - self._setup() - FLAGS.num_gpus = 1 - FLAGS.enable_eager = True - FLAGS.turn_off_distribution_strategy = False - FLAGS.batch_size = 128 - self._run_and_report_benchmark() - - def benchmark_graph_1_gpu(self): - self._setup() - FLAGS.num_gpus = 1 - FLAGS.enable_eager = False - FLAGS.turn_off_distribution_strategy = False - FLAGS.batch_size = 128 - self._run_and_report_benchmark() - - def benchmark_2_gpu(self): - self._setup() - FLAGS.num_gpus = 2 - FLAGS.enable_eager = True - FLAGS.turn_off_distribution_strategy = False - FLAGS.batch_size = 128 * 2 # 2 GPUs - self._run_and_report_benchmark() - - def benchmark_graph_2_gpu(self): - self._setup() - FLAGS.num_gpus = 2 - FLAGS.enable_eager = False - FLAGS.turn_off_distribution_strategy = False - FLAGS.batch_size = 128 * 2 # 2 GPUs - self._run_and_report_benchmark() - - -class Resnet56KerasBenchmarkSynth(Resnet56KerasBenchmarkBase): - """Synthetic benchmarks for ResNet56 and Keras.""" - - def __init__(self, output_dir=None): - def_flags = {} - def_flags['skip_eval'] = True - def_flags['use_synthetic_data'] = True - def_flags['train_steps'] = 110 - def_flags['log_steps'] = 10 - - super(Resnet56KerasBenchmarkSynth, self).__init__( - output_dir=output_dir, default_flags=def_flags) - - -class Resnet56KerasBenchmarkReal(Resnet56KerasBenchmarkBase): - """Real data benchmarks for ResNet56 and Keras.""" - - def __init__(self, output_dir=None): - def_flags = {} - def_flags['skip_eval'] = True - def_flags['data_dir'] = DATA_DIR - def_flags['train_steps'] = 110 - def_flags['log_steps'] = 10 - - super(Resnet56KerasBenchmarkReal, self).__init__( - output_dir=output_dir, default_flags=def_flags) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_cifar_main.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_cifar_main.py deleted file mode 100644 index 48c28312..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_cifar_main.py +++ /dev/null @@ -1,200 +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. -# ============================================================================== -"""Runs a ResNet model on the Cifar-10 dataset.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -from absl import app as absl_app -from absl import flags -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.resnet import cifar10_main as cifar_main -from official.resnet.keras import keras_common -from official.resnet.keras import resnet_cifar_model -from official.utils.flags import core as flags_core -from official.utils.logs import logger -from official.utils.misc import distribution_utils - - -LR_SCHEDULE = [ # (multiplier, epoch to start) tuples - (0.1, 91), (0.01, 136), (0.001, 182) -] - - -def learning_rate_schedule(current_epoch, - current_batch, - batches_per_epoch, - batch_size): - """Handles linear scaling rule and LR decay. - - Scale learning rate at epoch boundaries provided in LR_SCHEDULE by the - provided scaling factor. - - Args: - current_epoch: integer, current epoch indexed from 0. - current_batch: integer, current batch in the current epoch, indexed from 0. - batches_per_epoch: integer, number of steps in an epoch. - batch_size: integer, total batch sized. - - Returns: - Adjusted learning rate. - """ - initial_learning_rate = keras_common.BASE_LEARNING_RATE * batch_size / 128 - learning_rate = initial_learning_rate - for mult, start_epoch in LR_SCHEDULE: - if current_epoch >= start_epoch: - learning_rate = initial_learning_rate * mult - else: - break - return learning_rate - - -def parse_record_keras(raw_record, is_training, dtype): - """Parses a record containing a training example of an image. - - The input record is parsed into a label and image, and the image is passed - through preprocessing steps (cropping, flipping, and so on). - - This method converts the label to one hot to fit the loss function. - - Args: - raw_record: scalar Tensor tf.string containing a serialized - Example protocol buffer. - is_training: A boolean denoting whether the input is for training. - dtype: Data type to use for input images. - - Returns: - Tuple with processed image tensor and one-hot-encoded label tensor. - """ - image, label = cifar_main.parse_record(raw_record, is_training, dtype) - label = tf.sparse_to_dense(label, (cifar_main.NUM_CLASSES,), 1) - return image, label - - -def run(flags_obj): - """Run ResNet Cifar-10 training and eval loop using native Keras APIs. - - Args: - flags_obj: An object containing parsed flag values. - - Raises: - ValueError: If fp16 is passed as it is not currently supported. - - Returns: - Dictionary of training and eval stats. - """ - if flags_obj.enable_eager: - tf.enable_eager_execution() - - dtype = flags_core.get_tf_dtype(flags_obj) - if dtype == 'fp16': - raise ValueError('dtype fp16 is not supported in Keras. Use the default ' - 'value(fp32).') - - data_format = flags_obj.data_format - if data_format is None: - data_format = ('channels_first' - if tf.test.is_built_with_cuda() else 'channels_last') - tf.keras.backend.set_image_data_format(data_format) - - if flags_obj.use_synthetic_data: - input_fn = keras_common.get_synth_input_fn( - height=cifar_main.HEIGHT, - width=cifar_main.WIDTH, - num_channels=cifar_main.NUM_CHANNELS, - num_classes=cifar_main.NUM_CLASSES, - dtype=flags_core.get_tf_dtype(flags_obj)) - else: - input_fn = cifar_main.input_fn - - train_input_dataset = input_fn( - is_training=True, - data_dir=flags_obj.data_dir, - batch_size=flags_obj.batch_size, - num_epochs=flags_obj.train_epochs, - parse_record_fn=parse_record_keras) - - eval_input_dataset = input_fn( - is_training=False, - data_dir=flags_obj.data_dir, - batch_size=flags_obj.batch_size, - num_epochs=flags_obj.train_epochs, - parse_record_fn=parse_record_keras) - - strategy = distribution_utils.get_distribution_strategy( - num_gpus=flags_obj.num_gpus, - turn_off_distribution_strategy=flags_obj.turn_off_distribution_strategy) - - strategy_scope = keras_common.get_strategy_scope(strategy) - - with strategy_scope: - optimizer = keras_common.get_optimizer() - model = resnet_cifar_model.resnet56(classes=cifar_main.NUM_CLASSES) - - model.compile(loss='categorical_crossentropy', - optimizer=optimizer, - metrics=['categorical_accuracy']) - - time_callback, tensorboard_callback, lr_callback = keras_common.get_callbacks( - learning_rate_schedule, cifar_main.NUM_IMAGES['train']) - - train_steps = cifar_main.NUM_IMAGES['train'] // flags_obj.batch_size - train_epochs = flags_obj.train_epochs - - if flags_obj.train_steps: - train_steps = min(flags_obj.train_steps, train_steps) - train_epochs = 1 - - num_eval_steps = (cifar_main.NUM_IMAGES['validation'] // - flags_obj.batch_size) - - validation_data = eval_input_dataset - if flags_obj.skip_eval: - tf.keras.backend.set_learning_phase(1) - num_eval_steps = None - validation_data = None - - history = model.fit(train_input_dataset, - epochs=train_epochs, - steps_per_epoch=train_steps, - callbacks=[ - time_callback, - lr_callback, - tensorboard_callback - ], - validation_steps=num_eval_steps, - validation_data=validation_data, - verbose=2) - eval_output = None - if not flags_obj.skip_eval: - eval_output = model.evaluate(eval_input_dataset, - steps=num_eval_steps, - verbose=1) - stats = keras_common.build_stats(history, eval_output, time_callback) - return stats - - -def main(_): - with logger.benchmark_context(flags.FLAGS): - return run(flags.FLAGS) - - -if __name__ == '__main__': - tf.logging.set_verbosity(tf.logging.INFO) - cifar_main.define_cifar_flags() - keras_common.define_keras_flags() - absl_app.run(main) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_common.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_common.py deleted file mode 100644 index ac9dad78..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_common.py +++ /dev/null @@ -1,265 +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. -# ============================================================================== -"""Common util functions and classes used by both keras cifar and imagenet.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import time - -import numpy as np - -# pylint: disable=g-bad-import-order -from absl import flags -import tensorflow as tf -from tensorflow.python.keras.optimizer_v2 import (gradient_descent as - gradient_descent_v2) - -FLAGS = flags.FLAGS -BASE_LEARNING_RATE = 0.1 # This matches Jing's version. -TRAIN_TOP_1 = 'training_accuracy_top_1' - - -class BatchTimestamp(object): - """A structure to store batch time stamp.""" - - def __init__(self, batch_index, timestamp): - self.batch_index = batch_index - self.timestamp = timestamp - - -class TimeHistory(tf.keras.callbacks.Callback): - """Callback for Keras models.""" - - def __init__(self, batch_size, log_steps): - """Callback for logging performance (# image/second). - - Args: - batch_size: Total batch size. - - """ - self.batch_size = batch_size - super(TimeHistory, self).__init__() - self.log_steps = log_steps - - # Logs start of step 0 then end of each step based on log_steps interval. - self.timestamp_log = [] - - def on_train_begin(self, logs=None): - self.record_batch = True - - def on_train_end(self, logs=None): - self.train_finish_time = time.time() - - def on_batch_begin(self, batch, logs=None): - if self.record_batch: - timestamp = time.time() - self.start_time = timestamp - self.record_batch = False - if batch == 0: - self.timestamp_log.append(BatchTimestamp(batch, timestamp)) - - def on_batch_end(self, batch, logs=None): - if batch % self.log_steps == 0: - timestamp = time.time() - elapsed_time = timestamp - self.start_time - examples_per_second = (self.batch_size * self.log_steps) / elapsed_time - if batch != 0: - self.record_batch = True - self.timestamp_log.append(BatchTimestamp(batch, timestamp)) - tf.logging.info("BenchmarkMetric: {'num_batches':%d, 'time_taken': %f," - "'images_per_second': %f}" % - (batch, elapsed_time, examples_per_second)) - - -class LearningRateBatchScheduler(tf.keras.callbacks.Callback): - """Callback to update learning rate on every batch (not epoch boundaries). - - N.B. Only support Keras optimizers, not TF optimizers. - - Args: - schedule: a function that takes an epoch index and a batch index as input - (both integer, indexed from 0) and returns a new learning rate as - output (float). - """ - - def __init__(self, schedule, batch_size, num_images): - super(LearningRateBatchScheduler, self).__init__() - self.schedule = schedule - self.batches_per_epoch = num_images / batch_size - self.batch_size = batch_size - self.epochs = -1 - self.prev_lr = -1 - - def on_epoch_begin(self, epoch, logs=None): - if not hasattr(self.model.optimizer, 'learning_rate'): - raise ValueError('Optimizer must have a "learning_rate" attribute.') - self.epochs += 1 - - def on_batch_begin(self, batch, logs=None): - """Executes before step begins.""" - lr = self.schedule(self.epochs, - batch, - self.batches_per_epoch, - self.batch_size) - if not isinstance(lr, (float, np.float32, np.float64)): - raise ValueError('The output of the "schedule" function should be float.') - if lr != self.prev_lr: - self.model.optimizer.learning_rate = lr # lr should be a float here - self.prev_lr = lr - tf.logging.debug('Epoch %05d Batch %05d: LearningRateBatchScheduler ' - 'change learning rate to %s.', self.epochs, batch, lr) - - -def get_optimizer(): - """Returns optimizer to use.""" - # The learning_rate is overwritten at the beginning of each step by callback. - return gradient_descent_v2.SGD(learning_rate=0.1, momentum=0.9) - - -def get_callbacks(learning_rate_schedule_fn, num_images): - """Returns common callbacks.""" - time_callback = TimeHistory(FLAGS.batch_size, FLAGS.log_steps) - - tensorboard_callback = tf.keras.callbacks.TensorBoard( - log_dir=FLAGS.model_dir) - - lr_callback = LearningRateBatchScheduler( - learning_rate_schedule_fn, - batch_size=FLAGS.batch_size, - num_images=num_images) - - return time_callback, tensorboard_callback, lr_callback - - -def build_stats(history, eval_output, time_callback): - """Normalizes and returns dictionary of stats. - - Args: - history: Results of the training step. Supports both categorical_accuracy - and sparse_categorical_accuracy. - eval_output: Output of the eval step. Assumes first value is eval_loss and - second value is accuracy_top_1. - time_callback: Time tracking callback likely used during keras.fit. - - Returns: - Dictionary of normalized results. - """ - stats = {} - if eval_output: - stats['accuracy_top_1'] = eval_output[1].item() - stats['eval_loss'] = eval_output[0].item() - - if history and history.history: - train_hist = history.history - # Gets final loss from training. - stats['loss'] = train_hist['loss'][-1].item() - # Gets top_1 training accuracy. - if 'categorical_accuracy' in train_hist: - stats[TRAIN_TOP_1] = train_hist['categorical_accuracy'][-1].item() - elif 'sparse_categorical_accuracy' in train_hist: - stats[TRAIN_TOP_1] = train_hist['sparse_categorical_accuracy'][-1].item() - - if time_callback: - timestamp_log = time_callback.timestamp_log - stats['step_timestamp_log'] = timestamp_log - stats['train_finish_time'] = time_callback.train_finish_time - if len(timestamp_log) > 1: - stats['avg_exp_per_second'] = ( - time_callback.batch_size * time_callback.log_steps * - (len(time_callback.timestamp_log)-1) / - (timestamp_log[-1].timestamp - timestamp_log[0].timestamp)) - - return stats - - -def define_keras_flags(): - flags.DEFINE_boolean(name='enable_eager', default=False, help='Enable eager?') - flags.DEFINE_boolean(name='skip_eval', default=False, help='Skip evaluation?') - flags.DEFINE_integer( - name='train_steps', default=None, - help='The number of steps to run for training. If it is larger than ' - '# batches per epoch, then use # batches per epoch. When this flag is ' - 'set, only one epoch is going to run for training.') - flags.DEFINE_integer( - name='log_steps', default=100, - help='For every log_steps, we log the timing information such as ' - 'examples per second. Besides, for every log_steps, we store the ' - 'timestamp of a batch end.') - - -def get_synth_input_fn(height, width, num_channels, num_classes, - dtype=tf.float32): - """Returns an input function that returns a dataset with random data. - - This input_fn returns a data set that iterates over a set of random data and - bypasses all preprocessing, e.g. jpeg decode and copy. The host to device - copy is still included. This used to find the upper throughput bound when - tuning the full input pipeline. - - Args: - height: Integer height that will be used to create a fake image tensor. - width: Integer width that will be used to create a fake image tensor. - num_channels: Integer depth that will be used to create a fake image tensor. - num_classes: Number of classes that should be represented in the fake labels - tensor - dtype: Data type for features/images. - - Returns: - An input_fn that can be used in place of a real one to return a dataset - that can be used for iteration. - """ - # pylint: disable=unused-argument - def input_fn(is_training, data_dir, batch_size, *args, **kwargs): - """Returns dataset filled with random data.""" - # Synthetic input should be within [0, 255]. - inputs = tf.truncated_normal( - [height, width, num_channels], - dtype=dtype, - mean=127, - stddev=60, - name='synthetic_inputs') - - labels = tf.random_uniform( - [1], - minval=0, - maxval=num_classes - 1, - dtype=tf.int32, - name='synthetic_labels') - data = tf.data.Dataset.from_tensors((inputs, labels)).repeat() - data = data.batch(batch_size) - data = data.prefetch(buffer_size=tf.contrib.data.AUTOTUNE) - return data - - return input_fn - - -def get_strategy_scope(strategy): - if strategy: - strategy_scope = strategy.scope() - else: - strategy_scope = DummyContextManager() - - return strategy_scope - - -class DummyContextManager(object): - - def __enter__(self): - pass - - def __exit__(self, *args): - pass diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_common_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_common_test.py deleted file mode 100644 index 9d84d7b8..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_common_test.py +++ /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. -# ============================================================================== -"""Tests for the keras_common module.""" -from __future__ import absolute_import -from __future__ import print_function - -from mock import Mock -import numpy as np -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.resnet.keras import keras_common - -tf.logging.set_verbosity(tf.logging.ERROR) - - -class KerasCommonTests(tf.test.TestCase): - """Tests for keras_common.""" - - @classmethod - def setUpClass(cls): # pylint: disable=invalid-name - super(KerasCommonTests, cls).setUpClass() - - def test_build_stats(self): - - history = self._build_history(1.145, cat_accuracy=.99988) - eval_output = self._build_eval_output(.56432111, 5.990) - th = keras_common.TimeHistory(128, 100) - - th.batch_start_timestamps = [1, 2, 3] - th.batch_end_timestamps = [4, 5, 6] - th.train_finish_time = 12345 - stats = keras_common.build_stats(history, eval_output, th) - - self.assertEqual(1.145, stats['loss']) - self.assertEqual(.99988, stats['training_accuracy_top_1']) - - self.assertEqual(.56432111, stats['accuracy_top_1']) - self.assertEqual(5.990, stats['eval_loss']) - - self.assertItemsEqual([1, 2, 3], stats['batch_start_timestamps']) - self.assertItemsEqual([4, 5, 6], stats['batch_end_timestamps']) - self.assertEqual(12345, stats['train_finish_time']) - - def test_build_stats_sparse(self): - - history = self._build_history(1.145, cat_accuracy_sparse=.99988) - eval_output = self._build_eval_output(.928, 1.9844) - stats = keras_common.build_stats(history, eval_output, None) - - self.assertEqual(1.145, stats['loss']) - self.assertEqual(.99988, stats['training_accuracy_top_1']) - - self.assertEqual(.928, stats['accuracy_top_1']) - self.assertEqual(1.9844, stats['eval_loss']) - - def test_time_history(self): - th = keras_common.TimeHistory(batch_size=128, log_steps=3) - - th.on_train_begin() - th.on_batch_begin(0) - th.on_batch_end(0) - th.on_batch_begin(1) - th.on_batch_end(1) - th.on_batch_begin(2) - th.on_batch_end(2) - th.on_batch_begin(3) - th.on_batch_end(3) - th.on_batch_begin(4) - th.on_batch_end(4) - th.on_batch_begin(5) - th.on_batch_end(5) - th.on_batch_begin(6) - th.on_batch_end(6) - th.on_train_end() - - self.assertEqual(3, len(th.batch_start_timestamps)) - self.assertEqual(2, len(th.batch_end_timestamps)) - - self.assertEqual(0, th.batch_start_timestamps[0].batch_index) - self.assertEqual(1, th.batch_start_timestamps[1].batch_index) - self.assertEqual(4, th.batch_start_timestamps[2].batch_index) - - self.assertEqual(3, th.batch_end_timestamps[0].batch_index) - self.assertEqual(6, th.batch_end_timestamps[1].batch_index) - - def _build_history(self, loss, cat_accuracy=None, - cat_accuracy_sparse=None): - history_p = Mock() - history = {} - history_p.history = history - history['loss'] = [np.float64(loss)] - if cat_accuracy: - history['categorical_accuracy'] = [np.float64(cat_accuracy)] - if cat_accuracy_sparse: - history['sparse_categorical_accuracy'] = [np.float64(cat_accuracy_sparse)] - - return history_p - - def _build_eval_output(self, top_1, eval_loss): - eval_output = [np.float64(eval_loss), np.float64(top_1)] - return eval_output diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_imagenet_benchmark.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_imagenet_benchmark.py deleted file mode 100644 index 701df554..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_imagenet_benchmark.py +++ /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. -# ============================================================================== -"""Executes Keras benchmarks and accuracy tests.""" -from __future__ import print_function - -import os -import time - -from absl import flags - -from official.resnet import imagenet_main -from official.resnet.keras import keras_benchmark -from official.resnet.keras import keras_common -from official.resnet.keras import keras_imagenet_main - -MIN_TOP_1_ACCURACY = 0.76 -MAX_TOP_1_ACCURACY = 0.77 -DATA_DIR = '/data/imagenet/' - -FLAGS = flags.FLAGS - - -class Resnet50KerasAccuracy(keras_benchmark.KerasBenchmark): - """Benchmark accuracy tests for ResNet50 in Keras.""" - - def __init__(self, output_dir=None): - flag_methods = [ - keras_common.define_keras_flags, imagenet_main.define_imagenet_flags - ] - - super(Resnet50KerasAccuracy, self).__init__( - output_dir=output_dir, flag_methods=flag_methods) - - def benchmark_graph_8_gpu(self): - """Test Keras model with Keras fit/dist_strat and 8 GPUs.""" - self._setup() - FLAGS.num_gpus = 8 - FLAGS.data_dir = DATA_DIR - FLAGS.batch_size = 128 * 8 - FLAGS.train_epochs = 90 - FLAGS.model_dir = self._get_model_dir('keras_resnet50_8_gpu') - FLAGS.dtype = 'fp32' - self._run_and_report_benchmark() - - def benchmark_8_gpu(self): - """Test Keras model with eager, dist_strat and 8 GPUs.""" - self._setup() - FLAGS.num_gpus = 8 - FLAGS.data_dir = DATA_DIR - FLAGS.batch_size = 128 * 8 - FLAGS.train_epochs = 90 - FLAGS.model_dir = self._get_model_dir('keras_resnet50_eager_8_gpu') - FLAGS.dtype = 'fp32' - FLAGS.enable_eager = True - self._run_and_report_benchmark() - - def _run_and_report_benchmark(self): - start_time_sec = time.time() - stats = keras_imagenet_main.run(flags.FLAGS) - wall_time_sec = time.time() - start_time_sec - - super(Resnet50KerasAccuracy, self)._report_benchmark( - stats, - wall_time_sec, - top_1_min=MIN_TOP_1_ACCURACY, - top_1_max=MAX_TOP_1_ACCURACY, - total_batch_size=FLAGS.batch_size, - log_steps=100) - - def _get_model_dir(self, folder_name): - return os.path.join(self.output_dir, folder_name) - - -class Resnet50KerasBenchmarkBase(keras_benchmark.KerasBenchmark): - """Resnet50 benchmarks.""" - - def __init__(self, output_dir=None, default_flags=None): - flag_methods = [ - keras_common.define_keras_flags, imagenet_main.define_imagenet_flags - ] - - super(Resnet50KerasBenchmarkBase, self).__init__( - output_dir=output_dir, - flag_methods=flag_methods, - default_flags=default_flags) - - def _run_and_report_benchmark(self): - start_time_sec = time.time() - stats = keras_imagenet_main.run(FLAGS) - wall_time_sec = time.time() - start_time_sec - - super(Resnet50KerasBenchmarkBase, self)._report_benchmark( - stats, - wall_time_sec, - total_batch_size=FLAGS.batch_size, - log_steps=FLAGS.log_steps) - - def benchmark_1_gpu_no_dist_strat(self): - self._setup() - - FLAGS.num_gpus = 1 - FLAGS.enable_eager = True - FLAGS.turn_off_distribution_strategy = True - FLAGS.batch_size = 128 - self._run_and_report_benchmark() - - def benchmark_graph_1_gpu_no_dist_strat(self): - self._setup() - - FLAGS.num_gpus = 1 - FLAGS.enable_eager = False - FLAGS.turn_off_distribution_strategy = True - FLAGS.batch_size = 128 - self._run_and_report_benchmark() - - def benchmark_1_gpu(self): - self._setup() - - FLAGS.num_gpus = 1 - FLAGS.enable_eager = True - FLAGS.turn_off_distribution_strategy = False - FLAGS.batch_size = 128 - self._run_and_report_benchmark() - - def benchmark_graph_1_gpu(self): - self._setup() - - FLAGS.num_gpus = 1 - FLAGS.enable_eager = False - FLAGS.turn_off_distribution_strategy = False - FLAGS.batch_size = 128 - self._run_and_report_benchmark() - - def benchmark_8_gpu(self): - self._setup() - - FLAGS.num_gpus = 8 - FLAGS.enable_eager = True - FLAGS.turn_off_distribution_strategy = False - FLAGS.batch_size = 128 * 8 # 8 GPUs - self._run_and_report_benchmark() - - def benchmark_graph_8_gpu(self): - self._setup() - - FLAGS.num_gpus = 8 - FLAGS.enable_eager = False - FLAGS.turn_off_distribution_strategy = False - FLAGS.batch_size = 128 * 8 # 8 GPUs - self._run_and_report_benchmark() - - def fill_report_object(self, stats): - super(Resnet50KerasBenchmarkBase, self).fill_report_object( - stats, - total_batch_size=FLAGS.batch_size, - log_steps=FLAGS.log_steps) - - -class Resnet50KerasBenchmarkSynth(Resnet50KerasBenchmarkBase): - """Resnet50 synthetic benchmark tests.""" - - def __init__(self, output_dir=None): - def_flags = {} - def_flags['skip_eval'] = True - def_flags['use_synthetic_data'] = True - def_flags['train_steps'] = 110 - def_flags['log_steps'] = 10 - - super(Resnet50KerasBenchmarkSynth, self).__init__( - output_dir=output_dir, default_flags=def_flags) - - -class Resnet50KerasBenchmarkReal(Resnet50KerasBenchmarkBase): - """Resnet50 real data benchmark tests.""" - - def __init__(self, output_dir=None): - def_flags = {} - def_flags['skip_eval'] = True - def_flags['data_dir'] = DATA_DIR - def_flags['train_steps'] = 110 - def_flags['log_steps'] = 10 - - super(Resnet50KerasBenchmarkReal, self).__init__( - output_dir=output_dir, default_flags=def_flags) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_imagenet_main.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_imagenet_main.py deleted file mode 100644 index 513cd209..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/keras_imagenet_main.py +++ /dev/null @@ -1,193 +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. -# ============================================================================== -"""Runs a ResNet model on the ImageNet dataset.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -from absl import app as absl_app -from absl import flags -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.resnet import imagenet_main -from official.resnet.keras import keras_common -from official.resnet.keras import resnet_model -from official.utils.flags import core as flags_core -from official.utils.logs import logger -from official.utils.misc import distribution_utils - - -LR_SCHEDULE = [ # (multiplier, epoch to start) tuples - (1.0, 5), (0.1, 30), (0.01, 60), (0.001, 80) -] - - -def learning_rate_schedule(current_epoch, - current_batch, - batches_per_epoch, - batch_size): - """Handles linear scaling rule, gradual warmup, and LR decay. - - Scale learning rate at epoch boundaries provided in LR_SCHEDULE by the - provided scaling factor. - - Args: - current_epoch: integer, current epoch indexed from 0. - current_batch: integer, current batch in the current epoch, indexed from 0. - batches_per_epoch: integer, number of steps in an epoch. - batch_size: integer, total batch sized. - - Returns: - Adjusted learning rate. - """ - initial_lr = keras_common.BASE_LEARNING_RATE * batch_size / 256 - epoch = current_epoch + float(current_batch) / batches_per_epoch - warmup_lr_multiplier, warmup_end_epoch = LR_SCHEDULE[0] - if epoch < warmup_end_epoch: - # Learning rate increases linearly per step. - return initial_lr * warmup_lr_multiplier * epoch / warmup_end_epoch - for mult, start_epoch in LR_SCHEDULE: - if epoch >= start_epoch: - learning_rate = initial_lr * mult - else: - break - return learning_rate - - -def parse_record_keras(raw_record, is_training, dtype): - """Adjust the shape of label.""" - image, label = imagenet_main.parse_record(raw_record, is_training, dtype) - - # Subtract one so that labels are in [0, 1000), and cast to float32 for - # Keras model. - label = tf.cast(tf.cast(tf.reshape(label, shape=[1]), dtype=tf.int32) - 1, - dtype=tf.float32) - return image, label - - -def run(flags_obj): - """Run ResNet ImageNet training and eval loop using native Keras APIs. - - Args: - flags_obj: An object containing parsed flag values. - - Raises: - ValueError: If fp16 is passed as it is not currently supported. - """ - if flags_obj.enable_eager: - tf.enable_eager_execution() - - dtype = flags_core.get_tf_dtype(flags_obj) - if dtype == 'fp16': - raise ValueError('dtype fp16 is not supported in Keras. Use the default ' - 'value(fp32).') - - data_format = flags_obj.data_format - if data_format is None: - data_format = ('channels_first' - if tf.test.is_built_with_cuda() else 'channels_last') - tf.keras.backend.set_image_data_format(data_format) - - # pylint: disable=protected-access - if flags_obj.use_synthetic_data: - input_fn = keras_common.get_synth_input_fn( - height=imagenet_main.DEFAULT_IMAGE_SIZE, - width=imagenet_main.DEFAULT_IMAGE_SIZE, - num_channels=imagenet_main.NUM_CHANNELS, - num_classes=imagenet_main.NUM_CLASSES, - dtype=flags_core.get_tf_dtype(flags_obj)) - else: - input_fn = imagenet_main.input_fn - - train_input_dataset = input_fn(is_training=True, - data_dir=flags_obj.data_dir, - batch_size=flags_obj.batch_size, - num_epochs=flags_obj.train_epochs, - parse_record_fn=parse_record_keras) - - eval_input_dataset = input_fn(is_training=False, - data_dir=flags_obj.data_dir, - batch_size=flags_obj.batch_size, - num_epochs=flags_obj.train_epochs, - parse_record_fn=parse_record_keras) - - strategy = distribution_utils.get_distribution_strategy( - num_gpus=flags_obj.num_gpus, - turn_off_distribution_strategy=flags_obj.turn_off_distribution_strategy) - - strategy_scope = keras_common.get_strategy_scope(strategy) - - with strategy_scope: - optimizer = keras_common.get_optimizer() - model = resnet_model.resnet50(num_classes=imagenet_main.NUM_CLASSES) - - model.compile(loss='sparse_categorical_crossentropy', - optimizer=optimizer, - metrics=['sparse_categorical_accuracy']) - - time_callback, tensorboard_callback, lr_callback = keras_common.get_callbacks( - learning_rate_schedule, imagenet_main.NUM_IMAGES['train']) - - train_steps = imagenet_main.NUM_IMAGES['train'] // flags_obj.batch_size - train_epochs = flags_obj.train_epochs - - if flags_obj.train_steps: - train_steps = min(flags_obj.train_steps, train_steps) - train_epochs = 1 - - num_eval_steps = (imagenet_main.NUM_IMAGES['validation'] // - flags_obj.batch_size) - - validation_data = eval_input_dataset - if flags_obj.skip_eval: - # Only build the training graph. This reduces memory usage introduced by - # control flow ops in layers that have different implementations for - # training and inference (e.g., batch norm). - tf.keras.backend.set_learning_phase(1) - num_eval_steps = None - validation_data = None - - history = model.fit(train_input_dataset, - epochs=train_epochs, - steps_per_epoch=train_steps, - callbacks=[ - time_callback, - lr_callback, - tensorboard_callback - ], - validation_steps=num_eval_steps, - validation_data=validation_data, - verbose=2) - - eval_output = None - if not flags_obj.skip_eval: - eval_output = model.evaluate(eval_input_dataset, - steps=num_eval_steps, - verbose=1) - stats = keras_common.build_stats(history, eval_output, time_callback) - return stats - - -def main(_): - with logger.benchmark_context(flags.FLAGS): - return run(flags.FLAGS) - - -if __name__ == '__main__': - tf.logging.set_verbosity(tf.logging.INFO) - imagenet_main.define_imagenet_flags() - keras_common.define_keras_flags() - absl_app.run(main) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/resnet_cifar_model.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/resnet_cifar_model.py deleted file mode 100644 index 75608dfb..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/resnet_cifar_model.py +++ /dev/null @@ -1,287 +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. -# ============================================================================== -"""ResNet56 model for Keras adapted from tf.keras.applications.ResNet50. - -# Reference: -- [Deep Residual Learning for Image Recognition]( - https://arxiv.org/abs/1512.03385) -Adapted from code contributed by BigMoyan. -""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import tensorflow as tf -from tensorflow.python.keras import backend -from tensorflow.python.keras import layers - - -BATCH_NORM_DECAY = 0.997 -BATCH_NORM_EPSILON = 1e-5 -L2_WEIGHT_DECAY = 2e-4 - - -def identity_building_block(input_tensor, - kernel_size, - filters, - stage, - block, - training=None): - """The identity block is the block that has no conv layer at shortcut. - - Arguments: - input_tensor: input tensor - kernel_size: default 3, the kernel size of - middle conv layer at main path - filters: list of integers, the filters of 3 conv layer at main path - stage: integer, current stage label, used for generating layer names - block: 'a','b'..., current block label, used for generating layer names - training: Only used if training keras model with Estimator. In other - scenarios it is handled automatically. - - Returns: - Output tensor for the block. - """ - filters1, filters2 = filters - if tf.keras.backend.image_data_format() == 'channels_last': - bn_axis = 3 - else: - bn_axis = 1 - conv_name_base = 'res' + str(stage) + block + '_branch' - bn_name_base = 'bn' + str(stage) + block + '_branch' - - x = tf.keras.layers.Conv2D(filters1, kernel_size, - padding='same', - kernel_initializer='he_normal', - kernel_regularizer= - tf.keras.regularizers.l2(L2_WEIGHT_DECAY), - bias_regularizer= - tf.keras.regularizers.l2(L2_WEIGHT_DECAY), - name=conv_name_base + '2a')(input_tensor) - x = tf.keras.layers.BatchNormalization(axis=bn_axis, - name=bn_name_base + '2a', - momentum=BATCH_NORM_DECAY, - epsilon=BATCH_NORM_EPSILON)( - x, training=training) - x = tf.keras.layers.Activation('relu')(x) - - x = tf.keras.layers.Conv2D(filters2, kernel_size, - padding='same', - kernel_initializer='he_normal', - kernel_regularizer= - tf.keras.regularizers.l2(L2_WEIGHT_DECAY), - bias_regularizer= - tf.keras.regularizers.l2(L2_WEIGHT_DECAY), - name=conv_name_base + '2b')(x) - x = tf.keras.layers.BatchNormalization(axis=bn_axis, - name=bn_name_base + '2b', - momentum=BATCH_NORM_DECAY, - epsilon=BATCH_NORM_EPSILON)( - x, training=training) - - x = tf.keras.layers.add([x, input_tensor]) - x = tf.keras.layers.Activation('relu')(x) - return x - - -def conv_building_block(input_tensor, - kernel_size, - filters, - stage, - block, - strides=(2, 2), - training=None): - """A block that has a conv layer at shortcut. - - Arguments: - input_tensor: input tensor - kernel_size: default 3, the kernel size of - middle conv layer at main path - filters: list of integers, the filters of 3 conv layer at main path - stage: integer, current stage label, used for generating layer names - block: 'a','b'..., current block label, used for generating layer names - strides: Strides for the first conv layer in the block. - training: Only used if training keras model with Estimator. In other - scenarios it is handled automatically. - - Returns: - Output tensor for the block. - - Note that from stage 3, - the first conv layer at main path is with strides=(2, 2) - And the shortcut should have strides=(2, 2) as well - """ - filters1, filters2 = filters - if tf.keras.backend.image_data_format() == 'channels_last': - bn_axis = 3 - else: - bn_axis = 1 - conv_name_base = 'res' + str(stage) + block + '_branch' - bn_name_base = 'bn' + str(stage) + block + '_branch' - - x = tf.keras.layers.Conv2D(filters1, kernel_size, strides=strides, - padding='same', - kernel_initializer='he_normal', - kernel_regularizer= - tf.keras.regularizers.l2(L2_WEIGHT_DECAY), - bias_regularizer= - tf.keras.regularizers.l2(L2_WEIGHT_DECAY), - name=conv_name_base + '2a')(input_tensor) - x = tf.keras.layers.BatchNormalization(axis=bn_axis, - name=bn_name_base + '2a', - momentum=BATCH_NORM_DECAY, - epsilon=BATCH_NORM_EPSILON)( - x, training=training) - x = tf.keras.layers.Activation('relu')(x) - - x = tf.keras.layers.Conv2D(filters2, kernel_size, padding='same', - kernel_initializer='he_normal', - kernel_regularizer= - tf.keras.regularizers.l2(L2_WEIGHT_DECAY), - bias_regularizer= - tf.keras.regularizers.l2(L2_WEIGHT_DECAY), - name=conv_name_base + '2b')(x) - x = tf.keras.layers.BatchNormalization(axis=bn_axis, - name=bn_name_base + '2b', - momentum=BATCH_NORM_DECAY, - epsilon=BATCH_NORM_EPSILON)( - x, training=training) - - shortcut = tf.keras.layers.Conv2D(filters2, (1, 1), strides=strides, - kernel_initializer='he_normal', - kernel_regularizer= - tf.keras.regularizers.l2(L2_WEIGHT_DECAY), - bias_regularizer= - tf.keras.regularizers.l2(L2_WEIGHT_DECAY), - name=conv_name_base + '1')(input_tensor) - shortcut = tf.keras.layers.BatchNormalization( - axis=bn_axis, name=bn_name_base + '1', - momentum=BATCH_NORM_DECAY, epsilon=BATCH_NORM_EPSILON)( - shortcut, training=training) - - x = tf.keras.layers.add([x, shortcut]) - x = tf.keras.layers.Activation('relu')(x) - return x - - -def resnet56(classes=100, training=None): - """Instantiates the ResNet56 architecture. - - Arguments: - classes: optional number of classes to classify images into - training: Only used if training keras model with Estimator. In other - scenarios it is handled automatically. - - Returns: - A Keras model instance. - """ - input_shape = (32, 32, 3) - img_input = layers.Input(shape=input_shape) - - if backend.image_data_format() == 'channels_first': - x = layers.Lambda(lambda x: backend.permute_dimensions(x, (0, 3, 1, 2)), - name='transpose')(img_input) - bn_axis = 1 - else: # channel_last - x = img_input - bn_axis = 3 - - x = tf.keras.layers.ZeroPadding2D(padding=(1, 1), name='conv1_pad')(x) - x = tf.keras.layers.Conv2D(16, (3, 3), - strides=(1, 1), - padding='valid', - kernel_initializer='he_normal', - kernel_regularizer= - tf.keras.regularizers.l2(L2_WEIGHT_DECAY), - bias_regularizer= - tf.keras.regularizers.l2(L2_WEIGHT_DECAY), - name='conv1')(x) - x = tf.keras.layers.BatchNormalization(axis=bn_axis, name='bn_conv1', - momentum=BATCH_NORM_DECAY, - epsilon=BATCH_NORM_EPSILON)( - x, training=training) - x = tf.keras.layers.Activation('relu')(x) - - x = conv_building_block(x, 3, [16, 16], stage=2, block='a', strides=(1, 1), - training=training) - x = identity_building_block(x, 3, [16, 16], stage=2, block='b', - training=training) - x = identity_building_block(x, 3, [16, 16], stage=2, block='c', - training=training) - x = identity_building_block(x, 3, [16, 16], stage=2, block='d', - training=training) - x = identity_building_block(x, 3, [16, 16], stage=2, block='e', - training=training) - x = identity_building_block(x, 3, [16, 16], stage=2, block='f', - training=training) - x = identity_building_block(x, 3, [16, 16], stage=2, block='g', - training=training) - x = identity_building_block(x, 3, [16, 16], stage=2, block='h', - training=training) - x = identity_building_block(x, 3, [16, 16], stage=2, block='i', - training=training) - - x = conv_building_block(x, 3, [32, 32], stage=3, block='a', - training=training) - x = identity_building_block(x, 3, [32, 32], stage=3, block='b', - training=training) - x = identity_building_block(x, 3, [32, 32], stage=3, block='c', - training=training) - x = identity_building_block(x, 3, [32, 32], stage=3, block='d', - training=training) - x = identity_building_block(x, 3, [32, 32], stage=3, block='e', - training=training) - x = identity_building_block(x, 3, [32, 32], stage=3, block='f', - training=training) - x = identity_building_block(x, 3, [32, 32], stage=3, block='g', - training=training) - x = identity_building_block(x, 3, [32, 32], stage=3, block='h', - training=training) - x = identity_building_block(x, 3, [32, 32], stage=3, block='i', - training=training) - - x = conv_building_block(x, 3, [64, 64], stage=4, block='a', - training=training) - x = identity_building_block(x, 3, [64, 64], stage=4, block='b', - training=training) - x = identity_building_block(x, 3, [64, 64], stage=4, block='c', - training=training) - x = identity_building_block(x, 3, [64, 64], stage=4, block='d', - training=training) - x = identity_building_block(x, 3, [64, 64], stage=4, block='e', - training=training) - x = identity_building_block(x, 3, [64, 64], stage=4, block='f', - training=training) - x = identity_building_block(x, 3, [64, 64], stage=4, block='g', - training=training) - x = identity_building_block(x, 3, [64, 64], stage=4, block='h', - training=training) - x = identity_building_block(x, 3, [64, 64], stage=4, block='i', - training=training) - - x = tf.keras.layers.GlobalAveragePooling2D(name='avg_pool')(x) - x = tf.keras.layers.Dense(classes, activation='softmax', - kernel_initializer='he_normal', - kernel_regularizer= - tf.keras.regularizers.l2(L2_WEIGHT_DECAY), - bias_regularizer= - tf.keras.regularizers.l2(L2_WEIGHT_DECAY), - name='fc10')(x) - - inputs = img_input - # Create model. - model = tf.keras.models.Model(inputs, x, name='resnet56') - - return model diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/resnet_model.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/resnet_model.py deleted file mode 100644 index 62aaa59e..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/keras/resnet_model.py +++ /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. -# ============================================================================== -"""ResNet50 model for Keras. - -Adapted from tf.keras.applications.resnet50.ResNet50(). -This is ResNet model version 1.5. - -Related papers/blogs: -- https://arxiv.org/abs/1512.03385 -- https://arxiv.org/pdf/1603.05027v2.pdf -- http://torch.ch/blog/2016/02/04/resnets.html - -""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os -import warnings - -from tensorflow.python.keras import backend -from tensorflow.python.keras import layers -from tensorflow.python.keras import models -from tensorflow.python.keras import regularizers -from tensorflow.python.keras import utils - - -L2_WEIGHT_DECAY = 1e-4 -BATCH_NORM_DECAY = 0.9 -BATCH_NORM_EPSILON = 1e-5 - - -def identity_block(input_tensor, kernel_size, filters, stage, block): - """The identity block is the block that has no conv layer at shortcut. - - # Arguments - input_tensor: input tensor - kernel_size: default 3, the kernel size of - middle conv layer at main path - filters: list of integers, the filters of 3 conv layer at main path - stage: integer, current stage label, used for generating layer names - block: 'a','b'..., current block label, used for generating layer names - - # Returns - Output tensor for the block. - """ - filters1, filters2, filters3 = filters - if backend.image_data_format() == 'channels_last': - bn_axis = 3 - else: - bn_axis = 1 - conv_name_base = 'res' + str(stage) + block + '_branch' - bn_name_base = 'bn' + str(stage) + block + '_branch' - - x = layers.Conv2D(filters1, (1, 1), use_bias=False, - kernel_initializer='he_normal', - kernel_regularizer=regularizers.l2(L2_WEIGHT_DECAY), - name=conv_name_base + '2a')(input_tensor) - x = layers.BatchNormalization(axis=bn_axis, - momentum=BATCH_NORM_DECAY, - epsilon=BATCH_NORM_EPSILON, - name=bn_name_base + '2a')(x) - x = layers.Activation('relu')(x) - - x = layers.Conv2D(filters2, kernel_size, - padding='same', use_bias=False, - kernel_initializer='he_normal', - kernel_regularizer=regularizers.l2(L2_WEIGHT_DECAY), - name=conv_name_base + '2b')(x) - x = layers.BatchNormalization(axis=bn_axis, - momentum=BATCH_NORM_DECAY, - epsilon=BATCH_NORM_EPSILON, - name=bn_name_base + '2b')(x) - x = layers.Activation('relu')(x) - - x = layers.Conv2D(filters3, (1, 1), use_bias=False, - kernel_initializer='he_normal', - kernel_regularizer=regularizers.l2(L2_WEIGHT_DECAY), - name=conv_name_base + '2c')(x) - x = layers.BatchNormalization(axis=bn_axis, - momentum=BATCH_NORM_DECAY, - epsilon=BATCH_NORM_EPSILON, - name=bn_name_base + '2c')(x) - - x = layers.add([x, input_tensor]) - x = layers.Activation('relu')(x) - return x - - -def conv_block(input_tensor, - kernel_size, - filters, - stage, - block, - strides=(2, 2)): - """A block that has a conv layer at shortcut. - - # Arguments - input_tensor: input tensor - kernel_size: default 3, the kernel size of - middle conv layer at main path - filters: list of integers, the filters of 3 conv layer at main path - stage: integer, current stage label, used for generating layer names - block: 'a','b'..., current block label, used for generating layer names - strides: Strides for the second conv layer in the block. - - # Returns - Output tensor for the block. - - Note that from stage 3, - the second conv layer at main path is with strides=(2, 2) - And the shortcut should have strides=(2, 2) as well - """ - filters1, filters2, filters3 = filters - if backend.image_data_format() == 'channels_last': - bn_axis = 3 - else: - bn_axis = 1 - conv_name_base = 'res' + str(stage) + block + '_branch' - bn_name_base = 'bn' + str(stage) + block + '_branch' - - x = layers.Conv2D(filters1, (1, 1), use_bias=False, - kernel_initializer='he_normal', - kernel_regularizer=regularizers.l2(L2_WEIGHT_DECAY), - name=conv_name_base + '2a')(input_tensor) - x = layers.BatchNormalization(axis=bn_axis, - momentum=BATCH_NORM_DECAY, - epsilon=BATCH_NORM_EPSILON, - name=bn_name_base + '2a')(x) - x = layers.Activation('relu')(x) - - x = layers.Conv2D(filters2, kernel_size, strides=strides, padding='same', - use_bias=False, kernel_initializer='he_normal', - kernel_regularizer=regularizers.l2(L2_WEIGHT_DECAY), - name=conv_name_base + '2b')(x) - x = layers.BatchNormalization(axis=bn_axis, - momentum=BATCH_NORM_DECAY, - epsilon=BATCH_NORM_EPSILON, - name=bn_name_base + '2b')(x) - x = layers.Activation('relu')(x) - - x = layers.Conv2D(filters3, (1, 1), use_bias=False, - kernel_initializer='he_normal', - kernel_regularizer=regularizers.l2(L2_WEIGHT_DECAY), - name=conv_name_base + '2c')(x) - x = layers.BatchNormalization(axis=bn_axis, - momentum=BATCH_NORM_DECAY, - epsilon=BATCH_NORM_EPSILON, - name=bn_name_base + '2c')(x) - - shortcut = layers.Conv2D(filters3, (1, 1), strides=strides, use_bias=False, - kernel_initializer='he_normal', - kernel_regularizer=regularizers.l2(L2_WEIGHT_DECAY), - name=conv_name_base + '1')(input_tensor) - shortcut = layers.BatchNormalization(axis=bn_axis, - momentum=BATCH_NORM_DECAY, - epsilon=BATCH_NORM_EPSILON, - name=bn_name_base + '1')(shortcut) - - x = layers.add([x, shortcut]) - x = layers.Activation('relu')(x) - return x - - -def resnet50(num_classes): - # TODO(tfboyd): add training argument, just lik resnet56. - """Instantiates the ResNet50 architecture. - - Args: - num_classes: `int` number of classes for image classification. - - Returns: - A Keras model instance. - """ - input_shape = (224, 224, 3) - img_input = layers.Input(shape=input_shape) - - if backend.image_data_format() == 'channels_first': - x = layers.Lambda(lambda x: backend.permute_dimensions(x, (0, 3, 1, 2)), - name='transpose')(img_input) - bn_axis = 1 - else: # channels_last - x = img_input - bn_axis = 3 - - x = layers.ZeroPadding2D(padding=(3, 3), name='conv1_pad')(x) - x = layers.Conv2D(64, (7, 7), - strides=(2, 2), - padding='valid', use_bias=False, - kernel_initializer='he_normal', - kernel_regularizer=regularizers.l2(L2_WEIGHT_DECAY), - name='conv1')(x) - x = layers.BatchNormalization(axis=bn_axis, - momentum=BATCH_NORM_DECAY, - epsilon=BATCH_NORM_EPSILON, - name='bn_conv1')(x) - x = layers.Activation('relu')(x) - x = layers.ZeroPadding2D(padding=(1, 1), name='pool1_pad')(x) - x = layers.MaxPooling2D((3, 3), strides=(2, 2))(x) - - x = conv_block(x, 3, [64, 64, 256], stage=2, block='a', strides=(1, 1)) - x = identity_block(x, 3, [64, 64, 256], stage=2, block='b') - x = identity_block(x, 3, [64, 64, 256], stage=2, block='c') - - x = conv_block(x, 3, [128, 128, 512], stage=3, block='a') - x = identity_block(x, 3, [128, 128, 512], stage=3, block='b') - x = identity_block(x, 3, [128, 128, 512], stage=3, block='c') - x = identity_block(x, 3, [128, 128, 512], stage=3, block='d') - - x = conv_block(x, 3, [256, 256, 1024], stage=4, block='a') - x = identity_block(x, 3, [256, 256, 1024], stage=4, block='b') - x = identity_block(x, 3, [256, 256, 1024], stage=4, block='c') - x = identity_block(x, 3, [256, 256, 1024], stage=4, block='d') - x = identity_block(x, 3, [256, 256, 1024], stage=4, block='e') - x = identity_block(x, 3, [256, 256, 1024], stage=4, block='f') - - x = conv_block(x, 3, [512, 512, 2048], stage=5, block='a') - x = identity_block(x, 3, [512, 512, 2048], stage=5, block='b') - x = identity_block(x, 3, [512, 512, 2048], stage=5, block='c') - - x = layers.GlobalAveragePooling2D(name='avg_pool')(x) - x = layers.Dense( - num_classes, activation='softmax', - kernel_regularizer=regularizers.l2(L2_WEIGHT_DECAY), - bias_regularizer=regularizers.l2(L2_WEIGHT_DECAY), - name='fc1000')(x) - - # Create model. - return models.Model(img_input, x, name='resnet50') diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/layer_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/layer_test.py deleted file mode 100644 index bbef600e..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/layer_test.py +++ /dev/null @@ -1,204 +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. -# ============================================================================== -"""Test that the definitions of ResNet layers haven't changed. - -These tests will fail if either: - a) The graph of a resnet layer changes and the change is significant enough - that it can no longer load existing checkpoints. - b) The numerical results produced by the layer change. - -A warning will be issued if the graph changes, but the checkpoint still loads. - -In the event that a layer change is intended, or the TensorFlow implementation -of a layer changes (and thus changes the graph), regenerate using the command: - - $ python3 layer_test.py -regen -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import sys - -import tensorflow as tf # pylint: disable=g-bad-import-order -from official.resnet import resnet_model -from official.utils.testing import reference_data - - -DATA_FORMAT = "channels_last" # CPU instructions often preclude channels_first -BATCH_SIZE = 32 -BLOCK_TESTS = [ - dict(bottleneck=True, projection=True, resnet_version=1, width=8, - channels=4), - dict(bottleneck=True, projection=True, resnet_version=2, width=8, - channels=4), - dict(bottleneck=True, projection=False, resnet_version=1, width=8, - channels=4), - dict(bottleneck=True, projection=False, resnet_version=2, width=8, - channels=4), - dict(bottleneck=False, projection=True, resnet_version=1, width=8, - channels=4), - dict(bottleneck=False, projection=True, resnet_version=2, width=8, - channels=4), - dict(bottleneck=False, projection=False, resnet_version=1, width=8, - channels=4), - dict(bottleneck=False, projection=False, resnet_version=2, width=8, - channels=4), -] - - -class BaseTest(reference_data.BaseTest): - """Tests for core ResNet layers.""" - - @property - def test_name(self): - return "resnet" - - def _batch_norm_ops(self, test=False): - name = "batch_norm" - - g = tf.Graph() - with g.as_default(): - tf.set_random_seed(self.name_to_seed(name)) - input_tensor = tf.get_variable( - "input_tensor", dtype=tf.float32, - initializer=tf.random_uniform((32, 16, 16, 3), maxval=1) - ) - layer = resnet_model.batch_norm( - inputs=input_tensor, data_format=DATA_FORMAT, training=True) - - self._save_or_test_ops( - name=name, graph=g, ops_to_eval=[input_tensor, layer], test=test, - correctness_function=self.default_correctness_function - ) - - def make_projection(self, filters_out, strides, data_format): - """1D convolution with stride projector. - - Args: - filters_out: Number of filters in the projection. - strides: Stride length for convolution. - data_format: channels_first or channels_last - - Returns: - A CNN projector function with kernel_size 1. - """ - def projection_shortcut(inputs): - return resnet_model.conv2d_fixed_padding( - inputs=inputs, filters=filters_out, kernel_size=1, strides=strides, - data_format=data_format) - return projection_shortcut - - def _resnet_block_ops(self, test, batch_size, bottleneck, projection, - resnet_version, width, channels): - """Test whether resnet block construction has changed. - - Args: - test: Whether or not to run as a test case. - batch_size: Number of points in the fake image. This is needed due to - batch normalization. - bottleneck: Whether or not to use bottleneck layers. - projection: Whether or not to project the input. - resnet_version: Which version of ResNet to test. - width: The width of the fake image. - channels: The number of channels in the fake image. - """ - - name = "batch-size-{}_{}{}_version-{}_width-{}_channels-{}".format( - batch_size, - "bottleneck" if bottleneck else "building", - "_projection" if projection else "", - resnet_version, - width, - channels - ) - - if resnet_version == 1: - block_fn = resnet_model._building_block_v1 - if bottleneck: - block_fn = resnet_model._bottleneck_block_v1 - else: - block_fn = resnet_model._building_block_v2 - if bottleneck: - block_fn = resnet_model._bottleneck_block_v2 - - g = tf.Graph() - with g.as_default(): - tf.set_random_seed(self.name_to_seed(name)) - strides = 1 - channels_out = channels - projection_shortcut = None - if projection: - strides = 2 - channels_out *= strides - projection_shortcut = self.make_projection( - filters_out=channels_out, strides=strides, data_format=DATA_FORMAT) - - filters = channels_out - if bottleneck: - filters = channels_out // 4 - - input_tensor = tf.get_variable( - "input_tensor", dtype=tf.float32, - initializer=tf.random_uniform((batch_size, width, width, channels), - maxval=1) - ) - - layer = block_fn(inputs=input_tensor, filters=filters, training=True, - projection_shortcut=projection_shortcut, strides=strides, - data_format=DATA_FORMAT) - - self._save_or_test_ops( - name=name, graph=g, ops_to_eval=[input_tensor, layer], test=test, - correctness_function=self.default_correctness_function - ) - - def test_batch_norm(self): - self._batch_norm_ops(test=True) - - def test_block_0(self): - self._resnet_block_ops(test=True, batch_size=BATCH_SIZE, **BLOCK_TESTS[0]) - - def test_block_1(self): - self._resnet_block_ops(test=True, batch_size=BATCH_SIZE, **BLOCK_TESTS[1]) - - def test_block_2(self): - self._resnet_block_ops(test=True, batch_size=BATCH_SIZE, **BLOCK_TESTS[2]) - - def test_block_3(self): - self._resnet_block_ops(test=True, batch_size=BATCH_SIZE, **BLOCK_TESTS[3]) - - def test_block_4(self): - self._resnet_block_ops(test=True, batch_size=BATCH_SIZE, **BLOCK_TESTS[4]) - - def test_block_5(self): - self._resnet_block_ops(test=True, batch_size=BATCH_SIZE, **BLOCK_TESTS[5]) - - def test_block_6(self): - self._resnet_block_ops(test=True, batch_size=BATCH_SIZE, **BLOCK_TESTS[6]) - - def test_block_7(self): - self._resnet_block_ops(test=True, batch_size=BATCH_SIZE, **BLOCK_TESTS[7]) - - def regenerate(self): - """Create reference data files for ResNet layer tests.""" - self._batch_norm_ops(test=False) - for block_params in BLOCK_TESTS: - self._resnet_block_ops(test=False, batch_size=BATCH_SIZE, **block_params) - - -if __name__ == "__main__": - reference_data.main(argv=sys.argv, test_class=BaseTest) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/resnet_model.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/resnet_model.py deleted file mode 100644 index e17aab5f..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/resnet_model.py +++ /dev/null @@ -1,546 +0,0 @@ -# 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. -# ============================================================================== -"""Contains definitions for Residual Networks. - -Residual networks ('v1' ResNets) were originally proposed in: -[1] Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun - Deep Residual Learning for Image Recognition. arXiv:1512.03385 - -The full preactivation 'v2' ResNet variant was introduced by: -[2] Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun - Identity Mappings in Deep Residual Networks. arXiv: 1603.05027 - -The key difference of the full preactivation 'v2' variant compared to the -'v1' variant in [1] is the use of batch normalization before every weight layer -rather than after. -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import tensorflow as tf - -_BATCH_NORM_DECAY = 0.997 -_BATCH_NORM_EPSILON = 1e-5 -DEFAULT_VERSION = 2 -DEFAULT_DTYPE = tf.float32 -CASTABLE_TYPES = (tf.float16,) -ALLOWED_TYPES = (DEFAULT_DTYPE,) + CASTABLE_TYPES - - -################################################################################ -# Convenience functions for building the ResNet model. -################################################################################ -def batch_norm(inputs, training, data_format): - """Performs a batch normalization using a standard set of parameters.""" - # We set fused=True for a significant performance boost. See - # https://www.tensorflow.org/performance/performance_guide#common_fused_ops - return tf.layers.batch_normalization( - inputs=inputs, axis=1 if data_format == 'channels_first' else 3, - momentum=_BATCH_NORM_DECAY, epsilon=_BATCH_NORM_EPSILON, center=True, - scale=True, training=training, fused=True) - - -def fixed_padding(inputs, kernel_size, data_format): - """Pads the input along the spatial dimensions independently of input size. - - Args: - inputs: A tensor of size [batch, channels, height_in, width_in] or - [batch, height_in, width_in, channels] depending on data_format. - kernel_size: The kernel to be used in the conv2d or max_pool2d operation. - Should be a positive integer. - data_format: The input format ('channels_last' or 'channels_first'). - - Returns: - A tensor with the same format as the input with the data either intact - (if kernel_size == 1) or padded (if kernel_size > 1). - """ - pad_total = kernel_size - 1 - pad_beg = pad_total // 2 - pad_end = pad_total - pad_beg - - if data_format == 'channels_first': - padded_inputs = tf.pad(inputs, [[0, 0], [0, 0], - [pad_beg, pad_end], [pad_beg, pad_end]]) - else: - padded_inputs = tf.pad(inputs, [[0, 0], [pad_beg, pad_end], - [pad_beg, pad_end], [0, 0]]) - return padded_inputs - - -def conv2d_fixed_padding(inputs, filters, kernel_size, strides, data_format): - """Strided 2-D convolution with explicit padding.""" - # The padding is consistent and is based only on `kernel_size`, not on the - # dimensions of `inputs` (as opposed to using `tf.layers.conv2d` alone). - if strides > 1: - inputs = fixed_padding(inputs, kernel_size, data_format) - - return tf.layers.conv2d( - inputs=inputs, filters=filters, kernel_size=kernel_size, strides=strides, - padding=('SAME' if strides == 1 else 'VALID'), use_bias=False, - kernel_initializer=tf.variance_scaling_initializer(), - data_format=data_format) - - -################################################################################ -# ResNet block definitions. -################################################################################ -def _building_block_v1(inputs, filters, training, projection_shortcut, strides, - data_format): - """A single block for ResNet v1, without a bottleneck. - - Convolution then batch normalization then ReLU as described by: - Deep Residual Learning for Image Recognition - https://arxiv.org/pdf/1512.03385.pdf - by Kaiming He, Xiangyu Zhang, Shaoqing Ren, and Jian Sun, Dec 2015. - - Args: - inputs: A tensor of size [batch, channels, height_in, width_in] or - [batch, height_in, width_in, channels] depending on data_format. - filters: The number of filters for the convolutions. - training: A Boolean for whether the model is in training or inference - mode. Needed for batch normalization. - projection_shortcut: The function to use for projection shortcuts - (typically a 1x1 convolution when downsampling the input). - strides: The block's stride. If greater than 1, this block will ultimately - downsample the input. - data_format: The input format ('channels_last' or 'channels_first'). - - Returns: - The output tensor of the block; shape should match inputs. - """ - shortcut = inputs - - if projection_shortcut is not None: - shortcut = projection_shortcut(inputs) - shortcut = batch_norm(inputs=shortcut, training=training, - data_format=data_format) - - inputs = conv2d_fixed_padding( - inputs=inputs, filters=filters, kernel_size=3, strides=strides, - data_format=data_format) - inputs = batch_norm(inputs, training, data_format) - inputs = tf.nn.relu(inputs) - - inputs = conv2d_fixed_padding( - inputs=inputs, filters=filters, kernel_size=3, strides=1, - data_format=data_format) - inputs = batch_norm(inputs, training, data_format) - inputs += shortcut - inputs = tf.nn.relu(inputs) - - return inputs - - -def _building_block_v2(inputs, filters, training, projection_shortcut, strides, - data_format): - """A single block for ResNet v2, without a bottleneck. - - Batch normalization then ReLu then convolution as described by: - Identity Mappings in Deep Residual Networks - https://arxiv.org/pdf/1603.05027.pdf - by Kaiming He, Xiangyu Zhang, Shaoqing Ren, and Jian Sun, Jul 2016. - - Args: - inputs: A tensor of size [batch, channels, height_in, width_in] or - [batch, height_in, width_in, channels] depending on data_format. - filters: The number of filters for the convolutions. - training: A Boolean for whether the model is in training or inference - mode. Needed for batch normalization. - projection_shortcut: The function to use for projection shortcuts - (typically a 1x1 convolution when downsampling the input). - strides: The block's stride. If greater than 1, this block will ultimately - downsample the input. - data_format: The input format ('channels_last' or 'channels_first'). - - Returns: - The output tensor of the block; shape should match inputs. - """ - shortcut = inputs - inputs = batch_norm(inputs, training, data_format) - inputs = tf.nn.relu(inputs) - - # The projection shortcut should come after the first batch norm and ReLU - # since it performs a 1x1 convolution. - if projection_shortcut is not None: - shortcut = projection_shortcut(inputs) - - inputs = conv2d_fixed_padding( - inputs=inputs, filters=filters, kernel_size=3, strides=strides, - data_format=data_format) - - inputs = batch_norm(inputs, training, data_format) - inputs = tf.nn.relu(inputs) - inputs = conv2d_fixed_padding( - inputs=inputs, filters=filters, kernel_size=3, strides=1, - data_format=data_format) - - return inputs + shortcut - - -def _bottleneck_block_v1(inputs, filters, training, projection_shortcut, - strides, data_format): - """A single block for ResNet v1, with a bottleneck. - - Similar to _building_block_v1(), except using the "bottleneck" blocks - described in: - Convolution then batch normalization then ReLU as described by: - Deep Residual Learning for Image Recognition - https://arxiv.org/pdf/1512.03385.pdf - by Kaiming He, Xiangyu Zhang, Shaoqing Ren, and Jian Sun, Dec 2015. - - Args: - inputs: A tensor of size [batch, channels, height_in, width_in] or - [batch, height_in, width_in, channels] depending on data_format. - filters: The number of filters for the convolutions. - training: A Boolean for whether the model is in training or inference - mode. Needed for batch normalization. - projection_shortcut: The function to use for projection shortcuts - (typically a 1x1 convolution when downsampling the input). - strides: The block's stride. If greater than 1, this block will ultimately - downsample the input. - data_format: The input format ('channels_last' or 'channels_first'). - - Returns: - The output tensor of the block; shape should match inputs. - """ - shortcut = inputs - - if projection_shortcut is not None: - shortcut = projection_shortcut(inputs) - shortcut = batch_norm(inputs=shortcut, training=training, - data_format=data_format) - - inputs = conv2d_fixed_padding( - inputs=inputs, filters=filters, kernel_size=1, strides=1, - data_format=data_format) - inputs = batch_norm(inputs, training, data_format) - inputs = tf.nn.relu(inputs) - - inputs = conv2d_fixed_padding( - inputs=inputs, filters=filters, kernel_size=3, strides=strides, - data_format=data_format) - inputs = batch_norm(inputs, training, data_format) - inputs = tf.nn.relu(inputs) - - inputs = conv2d_fixed_padding( - inputs=inputs, filters=4 * filters, kernel_size=1, strides=1, - data_format=data_format) - inputs = batch_norm(inputs, training, data_format) - inputs += shortcut - inputs = tf.nn.relu(inputs) - - return inputs - - -def _bottleneck_block_v2(inputs, filters, training, projection_shortcut, - strides, data_format): - """A single block for ResNet v2, with a bottleneck. - - Similar to _building_block_v2(), except using the "bottleneck" blocks - described in: - Convolution then batch normalization then ReLU as described by: - Deep Residual Learning for Image Recognition - https://arxiv.org/pdf/1512.03385.pdf - by Kaiming He, Xiangyu Zhang, Shaoqing Ren, and Jian Sun, Dec 2015. - - Adapted to the ordering conventions of: - Batch normalization then ReLu then convolution as described by: - Identity Mappings in Deep Residual Networks - https://arxiv.org/pdf/1603.05027.pdf - by Kaiming He, Xiangyu Zhang, Shaoqing Ren, and Jian Sun, Jul 2016. - - Args: - inputs: A tensor of size [batch, channels, height_in, width_in] or - [batch, height_in, width_in, channels] depending on data_format. - filters: The number of filters for the convolutions. - training: A Boolean for whether the model is in training or inference - mode. Needed for batch normalization. - projection_shortcut: The function to use for projection shortcuts - (typically a 1x1 convolution when downsampling the input). - strides: The block's stride. If greater than 1, this block will ultimately - downsample the input. - data_format: The input format ('channels_last' or 'channels_first'). - - Returns: - The output tensor of the block; shape should match inputs. - """ - shortcut = inputs - inputs = batch_norm(inputs, training, data_format) - inputs = tf.nn.relu(inputs) - - # The projection shortcut should come after the first batch norm and ReLU - # since it performs a 1x1 convolution. - if projection_shortcut is not None: - shortcut = projection_shortcut(inputs) - - inputs = conv2d_fixed_padding( - inputs=inputs, filters=filters, kernel_size=1, strides=1, - data_format=data_format) - - inputs = batch_norm(inputs, training, data_format) - inputs = tf.nn.relu(inputs) - inputs = conv2d_fixed_padding( - inputs=inputs, filters=filters, kernel_size=3, strides=strides, - data_format=data_format) - - inputs = batch_norm(inputs, training, data_format) - inputs = tf.nn.relu(inputs) - inputs = conv2d_fixed_padding( - inputs=inputs, filters=4 * filters, kernel_size=1, strides=1, - data_format=data_format) - - return inputs + shortcut - - -def block_layer(inputs, filters, bottleneck, block_fn, blocks, strides, - training, name, data_format): - """Creates one layer of blocks for the ResNet model. - - Args: - inputs: A tensor of size [batch, channels, height_in, width_in] or - [batch, height_in, width_in, channels] depending on data_format. - filters: The number of filters for the first convolution of the layer. - bottleneck: Is the block created a bottleneck block. - block_fn: The block to use within the model, either `building_block` or - `bottleneck_block`. - blocks: The number of blocks contained in the layer. - strides: The stride to use for the first convolution of the layer. If - greater than 1, this layer will ultimately downsample the input. - training: Either True or False, whether we are currently training the - model. Needed for batch norm. - name: A string name for the tensor output of the block layer. - data_format: The input format ('channels_last' or 'channels_first'). - - Returns: - The output tensor of the block layer. - """ - - # Bottleneck blocks end with 4x the number of filters as they start with - filters_out = filters * 4 if bottleneck else filters - - def projection_shortcut(inputs): - return conv2d_fixed_padding( - inputs=inputs, filters=filters_out, kernel_size=1, strides=strides, - data_format=data_format) - - # Only the first block per block_layer uses projection_shortcut and strides - inputs = block_fn(inputs, filters, training, projection_shortcut, strides, - data_format) - - for _ in range(1, blocks): - inputs = block_fn(inputs, filters, training, None, 1, data_format) - - return tf.identity(inputs, name) - - -class Model(object): - """Base class for building the Resnet Model.""" - - def __init__(self, resnet_size, bottleneck, num_classes, num_filters, - kernel_size, - conv_stride, first_pool_size, first_pool_stride, - block_sizes, block_strides, - resnet_version=DEFAULT_VERSION, data_format=None, - dtype=DEFAULT_DTYPE): - """Creates a model for classifying an image. - - Args: - resnet_size: A single integer for the size of the ResNet model. - bottleneck: Use regular blocks or bottleneck blocks. - num_classes: The number of classes used as labels. - num_filters: The number of filters to use for the first block layer - of the model. This number is then doubled for each subsequent block - layer. - kernel_size: The kernel size to use for convolution. - conv_stride: stride size for the initial convolutional layer - first_pool_size: Pool size to be used for the first pooling layer. - If none, the first pooling layer is skipped. - first_pool_stride: stride size for the first pooling layer. Not used - if first_pool_size is None. - block_sizes: A list containing n values, where n is the number of sets of - block layers desired. Each value should be the number of blocks in the - i-th set. - block_strides: List of integers representing the desired stride size for - each of the sets of block layers. Should be same length as block_sizes. - resnet_version: Integer representing which version of the ResNet network - to use. See README for details. Valid values: [1, 2] - data_format: Input format ('channels_last', 'channels_first', or None). - If set to None, the format is dependent on whether a GPU is available. - dtype: The TensorFlow dtype to use for calculations. If not specified - tf.float32 is used. - - Raises: - ValueError: if invalid version is selected. - """ - self.resnet_size = resnet_size - - if not data_format: - data_format = ( - 'channels_first' if tf.test.is_built_with_cuda() else 'channels_last') - - self.resnet_version = resnet_version - if resnet_version not in (1, 2): - raise ValueError( - 'Resnet version should be 1 or 2. See README for citations.') - - self.bottleneck = bottleneck - if bottleneck: - if resnet_version == 1: - self.block_fn = _bottleneck_block_v1 - else: - self.block_fn = _bottleneck_block_v2 - else: - if resnet_version == 1: - self.block_fn = _building_block_v1 - else: - self.block_fn = _building_block_v2 - - if dtype not in ALLOWED_TYPES: - raise ValueError('dtype must be one of: {}'.format(ALLOWED_TYPES)) - - self.data_format = data_format - self.num_classes = num_classes - self.num_filters = num_filters - self.kernel_size = kernel_size - self.conv_stride = conv_stride - self.first_pool_size = first_pool_size - self.first_pool_stride = first_pool_stride - self.block_sizes = block_sizes - self.block_strides = block_strides - self.dtype = dtype - self.pre_activation = resnet_version == 2 - - def _custom_dtype_getter(self, getter, name, shape=None, dtype=DEFAULT_DTYPE, - *args, **kwargs): - """Creates variables in fp32, then casts to fp16 if necessary. - - This function is a custom getter. A custom getter is a function with the - same signature as tf.get_variable, except it has an additional getter - parameter. Custom getters can be passed as the `custom_getter` parameter of - tf.variable_scope. Then, tf.get_variable will call the custom getter, - instead of directly getting a variable itself. This can be used to change - the types of variables that are retrieved with tf.get_variable. - The `getter` parameter is the underlying variable getter, that would have - been called if no custom getter was used. Custom getters typically get a - variable with `getter`, then modify it in some way. - - This custom getter will create an fp32 variable. If a low precision - (e.g. float16) variable was requested it will then cast the variable to the - requested dtype. The reason we do not directly create variables in low - precision dtypes is that applying small gradients to such variables may - cause the variable not to change. - - Args: - getter: The underlying variable getter, that has the same signature as - tf.get_variable and returns a variable. - name: The name of the variable to get. - shape: The shape of the variable to get. - dtype: The dtype of the variable to get. Note that if this is a low - precision dtype, the variable will be created as a tf.float32 variable, - then cast to the appropriate dtype - *args: Additional arguments to pass unmodified to getter. - **kwargs: Additional keyword arguments to pass unmodified to getter. - - Returns: - A variable which is cast to fp16 if necessary. - """ - - if dtype in CASTABLE_TYPES: - var = getter(name, shape, tf.float32, *args, **kwargs) - return tf.cast(var, dtype=dtype, name=name + '_cast') - else: - return getter(name, shape, dtype, *args, **kwargs) - - def _model_variable_scope(self): - """Returns a variable scope that the model should be created under. - - If self.dtype is a castable type, model variable will be created in fp32 - then cast to self.dtype before being used. - - Returns: - A variable scope for the model. - """ - - return tf.variable_scope('resnet_model', - custom_getter=self._custom_dtype_getter) - - def __call__(self, inputs, training): - """Add operations to classify a batch of input images. - - Args: - inputs: A Tensor representing a batch of input images. - training: A boolean. Set to True to add operations required only when - training the classifier. - - Returns: - A logits Tensor with shape [, self.num_classes]. - """ - - with self._model_variable_scope(): - if self.data_format == 'channels_first': - # Convert the inputs from channels_last (NHWC) to channels_first (NCHW). - # This provides a large performance boost on GPU. See - # https://www.tensorflow.org/performance/performance_guide#data_formats - inputs = tf.transpose(inputs, [0, 3, 1, 2]) - - inputs = conv2d_fixed_padding( - inputs=inputs, filters=self.num_filters, kernel_size=self.kernel_size, - strides=self.conv_stride, data_format=self.data_format) - inputs = tf.identity(inputs, 'initial_conv') - - # We do not include batch normalization or activation functions in V2 - # for the initial conv1 because the first ResNet unit will perform these - # for both the shortcut and non-shortcut paths as part of the first - # block's projection. Cf. Appendix of [2]. - if self.resnet_version == 1: - inputs = batch_norm(inputs, training, self.data_format) - inputs = tf.nn.relu(inputs) - - if self.first_pool_size: - inputs = tf.layers.max_pooling2d( - inputs=inputs, pool_size=self.first_pool_size, - strides=self.first_pool_stride, padding='SAME', - data_format=self.data_format) - inputs = tf.identity(inputs, 'initial_max_pool') - - for i, num_blocks in enumerate(self.block_sizes): - num_filters = self.num_filters * (2**i) - inputs = block_layer( - inputs=inputs, filters=num_filters, bottleneck=self.bottleneck, - block_fn=self.block_fn, blocks=num_blocks, - strides=self.block_strides[i], training=training, - name='block_layer{}'.format(i + 1), data_format=self.data_format) - - # Only apply the BN and ReLU for model that does pre_activation in each - # building/bottleneck block, eg resnet V2. - if self.pre_activation: - inputs = batch_norm(inputs, training, self.data_format) - inputs = tf.nn.relu(inputs) - - # The current top layer has shape - # `batch_size x pool_size x pool_size x final_size`. - # ResNet does an Average Pooling layer over pool_size, - # but that is the same as doing a reduce_mean. We do a reduce_mean - # here because it performs better than AveragePooling2D. - axes = [2, 3] if self.data_format == 'channels_first' else [1, 2] - inputs = tf.reduce_mean(inputs, axes, keepdims=True) - inputs = tf.identity(inputs, 'final_reduce_mean') - - inputs = tf.squeeze(inputs, axes) - inputs = tf.layers.dense(inputs=inputs, units=self.num_classes) - inputs = tf.identity(inputs, 'final_dense') - return inputs diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/resnet_run_loop.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/resnet_run_loop.py deleted file mode 100644 index 3b3721ee..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/resnet_run_loop.py +++ /dev/null @@ -1,645 +0,0 @@ -# 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. -# ============================================================================== -"""Contains utility and supporting functions for ResNet. - - This module contains ResNet code which does not directly build layers. This -includes dataset management, hyperparameter and optimizer code, and argument -parsing. Code for defining the ResNet layers can be found in resnet_model.py. -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import functools -import math -import multiprocessing -import os - -# pylint: disable=g-bad-import-order -from absl import flags -import tensorflow as tf -from tensorflow.contrib.data.python.ops import threadpool - -from official.resnet import resnet_model -from official.utils.flags import core as flags_core -from official.utils.export import export -from official.utils.logs import hooks_helper -from official.utils.logs import logger -from official.resnet import imagenet_preprocessing -from official.utils.misc import distribution_utils -from official.utils.misc import model_helpers - - -################################################################################ -# Functions for input processing. -################################################################################ -def process_record_dataset(dataset, - is_training, - batch_size, - shuffle_buffer, - parse_record_fn, - num_epochs=1, - dtype=tf.float32, - datasets_num_private_threads=None, - num_parallel_batches=1): - """Given a Dataset with raw records, return an iterator over the records. - - Args: - dataset: A Dataset representing raw records - is_training: A boolean denoting whether the input is for training. - batch_size: The number of samples per batch. - shuffle_buffer: The buffer size to use when shuffling records. A larger - value results in better randomness, but smaller values reduce startup - time and use less memory. - parse_record_fn: A function that takes a raw record and returns the - corresponding (image, label) pair. - num_epochs: The number of epochs to repeat the dataset. - dtype: Data type to use for images/features. - datasets_num_private_threads: Number of threads for a private - threadpool created for all datasets computation. - num_parallel_batches: Number of parallel batches for tf.data. - - Returns: - Dataset of (image, label) pairs ready for iteration. - """ - - # Prefetches a batch at a time to smooth out the time taken to load input - # files for shuffling and processing. - dataset = dataset.prefetch(buffer_size=batch_size) - if is_training: - # Shuffles records before repeating to respect epoch boundaries. - dataset = dataset.shuffle(buffer_size=shuffle_buffer) - - # Repeats the dataset for the number of epochs to train. - dataset = dataset.repeat(num_epochs) - - # Parses the raw records into images and labels. - dataset = dataset.apply( - tf.contrib.data.map_and_batch( - lambda value: parse_record_fn(value, is_training, dtype), - batch_size=batch_size, - num_parallel_batches=num_parallel_batches, - drop_remainder=False)) - - # Operations between the final prefetch and the get_next call to the iterator - # will happen synchronously during run time. We prefetch here again to - # background all of the above processing work and keep it out of the - # critical training path. Setting buffer_size to tf.contrib.data.AUTOTUNE - # allows DistributionStrategies to adjust how many batches to fetch based - # on how many devices are present. - dataset = dataset.prefetch(buffer_size=tf.contrib.data.AUTOTUNE) - - # Defines a specific size thread pool for tf.data operations. - if datasets_num_private_threads: - tf.logging.info('datasets_num_private_threads: %s', - datasets_num_private_threads) - dataset = threadpool.override_threadpool( - dataset, - threadpool.PrivateThreadPool( - datasets_num_private_threads, - display_name='input_pipeline_thread_pool')) - - return dataset - - -def get_synth_input_fn(height, width, num_channels, num_classes, - dtype=tf.float32): - """Returns an input function that returns a dataset with random data. - - This input_fn returns a data set that iterates over a set of random data and - bypasses all preprocessing, e.g. jpeg decode and copy. The host to device - copy is still included. This used to find the upper throughput bound when - tunning the full input pipeline. - - Args: - height: Integer height that will be used to create a fake image tensor. - width: Integer width that will be used to create a fake image tensor. - num_channels: Integer depth that will be used to create a fake image tensor. - num_classes: Number of classes that should be represented in the fake labels - tensor - dtype: Data type for features/images. - - Returns: - An input_fn that can be used in place of a real one to return a dataset - that can be used for iteration. - """ - # pylint: disable=unused-argument - def input_fn(is_training, data_dir, batch_size, *args, **kwargs): - """Returns dataset filled with random data.""" - # Synthetic input should be within [0, 255]. - inputs = tf.truncated_normal( - [batch_size] + [height, width, num_channels], - dtype=dtype, - mean=127, - stddev=60, - name='synthetic_inputs') - - labels = tf.random_uniform( - [batch_size], - minval=0, - maxval=num_classes - 1, - dtype=tf.int32, - name='synthetic_labels') - data = tf.data.Dataset.from_tensors((inputs, labels)).repeat() - data = data.prefetch(buffer_size=tf.contrib.data.AUTOTUNE) - return data - - return input_fn - - -def image_bytes_serving_input_fn(image_shape, dtype=tf.float32): - """Serving input fn for raw jpeg images.""" - - def _preprocess_image(image_bytes): - """Preprocess a single raw image.""" - # Bounding box around the whole image. - bbox = tf.constant([0.0, 0.0, 1.0, 1.0], dtype=dtype, shape=[1, 1, 4]) - height, width, num_channels = image_shape - image = imagenet_preprocessing.preprocess_image( - image_bytes, bbox, height, width, num_channels, is_training=False) - return image - - image_bytes_list = tf.placeholder( - shape=[None], dtype=tf.string, name='input_tensor') - images = tf.map_fn( - _preprocess_image, image_bytes_list, back_prop=False, dtype=dtype) - return tf.estimator.export.TensorServingInputReceiver( - images, {'image_bytes': image_bytes_list}) - - -def override_flags_and_set_envars_for_gpu_thread_pool(flags_obj): - """Override flags and set env_vars for performance. - - These settings exist to test the difference between using stock settings - and manual tuning. It also shows some of the ENV_VARS that can be tweaked to - squeeze a few extra examples per second. These settings are defaulted to the - current platform of interest, which changes over time. - - On systems with small numbers of cpu cores, e.g. under 8 logical cores, - setting up a gpu thread pool with `tf_gpu_thread_mode=gpu_private` may perform - poorly. - - Args: - flags_obj: Current flags, which will be adjusted possibly overriding - what has been set by the user on the command-line. - """ - cpu_count = multiprocessing.cpu_count() - tf.logging.info('Logical CPU cores: %s', cpu_count) - - # Sets up thread pool for each GPU for op scheduling. - per_gpu_thread_count = 1 - total_gpu_thread_count = per_gpu_thread_count * flags_obj.num_gpus - os.environ['TF_GPU_THREAD_MODE'] = flags_obj.tf_gpu_thread_mode - os.environ['TF_GPU_THREAD_COUNT'] = str(per_gpu_thread_count) - tf.logging.info('TF_GPU_THREAD_COUNT: %s', os.environ['TF_GPU_THREAD_COUNT']) - tf.logging.info('TF_GPU_THREAD_MODE: %s', os.environ['TF_GPU_THREAD_MODE']) - - # Reduces general thread pool by number of threads used for GPU pool. - main_thread_count = cpu_count - total_gpu_thread_count - flags_obj.inter_op_parallelism_threads = main_thread_count - - # Sets thread count for tf.data. Logical cores minus threads assign to the - # private GPU pool along with 2 thread per GPU for event monitoring and - # sending / receiving tensors. - num_monitoring_threads = 2 * flags_obj.num_gpus - flags_obj.datasets_num_private_threads = (cpu_count - total_gpu_thread_count - - num_monitoring_threads) - - -################################################################################ -# Functions for running training/eval/validation loops for the model. -################################################################################ -def learning_rate_with_decay( - batch_size, batch_denom, num_images, boundary_epochs, decay_rates, - base_lr=0.1, warmup=False): - """Get a learning rate that decays step-wise as training progresses. - - Args: - batch_size: the number of examples processed in each training batch. - batch_denom: this value will be used to scale the base learning rate. - `0.1 * batch size` is divided by this number, such that when - batch_denom == batch_size, the initial learning rate will be 0.1. - num_images: total number of images that will be used for training. - boundary_epochs: list of ints representing the epochs at which we - decay the learning rate. - decay_rates: list of floats representing the decay rates to be used - for scaling the learning rate. It should have one more element - than `boundary_epochs`, and all elements should have the same type. - base_lr: Initial learning rate scaled based on batch_denom. - warmup: Run a 5 epoch warmup to the initial lr. - Returns: - Returns a function that takes a single argument - the number of batches - trained so far (global_step)- and returns the learning rate to be used - for training the next batch. - """ - initial_learning_rate = base_lr * batch_size / batch_denom - batches_per_epoch = num_images / batch_size - - # Reduce the learning rate at certain epochs. - # CIFAR-10: divide by 10 at epoch 100, 150, and 200 - # ImageNet: divide by 10 at epoch 30, 60, 80, and 90 - boundaries = [int(batches_per_epoch * epoch) for epoch in boundary_epochs] - vals = [initial_learning_rate * decay for decay in decay_rates] - - def learning_rate_fn(global_step): - """Builds scaled learning rate function with 5 epoch warm up.""" - lr = tf.train.piecewise_constant(global_step, boundaries, vals) - if warmup: - warmup_steps = int(batches_per_epoch * 5) - warmup_lr = ( - initial_learning_rate * tf.cast(global_step, tf.float32) / tf.cast( - warmup_steps, tf.float32)) - return tf.cond(global_step < warmup_steps, lambda: warmup_lr, lambda: lr) - return lr - - return learning_rate_fn - - -def resnet_model_fn(features, labels, mode, model_class, - resnet_size, weight_decay, learning_rate_fn, momentum, - data_format, resnet_version, loss_scale, - loss_filter_fn=None, dtype=resnet_model.DEFAULT_DTYPE, - fine_tune=False): - """Shared functionality for different resnet model_fns. - - Initializes the ResnetModel representing the model layers - and uses that model to build the necessary EstimatorSpecs for - the `mode` in question. For training, this means building losses, - the optimizer, and the train op that get passed into the EstimatorSpec. - For evaluation and prediction, the EstimatorSpec is returned without - a train op, but with the necessary parameters for the given mode. - - Args: - features: tensor representing input images - labels: tensor representing class labels for all input images - mode: current estimator mode; should be one of - `tf.estimator.ModeKeys.TRAIN`, `EVALUATE`, `PREDICT` - model_class: a class representing a TensorFlow model that has a __call__ - function. We assume here that this is a subclass of ResnetModel. - resnet_size: A single integer for the size of the ResNet model. - weight_decay: weight decay loss rate used to regularize learned variables. - learning_rate_fn: function that returns the current learning rate given - the current global_step - momentum: momentum term used for optimization - data_format: Input format ('channels_last', 'channels_first', or None). - If set to None, the format is dependent on whether a GPU is available. - resnet_version: Integer representing which version of the ResNet network to - use. See README for details. Valid values: [1, 2] - loss_scale: The factor to scale the loss for numerical stability. A detailed - summary is present in the arg parser help text. - loss_filter_fn: function that takes a string variable name and returns - True if the var should be included in loss calculation, and False - otherwise. If None, batch_normalization variables will be excluded - from the loss. - dtype: the TensorFlow dtype to use for calculations. - fine_tune: If True only train the dense layers(final layers). - - Returns: - EstimatorSpec parameterized according to the input params and the - current mode. - """ - - # Generate a summary node for the images - tf.summary.image('images', features, max_outputs=6) - # Checks that features/images have same data type being used for calculations. - assert features.dtype == dtype - - model = model_class(resnet_size, data_format, resnet_version=resnet_version, - dtype=dtype) - - logits = model(features, mode == tf.estimator.ModeKeys.TRAIN) - - # This acts as a no-op if the logits are already in fp32 (provided logits are - # not a SparseTensor). If dtype is is low precision, logits must be cast to - # fp32 for numerical stability. - logits = tf.cast(logits, tf.float32) - - predictions = { - 'classes': tf.argmax(logits, axis=1), - 'probabilities': tf.nn.softmax(logits, name='softmax_tensor') - } - - if mode == tf.estimator.ModeKeys.PREDICT: - # Return the predictions and the specification for serving a SavedModel - return tf.estimator.EstimatorSpec( - mode=mode, - predictions=predictions, - export_outputs={ - 'predict': tf.estimator.export.PredictOutput(predictions) - }) - - # Calculate loss, which includes softmax cross entropy and L2 regularization. - cross_entropy = tf.losses.sparse_softmax_cross_entropy( - logits=logits, labels=labels) - - # Create a tensor named cross_entropy for logging purposes. - tf.identity(cross_entropy, name='cross_entropy') - tf.summary.scalar('cross_entropy', cross_entropy) - - # If no loss_filter_fn is passed, assume we want the default behavior, - # which is that batch_normalization variables are excluded from loss. - def exclude_batch_norm(name): - return 'batch_normalization' not in name - loss_filter_fn = loss_filter_fn or exclude_batch_norm - - # Add weight decay to the loss. - l2_loss = weight_decay * tf.add_n( - # loss is computed using fp32 for numerical stability. - [tf.nn.l2_loss(tf.cast(v, tf.float32)) for v in tf.trainable_variables() - if loss_filter_fn(v.name)]) - tf.summary.scalar('l2_loss', l2_loss) - loss = cross_entropy + l2_loss - - if mode == tf.estimator.ModeKeys.TRAIN: - global_step = tf.train.get_or_create_global_step() - - learning_rate = learning_rate_fn(global_step) - - # Create a tensor named learning_rate for logging purposes - tf.identity(learning_rate, name='learning_rate') - tf.summary.scalar('learning_rate', learning_rate) - - optimizer = tf.train.MomentumOptimizer( - learning_rate=learning_rate, - momentum=momentum - ) - - def _dense_grad_filter(gvs): - """Only apply gradient updates to the final layer. - - This function is used for fine tuning. - - Args: - gvs: list of tuples with gradients and variable info - Returns: - filtered gradients so that only the dense layer remains - """ - return [(g, v) for g, v in gvs if 'dense' in v.name] - - if loss_scale != 1: - # When computing fp16 gradients, often intermediate tensor values are - # so small, they underflow to 0. To avoid this, we multiply the loss by - # loss_scale to make these tensor values loss_scale times bigger. - scaled_grad_vars = optimizer.compute_gradients(loss * loss_scale) - - if fine_tune: - scaled_grad_vars = _dense_grad_filter(scaled_grad_vars) - - # Once the gradient computation is complete we can scale the gradients - # back to the correct scale before passing them to the optimizer. - unscaled_grad_vars = [(grad / loss_scale, var) - for grad, var in scaled_grad_vars] - minimize_op = optimizer.apply_gradients(unscaled_grad_vars, global_step) - else: - grad_vars = optimizer.compute_gradients(loss) - if fine_tune: - grad_vars = _dense_grad_filter(grad_vars) - minimize_op = optimizer.apply_gradients(grad_vars, global_step) - - update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) - train_op = tf.group(minimize_op, update_ops) - else: - train_op = None - - accuracy = tf.metrics.accuracy(labels, predictions['classes']) - accuracy_top_5 = tf.metrics.mean(tf.nn.in_top_k(predictions=logits, - targets=labels, - k=5, - name='top_5_op')) - metrics = {'accuracy': accuracy, - 'accuracy_top_5': accuracy_top_5} - - # Create a tensor named train_accuracy for logging purposes - tf.identity(accuracy[1], name='train_accuracy') - tf.identity(accuracy_top_5[1], name='train_accuracy_top_5') - tf.summary.scalar('train_accuracy', accuracy[1]) - tf.summary.scalar('train_accuracy_top_5', accuracy_top_5[1]) - - return tf.estimator.EstimatorSpec( - mode=mode, - predictions=predictions, - loss=loss, - train_op=train_op, - eval_metric_ops=metrics) - - -def resnet_main( - flags_obj, model_function, input_function, dataset_name, shape=None): - """Shared main loop for ResNet Models. - - Args: - flags_obj: An object containing parsed flags. See define_resnet_flags() - for details. - model_function: the function that instantiates the Model and builds the - ops for train/eval. This will be passed directly into the estimator. - input_function: the function that processes the dataset and returns a - dataset that the estimator can train on. This will be wrapped with - all the relevant flags for running and passed to estimator. - dataset_name: the name of the dataset for training and evaluation. This is - used for logging purpose. - shape: list of ints representing the shape of the images used for training. - This is only used if flags_obj.export_dir is passed. - - Returns: - Dict of results of the run. - """ - - model_helpers.apply_clean(flags.FLAGS) - - # Ensures flag override logic is only executed if explicitly triggered. - if flags_obj.tf_gpu_thread_mode: - override_flags_and_set_envars_for_gpu_thread_pool(flags_obj) - - # Creates session config. allow_soft_placement = True, is required for - # multi-GPU and is not harmful for other modes. - session_config = tf.ConfigProto( - inter_op_parallelism_threads=flags_obj.inter_op_parallelism_threads, - intra_op_parallelism_threads=flags_obj.intra_op_parallelism_threads, - allow_soft_placement=True) - - distribution_strategy = distribution_utils.get_distribution_strategy( - flags_core.get_num_gpus(flags_obj), flags_obj.all_reduce_alg) - - # Creates a `RunConfig` that checkpoints every 24 hours which essentially - # results in checkpoints determined only by `epochs_between_evals`. - run_config = tf.estimator.RunConfig( - train_distribute=distribution_strategy, - session_config=session_config, - save_checkpoints_secs=60*60*24) - - # Initializes model with all but the dense layer from pretrained ResNet. - if flags_obj.pretrained_model_checkpoint_path is not None: - warm_start_settings = tf.estimator.WarmStartSettings( - flags_obj.pretrained_model_checkpoint_path, - vars_to_warm_start='^(?!.*dense)') - else: - warm_start_settings = None - - classifier = tf.estimator.Estimator( - model_fn=model_function, model_dir=flags_obj.model_dir, config=run_config, - warm_start_from=warm_start_settings, params={ - 'resnet_size': int(flags_obj.resnet_size), - 'data_format': flags_obj.data_format, - 'batch_size': flags_obj.batch_size, - 'resnet_version': int(flags_obj.resnet_version), - 'loss_scale': flags_core.get_loss_scale(flags_obj), - 'dtype': flags_core.get_tf_dtype(flags_obj), - 'fine_tune': flags_obj.fine_tune - }) - - run_params = { - 'batch_size': flags_obj.batch_size, - 'dtype': flags_core.get_tf_dtype(flags_obj), - 'resnet_size': flags_obj.resnet_size, - 'resnet_version': flags_obj.resnet_version, - 'synthetic_data': flags_obj.use_synthetic_data, - 'train_epochs': flags_obj.train_epochs, - } - if flags_obj.use_synthetic_data: - dataset_name = dataset_name + '-synthetic' - - benchmark_logger = logger.get_benchmark_logger() - benchmark_logger.log_run_info('resnet', dataset_name, run_params, - test_id=flags_obj.benchmark_test_id) - - train_hooks = hooks_helper.get_train_hooks( - flags_obj.hooks, - model_dir=flags_obj.model_dir, - batch_size=flags_obj.batch_size) - - def input_fn_train(num_epochs): - return input_function( - is_training=True, - data_dir=flags_obj.data_dir, - batch_size=distribution_utils.per_device_batch_size( - flags_obj.batch_size, flags_core.get_num_gpus(flags_obj)), - num_epochs=num_epochs, - dtype=flags_core.get_tf_dtype(flags_obj), - datasets_num_private_threads=flags_obj.datasets_num_private_threads, - num_parallel_batches=flags_obj.datasets_num_parallel_batches) - - def input_fn_eval(): - return input_function( - is_training=False, - data_dir=flags_obj.data_dir, - batch_size=distribution_utils.per_device_batch_size( - flags_obj.batch_size, flags_core.get_num_gpus(flags_obj)), - num_epochs=1, - dtype=flags_core.get_tf_dtype(flags_obj)) - - if flags_obj.eval_only or not flags_obj.train_epochs: - # If --eval_only is set, perform a single loop with zero train epochs. - schedule, n_loops = [0], 1 - else: - # Compute the number of times to loop while training. All but the last - # pass will train for `epochs_between_evals` epochs, while the last will - # train for the number needed to reach `training_epochs`. For instance if - # train_epochs = 25 and epochs_between_evals = 10 - # schedule will be set to [10, 10, 5]. That is to say, the loop will: - # Train for 10 epochs and then evaluate. - # Train for another 10 epochs and then evaluate. - # Train for a final 5 epochs (to reach 25 epochs) and then evaluate. - n_loops = math.ceil(flags_obj.train_epochs / flags_obj.epochs_between_evals) - schedule = [flags_obj.epochs_between_evals for _ in range(int(n_loops))] - schedule[-1] = flags_obj.train_epochs - sum(schedule[:-1]) # over counting. - - for cycle_index, num_train_epochs in enumerate(schedule): - tf.logging.info('Starting cycle: %d/%d', cycle_index, int(n_loops)) - - if num_train_epochs: - classifier.train(input_fn=lambda: input_fn_train(num_train_epochs), - hooks=train_hooks, max_steps=flags_obj.max_train_steps) - - tf.logging.info('Starting to evaluate.') - - # flags_obj.max_train_steps is generally associated with testing and - # profiling. As a result it is frequently called with synthetic data, which - # will iterate forever. Passing steps=flags_obj.max_train_steps allows the - # eval (which is generally unimportant in those circumstances) to terminate. - # Note that eval will run for max_train_steps each loop, regardless of the - # global_step count. - eval_results = classifier.evaluate(input_fn=input_fn_eval, - steps=flags_obj.max_train_steps) - - benchmark_logger.log_evaluation_result(eval_results) - - if model_helpers.past_stop_threshold( - flags_obj.stop_threshold, eval_results['accuracy']): - break - - if flags_obj.export_dir is not None: - # Exports a saved model for the given classifier. - export_dtype = flags_core.get_tf_dtype(flags_obj) - if flags_obj.image_bytes_as_serving_input: - input_receiver_fn = functools.partial( - image_bytes_serving_input_fn, shape, dtype=export_dtype) - else: - input_receiver_fn = export.build_tensor_serving_input_receiver_fn( - shape, batch_size=flags_obj.batch_size, dtype=export_dtype) - classifier.export_savedmodel(flags_obj.export_dir, input_receiver_fn, - strip_default_attrs=True) - return eval_results - -def define_resnet_flags(resnet_size_choices=None): - """Add flags and validators for ResNet.""" - flags_core.define_base() - flags_core.define_performance(num_parallel_calls=False, - tf_gpu_thread_mode=True, - datasets_num_private_threads=True, - datasets_num_parallel_batches=True) - flags_core.define_image() - flags_core.define_benchmark() - flags.adopt_module_key_flags(flags_core) - - flags.DEFINE_enum( - name='resnet_version', short_name='rv', default='1', - enum_values=['1', '2'], - help=flags_core.help_wrap( - 'Version of ResNet. (1 or 2) See README.md for details.')) - flags.DEFINE_bool( - name='fine_tune', short_name='ft', default=False, - help=flags_core.help_wrap( - 'If True do not train any parameters except for the final layer.')) - flags.DEFINE_string( - name='pretrained_model_checkpoint_path', short_name='pmcp', default=None, - help=flags_core.help_wrap( - 'If not None initialize all the network except the final layer with ' - 'these values')) - flags.DEFINE_boolean( - name='eval_only', default=False, - help=flags_core.help_wrap('Skip training and only perform evaluation on ' - 'the latest checkpoint.')) - flags.DEFINE_boolean( - name='image_bytes_as_serving_input', default=False, - help=flags_core.help_wrap( - 'If True exports savedmodel with serving signature that accepts ' - 'JPEG image bytes instead of a fixed size [HxWxC] tensor that ' - 'represents the image. The former is easier to use for serving at ' - 'the expense of image resize/cropping being done as part of model ' - 'inference. Note, this flag only applies to ImageNet and cannot ' - 'be used for CIFAR.')) - flags.DEFINE_boolean( - name='turn_off_distribution_strategy', default=False, - help=flags_core.help_wrap('Set to True to not use distribution ' - 'strategies.')) - choice_kwargs = dict( - name='resnet_size', short_name='rs', default='50', - help=flags_core.help_wrap('The size of the ResNet model to use.')) - - if resnet_size_choices is None: - flags.DEFINE_string(**choice_kwargs) - else: - flags.DEFINE_enum(enum_values=resnet_size_choices, **choice_kwargs) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/README.md b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/README.md deleted file mode 100644 index d494fe48..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/README.md +++ /dev/null @@ -1,369 +0,0 @@ -# Transformer Translation Model -This is an implementation of the Transformer translation model as described in the [Attention is All You Need](https://arxiv.org/abs/1706.03762) paper. Based on the code provided by the authors: [Transformer code](https://github.com/tensorflow/tensor2tensor/blob/master/tensor2tensor/models/transformer.py) from [Tensor2Tensor](https://github.com/tensorflow/tensor2tensor). - -Transformer is a neural network architecture that solves sequence to sequence problems using attention mechanisms. Unlike traditional neural seq2seq models, Transformer does not involve recurrent connections. The attention mechanism learns dependencies between tokens in two sequences. Since attention weights apply to all tokens in the sequences, the Transformer model is able to easily capture long-distance dependencies. - -Transformer's overall structure follows the standard encoder-decoder pattern. The encoder uses self-attention to compute a representation of the input sequence. The decoder generates the output sequence one token at a time, taking the encoder output and previous decoder-outputted tokens as inputs. - -The model also applies embeddings on the input and output tokens, and adds a constant positional encoding. The positional encoding adds information about the position of each token. - -## Contents - * [Contents](#contents) - * [Walkthrough](#walkthrough) - * [Benchmarks](#benchmarks) - * [Training times](#training-times) - * [Evaluation results](#evaluation-results) - * [Detailed instructions](#detailed-instructions) - * [Environment preparation](#environment-preparation) - * [Download and preprocess datasets](#download-and-preprocess-datasets) - * [Model training and evaluation](#model-training-and-evaluation) - * [Translate using the model](#translate-using-the-model) - * [Compute official BLEU score](#compute-official-bleu-score) - * [TPU](#tpu) - * [Export trained model](#export-trained-model) - * [Example translation](#example-translation) - * [Implementation overview](#implementation-overview) - * [Model Definition](#model-definition) - * [Model Estimator](#model-estimator) - * [Other scripts](#other-scripts) - * [Test dataset](#test-dataset) - * [Term definitions](#term-definitions) - -## Walkthrough - -Below are the commands for running the Transformer model. See the [Detailed instrutions](#detailed-instructions) for more details on running the model. - -``` -cd /path/to/models/official/transformer - -# Ensure that PYTHONPATH is correctly defined as described in -# https://github.com/tensorflow/models/tree/master/official#requirements -# export PYTHONPATH="$PYTHONPATH:/path/to/models" - -# Export variables -PARAM_SET=big -DATA_DIR=$HOME/transformer/data -MODEL_DIR=$HOME/transformer/model_$PARAM_SET -VOCAB_FILE=$DATA_DIR/vocab.ende.32768 - -# Download training/evaluation datasets -python data_download.py --data_dir=$DATA_DIR - -# Train the model for 10 epochs, and evaluate after every epoch. -python transformer_main.py --data_dir=$DATA_DIR --model_dir=$MODEL_DIR \ - --vocab_file=$VOCAB_FILE --param_set=$PARAM_SET \ - --bleu_source=test_data/newstest2014.en --bleu_ref=test_data/newstest2014.de - -# Run during training in a separate process to get continuous updates, -# or after training is complete. -tensorboard --logdir=$MODEL_DIR - -# Translate some text using the trained model -python translate.py --model_dir=$MODEL_DIR --vocab_file=$VOCAB_FILE \ - --param_set=$PARAM_SET --text="hello world" - -# Compute model's BLEU score using the newstest2014 dataset. -python translate.py --model_dir=$MODEL_DIR --vocab_file=$VOCAB_FILE \ - --param_set=$PARAM_SET --file=test_data/newstest2014.en --file_out=translation.en -python compute_bleu.py --translation=translation.en --reference=test_data/newstest2014.de -``` - -## Benchmarks -### Training times - -Currently, both big and base parameter sets run on a single GPU. The measurements below -are reported from running the model on a P100 GPU. - -Param Set | batches/sec | batches per epoch | time per epoch ---- | --- | --- | --- -base | 4.8 | 83244 | 4 hr -big | 1.1 | 41365 | 10 hr - -### Evaluation results -Below are the case-insensitive BLEU scores after 10 epochs. - -Param Set | Score ---- | --- | -base | 27.7 -big | 28.9 - - -## Detailed instructions - - -0. ### Environment preparation - - #### Add models repo to PYTHONPATH - Follow the instructions described in the [Requirements](https://github.com/tensorflow/models/tree/master/official#requirements) section to add the models folder to the python path. - - #### Export variables (optional) - - Export the following variables, or modify the values in each of the snippets below: - ``` - PARAM_SET=big - DATA_DIR=$HOME/transformer/data - MODEL_DIR=$HOME/transformer/model_$PARAM_SET - VOCAB_FILE=$DATA_DIR/vocab.ende.32768 - ``` - -1. ### Download and preprocess datasets - - [data_download.py](data_download.py) downloads and preprocesses the training and evaluation WMT datasets. After the data is downloaded and extracted, the training data is used to generate a vocabulary of subtokens. The evaluation and training strings are tokenized, and the resulting data is sharded, shuffled, and saved as TFRecords. - - 1.75GB of compressed data will be downloaded. In total, the raw files (compressed, extracted, and combined files) take up 8.4GB of disk space. The resulting TFRecord and vocabulary files are 722MB. The script takes around 40 minutes to run, with the bulk of the time spent downloading and ~15 minutes spent on preprocessing. - - Command to run: - ``` - python data_download.py --data_dir=$DATA_DIR - ``` - - Arguments: - * `--data_dir`: Path where the preprocessed TFRecord data, and vocab file will be saved. - * Use the `--help` or `-h` flag to get a full list of possible arguments. - -2. ### Model training and evaluation - - [transformer_main.py](transformer_main.py) creates a Transformer model, and trains it using Tensorflow Estimator. - - Command to run: - ``` - python transformer_main.py --data_dir=$DATA_DIR --model_dir=$MODEL_DIR \ - --vocab_file=$VOCAB_FILE --param_set=$PARAM_SET - ``` - - Arguments: - * `--data_dir`: This should be set to the same directory given to the `data_download`'s `data_dir` argument. - * `--model_dir`: Directory to save Transformer model training checkpoints. - * `--vocab_file`: Path to subtoken vacbulary file. If data_download was used, you may find the file in `data_dir`. - * `--param_set`: Parameter set to use when creating and training the model. Options are `base` and `big` (default). - * Use the `--help` or `-h` flag to get a full list of possible arguments. - - #### Customizing training schedule - - By default, the model will train for 10 epochs, and evaluate after every epoch. The training schedule may be defined through the flags: - * Training with epochs (default): - * `--train_epochs`: The total number of complete passes to make through the dataset - * `--epochs_between_evals`: The number of epochs to train between evaluations. - * Training with steps: - * `--train_steps`: sets the total number of training steps to run. - * `--steps_between_evals`: Number of training steps to run between evaluations. - - Only one of `train_epochs` or `train_steps` may be set. Since the default option is to evaluate the model after training for an epoch, it may take 4 or more hours between model evaluations. To get more frequent evaluations, use the flags `--train_steps=250000 --steps_between_evals=1000`. - - Note: At the beginning of each training session, the training dataset is reloaded and shuffled. Stopping the training before completing an epoch may result in worse model quality, due to the chance that some examples may be seen more than others. Therefore, it is recommended to use epochs when the model quality is important. - - #### Compute BLEU score during model evaluation - - Use these flags to compute the BLEU when the model evaluates: - * `--bleu_source`: Path to file containing text to translate. - * `--bleu_ref`: Path to file containing the reference translation. - * `--stop_threshold`: Train until the BLEU score reaches this lower bound. This setting overrides the `--train_steps` and `--train_epochs` flags. - - The test source and reference files located in the `test_data` directory are extracted from the preprocessed dataset from the [NMT Seq2Seq tutorial](https://google.github.io/seq2seq/nmt/#download-data). - - When running `transformer_main.py`, use the flags: `--bleu_source=test_data/newstest2014.en --bleu_ref=test_data/newstest2014.de` - - #### Tensorboard - Training and evaluation metrics (loss, accuracy, approximate BLEU score, etc.) are logged, and can be displayed in the browser using Tensorboard. - ``` - tensorboard --logdir=$MODEL_DIR - ``` - The values are displayed at [localhost:6006](localhost:6006). - -3. ### Translate using the model - [translate.py](translate.py) contains the script to use the trained model to translate input text or file. Each line in the file is translated separately. - - Command to run: - ``` - python translate.py --model_dir=$MODEL_DIR --vocab_file=$VOCAB_FILE \ - --param_set=$PARAM_SET --text="hello world" - ``` - - Arguments for initializing the Subtokenizer and trained model: - * `--model_dir` and `--param_set`: These parameters are used to rebuild the trained model - * `--vocab_file`: Path to subtoken vacbulary file. If data_download was used, you may find the file in `data_dir`. - - Arguments for specifying what to translate: - * `--text`: Text to translate - * `--file`: Path to file containing text to translate - * `--file_out`: If `--file` is set, then this file will store the input file's translations. - - To translate the newstest2014 data, run: - ``` - python translate.py --model_dir=$MODEL_DIR --vocab_file=$VOCAB_FILE \ - --param_set=$PARAM_SET --file=test_data/newstest2014.en --file_out=translation.en - ``` - - Translating the file takes around 15 minutes on a GTX1080, or 5 minutes on a P100. - -4. ### Compute official BLEU score - Use [compute_bleu.py](compute_bleu.py) to compute the BLEU by comparing generated translations to the reference translation. - - Command to run: - ``` - python compute_bleu.py --translation=translation.en --reference=test_data/newstest2014.de - ``` - - Arguments: - * `--translation`: Path to file containing generated translations. - * `--reference`: Path to file containing reference translations. - * Use the `--help` or `-h` flag to get a full list of possible arguments. - -5. ### TPU - TPU support for this version of Transformer is experimental. Currently it is present for - demonstration purposes only, but will be optimized in the coming weeks. - -## Export trained model -To export the model as a Tensorflow [SavedModel](https://www.tensorflow.org/guide/saved_model) format, use the argument `--export_dir` when running `transformer_main.py`. A folder will be created in the directory with the name as the timestamp (e.g. $EXPORT_DIR/1526427396). - -``` -EXPORT_DIR=$HOME/transformer/saved_model -python transformer_main.py --data_dir=$DATA_DIR --model_dir=$MODEL_DIR \ - --vocab_file=$VOCAB_FILE --param_set=$PARAM_SET --export_model=$EXPORT_DIR -``` - -To inspect the SavedModel, use saved_model_cli: -``` -SAVED_MODEL_DIR=$EXPORT_DIR/{TIMESTAMP} # replace {TIMESTAMP} with the name of the folder created -saved_model_cli show --dir=$SAVED_MODEL_DIR --all -``` - -### Example translation -Let's translate **"hello world!"**, **"goodbye world."**, and **"Would you like some pie?"**. - -The SignatureDef for "translate" is: - - signature_def['translate']: - The given SavedModel SignatureDef contains the following input(s): - inputs['input'] tensor_info: - dtype: DT_INT64 - shape: (-1, -1) - name: Placeholder:0 - The given SavedModel SignatureDef contains the following output(s): - outputs['outputs'] tensor_info: - dtype: DT_INT32 - shape: (-1, -1) - name: model/Transformer/strided_slice_19:0 - outputs['scores'] tensor_info: - dtype: DT_FLOAT - shape: (-1) - name: model/Transformer/strided_slice_20:0 - -Follow the steps below to use the translate signature def: - -1. #### Encode the inputs to integer arrays. - This can be done using `utils.tokenizer.Subtokenizer`, and the vocab file in the SavedModel assets (`$SAVED_MODEL_DIR/assets.extra/vocab.txt`). - - ``` - from official.transformer.utils.tokenizer import Subtokenizer - s = Subtokenizer(PATH_TO_VOCAB_FILE) - print(s.encode("hello world!", add_eos=True)) - ``` - - The encoded inputs are: - * `"hello world!" = [6170, 3731, 178, 207, 1]` - * `"goodbye world." = [15431, 13966, 36, 178, 3, 1]` - * `"Would you like some pie?" = [9092, 72, 155, 202, 19851, 102, 1]` - -2. #### Run `saved_model_cli` to obtain the predicted translations - The encoded inputs should be padded so that they are the same length. The padding token is `0`. - ``` - ENCODED_INPUTS="[[26228, 145, 178, 1, 0, 0, 0], \ - [15431, 13966, 36, 178, 3, 1, 0], \ - [9092, 72, 155, 202, 19851, 102, 1]]" - ``` - - Now, use the `run` command with `saved_model_cli` to get the outputs. - - ``` - saved_model_cli run --dir=$SAVED_MODEL_DIR --tag_set=serve --signature_def=translate \ - --input_expr="input=$ENCODED_INPUTS" - ``` - - The outputs will look similar to: - ``` - Result for output key outputs: - [[18744 145 297 1 0 0 0 0 0 0 0 0 - 0 0] - [ 5450 4642 21 11 297 3 1 0 0 0 0 0 - 0 0] - [25940 22 66 103 21713 31 102 1 0 0 0 0 - 0 0]] - Result for output key scores: - [-1.5493642 -1.4032784 -3.252089 ] - ``` - -3. #### Decode the outputs to strings. - Use the `Subtokenizer` and vocab file as described in step 1 to decode the output integer arrays. - ``` - from official.transformer.utils.tokenizer import Subtokenizer - s = Subtokenizer(PATH_TO_VOCAB_FILE) - print(s.decode([18744, 145, 297, 1])) - ``` - The decoded outputs from above are: - * `[18744, 145, 297, 1] = "Hallo Welt"` - * `[5450, 4642, 21, 11, 297, 3, 1] = "Abschied von der Welt."` - * `[25940, 22, 66, 103, 21713, 31, 102, 1] = "Möchten Sie einen Kuchen?"` - -## Implementation overview - -A brief look at each component in the code: - -### Model Definition -The [model](model) subdirectory contains the implementation of the Transformer model. The following files define the Transformer model and its layers: -* [transformer.py](model/transformer.py): Defines the transformer model and its encoder/decoder layer stacks. -* [embedding_layer.py](model/embedding_layer.py): Contains the layer that calculates the embeddings. The embedding weights are also used to calculate the pre-softmax probabilities from the decoder output. -* [attention_layer.py](model/attention_layer.py): Defines the multi-headed and self attention layers that are used in the encoder/decoder stacks. -* [ffn_layer.py](model/ffn_layer.py): Defines the feedforward network that is used in the encoder/decoder stacks. The network is composed of 2 fully connected layers. - -Other files: -* [beam_search.py](model/beam_search.py) contains the beam search implementation, which is used during model inference to find high scoring translations. -* [model_params.py](model/model_params.py) contains the parameters used for the big and base models. -* [model_utils.py](model/model_utils.py) defines some helper functions used in the model (calculating padding, bias, etc.). - - -### Model Estimator -[transformer_main.py](model/transformer.py) creates an `Estimator` to train and evaluate the model. - -Helper functions: -* [utils/dataset.py](utils/dataset.py): contains functions for creating a `dataset` that is passed to the `Estimator`. -* [utils/metrics.py](utils/metrics.py): defines metrics functions used by the `Estimator` to evaluate the - -### Other scripts - -Aside from the main file to train the Transformer model, we provide other scripts for using the model or downloading the data: - -#### Data download and preprocessing - -[data_download.py](data_download.py) downloads and extracts data, then uses `Subtokenizer` to tokenize strings into arrays of int IDs. The int arrays are converted to `tf.Examples` and saved in the `tf.RecordDataset` format. - - The data is downloaded from the Workshop of Machine Transtion (WMT) [news translation task](http://www.statmt.org/wmt17/translation-task.html). The following datasets are used: - - * Europarl v7 - * Common Crawl corpus - * News Commentary v12 - - See the [download section](http://www.statmt.org/wmt17/translation-task.html#download) to explore the raw datasets. The parameters in this model are tuned to fit the English-German translation data, so the EN-DE texts are extracted from the downloaded compressed files. - -The text is transformed into arrays of integer IDs using the `Subtokenizer` defined in [`utils/tokenizer.py`](util/tokenizer.py). During initialization of the `Subtokenizer`, the raw training data is used to generate a vocabulary list containing common subtokens. - -The target vocabulary size of the WMT dataset is 32,768. The set of subtokens is found through binary search on the minimum number of times a subtoken appears in the data. The actual vocabulary size is 33,708, and is stored in a 324kB file. - -#### Translation -Translation is defined in [translate.py](translate.py). First, `Subtokenizer` tokenizes the input. The vocabulary file is the same used to tokenize the training/eval files. Next, beam search is used to find the combination of tokens that maximizes the probability outputted by the model decoder. The tokens are then converted back to strings with `Subtokenizer`. - -#### BLEU computation -[compute_bleu.py](compute_bleu.py): Implementation from [https://github.com/tensorflow/tensor2tensor/blob/master/tensor2tensor/utils/bleu_hook.py](https://github.com/tensorflow/tensor2tensor/blob/master/tensor2tensor/utils/bleu_hook.py). - -### Test dataset -The [newstest2014 files](test_data) are extracted from the [NMT Seq2Seq tutorial](https://google.github.io/seq2seq/nmt/#download-data). The raw text files are converted from the SGM format of the [WMT 2016](http://www.statmt.org/wmt16/translation-task.html) test sets. - -## Term definitions - -**Steps / Epochs**: -* Step: unit for processing a single batch of data -* Epoch: a complete run through the dataset - -Example: Consider a training a dataset with 100 examples that is divided into 20 batches with 5 examples per batch. A single training step trains the model on one batch. After 20 training steps, the model will have trained on every batch in the dataset, or one epoch. - -**Subtoken**: Words are referred to as tokens, and parts of words are referred to as 'subtokens'. For example, the word 'inclined' may be split into `['incline', 'd_']`. The '\_' indicates the end of the token. The subtoken vocabulary list is guaranteed to contain the alphabet (including numbers and special characters), so all words can be tokenized. diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/__init__.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/compute_bleu.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/compute_bleu.py deleted file mode 100644 index cf15d1cc..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/compute_bleu.py +++ /dev/null @@ -1,137 +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. -# ============================================================================== -"""Script to compute official BLEU score. - -Source: -https://github.com/tensorflow/tensor2tensor/blob/master/tensor2tensor/utils/bleu_hook.py -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import re -import sys -import unicodedata - -# pylint: disable=g-bad-import-order -import six -from absl import app as absl_app -from absl import flags -import tensorflow as tf -# pylint: enable=g-bad-import-order - -from official.transformer.utils import metrics -from official.utils.flags import core as flags_core - - -class UnicodeRegex(object): - """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): - r"""Tokenize a string following the official BLEU implementation. - - See https://github.com/moses-smt/mosesdecoder/' - 'blob/master/scripts/generic/mteval-v14.pl#L954-L983 - In our case, the input string is expected to be just one line - and no HTML entities de-escaping is needed. - So we just tokenize on punctuation and symbols, - except when a punctuation is preceded and followed by a digit - (e.g. a comma/dot as a thousand/decimal separator). - - Note that a numer (e.g. a year) followed by a dot at the end of sentence - is NOT tokenized, - i.e. the dot stays with the number because `s/(\p{P})(\P{N})/ $1 $2/g` - does not match this case (unless we add a space after each sentence). - However, this error is already in the original mteval-v14.pl - and we want to be consistent with it. - - 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_filename, hyp_filename, case_sensitive=False): - """Compute BLEU for two files (reference and hypothesis translation).""" - ref_lines = tf.gfile.Open(ref_filename).read().strip().splitlines() - hyp_lines = tf.gfile.Open(hyp_filename).read().strip().splitlines() - - if len(ref_lines) != len(hyp_lines): - raise ValueError("Reference and translation files have different number of " - "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 metrics.compute_bleu(ref_tokens, hyp_tokens) * 100 - - -def main(unused_argv): - if FLAGS.bleu_variant in ("both", "uncased"): - score = bleu_wrapper(FLAGS.reference, FLAGS.translation, False) - tf.logging.info("Case-insensitive results: %f" % score) - - if FLAGS.bleu_variant in ("both", "cased"): - score = bleu_wrapper(FLAGS.reference, FLAGS.translation, True) - tf.logging.info("Case-sensitive results: %f" % score) - - -def define_compute_bleu_flags(): - """Add flags for computing BLEU score.""" - flags.DEFINE_string( - name="translation", default=None, - help=flags_core.help_wrap("File containing translated text.")) - flags.mark_flag_as_required("translation") - - flags.DEFINE_string( - name="reference", default=None, - help=flags_core.help_wrap("File containing reference translation.")) - flags.mark_flag_as_required("reference") - - flags.DEFINE_enum( - name="bleu_variant", short_name="bv", default="both", - enum_values=["both", "uncased", "cased"], case_sensitive=False, - help=flags_core.help_wrap( - "Specify one or more BLEU variants to calculate. Variants: \"cased\"" - ", \"uncased\", or \"both\".")) - - -if __name__ == "__main__": - tf.logging.set_verbosity(tf.logging.INFO) - define_compute_bleu_flags() - FLAGS = flags.FLAGS - absl_app.run(main) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/compute_bleu_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/compute_bleu_test.py deleted file mode 100644 index c70b23d5..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/compute_bleu_test.py +++ /dev/null @@ -1,64 +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. -# ============================================================================== -"""Test functions in compute_blue.py.""" - -import tempfile - -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.transformer import compute_bleu - - -class ComputeBleuTest(tf.test.TestCase): - - def _create_temp_file(self, text): - temp_file = tempfile.NamedTemporaryFile(delete=False) - with tf.gfile.Open(temp_file.name, 'w') as w: - w.write(text) - return temp_file.name - - def test_bleu_same(self): - ref = self._create_temp_file("test 1 two 3\nmore tests!") - hyp = self._create_temp_file("test 1 two 3\nmore tests!") - - uncased_score = compute_bleu.bleu_wrapper(ref, hyp, False) - cased_score = compute_bleu.bleu_wrapper(ref, hyp, True) - self.assertEqual(100, uncased_score) - self.assertEqual(100, cased_score) - - def test_bleu_same_different_case(self): - ref = self._create_temp_file("Test 1 two 3\nmore tests!") - hyp = self._create_temp_file("test 1 two 3\nMore tests!") - uncased_score = compute_bleu.bleu_wrapper(ref, hyp, False) - cased_score = compute_bleu.bleu_wrapper(ref, hyp, True) - self.assertEqual(100, uncased_score) - self.assertLess(cased_score, 100) - - def test_bleu_different(self): - ref = self._create_temp_file("Testing\nmore tests!") - hyp = self._create_temp_file("Dog\nCat") - uncased_score = compute_bleu.bleu_wrapper(ref, hyp, False) - cased_score = compute_bleu.bleu_wrapper(ref, hyp, True) - self.assertLess(uncased_score, 100) - self.assertLess(cased_score, 100) - - def test_bleu_tokenize(self): - s = "Test0, 1 two, 3" - tokenized = compute_bleu.bleu_tokenize(s) - self.assertEqual(["Test0", ",", "1", "two", ",", "3"], tokenized) - - -if __name__ == "__main__": - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/data_download.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/data_download.py deleted file mode 100644 index 8519189a..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/data_download.py +++ /dev/null @@ -1,421 +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. -# ============================================================================== -"""Download and preprocess WMT17 ende training and evaluation datasets.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os -import random -import tarfile - -# pylint: disable=g-bad-import-order -import six -from six.moves import urllib -from absl import app as absl_app -from absl import flags -import tensorflow as tf -# pylint: enable=g-bad-import-order - -from official.transformer.utils import tokenizer -from official.utils.flags import core as flags_core - -# Data sources for training/evaluating the transformer translation model. -# If any of the training sources are changed, then either: -# 1) use the flag `--search` to find the best min count or -# 2) update the _TRAIN_DATA_MIN_COUNT constant. -# min_count is the minimum number of times a token must appear in the data -# before it is added to the vocabulary. "Best min count" refers to the value -# that generates a vocabulary set that is closest in size to _TARGET_VOCAB_SIZE. -_TRAIN_DATA_SOURCES = [ - { - "url": "http://data.statmt.org/wmt17/translation-task/" - "training-parallel-nc-v12.tgz", - "input": "news-commentary-v12.de-en.en", - "target": "news-commentary-v12.de-en.de", - }, - { - "url": "http://www.statmt.org/wmt13/training-parallel-commoncrawl.tgz", - "input": "commoncrawl.de-en.en", - "target": "commoncrawl.de-en.de", - }, - { - "url": "http://www.statmt.org/wmt13/training-parallel-europarl-v7.tgz", - "input": "europarl-v7.de-en.en", - "target": "europarl-v7.de-en.de", - }, -] -# Use pre-defined minimum count to generate subtoken vocabulary. -_TRAIN_DATA_MIN_COUNT = 6 - -_EVAL_DATA_SOURCES = [ - { - "url": "http://data.statmt.org/wmt17/translation-task/dev.tgz", - "input": "newstest2013.en", - "target": "newstest2013.de", - } -] - -# Vocabulary constants -_TARGET_VOCAB_SIZE = 32768 # Number of subtokens in the vocabulary list. -_TARGET_THRESHOLD = 327 # Accept vocabulary if size is within this threshold -VOCAB_FILE = "vocab.ende.%d" % _TARGET_VOCAB_SIZE - -# Strings to inclue in the generated files. -_PREFIX = "wmt32k" -_TRAIN_TAG = "train" -_EVAL_TAG = "dev" # Following WMT and Tensor2Tensor conventions, in which the - # evaluation datasets are tagged as "dev" for development. - -# Number of files to split train and evaluation data -_TRAIN_SHARDS = 100 -_EVAL_SHARDS = 1 - - -def find_file(path, filename, max_depth=5): - """Returns full filepath if the file is in path or a subdirectory.""" - for root, dirs, files in os.walk(path): - if filename in files: - return os.path.join(root, filename) - - # Don't search past max_depth - depth = root[len(path) + 1:].count(os.sep) - if depth > max_depth: - del dirs[:] # Clear dirs - return None - - -############################################################################### -# Download and extraction functions -############################################################################### -def get_raw_files(raw_dir, data_source): - """Return raw files from source. Downloads/extracts if needed. - - Args: - raw_dir: string directory to store raw files - data_source: dictionary with - {"url": url of compressed dataset containing input and target files - "input": file with data in input language - "target": file with data in target language} - - Returns: - dictionary with - {"inputs": list of files containing data in input language - "targets": list of files containing corresponding data in target language - } - """ - raw_files = { - "inputs": [], - "targets": [], - } # keys - for d in data_source: - input_file, target_file = download_and_extract( - raw_dir, d["url"], d["input"], d["target"]) - raw_files["inputs"].append(input_file) - raw_files["targets"].append(target_file) - return raw_files - - -def download_report_hook(count, block_size, total_size): - """Report hook for download progress. - - Args: - count: current block number - block_size: block size - total_size: total size - """ - percent = int(count * block_size * 100 / total_size) - print("\r%d%%" % percent + " completed", end="\r") - - -def download_from_url(path, url): - """Download content from a url. - - Args: - path: string directory where file will be downloaded - url: string url - - Returns: - Full path to downloaded file - """ - filename = url.split("/")[-1] - found_file = find_file(path, filename, max_depth=0) - if found_file is None: - filename = os.path.join(path, filename) - tf.logging.info("Downloading from %s to %s." % (url, filename)) - inprogress_filepath = filename + ".incomplete" - inprogress_filepath, _ = urllib.request.urlretrieve( - url, inprogress_filepath, reporthook=download_report_hook) - # Print newline to clear the carriage return from the download progress. - print() - tf.gfile.Rename(inprogress_filepath, filename) - return filename - else: - tf.logging.info("Already downloaded: %s (at %s)." % (url, found_file)) - return found_file - - -def download_and_extract(path, url, input_filename, target_filename): - """Extract files from downloaded compressed archive file. - - Args: - path: string directory where the files will be downloaded - url: url containing the compressed input and target files - input_filename: name of file containing data in source language - target_filename: name of file containing data in target language - - Returns: - Full paths to extracted input and target files. - - Raises: - OSError: if the the download/extraction fails. - """ - # Check if extracted files already exist in path - input_file = find_file(path, input_filename) - target_file = find_file(path, target_filename) - if input_file and target_file: - tf.logging.info("Already downloaded and extracted %s." % url) - return input_file, target_file - - # Download archive file if it doesn't already exist. - compressed_file = download_from_url(path, url) - - # Extract compressed files - tf.logging.info("Extracting %s." % compressed_file) - with tarfile.open(compressed_file, "r:gz") as corpus_tar: - corpus_tar.extractall(path) - - # Return filepaths of the requested files. - input_file = find_file(path, input_filename) - target_file = find_file(path, target_filename) - - if input_file and target_file: - return input_file, target_file - - raise OSError("Download/extraction failed for url %s to path %s" % - (url, path)) - - -def txt_line_iterator(path): - """Iterate through lines of file.""" - with tf.gfile.Open(path) as f: - for line in f: - yield line.strip() - - -def compile_files(raw_dir, raw_files, tag): - """Compile raw files into a single file for each language. - - Args: - raw_dir: Directory containing downloaded raw files. - raw_files: Dict containing filenames of input and target data. - {"inputs": list of files containing data in input language - "targets": list of files containing corresponding data in target language - } - tag: String to append to the compiled filename. - - Returns: - Full path of compiled input and target files. - """ - tf.logging.info("Compiling files with tag %s." % tag) - filename = "%s-%s" % (_PREFIX, tag) - input_compiled_file = os.path.join(raw_dir, filename + ".lang1") - target_compiled_file = os.path.join(raw_dir, filename + ".lang2") - - with tf.gfile.Open(input_compiled_file, mode="w") as input_writer: - with tf.gfile.Open(target_compiled_file, mode="w") as target_writer: - for i in range(len(raw_files["inputs"])): - input_file = raw_files["inputs"][i] - target_file = raw_files["targets"][i] - - tf.logging.info("Reading files %s and %s." % (input_file, target_file)) - write_file(input_writer, input_file) - write_file(target_writer, target_file) - return input_compiled_file, target_compiled_file - - -def write_file(writer, filename): - """Write all of lines from file using the writer.""" - for line in txt_line_iterator(filename): - writer.write(line) - writer.write("\n") - - -############################################################################### -# Data preprocessing -############################################################################### -def encode_and_save_files( - subtokenizer, data_dir, raw_files, tag, total_shards): - """Save data from files as encoded Examples in TFrecord format. - - Args: - subtokenizer: Subtokenizer object that will be used to encode the strings. - data_dir: The directory in which to write the examples - raw_files: A tuple of (input, target) data files. Each line in the input and - the corresponding line in target file will be saved in a tf.Example. - tag: String that will be added onto the file names. - total_shards: Number of files to divide the data into. - - Returns: - List of all files produced. - """ - # Create a file for each shard. - filepaths = [shard_filename(data_dir, tag, n + 1, total_shards) - for n in range(total_shards)] - - if all_exist(filepaths): - tf.logging.info("Files with tag %s already exist." % tag) - return filepaths - - tf.logging.info("Saving files with tag %s." % tag) - input_file = raw_files[0] - target_file = raw_files[1] - - # Write examples to each shard in round robin order. - tmp_filepaths = [fname + ".incomplete" for fname in filepaths] - writers = [tf.python_io.TFRecordWriter(fname) for fname in tmp_filepaths] - counter, shard = 0, 0 - for counter, (input_line, target_line) in enumerate(zip( - txt_line_iterator(input_file), txt_line_iterator(target_file))): - if counter > 0 and counter % 100000 == 0: - tf.logging.info("\tSaving case %d." % counter) - example = dict_to_example( - {"inputs": subtokenizer.encode(input_line, add_eos=True), - "targets": subtokenizer.encode(target_line, add_eos=True)}) - writers[shard].write(example.SerializeToString()) - shard = (shard + 1) % total_shards - for writer in writers: - writer.close() - - for tmp_name, final_name in zip(tmp_filepaths, filepaths): - tf.gfile.Rename(tmp_name, final_name) - - tf.logging.info("Saved %d Examples", counter + 1) - return filepaths - - -def shard_filename(path, tag, shard_num, total_shards): - """Create filename for data shard.""" - return os.path.join( - path, "%s-%s-%.5d-of-%.5d" % (_PREFIX, tag, shard_num, total_shards)) - - -def shuffle_records(fname): - """Shuffle records in a single file.""" - tf.logging.info("Shuffling records in file %s" % fname) - - # Rename file prior to shuffling - tmp_fname = fname + ".unshuffled" - tf.gfile.Rename(fname, tmp_fname) - - reader = tf.compat.v1.io.tf_record_iterator(tmp_fname) - records = [] - for record in reader: - records.append(record) - if len(records) % 100000 == 0: - tf.logging.info("\tRead: %d", len(records)) - - random.shuffle(records) - - # Write shuffled records to original file name - with tf.python_io.TFRecordWriter(fname) as w: - for count, record in enumerate(records): - w.write(record) - if count > 0 and count % 100000 == 0: - tf.logging.info("\tWriting record: %d" % count) - - tf.gfile.Remove(tmp_fname) - - -def dict_to_example(dictionary): - """Converts a dictionary of string->int to a tf.Example.""" - features = {} - for k, v in six.iteritems(dictionary): - features[k] = tf.train.Feature(int64_list=tf.train.Int64List(value=v)) - return tf.train.Example(features=tf.train.Features(feature=features)) - - -def all_exist(filepaths): - """Returns true if all files in the list exist.""" - for fname in filepaths: - if not tf.gfile.Exists(fname): - return False - return True - - -def make_dir(path): - if not tf.gfile.Exists(path): - tf.logging.info("Creating directory %s" % path) - tf.gfile.MakeDirs(path) - - -def main(unused_argv): - """Obtain training and evaluation data for the Transformer model.""" - make_dir(FLAGS.raw_dir) - make_dir(FLAGS.data_dir) - - # Get paths of download/extracted training and evaluation files. - tf.logging.info("Step 1/4: Downloading data from source") - train_files = get_raw_files(FLAGS.raw_dir, _TRAIN_DATA_SOURCES) - eval_files = get_raw_files(FLAGS.raw_dir, _EVAL_DATA_SOURCES) - - # Create subtokenizer based on the training files. - tf.logging.info("Step 2/4: Creating subtokenizer and building vocabulary") - train_files_flat = train_files["inputs"] + train_files["targets"] - vocab_file = os.path.join(FLAGS.data_dir, VOCAB_FILE) - subtokenizer = tokenizer.Subtokenizer.init_from_files( - vocab_file, train_files_flat, _TARGET_VOCAB_SIZE, _TARGET_THRESHOLD, - min_count=None if FLAGS.search else _TRAIN_DATA_MIN_COUNT) - - tf.logging.info("Step 3/4: Compiling training and evaluation data") - compiled_train_files = compile_files(FLAGS.raw_dir, train_files, _TRAIN_TAG) - compiled_eval_files = compile_files(FLAGS.raw_dir, eval_files, _EVAL_TAG) - - # Tokenize and save data as Examples in the TFRecord format. - tf.logging.info("Step 4/4: Preprocessing and saving data") - train_tfrecord_files = encode_and_save_files( - subtokenizer, FLAGS.data_dir, compiled_train_files, _TRAIN_TAG, - _TRAIN_SHARDS) - encode_and_save_files( - subtokenizer, FLAGS.data_dir, compiled_eval_files, _EVAL_TAG, - _EVAL_SHARDS) - - for fname in train_tfrecord_files: - shuffle_records(fname) - - -def define_data_download_flags(): - """Add flags specifying data download arguments.""" - flags.DEFINE_string( - name="data_dir", short_name="dd", default="/tmp/translate_ende", - help=flags_core.help_wrap( - "Directory for where the translate_ende_wmt32k dataset is saved.")) - flags.DEFINE_string( - name="raw_dir", short_name="rd", default="/tmp/translate_ende_raw", - help=flags_core.help_wrap( - "Path where the raw data will be downloaded and extracted.")) - flags.DEFINE_bool( - name="search", default=False, - help=flags_core.help_wrap( - "If set, use binary search to find the vocabulary set with size" - "closest to the target size (%d)." % _TARGET_VOCAB_SIZE)) - - -if __name__ == "__main__": - tf.logging.set_verbosity(tf.logging.INFO) - define_data_download_flags() - FLAGS = flags.FLAGS - absl_app.run(main) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/__init__.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/attention_layer.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/attention_layer.py deleted file mode 100644 index aed2e656..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/attention_layer.py +++ /dev/null @@ -1,148 +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 of multiheaded attention and self-attention layers.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import tensorflow as tf - - -class Attention(tf.layers.Layer): - """Multi-headed attention layer.""" - - def __init__(self, hidden_size, num_heads, attention_dropout, train): - if hidden_size % num_heads != 0: - raise ValueError("Hidden size must be evenly divisible by the number of " - "heads.") - - super(Attention, self).__init__() - self.hidden_size = hidden_size - self.num_heads = num_heads - self.attention_dropout = attention_dropout - self.train = train - - # Layers for linearly projecting the queries, keys, and values. - self.q_dense_layer = tf.layers.Dense(hidden_size, use_bias=False, name="q") - self.k_dense_layer = tf.layers.Dense(hidden_size, use_bias=False, name="k") - self.v_dense_layer = tf.layers.Dense(hidden_size, use_bias=False, name="v") - - self.output_dense_layer = tf.layers.Dense(hidden_size, use_bias=False, - name="output_transform") - - def split_heads(self, x): - """Split x into different heads, and transpose the resulting value. - - The tensor is transposed to insure the inner dimensions hold the correct - values during the matrix multiplication. - - Args: - x: A tensor with shape [batch_size, length, hidden_size] - - Returns: - A tensor with shape [batch_size, num_heads, length, hidden_size/num_heads] - """ - with tf.name_scope("split_heads"): - batch_size = tf.shape(x)[0] - length = tf.shape(x)[1] - - # Calculate depth of last dimension after it has been split. - depth = (self.hidden_size // self.num_heads) - - # Split the last dimension - x = tf.reshape(x, [batch_size, length, self.num_heads, depth]) - - # Transpose the result - return tf.transpose(x, [0, 2, 1, 3]) - - def combine_heads(self, x): - """Combine tensor that has been split. - - Args: - x: A tensor [batch_size, num_heads, length, hidden_size/num_heads] - - Returns: - A tensor with shape [batch_size, length, hidden_size] - """ - with tf.name_scope("combine_heads"): - batch_size = tf.shape(x)[0] - length = tf.shape(x)[2] - x = tf.transpose(x, [0, 2, 1, 3]) # --> [batch, length, num_heads, depth] - return tf.reshape(x, [batch_size, length, self.hidden_size]) - - def call(self, x, y, bias, cache=None): - """Apply attention mechanism to x and y. - - Args: - x: a tensor with shape [batch_size, length_x, hidden_size] - y: a tensor with shape [batch_size, length_y, hidden_size] - bias: attention bias that will be added to the result of the dot product. - cache: (Used during prediction) dictionary with tensors containing results - of previous attentions. The dictionary must have the items: - {"k": tensor with shape [batch_size, i, key_channels], - "v": tensor with shape [batch_size, i, value_channels]} - where i is the current decoded length. - - Returns: - Attention layer output with shape [batch_size, length_x, hidden_size] - """ - # Linearly project the query (q), key (k) and value (v) using different - # learned projections. This is in preparation of splitting them into - # multiple heads. Multi-head attention uses multiple queries, keys, and - # values rather than regular attention (which uses a single q, k, v). - q = self.q_dense_layer(x) - k = self.k_dense_layer(y) - v = self.v_dense_layer(y) - - if cache is not None: - # Combine cached keys and values with new keys and values. - k = tf.concat([cache["k"], k], axis=1) - v = tf.concat([cache["v"], v], axis=1) - - # Update cache - cache["k"] = k - cache["v"] = v - - # Split q, k, v into heads. - q = self.split_heads(q) - k = self.split_heads(k) - v = self.split_heads(v) - - # Scale q to prevent the dot product between q and k from growing too large. - depth = (self.hidden_size // self.num_heads) - q *= depth ** -0.5 - - # Calculate dot product attention - logits = tf.matmul(q, k, transpose_b=True) - logits += bias - weights = tf.nn.softmax(logits, name="attention_weights") - if self.train: - weights = tf.nn.dropout(weights, 1.0 - self.attention_dropout) - attention_output = tf.matmul(weights, v) - - # Recombine heads --> [batch_size, length, hidden_size] - attention_output = self.combine_heads(attention_output) - - # Run the combined outputs through another linear projection layer. - attention_output = self.output_dense_layer(attention_output) - return attention_output - - -class SelfAttention(Attention): - """Multiheaded self-attention layer.""" - - def call(self, x, bias, cache=None): - return super(SelfAttention, self).call(x, x, bias, cache) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/beam_search.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/beam_search.py deleted file mode 100644 index d720adfd..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/beam_search.py +++ /dev/null @@ -1,541 +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. -# ============================================================================== -"""Beam search to find the translated sequence with the highest probability. - -Source implementation from Tensor2Tensor: -https://github.com/tensorflow/tensor2tensor/blob/master/tensor2tensor/utils/beam_search.py -""" - -import tensorflow as tf -from tensorflow.python.util import nest - -# Default value for INF -INF = 1. * 1e7 - - -class _StateKeys(object): - """Keys to dictionary storing the state of the beam search loop.""" - - # Variable storing the loop index. - CUR_INDEX = "CUR_INDEX" - - # Top sequences that are alive for each batch item. Alive sequences are ones - # that have not generated an EOS token. Sequences that reach EOS are marked as - # finished and moved to the FINISHED_SEQ tensor. - # Has shape [batch_size, beam_size, CUR_INDEX + 1] - ALIVE_SEQ = "ALIVE_SEQ" - # Log probabilities of each alive sequence. Shape [batch_size, beam_size] - ALIVE_LOG_PROBS = "ALIVE_LOG_PROBS" - # Dictionary of cached values for each alive sequence. The cache stores - # the encoder output, attention bias, and the decoder attention output from - # the previous iteration. - ALIVE_CACHE = "ALIVE_CACHE" - - # Top finished sequences for each batch item. - # Has shape [batch_size, beam_size, CUR_INDEX + 1]. Sequences that are - # shorter than CUR_INDEX + 1 are padded with 0s. - FINISHED_SEQ = "FINISHED_SEQ" - # Scores for each finished sequence. Score = log probability / length norm - # Shape [batch_size, beam_size] - FINISHED_SCORES = "FINISHED_SCORES" - # Flags indicating which sequences in the finished sequences are finished. - # At the beginning, all of the sequences in FINISHED_SEQ are filler values. - # True -> finished sequence, False -> filler. Shape [batch_size, beam_size] - FINISHED_FLAGS = "FINISHED_FLAGS" - - -class SequenceBeamSearch(object): - """Implementation of beam search loop.""" - - def __init__(self, symbols_to_logits_fn, vocab_size, batch_size, - beam_size, alpha, max_decode_length, eos_id): - self.symbols_to_logits_fn = symbols_to_logits_fn - self.vocab_size = vocab_size - self.batch_size = batch_size - self.beam_size = beam_size - self.alpha = alpha - self.max_decode_length = max_decode_length - self.eos_id = eos_id - - def search(self, initial_ids, initial_cache): - """Beam search for sequences with highest scores.""" - state, state_shapes = self._create_initial_state(initial_ids, initial_cache) - - finished_state = tf.while_loop( - self._continue_search, self._search_step, loop_vars=[state], - shape_invariants=[state_shapes], parallel_iterations=1, back_prop=False) - finished_state = finished_state[0] - - alive_seq = finished_state[_StateKeys.ALIVE_SEQ] - alive_log_probs = finished_state[_StateKeys.ALIVE_LOG_PROBS] - finished_seq = finished_state[_StateKeys.FINISHED_SEQ] - finished_scores = finished_state[_StateKeys.FINISHED_SCORES] - finished_flags = finished_state[_StateKeys.FINISHED_FLAGS] - - # Account for corner case where there are no finished sequences for a - # particular batch item. In that case, return alive sequences for that batch - # item. - finished_seq = tf.where( - tf.reduce_any(finished_flags, 1), finished_seq, alive_seq) - finished_scores = tf.where( - tf.reduce_any(finished_flags, 1), finished_scores, alive_log_probs) - return finished_seq, finished_scores - - def _create_initial_state(self, initial_ids, initial_cache): - """Return initial state dictionary and its shape invariants. - - Args: - initial_ids: initial ids to pass into the symbols_to_logits_fn. - int tensor with shape [batch_size, 1] - initial_cache: dictionary storing values to be passed into the - symbols_to_logits_fn. - - Returns: - state and shape invariant dictionaries with keys from _StateKeys - """ - # Current loop index (starts at 0) - cur_index = tf.constant(0) - - # Create alive sequence with shape [batch_size, beam_size, 1] - alive_seq = _expand_to_beam_size(initial_ids, self.beam_size) - alive_seq = tf.expand_dims(alive_seq, axis=2) - - # Create tensor for storing initial log probabilities. - # Assume initial_ids are prob 1.0 - initial_log_probs = tf.constant( - [[0.] + [-float("inf")] * (self.beam_size - 1)]) - alive_log_probs = tf.tile(initial_log_probs, [self.batch_size, 1]) - - # Expand all values stored in the dictionary to the beam size, so that each - # beam has a separate cache. - alive_cache = nest.map_structure( - lambda t: _expand_to_beam_size(t, self.beam_size), initial_cache) - - # Initialize tensor storing finished sequences with filler values. - finished_seq = tf.zeros(tf.shape(alive_seq), tf.int32) - - # Set scores of the initial finished seqs to negative infinity. - finished_scores = tf.ones([self.batch_size, self.beam_size]) * -INF - - # Initialize finished flags with all False values. - finished_flags = tf.zeros([self.batch_size, self.beam_size], tf.bool) - - # Create state dictionary - state = { - _StateKeys.CUR_INDEX: cur_index, - _StateKeys.ALIVE_SEQ: alive_seq, - _StateKeys.ALIVE_LOG_PROBS: alive_log_probs, - _StateKeys.ALIVE_CACHE: alive_cache, - _StateKeys.FINISHED_SEQ: finished_seq, - _StateKeys.FINISHED_SCORES: finished_scores, - _StateKeys.FINISHED_FLAGS: finished_flags - } - - # Create state invariants for each value in the state dictionary. Each - # dimension must be a constant or None. A None dimension means either: - # 1) the dimension's value is a tensor that remains the same but may - # depend on the input sequence to the model (e.g. batch size). - # 2) the dimension may have different values on different iterations. - state_shape_invariants = { - _StateKeys.CUR_INDEX: tf.TensorShape([]), - _StateKeys.ALIVE_SEQ: tf.TensorShape([None, self.beam_size, None]), - _StateKeys.ALIVE_LOG_PROBS: tf.TensorShape([None, self.beam_size]), - _StateKeys.ALIVE_CACHE: nest.map_structure( - _get_shape_keep_last_dim, alive_cache), - _StateKeys.FINISHED_SEQ: tf.TensorShape([None, self.beam_size, None]), - _StateKeys.FINISHED_SCORES: tf.TensorShape([None, self.beam_size]), - _StateKeys.FINISHED_FLAGS: tf.TensorShape([None, self.beam_size]) - } - - return state, state_shape_invariants - - def _continue_search(self, state): - """Return whether to continue the search loop. - - The loops should terminate when - 1) when decode length has been reached, or - 2) when the worst score in the finished sequences is better than the best - score in the alive sequences (i.e. the finished sequences are provably - unchanging) - - Args: - state: A dictionary with the current loop state. - - Returns: - Bool tensor with value True if loop should continue, False if loop should - terminate. - """ - i = state[_StateKeys.CUR_INDEX] - alive_log_probs = state[_StateKeys.ALIVE_LOG_PROBS] - finished_scores = state[_StateKeys.FINISHED_SCORES] - finished_flags = state[_StateKeys.FINISHED_FLAGS] - - not_at_max_decode_length = tf.less(i, self.max_decode_length) - - # Calculate largest length penalty (the larger penalty, the better score). - max_length_norm = _length_normalization(self.alpha, self.max_decode_length) - # Get the best possible scores from alive sequences. - best_alive_scores = alive_log_probs[:, 0] / max_length_norm - - # Compute worst score in finished sequences for each batch element - finished_scores *= tf.to_float(finished_flags) # set filler scores to zero - lowest_finished_scores = tf.reduce_min(finished_scores, axis=1) - - # If there are no finished sequences in a batch element, then set the lowest - # finished score to -INF for that element. - finished_batches = tf.reduce_any(finished_flags, 1) - lowest_finished_scores += (1. - tf.to_float(finished_batches)) * -INF - - worst_finished_score_better_than_best_alive_score = tf.reduce_all( - tf.greater(lowest_finished_scores, best_alive_scores) - ) - - return tf.logical_and( - not_at_max_decode_length, - tf.logical_not(worst_finished_score_better_than_best_alive_score) - ) - - def _search_step(self, state): - """Beam search loop body. - - Grow alive sequences by a single ID. Sequences that have reached the EOS - token are marked as finished. The alive and finished sequences with the - highest log probabilities and scores are returned. - - A sequence's finished score is calculating by dividing the log probability - by the length normalization factor. Without length normalization, the - search is more likely to return shorter sequences. - - Args: - state: A dictionary with the current loop state. - - Returns: - new state dictionary. - """ - # Grow alive sequences by one token. - new_seq, new_log_probs, new_cache = self._grow_alive_seq(state) - # Collect top beam_size alive sequences - alive_state = self._get_new_alive_state(new_seq, new_log_probs, new_cache) - - # Combine newly finished sequences with existing finished sequences, and - # collect the top k scoring sequences. - finished_state = self._get_new_finished_state(state, new_seq, new_log_probs) - - # Increment loop index and create new state dictionary - new_state = {_StateKeys.CUR_INDEX: state[_StateKeys.CUR_INDEX] + 1} - new_state.update(alive_state) - new_state.update(finished_state) - return [new_state] - - def _grow_alive_seq(self, state): - """Grow alive sequences by one token, and collect top 2*beam_size sequences. - - 2*beam_size sequences are collected because some sequences may have reached - the EOS token. 2*beam_size ensures that at least beam_size sequences are - still alive. - - Args: - state: A dictionary with the current loop state. - Returns: - Tuple of - (Top 2*beam_size sequences [batch_size, 2 * beam_size, cur_index + 1], - Scores of returned sequences [batch_size, 2 * beam_size], - New alive cache, for each of the 2 * beam_size sequences) - """ - i = state[_StateKeys.CUR_INDEX] - alive_seq = state[_StateKeys.ALIVE_SEQ] - alive_log_probs = state[_StateKeys.ALIVE_LOG_PROBS] - alive_cache = state[_StateKeys.ALIVE_CACHE] - - beams_to_keep = 2 * self.beam_size - - # Get logits for the next candidate IDs for the alive sequences. Get the new - # cache values at the same time. - flat_ids = _flatten_beam_dim(alive_seq) # [batch_size * beam_size] - flat_cache = nest.map_structure(_flatten_beam_dim, alive_cache) - - flat_logits, flat_cache = self.symbols_to_logits_fn(flat_ids, i, flat_cache) - - # Unflatten logits to shape [batch_size, beam_size, vocab_size] - logits = _unflatten_beam_dim(flat_logits, self.batch_size, self.beam_size) - new_cache = nest.map_structure( - lambda t: _unflatten_beam_dim(t, self.batch_size, self.beam_size), - flat_cache) - - # Convert logits to normalized log probs - candidate_log_probs = _log_prob_from_logits(logits) - - # Calculate new log probabilities if each of the alive sequences were - # extended # by the the candidate IDs. - # Shape [batch_size, beam_size, vocab_size] - log_probs = candidate_log_probs + tf.expand_dims(alive_log_probs, axis=2) - - # Each batch item has beam_size * vocab_size candidate sequences. For each - # batch item, get the k candidates with the highest log probabilities. - flat_log_probs = tf.reshape(log_probs, - [-1, self.beam_size * self.vocab_size]) - topk_log_probs, topk_indices = tf.nn.top_k(flat_log_probs, k=beams_to_keep) - - # Extract the alive sequences that generate the highest log probabilities - # after being extended. - topk_beam_indices = topk_indices // self.vocab_size - topk_seq, new_cache = _gather_beams( - [alive_seq, new_cache], topk_beam_indices, self.batch_size, - beams_to_keep) - - # Append the most probable IDs to the topk sequences - topk_ids = topk_indices % self.vocab_size - topk_ids = tf.expand_dims(topk_ids, axis=2) - topk_seq = tf.concat([topk_seq, topk_ids], axis=2) - return topk_seq, topk_log_probs, new_cache - - def _get_new_alive_state(self, new_seq, new_log_probs, new_cache): - """Gather the top k sequences that are still alive. - - Args: - new_seq: New sequences generated by growing the current alive sequences - int32 tensor with shape [batch_size, 2 * beam_size, cur_index + 1] - new_log_probs: Log probabilities of new sequences - float32 tensor with shape [batch_size, beam_size] - new_cache: Dict of cached values for each sequence. - - Returns: - Dictionary with alive keys from _StateKeys: - {Top beam_size sequences that are still alive (don't end with eos_id) - Log probabilities of top alive sequences - Dict cache storing decoder states for top alive sequences} - """ - # To prevent finished sequences from being considered, set log probs to -INF - new_finished_flags = tf.equal(new_seq[:, :, -1], self.eos_id) - new_log_probs += tf.to_float(new_finished_flags) * -INF - - top_alive_seq, top_alive_log_probs, top_alive_cache = _gather_topk_beams( - [new_seq, new_log_probs, new_cache], new_log_probs, self.batch_size, - self.beam_size) - - return { - _StateKeys.ALIVE_SEQ: top_alive_seq, - _StateKeys.ALIVE_LOG_PROBS: top_alive_log_probs, - _StateKeys.ALIVE_CACHE: top_alive_cache - } - - def _get_new_finished_state(self, state, new_seq, new_log_probs): - """Combine new and old finished sequences, and gather the top k sequences. - - Args: - state: A dictionary with the current loop state. - new_seq: New sequences generated by growing the current alive sequences - int32 tensor with shape [batch_size, beam_size, i + 1] - new_log_probs: Log probabilities of new sequences - float32 tensor with shape [batch_size, beam_size] - - Returns: - Dictionary with finished keys from _StateKeys: - {Top beam_size finished sequences based on score, - Scores of finished sequences, - Finished flags of finished sequences} - """ - i = state[_StateKeys.CUR_INDEX] - finished_seq = state[_StateKeys.FINISHED_SEQ] - finished_scores = state[_StateKeys.FINISHED_SCORES] - finished_flags = state[_StateKeys.FINISHED_FLAGS] - - # First append a column of 0-ids to finished_seq to increment the length. - # New shape of finished_seq: [batch_size, beam_size, i + 1] - finished_seq = tf.concat( - [finished_seq, - tf.zeros([self.batch_size, self.beam_size, 1], tf.int32)], axis=2) - - # Calculate new seq scores from log probabilities. - length_norm = _length_normalization(self.alpha, i + 1) - new_scores = new_log_probs / length_norm - - # Set the scores of the still-alive seq in new_seq to large negative values. - new_finished_flags = tf.equal(new_seq[:, :, -1], self.eos_id) - new_scores += (1. - tf.to_float(new_finished_flags)) * -INF - - # Combine sequences, scores, and flags. - finished_seq = tf.concat([finished_seq, new_seq], axis=1) - finished_scores = tf.concat([finished_scores, new_scores], axis=1) - finished_flags = tf.concat([finished_flags, new_finished_flags], axis=1) - - # Return the finished sequences with the best scores. - top_finished_seq, top_finished_scores, top_finished_flags = ( - _gather_topk_beams([finished_seq, finished_scores, finished_flags], - finished_scores, self.batch_size, self.beam_size)) - - return { - _StateKeys.FINISHED_SEQ: top_finished_seq, - _StateKeys.FINISHED_SCORES: top_finished_scores, - _StateKeys.FINISHED_FLAGS: top_finished_flags - } - - -def sequence_beam_search( - symbols_to_logits_fn, initial_ids, initial_cache, vocab_size, beam_size, - alpha, max_decode_length, eos_id): - """Search for sequence of subtoken ids with the largest probability. - - Args: - symbols_to_logits_fn: A function that takes in ids, index, and cache as - arguments. The passed in arguments will have shape: - ids -> [batch_size * beam_size, index] - index -> [] (scalar) - cache -> nested dictionary of tensors [batch_size * beam_size, ...] - The function must return logits and new cache. - logits -> [batch * beam_size, vocab_size] - new cache -> same shape/structure as inputted cache - initial_ids: Starting ids for each batch item. - int32 tensor with shape [batch_size] - initial_cache: dict containing starting decoder variables information - vocab_size: int size of tokens - beam_size: int number of beams - alpha: float defining the strength of length normalization - max_decode_length: maximum length to decoded sequence - eos_id: int id of eos token, used to determine when a sequence has finished - - Returns: - Top decoded sequences [batch_size, beam_size, max_decode_length] - sequence scores [batch_size, beam_size] - """ - batch_size = tf.shape(initial_ids)[0] - sbs = SequenceBeamSearch(symbols_to_logits_fn, vocab_size, batch_size, - beam_size, alpha, max_decode_length, eos_id) - return sbs.search(initial_ids, initial_cache) - - -def _log_prob_from_logits(logits): - return logits - tf.reduce_logsumexp(logits, axis=2, keep_dims=True) - - -def _length_normalization(alpha, length): - """Return length normalization factor.""" - return tf.pow(((5. + tf.to_float(length)) / 6.), alpha) - - -def _expand_to_beam_size(tensor, beam_size): - """Tiles a given tensor by beam_size. - - Args: - tensor: tensor to tile [batch_size, ...] - beam_size: How much to tile the tensor by. - - Returns: - Tiled tensor [batch_size, beam_size, ...] - """ - tensor = tf.expand_dims(tensor, axis=1) - tile_dims = [1] * tensor.shape.ndims - tile_dims[1] = beam_size - - return tf.tile(tensor, tile_dims) - - -def _shape_list(tensor): - """Return a list of the tensor's shape, and ensure no None values in list.""" - # Get statically known shape (may contain None's for unknown dimensions) - shape = tensor.get_shape().as_list() - - # Ensure that the shape values are not None - dynamic_shape = tf.shape(tensor) - for i in range(len(shape)): # pylint: disable=consider-using-enumerate - if shape[i] is None: - shape[i] = dynamic_shape[i] - return shape - - -def _get_shape_keep_last_dim(tensor): - shape_list = _shape_list(tensor) - - # Only the last - for i in range(len(shape_list) - 1): - shape_list[i] = None - - if isinstance(shape_list[-1], tf.Tensor): - shape_list[-1] = None - return tf.TensorShape(shape_list) - - -def _flatten_beam_dim(tensor): - """Reshapes first two dimensions in to single dimension. - - Args: - tensor: Tensor to reshape of shape [A, B, ...] - - Returns: - Reshaped tensor of shape [A*B, ...] - """ - shape = _shape_list(tensor) - shape[0] *= shape[1] - shape.pop(1) # Remove beam dim - return tf.reshape(tensor, shape) - - -def _unflatten_beam_dim(tensor, batch_size, beam_size): - """Reshapes first dimension back to [batch_size, beam_size]. - - Args: - tensor: Tensor to reshape of shape [batch_size*beam_size, ...] - batch_size: Tensor, original batch size. - beam_size: int, original beam size. - - Returns: - Reshaped tensor of shape [batch_size, beam_size, ...] - """ - shape = _shape_list(tensor) - new_shape = [batch_size, beam_size] + shape[1:] - return tf.reshape(tensor, new_shape) - - -def _gather_beams(nested, beam_indices, batch_size, new_beam_size): - """Gather beams from nested structure of tensors. - - Each tensor in nested represents a batch of beams, where beam refers to a - single search state (beam search involves searching through multiple states - in parallel). - - This function is used to gather the top beams, specified by - beam_indices, from the nested tensors. - - Args: - nested: Nested structure (tensor, list, tuple or dict) containing tensors - with shape [batch_size, beam_size, ...]. - beam_indices: int32 tensor with shape [batch_size, new_beam_size]. Each - value in beam_indices must be between [0, beam_size), and are not - necessarily unique. - batch_size: int size of batch - new_beam_size: int number of beams to be pulled from the nested tensors. - - Returns: - Nested structure containing tensors with shape - [batch_size, new_beam_size, ...] - """ - # Computes the i'th coodinate that contains the batch index for gather_nd. - # Batch pos is a tensor like [[0,0,0,0,],[1,1,1,1],..]. - batch_pos = tf.range(batch_size * new_beam_size) // new_beam_size - batch_pos = tf.reshape(batch_pos, [batch_size, new_beam_size]) - - # Create coordinates to be passed to tf.gather_nd. Stacking creates a tensor - # with shape [batch_size, beam_size, 2], where the last dimension contains - # the (i, j) gathering coordinates. - coordinates = tf.stack([batch_pos, beam_indices], axis=2) - - return nest.map_structure( - lambda state: tf.gather_nd(state, coordinates), nested) - - -def _gather_topk_beams(nested, score_or_log_prob, batch_size, beam_size): - """Gather top beams from nested structure.""" - _, topk_indexes = tf.nn.top_k(score_or_log_prob, k=beam_size) - return _gather_beams(nested, topk_indexes, batch_size, beam_size) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/beam_search_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/beam_search_test.py deleted file mode 100644 index 17cbfa1f..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/beam_search_test.py +++ /dev/null @@ -1,101 +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. -# ============================================================================== -"""Test beam search helper methods.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.transformer.model import beam_search - - -class BeamSearchHelperTests(tf.test.TestCase): - - def test_expand_to_beam_size(self): - x = tf.ones([7, 4, 2, 5]) - x = beam_search._expand_to_beam_size(x, 3) - with self.test_session() as sess: - shape = sess.run(tf.shape(x)) - self.assertAllEqual([7, 3, 4, 2, 5], shape) - - def test_shape_list(self): - y = tf.placeholder(dtype=tf.int32, shape=[]) - x = tf.ones([7, y, 2, 5]) - shape = beam_search._shape_list(x) - self.assertIsInstance(shape[0], int) - self.assertIsInstance(shape[1], tf.Tensor) - self.assertIsInstance(shape[2], int) - self.assertIsInstance(shape[3], int) - - def test_get_shape_keep_last_dim(self): - y = tf.constant(4.0) - x = tf.ones([7, tf.to_int32(tf.sqrt(y)), 2, 5]) - shape = beam_search._get_shape_keep_last_dim(x) - self.assertAllEqual([None, None, None, 5], - shape.as_list()) - - def test_flatten_beam_dim(self): - x = tf.ones([7, 4, 2, 5]) - x = beam_search._flatten_beam_dim(x) - with self.test_session() as sess: - shape = sess.run(tf.shape(x)) - self.assertAllEqual([28, 2, 5], shape) - - def test_unflatten_beam_dim(self): - x = tf.ones([28, 2, 5]) - x = beam_search._unflatten_beam_dim(x, 7, 4) - with self.test_session() as sess: - shape = sess.run(tf.shape(x)) - self.assertAllEqual([7, 4, 2, 5], shape) - - def test_gather_beams(self): - x = tf.reshape(tf.range(24), [2, 3, 4]) - # x looks like: [[[ 0 1 2 3] - # [ 4 5 6 7] - # [ 8 9 10 11]] - # - # [[12 13 14 15] - # [16 17 18 19] - # [20 21 22 23]]] - - y = beam_search._gather_beams(x, [[1, 2], [0, 2]], 2, 2) - with self.test_session() as sess: - y = sess.run(y) - - self.assertAllEqual([[[4, 5, 6, 7], - [8, 9, 10, 11]], - [[12, 13, 14, 15], - [20, 21, 22, 23]]], - y) - - def test_gather_topk_beams(self): - x = tf.reshape(tf.range(24), [2, 3, 4]) - x_scores = [[0, 1, 1], [1, 0, 1]] - - y = beam_search._gather_topk_beams(x, x_scores, 2, 2) - with self.test_session() as sess: - y = sess.run(y) - - self.assertAllEqual([[[4, 5, 6, 7], - [8, 9, 10, 11]], - [[12, 13, 14, 15], - [20, 21, 22, 23]]], - y) - - -if __name__ == "__main__": - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/embedding_layer.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/embedding_layer.py deleted file mode 100644 index cc189626..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/embedding_layer.py +++ /dev/null @@ -1,109 +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 of embedding layer with shared weights.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.transformer.model import model_utils -from official.utils.accelerator import tpu as tpu_utils - - -class EmbeddingSharedWeights(tf.layers.Layer): - """Calculates input embeddings and pre-softmax linear with shared weights.""" - - def __init__(self, vocab_size, hidden_size, method="gather"): - """Specify characteristic parameters of embedding layer. - - Args: - vocab_size: Number of tokens in the embedding. (Typically ~32,000) - hidden_size: Dimensionality of the embedding. (Typically 512 or 1024) - method: Strategy for performing embedding lookup. "gather" uses tf.gather - which performs well on CPUs and GPUs, but very poorly on TPUs. "matmul" - one-hot encodes the indicies and formulates the embedding as a sparse - matrix multiplication. The matmul formulation is wasteful as it does - extra work, however matrix multiplication is very fast on TPUs which - makes "matmul" considerably faster than "gather" on TPUs. - """ - super(EmbeddingSharedWeights, self).__init__() - self.vocab_size = vocab_size - self.hidden_size = hidden_size - if method not in ("gather", "matmul"): - raise ValueError("method {} must be 'gather' or 'matmul'".format(method)) - self.method = method - - def build(self, _): - with tf.variable_scope("embedding_and_softmax", reuse=tf.AUTO_REUSE): - # Create and initialize weights. The random normal initializer was chosen - # randomly, and works well. - self.shared_weights = tf.get_variable( - "weights", [self.vocab_size, self.hidden_size], - initializer=tf.random_normal_initializer( - 0., self.hidden_size ** -0.5)) - - self.built = True - - def call(self, x): - """Get token embeddings of x. - - Args: - x: An int64 tensor with shape [batch_size, length] - Returns: - embeddings: float32 tensor with shape [batch_size, length, embedding_size] - padding: float32 tensor with shape [batch_size, length] indicating the - locations of the padding tokens in x. - """ - with tf.name_scope("embedding"): - # Create binary mask of size [batch_size, length] - mask = tf.to_float(tf.not_equal(x, 0)) - - if self.method == "gather": - embeddings = tf.gather(self.shared_weights, x) - embeddings *= tf.expand_dims(mask, -1) - else: # matmul - embeddings = tpu_utils.embedding_matmul( - embedding_table=self.shared_weights, - values=tf.cast(x, dtype=tf.int32), - mask=mask - ) - # embedding_matmul already zeros out masked positions, so - # `embeddings *= tf.expand_dims(mask, -1)` is unnecessary. - - - # Scale embedding by the sqrt of the hidden size - embeddings *= self.hidden_size ** 0.5 - - return embeddings - - - def linear(self, x): - """Computes logits by running x through a linear layer. - - Args: - x: A float32 tensor with shape [batch_size, length, hidden_size] - Returns: - float32 tensor with shape [batch_size, length, vocab_size]. - """ - with tf.name_scope("presoftmax_linear"): - batch_size = tf.shape(x)[0] - length = tf.shape(x)[1] - - x = tf.reshape(x, [-1, self.hidden_size]) - logits = tf.matmul(x, self.shared_weights, transpose_b=True) - - return tf.reshape(logits, [batch_size, length, self.vocab_size]) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/ffn_layer.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/ffn_layer.py deleted file mode 100644 index 5a7274f7..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/ffn_layer.py +++ /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. -# ============================================================================== -"""Implementation of fully connected network.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import tensorflow as tf - - -class FeedFowardNetwork(tf.layers.Layer): - """Fully connected feedforward network.""" - - def __init__(self, hidden_size, filter_size, relu_dropout, train, allow_pad): - super(FeedFowardNetwork, self).__init__() - self.hidden_size = hidden_size - self.filter_size = filter_size - self.relu_dropout = relu_dropout - self.train = train - self.allow_pad = allow_pad - - self.filter_dense_layer = tf.layers.Dense( - filter_size, use_bias=True, activation=tf.nn.relu, name="filter_layer") - self.output_dense_layer = tf.layers.Dense( - hidden_size, use_bias=True, name="output_layer") - - def call(self, x, padding=None): - """Return outputs of the feedforward network. - - Args: - x: tensor with shape [batch_size, length, hidden_size] - padding: (optional) If set, the padding values are temporarily removed - from x (provided self.allow_pad is set). The padding values are placed - back in the output tensor in the same locations. - shape [batch_size, length] - - Returns: - Output of the feedforward network. - tensor with shape [batch_size, length, hidden_size] - """ - padding = None if not self.allow_pad else padding - - # Retrieve dynamically known shapes - batch_size = tf.shape(x)[0] - length = tf.shape(x)[1] - - if padding is not None: - with tf.name_scope("remove_padding"): - # Flatten padding to [batch_size*length] - pad_mask = tf.reshape(padding, [-1]) - - nonpad_ids = tf.to_int32(tf.where(pad_mask < 1e-9)) - - # Reshape x to [batch_size*length, hidden_size] to remove padding - x = tf.reshape(x, [-1, self.hidden_size]) - x = tf.gather_nd(x, indices=nonpad_ids) - - # Reshape x from 2 dimensions to 3 dimensions. - x.set_shape([None, self.hidden_size]) - x = tf.expand_dims(x, axis=0) - - output = self.filter_dense_layer(x) - if self.train: - output = tf.nn.dropout(output, 1.0 - self.relu_dropout) - output = self.output_dense_layer(output) - - if padding is not None: - with tf.name_scope("re_add_padding"): - output = tf.squeeze(output, axis=0) - output = tf.scatter_nd( - indices=nonpad_ids, - updates=output, - shape=[batch_size * length, self.hidden_size] - ) - output = tf.reshape(output, [batch_size, length, self.hidden_size]) - return output diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/model_params.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/model_params.py deleted file mode 100644 index e978abea..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/model_params.py +++ /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. -# ============================================================================== -"""Defines Transformer model parameters.""" - -from collections import defaultdict - - -BASE_PARAMS = defaultdict( - lambda: None, # Set default value to None. - - # Input params - default_batch_size=2048, # Maximum number of tokens per batch of examples. - default_batch_size_tpu=32768, - max_length=256, # Maximum number of tokens per example. - - # Model params - initializer_gain=1.0, # Used in trainable variable initialization. - vocab_size=33708, # Number of tokens defined in the vocabulary file. - hidden_size=512, # Model dimension in the hidden layers. - num_hidden_layers=6, # Number of layers in the encoder and decoder stacks. - num_heads=8, # Number of heads to use in multi-headed attention. - filter_size=2048, # Inner layer dimension in the feedforward network. - - # Dropout values (only used when training) - layer_postprocess_dropout=0.1, - attention_dropout=0.1, - relu_dropout=0.1, - - # Training params - label_smoothing=0.1, - learning_rate=2.0, - learning_rate_decay_rate=1.0, - learning_rate_warmup_steps=16000, - - # Optimizer params - optimizer_adam_beta1=0.9, - optimizer_adam_beta2=0.997, - optimizer_adam_epsilon=1e-09, - - # Default prediction params - extra_decode_length=50, - beam_size=4, - alpha=0.6, # used to calculate length normalization in beam search - - # TPU specific parameters - use_tpu=False, - static_batch=False, - allow_ffn_pad=True, -) - -BIG_PARAMS = BASE_PARAMS.copy() -BIG_PARAMS.update( - default_batch_size=4096, - - # default batch size is smaller than for BASE_PARAMS due to memory limits. - default_batch_size_tpu=16384, - - hidden_size=1024, - filter_size=4096, - num_heads=16, -) - -# Parameters for running the model in multi gpu. These should not change the -# params that modify the model shape (such as the hidden_size or num_heads). -BASE_MULTI_GPU_PARAMS = BASE_PARAMS.copy() -BASE_MULTI_GPU_PARAMS.update( - learning_rate_warmup_steps=8000 -) - -BIG_MULTI_GPU_PARAMS = BIG_PARAMS.copy() -BIG_MULTI_GPU_PARAMS.update( - layer_postprocess_dropout=0.3, - learning_rate_warmup_steps=8000 -) - -# Parameters for testing the model -TINY_PARAMS = BASE_PARAMS.copy() -TINY_PARAMS.update( - default_batch_size=1024, - default_batch_size_tpu=1024, - hidden_size=32, - num_heads=4, - filter_size=256, -) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/model_utils.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/model_utils.py deleted file mode 100644 index 0fda67cc..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/model_utils.py +++ /dev/null @@ -1,110 +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. -# ============================================================================== -"""Transformer model helper methods.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import math - -import tensorflow as tf - -_NEG_INF = -1e9 - - -def get_position_encoding( - length, hidden_size, min_timescale=1.0, max_timescale=1.0e4): - """Return positional encoding. - - Calculates the position encoding as a mix of sine and cosine functions with - geometrically increasing wavelengths. - Defined and formulized in Attention is All You Need, section 3.5. - - Args: - length: Sequence length. - hidden_size: Size of the - min_timescale: Minimum scale that will be applied at each position - max_timescale: Maximum scale that will be applied at each position - - Returns: - Tensor with shape [length, hidden_size] - """ - position = tf.to_float(tf.range(length)) - num_timescales = hidden_size // 2 - log_timescale_increment = ( - math.log(float(max_timescale) / float(min_timescale)) / - (tf.to_float(num_timescales) - 1)) - inv_timescales = min_timescale * tf.exp( - tf.to_float(tf.range(num_timescales)) * -log_timescale_increment) - scaled_time = tf.expand_dims(position, 1) * tf.expand_dims(inv_timescales, 0) - signal = tf.concat([tf.sin(scaled_time), tf.cos(scaled_time)], axis=1) - return signal - - -def get_decoder_self_attention_bias(length): - """Calculate bias for decoder that maintains model's autoregressive property. - - Creates a tensor that masks out locations that correspond to illegal - connections, so prediction at position i cannot draw information from future - positions. - - Args: - length: int length of sequences in batch. - - Returns: - float tensor of shape [1, 1, length, length] - """ - with tf.name_scope("decoder_self_attention_bias"): - valid_locs = tf.matrix_band_part(tf.ones([length, length]), -1, 0) - valid_locs = tf.reshape(valid_locs, [1, 1, length, length]) - decoder_bias = _NEG_INF * (1.0 - valid_locs) - return decoder_bias - - -def get_padding(x, padding_value=0): - """Return float tensor representing the padding values in x. - - Args: - x: int tensor with any shape - padding_value: int value that - - Returns: - flaot tensor with same shape as x containing values 0 or 1. - 0 -> non-padding, 1 -> padding - """ - with tf.name_scope("padding"): - return tf.to_float(tf.equal(x, padding_value)) - - -def get_padding_bias(x): - """Calculate bias tensor from padding values in tensor. - - Bias tensor that is added to the pre-softmax multi-headed attention logits, - which has shape [batch_size, num_heads, length, length]. The tensor is zero at - non-padding locations, and -1e9 (negative infinity) at padding locations. - - Args: - x: int tensor with shape [batch_size, length] - - Returns: - Attention bias tensor of shape [batch_size, 1, 1, length]. - """ - with tf.name_scope("attention_bias"): - padding = get_padding(x) - attention_bias = padding * _NEG_INF - attention_bias = tf.expand_dims( - tf.expand_dims(attention_bias, axis=1), axis=1) - return attention_bias diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/model_utils_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/model_utils_test.py deleted file mode 100644 index 418f5e5c..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/model_utils_test.py +++ /dev/null @@ -1,68 +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. -# ============================================================================== -"""Test Transformer model helper methods.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.transformer.model import model_utils - -NEG_INF = -1e9 - - -class ModelUtilsTest(tf.test.TestCase): - - def test_get_padding(self): - x = tf.constant([[1, 0, 0, 0, 2], [3, 4, 0, 0, 0], [0, 5, 6, 0, 7]]) - padding = model_utils.get_padding(x, padding_value=0) - with self.test_session() as sess: - padding = sess.run(padding) - - self.assertAllEqual([[0, 1, 1, 1, 0], [0, 0, 1, 1, 1], [1, 0, 0, 1, 0]], - padding) - - def test_get_padding_bias(self): - x = tf.constant([[1, 0, 0, 0, 2], [3, 4, 0, 0, 0], [0, 5, 6, 0, 7]]) - bias = model_utils.get_padding_bias(x) - bias_shape = tf.shape(bias) - flattened_bias = tf.reshape(bias, [3, 5]) - with self.test_session() as sess: - flattened_bias, bias_shape = sess.run((flattened_bias, bias_shape)) - - self.assertAllEqual([[0, NEG_INF, NEG_INF, NEG_INF, 0], - [0, 0, NEG_INF, NEG_INF, NEG_INF], - [NEG_INF, 0, 0, NEG_INF, 0]], - flattened_bias) - self.assertAllEqual([3, 1, 1, 5], bias_shape) - - def test_get_decoder_self_attention_bias(self): - length = 5 - bias = model_utils.get_decoder_self_attention_bias(length) - with self.test_session() as sess: - bias = sess.run(bias) - - self.assertAllEqual([[[[0, NEG_INF, NEG_INF, NEG_INF, NEG_INF], - [0, 0, NEG_INF, NEG_INF, NEG_INF], - [0, 0, 0, NEG_INF, NEG_INF], - [0, 0, 0, 0, NEG_INF], - [0, 0, 0, 0, 0]]]], - bias) - - -if __name__ == "__main__": - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/transformer.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/transformer.py deleted file mode 100644 index d45615b7..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/model/transformer.py +++ /dev/null @@ -1,417 +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. -# ============================================================================== -"""Defines the Transformer model, and its encoder and decoder stacks. - -Model paper: https://arxiv.org/pdf/1706.03762.pdf -Transformer model code source: https://github.com/tensorflow/tensor2tensor -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.transformer.model import attention_layer -from official.transformer.model import beam_search -from official.transformer.model import embedding_layer -from official.transformer.model import ffn_layer -from official.transformer.model import model_utils -from official.transformer.utils.tokenizer import EOS_ID - -_NEG_INF = -1e9 - - -class Transformer(object): - """Transformer model for sequence to sequence data. - - Implemented as described in: https://arxiv.org/pdf/1706.03762.pdf - - The Transformer model consists of an encoder and decoder. The input is an int - sequence (or a batch of sequences). The encoder produces a continous - representation, and the decoder uses the encoder output to generate - probabilities for the output sequence. - """ - - def __init__(self, params, train): - """Initialize layers to build Transformer model. - - Args: - params: hyperparameter object defining layer sizes, dropout values, etc. - train: boolean indicating whether the model is in training mode. Used to - determine if dropout layers should be added. - """ - self.train = train - self.params = params - - self.embedding_softmax_layer = embedding_layer.EmbeddingSharedWeights( - params["vocab_size"], params["hidden_size"], - method="matmul" if params["tpu"] else "gather") - self.encoder_stack = EncoderStack(params, train) - self.decoder_stack = DecoderStack(params, train) - - def __call__(self, inputs, targets=None): - """Calculate target logits or inferred target sequences. - - Args: - inputs: int tensor with shape [batch_size, input_length]. - targets: None or int tensor with shape [batch_size, target_length]. - - Returns: - If targets is defined, then return logits for each word in the target - sequence. float tensor with shape [batch_size, target_length, vocab_size] - If target is none, then generate output sequence one token at a time. - returns a dictionary { - output: [batch_size, decoded length] - score: [batch_size, float]} - """ - # Variance scaling is used here because it seems to work in many problems. - # Other reasonable initializers may also work just as well. - initializer = tf.variance_scaling_initializer( - self.params["initializer_gain"], mode="fan_avg", distribution="uniform") - with tf.variable_scope("Transformer", initializer=initializer): - # Calculate attention bias for encoder self-attention and decoder - # multi-headed attention layers. - attention_bias = model_utils.get_padding_bias(inputs) - - # Run the inputs through the encoder layer to map the symbol - # representations to continuous representations. - encoder_outputs = self.encode(inputs, attention_bias) - - # Generate output sequence if targets is None, or return logits if target - # sequence is known. - if targets is None: - return self.predict(encoder_outputs, attention_bias) - else: - logits = self.decode(targets, encoder_outputs, attention_bias) - return logits - - def encode(self, inputs, attention_bias): - """Generate continuous representation for inputs. - - Args: - inputs: int tensor with shape [batch_size, input_length]. - attention_bias: float tensor with shape [batch_size, 1, 1, input_length] - - Returns: - float tensor with shape [batch_size, input_length, hidden_size] - """ - with tf.name_scope("encode"): - # Prepare inputs to the layer stack by adding positional encodings and - # applying dropout. - embedded_inputs = self.embedding_softmax_layer(inputs) - inputs_padding = model_utils.get_padding(inputs) - - with tf.name_scope("add_pos_encoding"): - length = tf.shape(embedded_inputs)[1] - pos_encoding = model_utils.get_position_encoding( - length, self.params["hidden_size"]) - encoder_inputs = embedded_inputs + pos_encoding - - if self.train: - encoder_inputs = tf.nn.dropout( - encoder_inputs, 1 - self.params["layer_postprocess_dropout"]) - - return self.encoder_stack(encoder_inputs, attention_bias, inputs_padding) - - def decode(self, targets, encoder_outputs, attention_bias): - """Generate logits for each value in the target sequence. - - Args: - targets: target values for the output sequence. - int tensor with shape [batch_size, target_length] - encoder_outputs: continuous representation of input sequence. - float tensor with shape [batch_size, input_length, hidden_size] - attention_bias: float tensor with shape [batch_size, 1, 1, input_length] - - Returns: - float32 tensor with shape [batch_size, target_length, vocab_size] - """ - with tf.name_scope("decode"): - # Prepare inputs to decoder layers by shifting targets, adding positional - # encoding and applying dropout. - decoder_inputs = self.embedding_softmax_layer(targets) - with tf.name_scope("shift_targets"): - # Shift targets to the right, and remove the last element - decoder_inputs = tf.pad( - decoder_inputs, [[0, 0], [1, 0], [0, 0]])[:, :-1, :] - with tf.name_scope("add_pos_encoding"): - length = tf.shape(decoder_inputs)[1] - decoder_inputs += model_utils.get_position_encoding( - length, self.params["hidden_size"]) - if self.train: - decoder_inputs = tf.nn.dropout( - decoder_inputs, 1 - self.params["layer_postprocess_dropout"]) - - # Run values - decoder_self_attention_bias = model_utils.get_decoder_self_attention_bias( - length) - outputs = self.decoder_stack( - decoder_inputs, encoder_outputs, decoder_self_attention_bias, - attention_bias) - logits = self.embedding_softmax_layer.linear(outputs) - return logits - - def _get_symbols_to_logits_fn(self, max_decode_length): - """Returns a decoding function that calculates logits of the next tokens.""" - - timing_signal = model_utils.get_position_encoding( - max_decode_length + 1, self.params["hidden_size"]) - decoder_self_attention_bias = model_utils.get_decoder_self_attention_bias( - max_decode_length) - - def symbols_to_logits_fn(ids, i, cache): - """Generate logits for next potential IDs. - - Args: - ids: Current decoded sequences. - int tensor with shape [batch_size * beam_size, i + 1] - i: Loop index - cache: dictionary of values storing the encoder output, encoder-decoder - attention bias, and previous decoder attention values. - - Returns: - Tuple of - (logits with shape [batch_size * beam_size, vocab_size], - updated cache values) - """ - # Set decoder input to the last generated IDs - decoder_input = ids[:, -1:] - - # Preprocess decoder input by getting embeddings and adding timing signal. - decoder_input = self.embedding_softmax_layer(decoder_input) - decoder_input += timing_signal[i:i + 1] - - self_attention_bias = decoder_self_attention_bias[:, :, i:i + 1, :i + 1] - decoder_outputs = self.decoder_stack( - decoder_input, cache.get("encoder_outputs"), self_attention_bias, - cache.get("encoder_decoder_attention_bias"), cache) - logits = self.embedding_softmax_layer.linear(decoder_outputs) - logits = tf.squeeze(logits, axis=[1]) - return logits, cache - return symbols_to_logits_fn - - def predict(self, encoder_outputs, encoder_decoder_attention_bias): - """Return predicted sequence.""" - batch_size = tf.shape(encoder_outputs)[0] - input_length = tf.shape(encoder_outputs)[1] - max_decode_length = input_length + self.params["extra_decode_length"] - - symbols_to_logits_fn = self._get_symbols_to_logits_fn(max_decode_length) - - # Create initial set of IDs that will be passed into symbols_to_logits_fn. - initial_ids = tf.zeros([batch_size], dtype=tf.int32) - - # Create cache storing decoder attention values for each layer. - cache = { - "layer_%d" % layer: { - "k": tf.zeros([batch_size, 0, self.params["hidden_size"]]), - "v": tf.zeros([batch_size, 0, self.params["hidden_size"]]), - } for layer in range(self.params["num_hidden_layers"])} - - # Add encoder output and attention bias to the cache. - cache["encoder_outputs"] = encoder_outputs - cache["encoder_decoder_attention_bias"] = encoder_decoder_attention_bias - - # Use beam search to find the top beam_size sequences and scores. - decoded_ids, scores = beam_search.sequence_beam_search( - symbols_to_logits_fn=symbols_to_logits_fn, - initial_ids=initial_ids, - initial_cache=cache, - vocab_size=self.params["vocab_size"], - beam_size=self.params["beam_size"], - alpha=self.params["alpha"], - max_decode_length=max_decode_length, - eos_id=EOS_ID) - - # Get the top sequence for each batch element - top_decoded_ids = decoded_ids[:, 0, 1:] - top_scores = scores[:, 0] - - return {"outputs": top_decoded_ids, "scores": top_scores} - - -class LayerNormalization(tf.layers.Layer): - """Applies layer normalization.""" - - def __init__(self, hidden_size): - super(LayerNormalization, self).__init__() - self.hidden_size = hidden_size - - def build(self, _): - self.scale = tf.get_variable("layer_norm_scale", [self.hidden_size], - initializer=tf.ones_initializer()) - self.bias = tf.get_variable("layer_norm_bias", [self.hidden_size], - initializer=tf.zeros_initializer()) - self.built = True - - def call(self, x, epsilon=1e-6): - mean = tf.reduce_mean(x, axis=[-1], keepdims=True) - variance = tf.reduce_mean(tf.square(x - mean), axis=[-1], keepdims=True) - norm_x = (x - mean) * tf.rsqrt(variance + epsilon) - return norm_x * self.scale + self.bias - - -class PrePostProcessingWrapper(object): - """Wrapper class that applies layer pre-processing and post-processing.""" - - def __init__(self, layer, params, train): - self.layer = layer - self.postprocess_dropout = params["layer_postprocess_dropout"] - self.train = train - - # Create normalization layer - self.layer_norm = LayerNormalization(params["hidden_size"]) - - def __call__(self, x, *args, **kwargs): - # Preprocessing: apply layer normalization - y = self.layer_norm(x) - - # Get layer output - y = self.layer(y, *args, **kwargs) - - # Postprocessing: apply dropout and residual connection - if self.train: - y = tf.nn.dropout(y, 1 - self.postprocess_dropout) - return x + y - - -class EncoderStack(tf.layers.Layer): - """Transformer encoder stack. - - The encoder stack is made up of N identical layers. Each layer is composed - of the sublayers: - 1. Self-attention layer - 2. Feedforward network (which is 2 fully-connected layers) - """ - - def __init__(self, params, train): - super(EncoderStack, self).__init__() - self.layers = [] - for _ in range(params["num_hidden_layers"]): - # Create sublayers for each layer. - self_attention_layer = attention_layer.SelfAttention( - params["hidden_size"], params["num_heads"], - params["attention_dropout"], train) - feed_forward_network = ffn_layer.FeedFowardNetwork( - params["hidden_size"], params["filter_size"], - params["relu_dropout"], train, params["allow_ffn_pad"]) - - self.layers.append([ - PrePostProcessingWrapper(self_attention_layer, params, train), - PrePostProcessingWrapper(feed_forward_network, params, train)]) - - # Create final layer normalization layer. - self.output_normalization = LayerNormalization(params["hidden_size"]) - - def call(self, encoder_inputs, attention_bias, inputs_padding): - """Return the output of the encoder layer stacks. - - Args: - encoder_inputs: tensor with shape [batch_size, input_length, hidden_size] - attention_bias: bias for the encoder self-attention layer. - [batch_size, 1, 1, input_length] - inputs_padding: P - - Returns: - Output of encoder layer stack. - float32 tensor with shape [batch_size, input_length, hidden_size] - """ - for n, layer in enumerate(self.layers): - # Run inputs through the sublayers. - self_attention_layer = layer[0] - feed_forward_network = layer[1] - - with tf.variable_scope("layer_%d" % n): - with tf.variable_scope("self_attention"): - encoder_inputs = self_attention_layer(encoder_inputs, attention_bias) - with tf.variable_scope("ffn"): - encoder_inputs = feed_forward_network(encoder_inputs, inputs_padding) - - return self.output_normalization(encoder_inputs) - - -class DecoderStack(tf.layers.Layer): - """Transformer decoder stack. - - Like the encoder stack, the decoder stack is made up of N identical layers. - Each layer is composed of the sublayers: - 1. Self-attention layer - 2. Multi-headed attention layer combining encoder outputs with results from - the previous self-attention layer. - 3. Feedforward network (2 fully-connected layers) - """ - - def __init__(self, params, train): - super(DecoderStack, self).__init__() - self.layers = [] - for _ in range(params["num_hidden_layers"]): - self_attention_layer = attention_layer.SelfAttention( - params["hidden_size"], params["num_heads"], - params["attention_dropout"], train) - enc_dec_attention_layer = attention_layer.Attention( - params["hidden_size"], params["num_heads"], - params["attention_dropout"], train) - feed_forward_network = ffn_layer.FeedFowardNetwork( - params["hidden_size"], params["filter_size"], - params["relu_dropout"], train, params["allow_ffn_pad"]) - - self.layers.append([ - PrePostProcessingWrapper(self_attention_layer, params, train), - PrePostProcessingWrapper(enc_dec_attention_layer, params, train), - PrePostProcessingWrapper(feed_forward_network, params, train)]) - - self.output_normalization = LayerNormalization(params["hidden_size"]) - - def call(self, decoder_inputs, encoder_outputs, decoder_self_attention_bias, - attention_bias, cache=None): - """Return the output of the decoder layer stacks. - - Args: - decoder_inputs: tensor with shape [batch_size, target_length, hidden_size] - encoder_outputs: tensor with shape [batch_size, input_length, hidden_size] - decoder_self_attention_bias: bias for decoder self-attention layer. - [1, 1, target_len, target_length] - attention_bias: bias for encoder-decoder attention layer. - [batch_size, 1, 1, input_length] - cache: (Used for fast decoding) A nested dictionary storing previous - decoder self-attention values. The items are: - {layer_n: {"k": tensor with shape [batch_size, i, key_channels], - "v": tensor with shape [batch_size, i, value_channels]}, - ...} - - Returns: - Output of decoder layer stack. - float32 tensor with shape [batch_size, target_length, hidden_size] - """ - for n, layer in enumerate(self.layers): - self_attention_layer = layer[0] - enc_dec_attention_layer = layer[1] - feed_forward_network = layer[2] - - # Run inputs through the sublayers. - layer_name = "layer_%d" % n - layer_cache = cache[layer_name] if cache is not None else None - with tf.variable_scope(layer_name): - with tf.variable_scope("self_attention"): - decoder_inputs = self_attention_layer( - decoder_inputs, decoder_self_attention_bias, cache=layer_cache) - with tf.variable_scope("encdec_attention"): - decoder_inputs = enc_dec_attention_layer( - decoder_inputs, encoder_outputs, attention_bias) - with tf.variable_scope("ffn"): - decoder_inputs = feed_forward_network(decoder_inputs) - - return self.output_normalization(decoder_inputs) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/test_data/newstest2014.de b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/test_data/newstest2014.de deleted file mode 100644 index ca2e2fb6..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/test_data/newstest2014.de +++ /dev/null @@ -1,3003 +0,0 @@ -Gutach: Noch mehr Sicherheit für Fußgänger -Sie stehen keine 100 Meter voneinander entfernt: Am Dienstag ist in Gutach die neue B 33-Fußgängerampel am Dorfparkplatz in Betrieb genommen worden - in Sichtweite der älteren Rathausampel. -Zwei Anlagen so nah beieinander: Absicht oder Schildbürgerstreich? -Diese Frage hat Gutachs Bürgermeister gestern klar beantwortet. -"Die Rathausampel ist damals installiert worden, weil diese den Schulweg sichert", erläuterte Eckert gestern. -Die Kluser-Ampel sichere sowohl Radfahrer als auch Busfahrgäste und die Bergle-Bewohner. -Die gestern offiziell in Betrieb genommene Anlage sei wichtig für den Kreuzungsbereich Sulzbachweg/Kirchstraße. -Wir haben das Museum, zwei Kirchen, Kurpark, die Bushaltestelle, einen Arzt und eine Bank sowie den Verkehrsfluss aus dem Wohngebiet ›Grub‹. -"Bei dem hohen Verkehrs- und Fußgängeraufkommen musste zu deren Sicherheit eine weitere Ampel her", so Eckert. -Dies bestätigt auch Peter Arnold vom Landratsamt Offenburg. -"Laut aktuellen Messungen durchfahren auf der B 33 täglich etwa 12 000 Fahrzeuge die Gemeinde Gutach, davon sind etwa zehn Prozent Schwerlastverkehr", betont Arnold. -Daher sei der Bau einer weiteren Ampel mehr als notwendig: "Sicherheit geht hier einfach vor", so Arnold. -Insgesamt seien vier Verkehrsschauen durchgeführt worden, auch ein Kreisverkehr wurde angedacht, allerdings wegen der Enge in dem Kreuzungsbereich Sulzbachweg/Kirchstraße wieder verworfen. -Es wurde laut Arnold bei der Standortwahl der Ampelmasten zuvor alles ausgetestet: "Mittels eines extra für uns mit besonders langen Holzstämmen beladener Transporter haben wir ausgestestet, ob diese Fahrzeuge aus dem Sulzbachweg auf die B 33 ausfahren können, ohne den Ampelmasten umzuknicken". -Die rund 15 000 Euro teure Ampelanlage selbst ist das "modernste, was es derzeit auf dem Markt gibt", erläuterte Arnold. -Die Anlage ist mit farbigen LEDs ausgestattet, die so kräftig leuchten, dass die Lichter von den Autofahrern beispielsweise auch bei tiefstehender Sonne gut zu erkennen sind. -Und sparsam ist sie auch: Die älteren Lichtanlagen verbrauchen etwa 100 Watt, die neuen gerade mal acht Watt. -Pro Fahrtrichtung gibt es drei Lichtanlagen. -Arnold erklärte die Technik der neuen Anlage: Diese ist mit zwei Radarsensoren ausgestattet. -Drückt der Fußgänger den Ampelknopf, testet der obere Radarsensor die Verkehrslage. -Ist die Straße frei, kommt unmittelbar Grün für den Fußgänger, wenn nicht, dauert es etwa 15 Sekunden. -Ein weiteres Radarsensor prüft, ob die Grünphase für den Fußgänger beendet werden kann. -"Sollte eine Gruppe oder gehbehinderte Menschen über die Straße gehen, wird die Grünphase verlängert, es kommt also jeder sicher über die Fahrbahn", erklärte Arnold. -Natürlich müsse der Autofahrer hier als Partner mitdenken und die Fahrbahn beobachten. -Dies war gestern nicht der Fall: Kaum zeigte die Ampel für Fußgänger grün, rauschte ein Oberklasse-Fahrzeug durch - bei leuchtendem Rot. -Josef Winkler schreibt sich seit mehr als 30 Jahren die Nöte seiner Kindheit und Jugend von der Seele. -Die Katastrophen seiner katholischen Dorfkindheit - die Sprachlosigkeit, der Hang zu roher Gewalt und stumpfer Sexualität, die Enge und Freudlosigkeit - hat der Kärntner Dichter vielfach beschrieben. -Bekannt ist der Büchner-Preisträger vor allem als Prosaautor, Theatertexte sind in seinem Werk rar. -Collage aus Prosatexten Gerhard Fresacher stellt für seine Aufführung "Wetterleuchten auf der Zungenspitze", die nun in der Garage X am Petersplatz zu sehen ist, daher eine Collage aus Prosatexten zusammen. -Der Theatermacher verbindet etwa Elemente aus dem autobiografisch geprägten Roman "Der Leibeigene" (1987) mit Prosaminiaturen aus "Leichnam, seine Familie belauernd" (2003). -Auf der weitgehend leergeräumten Bühne - wichtiges Requisit: ein zerknautschtes Sofa, auf dem andeutungsweise kopuliert und masturbiert wird - hangelt sich das achtköpfige Ensemble durch das Textmaterial. -Dabei scheint Regisseur Fresacher dem Text wenig zu vertrauen. -Die 70-minütige Performance übertüncht die Vorlage mit einer Fülle an Regieeinfällen, bekannt aus dem Repertoire postdramatischer Spielformen. -Vor allem die Schauspielerinnen kommen bei den mitunter etwas fragwürdigen szenischen Umsetzungen dran. -Sie werden hart angefasst, mit dem Kopf unter Wasser getaucht, mit ihren Abendroben an die Wand getackert. -Eingezwängt in Zellophan oder Mieder, staksen sie auf gefährlich hohen Stöckeln durch die Inszenierung, entweder monologisieren sie lautstark oder liegen völlig verstummt auf dem Bühnenboden. -Der Text vermittelt sich auf diese angestrengte Weise jedoch kaum. -Die besten Momente hat der Abend, wenn gesungen wird - die Bandbreite reicht von Deep Purple bis zu volkstümlichem Liedgut. -Erst gegen Ende kommt die überdrehte Aufführung etwas zur Ruhe, und Winklers nachgerade absurder Humor blitzt auf. -Eine Blackbox im Auto? -US-amerikanische Straßenplaner sind auf der Suche nach einer Geldquelle, um das verfallende Highway-System zu reparieren, und glauben die Lösung in einem kleinen schwarzen Kasten gefunden zu haben, der im Armaturenbrett jedes Autos Platz findet. -Die Geräte, die jeden gefahrenen Kilometer aufzeichnen und die Informationen an die Behörden melden, sind Kernpunkt eines kontroversen Versuchs von Washington und den Planungsbüros der Bundesstaaten, das veraltete System zur Finanzierung US-amerikanischer Straßen zu überarbeiten. -Das normalerweise eher langweilige Gebiet der Straßenplanung hat plötzlich eine intensive Debatte mit bunten Allianzen entfacht. -Libertäre haben sich mit Umweltgruppen verbündet und sprechen sich dafür aus, dass die Regierung die kleinen Boxen zum Aufzeichnen der gefahrenen Kilometer – und möglicherweise auch, wo sie gefahren wurden – verwenden und die Informationen dann für die Berechnung von Steuerbescheiden einsetzen kann. -Die Tea Party ist entsetzt. -Die amerikanische Bürgerrechtsvereinigung (ACLU) ist ebenfalls zutiefst besorgt und äußert eine Reihe von Datenschutzbedenken. -Doch während man sich im Kongress nicht auf ein Vorgehen einigen kann, warten mehrere Bundesstaaten nicht länger. -Sie prüfen derzeit, wie sie im Laufe der nächsten zehn Jahre zu einem System wechseln können, bei dem Fahrer pro gefahrener Meile bezahlen. -Tausende von Autofahrern haben die Fahrtenschreiber, von denen einige mit GPS-Überwachung ausgestattet sind, bereits getestet. -Das ist wirklich ein Muss für unser Land. -„Es ist nichts, das wir nur möglicherweise verwenden werden“, sagte Hasan Ikhrata, Geschäftsführer der Southern California Assn. of Governments, die eine Aufzeichnung der gefahrenen Meilen bei allen kalifornischen Autofahrern im Bundesstaat ab 2025 plant. -Die Art und Weise, wie wir diese Steuern zahlen, wird sich verändern. -Die Technologie dafür ist da. -Die Initiative kommt zu einem Zeitpunkt, da der Highway Trust Fund, der aus den Steuern finanziert wird, die US-Amerikaner an der Zapfsäule entrichten, pleite ist. -Doch in Amerika wird nicht mehr so viel getankt wie früher. -Autos verbrauchen weniger Benzin. -Die staatliche Mineralölsteuer von 18,4 Cent pro Gallone (weniger als 4 Eurocent pro Liter) ist seit 20 Jahren nicht gestiegen. -Politiker wagen bei hohen Spritpreisen nicht, die Steuer auch nur um einen Cent anzuheben. -„Die Benzinsteuer ist einfach nicht zukunftsfähig“, so Lee Munnich, ein Experte für Verkehrsgesetzgebung an der Universität von Minnesota. -Sein Bundesstaat hat kürzlich 500 Autos mit Fahrtenschreibern ausgerüstet, mit denen ein meilenbasiertes Bezahlsystem getestet werden soll. -„Das stellt die langfristig sinnvollste Alternative dar“, sagte er. -Bürokraten bezeichnen es als meilenbasierte Benutzergebühr. -Es überrascht nicht, dass die Idee bei städtischen Liberalen Anklang findet, denn die Steuer ließe sich beispielsweise dazu einsetzen, das Fahrverhalten so zu beeinflussen, dass Staus und klimaschädliche Abgase reduziert werden. -Die kalifornischen Planer setzen auf das System bei der Ausarbeitung von Strategien, mit denen die ambitionierten, gesetzlich verankerten Ziele des Bundesstaats zum Klimawandel erreicht werden sollen. -Doch der Republikaner Bill Shuster aus Pennsylvania, Vorsitzender des House Transportation Committee, hat ebenfalls erklärt, dass er darin die gangbarste langfristige Alternative sehe. -Auch die freien Vermarkter der Reason Foundation sind von der Idee angetan, Fahrer nach zurückgelegter Strecke zahlen zu lassen. -„Das ist keine Steuer, die in einem schwarzen Loch verschwindet“, erklärt Adrian Moore, Vizepräsident für Richtlinien bei Reason. -Die Leute bezahlen direkt für das, was sie bekommen. -Die Bewegung wird auch von zwei früheren amerikanischen Verkehrsministern unterstützt, die in einem Bericht im Jahr 2011 den Kongress aufgefordert hatten, sich in Richtung meilenbasierter Abrechnung zu bewegen. -Der US-Senat genehmigte letztes Jahr ein 90 Millionen Dollar teures Pilotprojekt, das 10.000 Autos umfasst hätte. -Doch die Mehrheit im Repräsentantenhaus verhinderte den Vorstoß und reagierte damit auf die Bedenken von Abgeordneten aus ländlichen Gebieten, die Menschen vertreten, die im Alltag oft viele Meilen auf dem Weg zur Arbeit oder in die Stadt zurücklegen müssen. -Mehrere Bundesstaaten und Großstädte bewegen sich nichtsdestotrotz auf eigene Faust in diese Richtung. -Am engagiertesten ist Oregon, das derzeit 5.000 Fahrer für das größte Experiment des Landes anwirbt. -Diese Fahrer werden bald die Meilengebühren statt der Mineralölsteuer an den Bundesstaat zahlen. -Nevada hat bereits ein Pilotprojekt abgeschlossen. -New York City erwägt ebenfalls ein solches. -Illinois testet es in eingeschränktem Maße mit Lkws. -Und die I-95-Koalition, zu der die Verkehrsministerien von 17 Bundesstaaten an der Ostküste gehören (einschließlich Maryland, Pennsylvania, Virginia und Florida), untersucht derzeit, wie man die Änderung einführen könnte. -Das Konzept ist kein universeller Hit. -In Nevada, wo vor kurzem 50 Freiwillige mit den Geräten ausgestattet wurden, waren Autofahrer skeptisch beim Gedanken, die Regierung könnte jede ihrer Bewegungen verfolgen. -„Bedenken gegen Big Brother und derartige Dinge waren ein großes Problem“, erklärt Alauddin Khan, Leiter des Strategie- und Ergebnismanagements im Verkehrsministerium von Nevada. -Die Leute wollten es nicht. -Als der Test anlief, warnte die ACLU von Nevada auf ihrer Website: „Es wäre relativ einfach, die Fahrtenschreiber in ausgewachsene Überwachungsgeräte zu verwandeln.“ -Es bestehe keine Notwendigkeit, eine gigantische, sperrige technologische Infrastruktur aufzubauen, die unweigerlich dazu verwendet werden würde, Daten über die täglichen Bewegungen von Einzelpersonen zu erfassen. -Nevada gehört zu einer Reihe von Bundesstaaten, die nun nach erschwinglicher Technologie Ausschau halten, mit der der Staat die gefahrenen Kilometer erfassen kann, aber nicht genau wann und wo. -Damit, so Khan, wäre auch die Öffentlichkeit beruhigter. -Die Jagd nach dieser Technologie hat einige Behörden zu einem kleinen Startup-Unternehmen namens True Mileage in Kalifornien geführt. -Die Firma ist ursprünglich nicht angetreten, um Bundesstaaten bei der Besteuerung von Autofahrern zu helfen. -Vielmehr war es ihr Ziel, in einem aufstrebenden Markt für Kfz-Versicherungen Fuß zu fassen, bei denen Fahrer auf Grundlage der gefahrenen Meilen zahlen sollen. -Doch die von ihr getesteten Geräte sind auch für die Straßenplaner interessant, denn sie arbeiten nicht mit GPS und liefern nur begrenzte Informationen, die regelmäßig per Modem hochgeladen werden. -„Die Leute sind eher bereit, sich daran zu beteiligen, wenn ihre Geschwindigkeit und Standorte nicht aufgezeichnet werden“, erklärte Ryan Morrison, Geschäftsführer von True Mileage. -In einigen dieser öffentlichen Pilotprogramme wurden große Fehler gemacht. -Es gibt wesentlich billigere und weniger intrusive Möglichkeiten, dies umzusetzen. -In Oregon experimentieren die Planer damit, Autofahrern eine Reihe von Auswahlmöglichkeiten zu geben. -Sie können sich für ein Gerät mit oder ohne GPS entscheiden. -Oder sie wählen überhaupt kein Gerät und zahlen stattdessen eine Pauschalgebühr auf Grundlage der durchschnittlich von allen Einwohnern des Bundesstaates gefahrenen Meilen. -Andere Stellen hoffen, das Konzept einer misstrauischen Öffentlichkeit verkaufen zu können, indem sie die Geräte mit mehr Funktionen ausstatten als mit wenigeren. -In New York City wollen Verkehrsbeamte ein Gerät zur Besteuerung entwickeln, mit dem sich auch Parkgebühren bezahlen lassen, eine Versicherung nur für gefahrene Kilometer bezahlt werden muss und Geschwindigkeitsdaten von anderen Fahrzeugen in Echtzeit erhoben werden, dank derer Autofahrer Staus ausweichen können. -„Autofahrer würden durch den Mehrwert der Vorteile, die das System bietet, zur Teilnahme motiviert“, heißt es in einem Planungsdokument der Stadt. -Einige Verkehrsplaner fragen sich allerdings, ob das ganze Gerede über das Bezahlen pro Meile nicht nur ein riesiges Ablenkungsmanöver sei. -Bei der Metropolitan Transportation Commission für das Gebiet der San Francisco Bay erklärten Beamte, der Kongress könne das Problem des bankrotten Highway Trust Fund einfach durch Erhöhung der Kraftstoffsteuer lösen. -Eine zusätzliche einmalige Gebühr oder eine jährliche Abgabe könnte Autofahrern mit Hybridfahrzeugen oder anderen Fahrzeugen, die wenig Benzin verbrauchen, auferlegt werden, sodass auch diese einen fairen Anteil zahlen. -„Es gibt keinen Grund für eine Operation, wenn ein Aspirin auch ausreicht“, erläutert Randy Rentschler, der Leiter für Gesetzgebung und öffentliche Angelegenheiten der Kommission. -Wenn wir das tun, machen sich Hunderte Millionen von Autofahrern sorgen über ihre Privatsphäre und zahlreiche andere Dinge. -Königsfeld: Kleine Mannschaft schlägt sich wacker -Die Freiwillige Feuerwehr bewältigte ihre Herbsthauptprobe trotz Personalmangels mit Bravour. -Nur elf Mann nahmen an der Übung teil. -Abteilungskommandant Hans Kammerer hatte sich in seiner Übungsannahme das Anwesen Feder in der Burgberger Straße ausgesucht. -Dort soll beim Sägen ein Kurzschluss entstanden sein. -Durch den entstehenden Brand und die Rauchentwicklung wurden zwei Menschen verletzt, einer davon konnte aber noch einen Notruf absenden. -Zwei Atemschutzträger der Wehr machten sich nach der ersten Überprüfung der Sachlage durch Hans Kammerer bereit, das Gebäude zu betreten. -Nach kurzer Zeit gelang es, die erste Person zu finden und ins Freie zu geleiten. -Die zweite Person musste getragen werden. -Dies war nicht so einfach, da auch eine enge Treppe zu überwinden war. -Das Gebäude, eine Werkstatt mit integrierter Stallung für zwei Pferde, war nicht einfach zu sichern. -Es lagerte viel Holz und auch Strohballen darin. -Weiter gab es noch elektrische Maschinen zur Holzbearbeitung. -Der erste Löschangriff erfolgte über den Tank im Fahrzeug. -Eine weitere Leitung erfolgte über einen Überflur- Hydranten in rund 100 Meter Entfernung. -Nun konnten drei Löschangriffe gestaltet werden. -Zum Einsatz kam auch im Gebäude ein Hohlstrahlrohr. -Hans Kammerer ging es bei der Übung auch darum aufzuzeigen, was mit wenig zur Verfügung stehendem Personal zu bewerkstelligen ist. -Im Ernstfall erfolgt eine Unterstützung über die Tageseinsatzgruppe Königsfeld. -Der Kommandant zeigte sich mit dem Ablauf der Übung zufrieden. -Vernetzung von Hochschulen und Firmen wichtig -Auf seinem Besuch im Landkreis Breisgau-Hochschwarzwald ließ sich Landtagspräsident Guido Wolf auch in Titisee-Neustadt darüber aufklären, woher die Arbeitskräfte von morgen kommen. -Im Förderzentrum Hochschwarzwald ging er zusammen mit den Geschäftsführenden Schulleitern der Beruflichen Schulen sowie des Kreisgymnasiums und den Elternbeiratsvorsitzenden dieser wichtigen Frage nach. -"Bildung ist ein wichtiger Standortfaktor", unterstrich Claudia Stehle, Direktorin der Hans-Thoma-Schule, die das vernetzte Schulprojekt Bildungszentrum Hochschwarzwald vorstellte. -Dabei zeigte sich Wolf beeindruckt über die Modellprojekte zur Ausbildung. -Seit 2011 existiert in der Wälderstadt eine erfolgreiche Zusammenarbeit zwischen Förderzentrum, Kreisgymnasium und Berufsschulzentrum. -Außerdem lobte er die Familienfreundlichkeit im Landkreis. -"Es ist wichtig, nicht nur die Familienphase im Blick zu haben, sondern aufgrund des demografischen Wandels auch die Phase um die Pflege der Betreuung von Angehörigen", erklärte Wolf. -Außerdem führte er an, dass sich immer mehr Beschäftigte um die Pflege und Betreuung ihrer Angehörigen kümmerten. -"Ohne die Unterstützung der Arbeitgeber kann die daraus resultierende Belastung der Beschäftigten zu einer Beendigung des Arbeitsverhältnisses führen", sagte Wolf. -Aber niemand könne es sich leisten, qualifizierte Arbeitskräfte zu verlieren, sagte er weiter. -Ein weiterer, besonders wichtiger Faktor sei die Vernetzung von Hochschulen und Unternehmen. -"Denn wer schon während seines Studiums die Vorzüge der Region kennengelernt hat, bleibt mit seinem erworbenen Wissen und seinen Fähigkeiten oft der regionalen Wirtschaft erhalten, was zur Stärkung der Region beiträgt", zeigte sich der Landtagspräsident überzeugt. -Denn nur wenn ausreichend Ausbildungsplätze angeboten würden, könne auch der Fachkräftebedarf gedeckt werden. -Seiner Meinung nach ist der Standortvorteil des Landkreises seine vorteilhafte Lage im Südwesten Deutschlands, in unmittelbarer Nähe zu Frankreich und zur Schweiz. -Außerdem erlebte er auf seiner Bahnfahrt von Titisee-Neustadt nach Freiburg die bereits unternommenen Anstrengungen für den Ausbau des öffentlichen Personennahverkehrs. -"Bleiben Sie weiterhin hartnäckig und sorgen Sie dafür, dass der öffentliche Nahverkehr besser wird", erklärte er an die Adresse der anwesenden Kommunalpolitiker. -Mit Musik einen unterhaltsamen Nachmittag erlebt -Bereits seit 15 Jahren lädt die Familie Kaul die Dietinger Senioren zu Kaffee, Kuchen und danach zu einem Vesper ein. -Früher wurden die älteren Mitbürger in der Raststätte bewirtet. -Mittlerweile findet dieser Nachmittag im Pflegeheim St. Josef statt. -Die Heimbewohner freuten sich auf die leckeren Kuchen und Torten. -Später gab es Wurstsalat. -Mit gängigen Melodien unterhielten Silvia Kimmich-Bantle und ihr Vater Karl Kimmich. -Die über 100 Jahre alte Natursteinmauer als vordere Abgrenzung des vor der Sanierung stehenden alten Schulhofes zur Kirchstraße soll erneut mit auf den Sanierungskostenplan der Stadt Bräunlingen gesetzt werden. -In den Tagen vor der Kilbig wurde bei den ersten Arbeiten der Sanierung des vorderen Schulhofes eine kleine Winkelmauer, zur Stabilisierung und statischen Entlastung der über einhundert Jahre alten Natursteinwand mit Balustrade eingebaut. -Das weitere Vorgehen sieht vor, so Alexander Misok vom Bräunlinger Stadtbauamt, das Landesdenkmalamt in die Planungen mit einzubeziehen und ein Sanierungskonzept für die alte Mauer zu erstellen. -Danach soll eine Kostenberechnung durch Fachleute vorgenommen werden, die dann dem Gemeinderat zur Entscheidung und Festlegung des weiteren Vorgehens auf den Tisch gelegt werden soll. -Eine Entscheidung darüber wird voraussichtlich bereits im kommenden Jahr fallen. -Nach der Sanierung, Pflasterung und Baumbepflanzung des alten Schulinnenhofes innerhalb der beiden Seitenflügel der 1912 erbauten Schule muss in absehbarer Zeit als Folgemaßnahme, die sanierungsbedürftige Begrenzungsmauer von Grund auf saniert und auf neuen Unterbau gestellt werden. -Die historische, einhundert Jahre alte Abgrenzungsmauer des alten Schulhofes zur Kirchstraße, ist vor allem durch Wasser- und Frostschäden, da keine Drainage vorhanden ist, sanierungsbedürftig und hat eine Bogenneigung nach vorne. -Tiefe Risse in einzelnen Steinen zeugen von Witterungsschäden, jedoch ist die Mauer derzeit nicht einsturzgefährdet. -Eine standsichere Mauer ist Voraussetzung für einen von Schülern benutzen Schulhof, was durch die aktuellen Befestigungsarbeiten erfolgt ist. -Ursprünglich war die Schulhofsanierung sogar schon in den Jahren 2008/2009 geplant, doch hohe unplanmäßige Ausgaben brachten eine Verschiebung. -Ein wichtiges Wort bei der historischen Schulmauersanierung, die sogar schon für Filmaufnahmen benutzt wurde, wird das Landesdenkmalamt mitsprechen. -Sollten Vorgaben von dieser Seite kommen, dann hofft die Stadt Bräunlingen auf Zuschüsse von den Denkmalschützern. -Die Schüler der Bräunlinger Grundschule, die inzwischen wieder in einem gut sanierten alten Schulgebäude mit neuer Aula unterrichtet werden, können bald den alten Schulhof benutzen. -Özdemir will Jazzausbildung in Stuttgart erhalten -In die anhaltenden Diskussionen um die Zukunft der Musikhochschulen in Baden-Württemberg hat sich nun auch Cem Özdemir, Bundesvorsitzender der Grünen, eingeschaltet. -"Ich halte es für falsch, die ganzheitliche Ausbildung in der Musikhochschule Stuttgart aufzugeben", sagte Özdemir den "Stuttgarter Nachrichten". -Jazz und Klassik gehören gerade am Jazzstandort Stuttgart zusammen. -Damit widerspricht Özdemir, der in Stuttgart als Favorit für ein Direktmandat bei der Bundestagswahl am 22. September gilt, der Grün-Roten Landesregierung. -Diese forciert auf der Basis einer Expertenstudie eine Neuordnung der fünf Musikhochschulen in Baden-Württemberg. -Danach soll unter anderem der Studiengang Jazz und Pop von der Musikhochschule Stuttgart an die Musikhochschule Mannheim verlagert werden. -US-Grenze: "Super-Tunnel" zum Drogenschmuggel entdeckt -An der Grenze zwischen Mexiko und den USA haben Ermittler einen "Super-Tunnel" zum Drogenschmuggel entdeckt und stillgelegt. -Wie die US-Zollbehörden mitteilten, war die mehr als 500 Meter lange Röhre zwischen Tijuana und San Diego mit Elektrizität, Bahngleisen und einer Belüftungsanlage ausgestattet. -Drei Verdächtige wurden demnach festgenommen, acht Tonnen Marihuana sowie fast 150 Kilogramm Kokain beschlagnahmt. -Der Tunnel war den Angaben zufolge noch nicht in Gebrauch genommen worden. -"Diese Kartelle sind dumm, wenn sie meinen, sie könnten sich unter dem Radar hinweg durchgraben", sagte die Generalstaatsanwältin des US-Distrikts Southern California, Laura Duffy, bei einer Pressekonferenz vor einem Lagerhaus in San Diego, wo das eine Ende des Tunnels entdeckt worden war. -An die Drogenbanden gewandt versicherte Duffy: "Wenn Ihr weiter solche Tunnel baut und versucht, sie zu nutzen, sind wir entschlossen, das zu einer riesigen Verschwendung Eures schmutzigen Geldes zu machen". -Sowohl die US-Behörden als auch die mexikanischen Sicherheitskräfte befinden sich in einem Dauer-Kampf gegen die Drogenkartelle. -Seit 2006 wurden in Mexiko mehr als 77. 000 Menschen im Zusammenhang mit der Drogenkriminalität getötet. -Von Experten konstruiert -Wegen der aufwendigen Ausstattung des Tunnels gehen die Ermittler davon aus, dass er von Architekten und Ingenieuren konstruiert wurde und dass der Bau rund ein Jahr in Anspruch nahm. -Die Gleisanlage war so ausgestattet, dass dort elektrisch betriebene Wagen eingesetzt werden konnten. -Auf mexikanischer Seite liegt der Zugang in einem Gebäude, das 80 Meter von der Grenze entfernt ist. -Laut Behörden führt eine Leiter 20 Meter in die Tiefe zum eigentlichen Tunneleingang. -Der Tunnel hat einen Querschnitt von 1,20 Meter Höhe und 90 Zentimeter Breite. -Er wäre damit auch geeignet gewesen, um die illegale Einwanderung Richtung USA zu fördern. -Zwei der Verdächtigen wurden in Zusammenhang mit dem Kokain-Fund festgenommen. -Der Dritte, ein Mexikaner, wurde wegen des beschlagnahmten Marihuanas gefasst. -Allen dreien droht als Höchststrafe lebenslanger Freiheitsentzug, wie Beamte sagten. -Seit 2006 seien acht derartige Tunnel entdeckt worden, hieß es auf der Pressekonferenz in San Diego weiter. -Es sei aber das erste Mal, dass in einem solchen Tunnelbau Kokain gefunden wurde. -Normalerweise wird Kokain in kleineren Mengen und nicht durch Tunnel geschmuggelt. -Dies zeige die "Verzweiflung" der Drogenbanden, deren traditionelle Routen inzwischen abgeschnitten seien, sagte Bill Sherman von der Drogenfahndung DEA in San Diego. -Sie würden alles tun, um in die USA zu gelangen. -Zu den Festgenommenen wurden keine Einzelheiten bekannt gegeben, zumindest einer sei Mexikaner, hieß es. -Sie müssen mit Haftstrafen bis zu zehn Jahren rechnen. -2014 wird Geisinger Festjahr -Das Jubiläum der Stadt Geisingen anlässlich der 1250. -Wiederkehr der ersten urkundlichen Erwähnung rückt immer näher. -Wenn in gut acht Wochen das Jahr zu Ende geht, steht das Jubiläumsjahr an. -Geisingen und Kirchen-Hausen wurden 794 erstmals urkundlich zusammen erwähnt. -In Kirchen wurde eine Urkunde gefertigt, in der beide Orte erwähnt sind. -Am 15. März soll es einen offiziellen Auftakt zum Jubiläumsjahr geben, und da Geisingen derzeit keine Halle hat, findet diese Auftaktveranstaltung in dem Ort statt, wo vor 1250 Jahren beide erstmals erwähnt wurde, in Kirchen-Hausen. -Im Rahmen des Jubiläums sind etliche Veranstaltungen sowohl in Geisingen, als auch in Kirchen-Hausen geplant. -Kirchen-Hausen wird im Juli an einem Festwochenende vom 18. bis 20. Juli die 1250. -Wiederkehr feiern. -Am Freitag mit einem Festakt, am Samstag und Sonntag mit einem Fest rund um die Kirchtalhalle. -An diesem Festwochenende ist außerdem noch das Kirchen-Hausener Kirchenfest. -Stadtmusikdirektor Rudolf Barth hat für die Stadt anlässlich des Jubiläums eine Auftragskomposition geschrieben, die an drei Terminen aufgeführt wird. -Am 21. Juni zusammen mit der Sonnwendfeier des Schwarzwaldvereins, am 12. Juli in der Arena mit Feuerwerk und am 13. Dezember als erstes Konzert der Stadtmusik in der neuen Festhalle. -Diese wird im Jubiläumsjahr fertig und eingeweiht, der offizielle Termin ist am letzten Septemberwochenende, am 27. und 28. September. -Anlässlich des Geisinger Stadtjubiläums wird auch noch eine neue Chronik herausgebracht. -"Es soll aber eine Chronik nicht nur von Geisingen selbst werden, sondern ein gedrucktes Werk über das Geisingen von heute, das eben aus Geisingen, Kirchen-Hausen, Leipferdingen, Aulfingen und Gutmadingen besteht", so Bürgermeister Walter Hengstler. -Und auch das Geisingen von heute feiert 2014 Geburtstag, 1974 wurde die heutige Raumschaft mit den Eingemeindungen vollzogen. -Die neue Chronik soll dann am 21. oder 22. November in der neuen Festhalle in Geisingen vorgestellt werden. -2014 ist aber auch sonst ein Jahr mit vielen Jubiläen. -Die Narrenzunft Strohglonki beginnt am 8. Februar mit einem Brauchtumsabend anlässlich ihres 60-jährigen Bestehens, die Harmonie Gutmadingen wird 90 Jahre und hat am 29. März das Festbankett und feiert dann vom 1. bis 4. Mai das Bezirksmusikfest. -Das Altenwerk Leipferdingen wird 40 Jahre alt, die Geisinger Schule ist seit 50 Jahren am neuen Standort und feiert dies am 10. Mai, der Musikverein Polyhymnia Leipferdingen wird 150 Jahre alt und feiert dies im Rahmen des Brunnenfestes vom 4. bis 7. Juli. -Die Stadt Geisingen wurde in einer Schenkungsurkunde, die im Besitz des Klosters St. Gallen ist, im Jahre 764 erstmals urkundlich erwähnt. -Die Urkunde wurde in Kirchen (Hausen) gefertigt, das damals Gerichtsplatz war. -Beide Gemeinden sind demnach in der gleichen Urkunde erwähnt. -Grund 2014 dieses 1250-jährige entsprechend zu feiern, zusätzlich liegt auch noch die Gemeindereform 40 Jahre zurück. -Rat nimmt Gleisanlagen ins Visier -Soll der Bahnübergang "Am Hirschen" aufwändig umgebaut werden, um die Verkehrssicherheit zu erhöhen? -Ja, haben die Gemeinderäte beschlossen. -Schiltach muss dafür 220 000 Euro in die Hand nehmen. -Die Deutsche Bahn will im kommenden Jahr die Kinzigtal-Bahnstrecke verbessern. -Besonders soll dabei die Sicherheit an Bahnübergängen erhöht werden - regelmäßig kommt es bundesweit zu Kollisionen von Zügen mit anderen Fahrzeuge. -Stimmen die Gemeinden den Umbaumaßnahmen zu, müssen sie sich mit einem Drittel an den Kosten beteiligen, den Rest teilen sich Bahn und Bund. -Schiltachs Stadtbaumeister Roland Grießhaber erläuterte dem Gemeinderat die Besonderheiten des Bahübergangs "Am Hirschen". -Dort plant die Stadt, in Höhe des dortigen Tunnelmundes westlich der Bahngleise eine Ausbuchtung zu bauen. -Dort könnten auch größere Fahrzeuge, zum Beispiel solche, die Langholz transportieren, bei Gegenverkehr stehen, ohne die Gleise blockieren zu müssen. -Grießhaber schlug daher vor, eine erforderliche Stützmauer so zu konstruieren, dass die Stadt die anschließende Straße zu gegebener Zeit verbreitern könne, um einen reibungslosen Verkehrsfluss zu gewährleisten. -Dies wurde im Gemeinderat kontrovers diskutiert, da einige Räte die Notwendigkeit einer Straßenverbreitung in Zweifel zogen: Thomas Kipp brachte mit seiner Äußerung die Stimmung der Kritiker auf den Punkt: "Müssen wir wegen so wenigen Fahrzeugen so viel Geld in die Hand nehmen?" -Bürgermeister Thomas Haas widersprach: Der Bahnübergang "Hirschen" werde regelmäßig für Langholztransporte genutzt. -Selbst Holz aus dem Bereich "Kuhbach" werde teilweise über diese Strecke transportiert, weil die bis zu 20 Meter langen Fahrzeuge die Strecke an der Häberlesbrücke nicht nutzen könnten, weil sie dort nicht in die Hauptstraße abbiegen könnten. -Darüber hinaus diene die Strecke als Umleitungfür diejenigen Fahrzeuge, die den Tunnel nicht benutzen dürfen, wenn die Hauptstraße gesperrt ist, argumentierte Haas. -Da abzusehen sei, dass Kirchberg- und Schlossbergtunnel umgebaut und saniert werden müssten, wie dies derzeit in Wolfach der Fall ist, könne dem Bahnübergang "Am "Hirschen" auch für einen längeren Zeitraum eine wichtige Verkehrsbedeutung zukommen. -Die Räte kamen überein, untersuchen zu lassen, welche Kosten die angedachte Verbreiterung der Straße verursachen würde. -Ungeachtet dessen wurde dem Umbau des Bahnübergangs mehrheitlich zugestimmt mit der Maßgabe, dass die erforderliche Stützmauer so gebaut wird, dass die Stadt die anschließende Straße Richtung "Bahnhofstraße" gegebenenfalls für Begegnungsverkehr auf rund 5,5 Meter ausbauen könne. -Der Umbau des Bahnübergangs bei der Gerberei Trautwein könnte dagegen ein ganz anderes Problem nach sich ziehen, das die Zufahrt in den stark ansteigenden "Geroltzhäuser Weg" betrifft. -Die Verwaltung hatte die Umbaupläne der Bahn vom Ingenieurbüro Breinlinger untersuchen lassen, da sie Zweifel daran hatte, ob der geplante Anschluss des Geroltzhäuser Wegs dann noch ohne weiteres möglich sei. -Die Untersuchung kam zu dem Ergebnis, dass nicht auszuschließen sei, dass die Neigungsverhältnisse der Straße dazu führen könnten, dass längere Fahrzeuge aufsitzen. -Die Verantwortung hierfür trage wiederum die Stadt als Straßenbaulastträgerin. -Der Umbau könne demnach Haftungsstreitigkeiten nach sich ziehen. -Da sowohl die Holzabfuhr als auch das Abbiegen im aktuellen Ausbauzustand funktionierten, solle man dem Umbau des Bahnübergangs nicht zustimmen, argumentierte Haas. -Auch sei an dieser Stelle das Risiko einer Kollision des Zuges mit einem auf dem Übergang stehenden Fahrzeug deutlich geringer als an den anderen Bahnübergängen, weil sich direkt daneben der Haltepunkt Mitte befinde, zu dem eine gute Sichtverbindung bestehe. -Selbst Züge vom Hauptbahnhof her müssten an dieser Stelle schon soweit abgebremst sein, dass keine Kollision drohe. -Mehrheitlich wurde dem Umbau dieses Bahnübergangs die Zustimmung verweigert, da die Stadt Schiltach das Problem mit aufsitzenden Fahrzeugen "ausbaden müsste", so die Räte, da sich die Bahn nach Abschluss der Bauarbeiten aus der Verantwortung zurückziehen könne. -Als unproblematisch betrachtet das Gremium die Umbauten an den Bahnübergängen "Vor Heubach" und "Vor Kuhbach", für die bereits Kreuzungsvereinbarungen mit der Bahn getroffen wurden. -Esa entscheidet über große Weltraummissionen: Missionen für Milliarden -Ferne Planeten, Gravitationswellen oder schwarze Löcher - Experten der europäischen Raumfahrtagentur müssen sich jetzt auf zwei Großprojekte einigen, die sie in den nächsten Jahren starten wollen. -30 Vorschläge standen zur Auswahl, fünf sind noch im Rennen. -Ferne Welten. -Mehr als 1000 Planeten bei anderen Sternen haben Astronomen bereits gefunden. -Ob es zumindest auf einigen davon Leben gibt, weiß keiner. -Die Auswahl fällt schwer: Soll man nach Gravitationswellen suchen? -Oder lieber den Ursprung des Kosmos erforschen? -Oder nach erdähnlichen Planeten fahnden und damit vielleicht außerirdisches Leben entdecken? -Diese Fragen stellen sich gerade die Verantwortlichen der europäischen Raumfahrtagentur Esa. -Für ihr Programm "Cosmic Vision" suchen sie Ideen für zwei große Raumfahrtprojekte, die die Forschung entscheidend voranbringen sollen. -Der Etat für solche "L-Missionen" liegt jeweils etwa bei einer Milliarde Euro. -Der Start wird voraussichtlich 2028 und 2034 sein. -Anfang September traf sich in Paris die Elite der europäischen Weltraumforschung, um Vorschläge für solche Missionen zu diskutieren. -30 Ideen standen zur Debatte, fünf davon sind nun in die engere Wahl gekommen. -Zahlreiche Gremien begutachten die Vorschläge, haken bei den Forschern nach. -In den nächsten Tagen soll eine endgültige Entscheidung durch das wissenschaftliche Programmkomitee fallen. -Insgesamt vier große Fragen hat die Esa ausgewählt, auf welche die Missionen des 2007 initiierten Programms "Cosmic Vision" Antworten finden sollen. -Was sind die Bedingungen für die Entstehung von Planeten und Leben? -Die genauen Vorgänge bei der Entstehung von Sternen aus großen Gaswolken - und damit auch der Entstehung von Planeten, die diese Sterne umkreisen - liegen immer noch im sprichwörtlichen Dunkeln. -Ein großes Infrarotteleskop im All könnte dieses Dunkel durchdringen. -Und wenn ein Stern Planeten besitzt, unter welchen Umständen kann dort Leben entstehen? -Um dieser Frage nachzugehen, soll 2017 das Weltraumteleskop "Cheops" starten, mit dem 500 bereits bekannte Planetensysteme in unserer näheren Umgebung genauer untersucht werden. -Die Möglichkeiten sind aber begrenzt, Cheops ist eine kleine Mission mit einem Budget von 150 Millionen Euro. -Mit größeren Instrumenten könnten die Astronomen erdähnliche Planeten abbilden und sogar die Zusammensetzung ihrer Atmosphären analysieren und so nach "Biomarkern" suchen: Gasen, die als Indiz für biologische Aktivität gelten. -Wie funktioniert das Sonnensystem? -Auch diese Frage soll letztlich Aufschluss darüber geben, welche Voraussetzungen es für die Entstehung von Leben gibt. -Eine wichtige Rolle spielt die magnetische Aktivität eines Sterns und die Wechselwirkung seines Magnetfelds sowie der von ihm ausgehenden Teilchenstrahlung mit dem Planetensystem. -In unserem Sonnensystem lassen sich diese Vorgänge exemplarisch untersuchen. -Mit dem "Solar Orbiter" hat die Esa bereits eine Mission der M-Klasse (dort liegt das Budget bei etwa einer halben Milliarde Euro) für diese Frage bewilligt. -Die Sonde soll 2017 starten und Oberfläche und Aktivität der Sonne genau beobachten. -Von großem Interesse für die Forscher sind auch Jupiter und seine Monde. -Sie wollen herausfinden, welche Rolle der Riesenplanet bei der Entwicklung des Sonnensystems gespielt hat. -Dafür sieht die Esa die erste der insgesamt drei L-Missionen des "Cosmic Vision"-Programms vor. -Die Sonde "Juice" (Jupiter Icy Moons Explorer) beginnt ihre Reise voraussichtlich 2022. -Acht Jahre später erreicht sie Jupiter und soll die Atmosphäre des Planeten sowie die eisigen Monde Europa, Kallisto und Ganymed untersuchen. -Was sind die grundlegenden physikalischen Gesetze des Universums? -Das Weltall bietet viele Möglichkeiten, Materie unter extremen Bedingungen zu studieren und so zu untersuchen, ob die uns bekannten Naturgesetze dort immer noch gültig sind. -Vielleicht finden sich auch Abweichungen, die den Physikern den Weg zu einer neuen Theorie weisen könnten, unter deren Dach sich alle bekannten Naturgesetze vereinen lassen. -Wie ist das Universum entstanden und woraus besteht es? -Vor 13,7 Milliarden Jahren entstand unser Kosmos durch den Urknall. -Neben der uns geläufigen Materie, aus der Sterne, Planeten und Lebewesen wie wir bestehen, gibt es Dunkle Materie, deren Schwerkraft Galaxien und Galaxienhaufen zusammenhält, und Dunkle Energie, durch deren Wirken sich die Expansion des Weltalls beschleunigt. -Bislang wissen die Forscher weder, welche physikalischen Gesetze in den ersten Sekundenbruchteilen des Urknalls gültig waren, noch woraus Dunkle Materie und Dunkle Energie bestehen. -Mit "Euclid" hat die Esa auch hier bereits eine M-Mission bewilligt, deren Start für 2020 vorgesehen ist. -Mit einem speziellen Teleskop soll die Sonde die Verteilung der Materie im Universum erstmalig am gesamten Himmel untersuchen und so Rückschlüsse auf die Beschaffenheit von Dunkler Materie und Dunkler Energie ermöglichen. -Wer das Rennen machen könnte -Von den 30 Vorschlägen werden am Ende des Auswahlverfahrens nur zwei übrig bleiben. -Die besten Chancen auf eine Bewilligung als L-Mission hat nach Meinung vieler Experten "E-Lisa", ein anspruchsvolles Konzept zum Nachweis von Gravitationswellen. -Darunter verstehen Physiker Änderungen in der Struktur der Raumzeit, die Albert Einstein vor fast 100 Jahren vorausgesagt hatte. -Nachgewiesen wurden sie bis heute nicht. -Elisa könnte das schaffen, hoffen die Wissenschaftler hinter dem Projekt. -Die Mission sieht einen aus zwei oder drei Sonden bestehenden Detektor vor, der im All stationiert wird. -Er könnte im Gegensatz zu Anlagen auf der Erde auch Gravitationswellen nachweisen, die beim Urknall entstanden sind, und so neue Erkenntnisse über die Entstehung des Kosmos liefern. -Vier weitere Vorschläge konkurrieren noch um die zweite L-Mission: ein großes Röntgenteleskop namens "Athena", das unter anderem schwarze Löcher erforschen soll, "Icy Planets", eine weitere Mission zu den äußeren Planeten des Sonnensystems, "Prism", eine Mission zur Vermessung der kosmischen Hintergrundstrahlung, und der "Exoplanet Finder", der erdähnliche Planeten aufspüren und untersuchen soll. -Bereits ausgeschieden sind unter anderem Vorschläge zum Proben-Rücktransport vom Mars, zur Erforschung von Mond, Venus und Asteroiden sowie zur Sonnenphysik. -Snowden bereit, mit Deutschland in Bezug auf US-Überwachung zu „kooperieren“ -Edward Snowden, der Whistleblower des US-amerikanischen Geheimdienstes, hat erklärt, er sei bereit, nach Berlin zu reisen und vor dem deutschen Bundestag auszusagen, wenn die amerikanische National Security Agency und ihr Direktor Keith Alexander keine Antworten über ihre Aktivitäten geben. -Der deutsche Abgeordnete Hans-Christian Ströbele traf Snowden am Donnerstag in Russland, wo dieser Asyl erhalten hat, um mit ihm die Aussage in Deutschland zu besprechen. -In einem Brief von Snowden, der am Freitag von Ströbele den Medien in Berlin präsentiert wurde, schreibt dieser: „Auch wenn das Ergebnis meiner Bemühungen nachweislich positiv war, behandelt meine Regierung eine abweichende Meinung weiterhin als Treuebruch und versucht, politische Meinungsäußerung mit Strafandrohung zu kriminalisieren, bei der keine Verteidigung möglich ist.“ -Die Wahrheit zu sagen ist aber kein Verbrechen. -In dem Brief schreibt Snowden, er sei davon überzeugt, dass die Unterstützung der internationalen Gemeinschaft die amerikanische Regierung davon überzeugen könne, die Anklage gegen ihn fallen zu lassen. -Die vom US-Justizministerium erhobene Anklage umfasst Spionage und Diebstahl von Staatseigentum. -Der deutsche Innenminister Hans-Peter Friedrich sagte gegenüber Zeit Online: „Wenn Herr Snowden bereit ist, mit deutschen Vertretern zu sprechen, dann finden wir Wege, das zu ermöglichen.“ -Die Beziehungen zwischen den USA und Deutschland sind durch die Behauptungen belastet, die NSA habe das Handy von Kanzlerin Angela Merkel abgehört. -Thomas Oppermann, der Abgeordnete, der den für den Geheimdienst zuständigen parlamentarischen Ausschuss leitet, erklärte, man solle die Gelegenheit ergreifen, Snowden als Zeuge anzuhören, wenn dies möglich sei, „ohne ihn zu gefährden und die Beziehungen zu den USA völlig zu ruinieren“. -Ströbele, ein Abgeordneter der Grünen, veröffentlichte ein Bild von sich und Snowden in seinem Twitter-Feed. -Er wurde bei seinem Besuch in Russland von zwei deutschen Journalisten begleitet. -Ströbele erklärte, dass laut dem Anwalt des früheren NSA-Mitarbeiters Snowden nicht nach Russland zurückkehren könne, wenn er das Land verlasse. -Wenn Snowden in Deutschland aussage, dann benötige er eine Zusicherung, dass er dort „sicher“ sei, so der Abgeordnete. -Snowden erklärt in seinem Brief, dass er sich einer „ernsthaften und fortlaufenden“ Verfolgungskampagne ausgesetzt sähe, die ihn aus seiner Heimat vertrieben habe. -Er sei jedoch berührt von der weltweiten Reaktion auf „meinen Akt der politischen Meinungsäußerung“. -Bürger auf der ganzen Welt sowie hochrangige Politiker – auch in den Vereinigten Staaten – haben die Enthüllungen über ein unkontrolliertes System umfassender Überwachung als ein Verdienst an der Allgemeinheit bezeichnet. -In seinem Brief macht Snowden den deutschen Behörden ein Angebot der Zusammenarbeit, „wenn die Schwierigkeiten rund um die humanitäre Situation gelöst wurden“. -Frontier Airlines erhebt Gebühren für Handgepäck -Frontier Airlines plant, bis zu 100 US-Dollar von Passagieren zu verlangen, die auf ihrem Flug Handgepäck verstauen möchten. -Frontier Airlines beabsichtigt, bis zu 100 US-Dollar für ein Stück Handgepäck und 2 Dollar für Kaffee oder Limonade zu berechnen, auch wenn in der Ankündigung am Mittwoch zu lesen war, dass Passagiere die ganze Dose behalten dürften. -Die neue Gebühr gilt für Handgepäck im Gepäckfach über dem Sitz, kleine Taschen unter dem Sitz sind weiterhin kostenlos. -Frontier erklärte, man berechne 25 US-Dollar, wenn die Gebühr im Voraus bezahlt werde, und 100 US-Dollar bei Reisenden, die erst am Flugsteig zahlten. -Frontier-Sprecherin Kate O'Malley sagte, die Gebühr von 100 US-Dollar ziele darauf ab, Reisende zu bewegen, sich im Vorfeld um die Zahlung zu kümmern. -„Wir möchten diese Summe nicht berechnen“, erklärte sie. -Fluggesellschaften begannen 2008 damit, für das erste und zweite aufgegebene Gepäckstück Gebühren zu verlangen. -Passagiere, die versuchten, diese zu umgehen, haben deshalb soviel wie möglich ins Handgepäck gestopft, was zur Folge hat, dass die Gepäckfächer über dem Sitz häufig überfüllt sind. -Gebühren sind eine Möglichkeit, Passagiere dazu zu bewegen, weniger mit an Bord zu nehmen. -O'Malley sagte, bei der neuen Gebühr gehe es nicht wirklich darum, Geld zu verdienen. -Es gehe um die treuesten Kunden von Frontier, die deutlich zum Ausdruck gebracht hätten, dass es immer schwerer werde, Platz im Fach über dem Sitz zu finden. -Passagiere, die Ihre Tickets auf der Website der Fluggesellschaft kaufen, müssen nicht bezahlen. -Das bedeutet, dass ein Passagier am Flugsteig von Frontier unter Umständen ein Gepäckstück kostenlos mitnehmen darf, die nächste Person aber 100 Dollar für ein vergleichbares Gepäckstück bezahlen muss. -O'Malley erklärte, die Website von Frontier und die Check-in-Verfahren würden geändert, sodass die Passagiere über die Gebühr informiert seien, ehe sie zum Flugsteig kämen. -Die neue Handgepäckgebühr von Frontier tritt erst im Sommer in Kraft, ein genaues Datum gibt es noch nicht. -Passagiere schimpfen häufig über Gepäckzuschläge und andere Gebühren, doch Fluggesellschaften greifen gern darauf zurück. -Sie argumentieren, dass der Transport des Gepäcks Geld koste und Passagiere diesen Service bezahlen sollten, wenn sie ihn wünschten. -Viele an der Wall Street betrachten die neuen Gepäckgebühren als Zeichen dafür, dass die Fluggesellschaften genug Geld einnehmen, um nach Jahren der Verluste die Kosten von Flugreisen zu decken. -Die meisten lassen bislang jedoch die Finger von Gebühren für Handgepäck. -Spirit Airlines Inc. verlangte vor drei Jahren als Erste Gebühren für Handgepäck und später folgte Allegiant Air, ebenfalls ein Billigflieger. -Die einzige andere Fluggesellschaft mit einer derartigen Gebühr sei die ungarische Wizz Air, erklärte der Airline-Berater Jay Sorensen, der die Zusatzgebühren aufmerksam verfolgt. -Er schätzte in einem Bericht vom Dezember 2011, dass die Handgepäckgebühr Spirit jährlich 50 Millionen US-Dollar einbringe. -Sorensen, der früher eine Führungsposition bei Midwest Airlines innehatte, ist kürzlich mit Spirit geflogen und war neugierig, was ihm am Flugsteig erwarten würde, wenn Passagiere mit der ungewöhnlichen Handgepäckgebühr des Unternehmens konfrontiert würden. -„Der Boarding-Prozess gehörte zu den reibungslosesten, die ich in meiner Laufbahn in der Luftfahrt erlebt habe“, sagte er. -Ich erwartete, Zähneknirschen und handgreifliche Diskussionen am Flugsteig zu sehen. -„Das Flugzeug war voll“, erklärte er, „und das Boarding lief wie geschmiert.“ -Frontier folgt Spirit auch bei den 2 Dollar, die für Kaffee, Tee, Limonade oder Saft verlangt werden. -Das Unternehmen erklärte, dass Passagiere, die Limonade oder Saft kauften, die ganze Dose behalten dürften und Kaffee gratis nachgefüllt werde. -Wasser ist weiterhin kostenlos. -US Airways hatte 2008 kurzzeitig versucht, für Erfrischungsgetränke Geld zu verlangen, machte dies aber sieben Monate später wieder rückgängig, nachdem sich Passagiere beschwert hatten und keine andere große Gesellschaft nachgezogen war. -Frontiers Schritt, Gebühren für das Handgepäck zu erheben, wenn Passagiere nicht direkt bei der Fluggesellschaft kaufen, ist die jüngste Initiative, mit der Kunden auf die eigene Website gelenkt werden sollen. -Fluggesellschaften bezahlen Online-Reisebüros wie Orbitz 10 bis 25 US-Dollar pro verkauftem Ticket. -Das liefert allen Gesellschaften einen Anreiz, Passagiere zum Direktkauf bei ihnen zu bewegen statt über ein Online-Reisebüro. -Frontier ist in diesem Bereich allerdings am weitesten gegangen. -Im September begann das Unternehmen damit, bei Buchung über ein Online-Reisebüro nur noch halb so viele Vielfliegermeilen zu vergeben. -Am Mittwoch wurden die Meilen noch einmal auf 25 Prozent der Flugstrecke gekürzt. -Eine bei einem Drittanbieter gebuchte Flugreise mit Frontier über 1000 Meilen bringt Kunden deshalb nur noch 250 Meilen ein. -Passagiere können auch ausschließlich beim Direktkauf auf der Website von Frontier Plätze reservieren. -Frontier verfügt über einen treuen Kundenstamm an seinem Sitz in Denver, doch das Geschäft schrumpft und das Unternehmen verliert Geld. -Der Erlös ging um 9 Prozent zurück und die Flugkapazität schrumpfte knapp 13 Prozent im ersten Quartal, wie im Finanzbericht zu lesen ist, der am Mittwoch von dem Mutterkonzern, Republic Airways Holdings Inc., veröffentlicht wurde. -Republic hat versucht, im Rahmen des Verkaufs der Gesellschaft die Finanzen von Frontier in den Griff zu bekommen. -Sprichwörter kommen aus der Bibel -Gut 40 Frauen besuchten das letzte Frauenfrühstück dieses Jahres in der evangelischen Kirchengemeinde Bisingen. -Thema des Tages im Gemeindehaus waren "Sprichwörter aus der Bibel" und "Redewendungen aus dem Mittelalter". -Zita Köhler, die Vorsitzende des Kirchengemeinde- rats, ging nach dem Frühstück in einem Vortrag auf die biblischen Sprichwörter ein. -"Sprichwörter enthalten Lebensweisheiten, Vorschriften oder Warnungen", erklärte sie. -Sie verglich mehrere Sprichwörter mit den entsprechenden Bibelstellen und erklärte die Bedeutung. -Sie nannte Sprichwörter wie: Holzauge sei wachsam, wie Schuppen von den Augen fallen, ein Auge auf jemand werfen, den Seinen gibt's der Herr im Schlaf, seine Hände in Unschuld waschen. -Mehrmals ließ die Referentin ihre Zuhörer auch raten, um welches Sprichwort es sich handelte. -Der Redewendungen aus dem Mittelalter nahm sich die Kirchengemeinderätin Christel Dehner an: Aller guten Dinge sind drei, blau machen, etwas verhauen. -Sie erklärte Bedeutung und Herkunft der Redewendungen, die sie "Brücken in die Vergangenheit" nannte. -Zum Abschluss wurde eine Tombola verlost. -Die Preise lagen zugedeckt auf einem Tisch und wurden humorvoll umschrieben, bevor sie an die Gewinner übergeben wurden. -Unter die Besucherinnen der Frühstücksrunde mischte sich auch Bürgermeisterkandidat Roman Waizenegger. -Lernen statt arbeitslos: Pilotprojekt für Ungelernte im Tourismus -An der Mecklenburgischen Seenplatte startetheute ein Pilotprojekt für die Tourismusbranche. -Unter Führung der Bundesarbeitsagentur sollen 49 Frauen und Männer, die bereits als Ungelernte in der Branche arbeiten, über drei Winter hinweg zu Fachkräften für Hotels und Gaststätten qualifiziert werden. -"Neu daran ist, dass die sechsmonatigen Kurse mit Abschlüssen enden", sagte Carmen Wiechert von der Arbeitsagentur Neubrandenburg. -Der Nutzen sei vielfältig: Die Teilnehmer würden dadurch nicht arbeitslos, die Firmen bekämen geschulte Fachkräfte, die immer stärker in Hotels und Gaststätten fehlten, und die Agentur müsse nicht Arbeitslosigkeit finanzieren. -An dem Projekt sind auch die Industrie- und Handelskammer in Neubrandenburg sowie der Deutsche Hotel- und Gaststättenverband (Dehoga) Mecklenburg-Vorpommern beteiligt. -NSA erklärt, ein „interner Fehler“ und nicht Hacker seien Schuld am Crash der Website -Die schattenhafte National Security Agency erklärte am späten Freitag, ein Fehler habe ihre öffentliche Website einige Stunden lahmgelegt und nicht Hacker, wie online behauptet wurde. -„NSA.gov war heute Nacht einige Stunden lang nicht erreichbar, da während eines geplanten Updates ein interner Fehler aufgetreten ist“, erläuterte die Spionageagentur in einer per E-Mail versandten Meldung. -Das Problem werde an diesem Abend behoben. -Die Behauptung, der Ausfall sei durch einen verteilt ausgeführten Denial of Service-Angriff (DDoS) verursacht worden, sei nicht wahr. -In den frühen Abendstunden war Online-Server-Trackern aufgefallen, dass die NSA-Website mindestens sechs Stunden lang nicht erreichbar war und dies für manche Nutzer auch weiterhin nicht ist. -Zuvor hatte ein Sprecher der NSA gegenüber ABC News erklärt, das sensible interne Netzwerk der Behörde sei „in keiner Weise“ kompromittiert. -Geheime Informationen seien nicht in Gefahr, so der Sprecher. -Mindestens eine Gruppe von Hacktivisten nahm für sich in Anspruch, die NSA-Website mit einem DDoS-Angriff lahmgelegt zu haben. -Bei derartigen Angriffen wird die Zielwebsite mit Seitenaufrufen überflutet, bis die Server überlastet sind und die Website zusammenbricht. -Die Cybertaktik ist relativ simpel und die Angriffe dienen nicht dazu, in das interne Netzwerk des Zielsystems einzudringen. -Die früher supergeheime NSA, deren Spitzname einst No Such Agency (Keine solche Behörde) lautete, findet sich inzwischen im hellen Licht der Öffentlichkeit und sieht sich nach den in den letzten Monaten bekannt gewordenen Enthüllungen über ihr ausgedehntes Überwachungsprogramm im In- und Ausland scharfer Kritik ausgesetzt – ein Resultat der geheimen NSA-Daten, die vom desillusionierten ehemaligen NSA-Mitarbeiter Edward Snowden gestohlen und veröffentlicht wurden. -Die wachsende Kontroverse um den Geheimdienst befeuerte anfängliche Spekulationen darüber, der Zwischenfall heute sei das Ergebnis einer gezielten Cyperoperation. -Leicht gesunken ist die Zahl der Menschen ohne Job im Oktober um 22 auf 1.307. -Zwar sei die Quote mit 3,1 Prozent besser als vergangenes Jahr und auch besser als im September, "wir hätten uns aber mehr erhofft", sagt Monika Felder-Bauer, stellvertretende Geschäftsstellenleiterin der Agentur für Arbeit in Sonthofen. -Denn zahlreiche Betriebe reagierten bislang verhalten bei Einstellungen. -Ein Grund hierfür ist laut Felder-Bauer: "Wir haben kaum Fachkräfte im Handwerk, im Gesundheitswesen und in der Altenpflege". -Jahreszeitenbedingt laufen seit September auch wieder Stellenangebote für Personal im Hotel- und Gaststättengewerbe ein. -Mitte Dezember beginnt dort die Wintersaison. -Den ganzen Hintergrundbericht dazu finden Sie im Allgäuer Anzeigeblatt vom 31.10.2013 (Seite 33). -Gewinneinbußen bei Bombardier nach Rückgang bei Lieferzahlen und Aufträgen -Der kanadische Flugzeug- und Eisenbahnhersteller Bombardier Inc meldete am Donnerstag einen 15-prozentigen Rückgang des Nettogewinns, nachdem er durch rückläufige Bestellungen und Auslieferungen bei Flugzeugen im dritten Quartal sowie Vertragsprobleme in der Eisenbahnsparte unter Druck geraten war. -Bombardier mit Sitz in Montreal veröffentlichte auch keine Flugtestdaten für sein brandneues Flugzeug der CSeries und erteilte keine neuen Auskünfte darüber, ob das Flugzeug gemäß des ehrgeizigen Zeitplans im nächsten September in die kommerzielle Nutzung gehen könne. -Auf den Jungfernflug des Testflugzeugs vor eineinhalb Monaten folgten bislang nur drei weitere Starts, was es fraglich erscheinen lässt, ob die Testphase wie geplant verläuft. -Die Ergebnisse blieben hinter den Prognosen zurück und führten zu einem Kurssturz von über acht Prozent an der Börse von Toronto. -Cameron Doerksen, Analyst bei National Bank Financial, senkte am Donnerstag seine Bewertung von „outperform“ auf „sector perform“ im Hinblick darauf, dass die Aktie in den nächsten ein bis zwei Quartalen ein begrenztes Kurspotenzial besitzt. -„Zwar wurden schwächere Lieferzahlen bei Flugzeugen weitestgehend erwartet, doch von der Margenentwicklung im Transportbereich sind wir ganz klar enttäuscht“, sagte Doerksen in einen Kundenschreiben. -Wir sind der Ansicht, dass Bombardier neue Bestellungen für die CSeries erhalten wird, wenn das Testprogramm für das Flugzeug voranschreitet. -Falls allerdings in den kommenden Monaten keine neuen Bestellungen bekannt gegeben werden, dann erwarten wir, dass der Markt dem Programm gegenüber skeptischer wird. -Bombardier hofft, dass die Flugzeugreihe der CSeries das Unternehmen in das untere Marktsegment katapultieren kann, das derzeit von Boeing und Airbus dominiert wird. -Das erste Testflugzeug wurde im März der Öffentlichkeit vorgestellt und flog nach monatelangen Verzögerungen erstmals im September. -Doch bisher sind verbindliche Bestellungen für die CSeries mit 177 moderat, da potenzielle Käufer auf die Ergebnisse der Flugtests warten und sehen möchten, ob die Behauptungen des Unternehmens hinsichtlich Treibstoffeffizienz und Kosteneinsparpotenzial des neuen Düsenverkehrsflugzeugs zutreffend sind. -Derzeit gibt es insgesamt 403 Bestellungen und Zusagen von 15 Kunden und Gesellschaften. -Vorstandsvorsitzender Pierre Beaudoin gibt sich zuversichtlich, dass Bombardier sein Ziel von 300 verbindlichen Bestellungen zu Beginn der kommerziellen Nutzung des Jets erreichen werde. -Die Unternehmensführung versicherte am Donnerstag gegenüber Analysten und Medien außerdem, das Programm liege im Terminplan. -„Das Testflugzeug ist nicht länger am Boden geblieben als erwartet“, sagte Beaudoin bei einer Telefonkonferenz und fügte hinzu, während der Standzeit des Flugzeugs seien Bodentests und Software-Updates angesetzt gewesen. -Jeder Hersteller plant anders. -Wir haben beschlossen, einen ersten Flug zu unternehmen und dann einen Überarbeitungszeitraum anzuhängen, was wir auch getan haben. -Das geschieht während des gesamten Flugprogramms. -Das zweite von fünf Testflugzeugen werde voraussichtlich in den nächsten Wochen starten, die restlichen kurz danach, hieß es von Unternehmensseite. -Dennoch bleiben Analysten skeptisch, ob zwölf Monate nach dem Jungfernflug der erste Kunde ein Flugzeug der CSeries in Betrieb nehmen kann. -Bombardier erklärte, es überprüfe die Planung für die Inbetriebnahme (EIS) und werde diese in den nächsten Monaten aktualisieren. -„Das langsame Voranschreiten der Flugtest – auch wenn sie offensichtlich Bombardiers interner Planung entsprechen – bestärkt unsere Ansicht, dass sich die Inbetriebnahme auf das erste Quartal 2015 verschiebt“, sagte Doerksen. -Im dritten Quartal mit Ende am 30. September fiel Bombardiers Nettogewinn auf 147 Millionen Dollar bzw. 8 Cent pro Aktie von 172 Millionen Dollar bzw. 9 Cent pro Aktie im Jahr zuvor. -Der angepasste Gewinn pro Aktie beträgt unverändert 9 Cent. -Der Umsatz gab geringfügig nach und sank von 4,2 auf 4,1 Milliarden Dollar. -Analysten hatten einen Gewinn von 10 Cent pro Aktie und einen Umsatz von 4,56 Milliarden Dollar erwartet, so Thomson Reuters I/B/E/S. -Der weltweit viertgrößte Flugzeughersteller gibt an, er habe in diesem Quartal 45 Flugzeuge ausgeliefert, ein Rückgang von 57 im letzten Jahr. -Die Nettobestellungen fielen von 83 Flugzeugen auf 26. -Der Auftragsbestand in der Luft- und Raumfahrtsparte betrug am 30. September 32,9 Milliarden Dollar und war damit gegenüber dem 31. Dezember unverändert. -„Im Bereich Luft- und Raumfahrt lagen die Ergebnisse im Rahmen unserer Vorgaben, aber der niedrige Auftragseingang und die Marktverhältnisse insgesamt waren enttäuschend“, sagte Beaudoin. -Der Umsatz in der Luft- und Raumfahrtsparte fiel um 13 Prozent auf zwei Milliarden Dollar. -Bombardier, der weltweit größte Eisenbahnhersteller, gab bekannt, dass der Umsatz in dieser Sparte um knapp elf Prozent auf 2,1 Milliarden Dollar gestiegen ist. -Der Auftragsbestand in der Transportsparte betrug am 30. September 32,6 Milliarden Dollar und war damit gegenüber dem 31. Dezember leicht erhöht. -Die Gewinnspanne im Bereich Transport wurde durch Abwicklungsprobleme bei einigen Großverträgen beeinträchtigt. -Laut Unternehmensführung wird es für das vierte Quartal neue Vorgaben geben. -Im nachmittäglichen Handel am Donnerstag gab die Bombardier-Aktie um 8,5 Prozent nach und lag bei 4,83 kanadischen Dollar, nachdem auch angekündigt wurde, dass der Google-Finanzvorstand Patrick Pichette in den Vorstand wechseln wird. -Die brasilianische Embraer SA, der weltweit drittgrößte kommerzielle Flugzeughersteller und Bombardiers engster Rivale, meldete am Donnerstag einen zehnprozentigen Rückgang des Quartalsgewinns. -USA: Schüsse am Flughafen von Los Angeles -Ein Unbekannter hat am Freitagvormittag (Ortszeit) am LAX-Flughafen Schüsse abgefeuert. -Während die Flughafenleitung über den Kurznachrichtendienst Twitter mitteilte, der Schütze sei in Gewahrsam, berichtete die "Los Angeles Times", der Mann sei tot. -Auch ein Sicherheitsmann sei getötet worden. -Medien berichteten außerdem von mehreren Verletzten. -US-Präsident Barack Obama werde laufend über die Lage informiert, sagte der Sprecher des Weißen Hauses, Jay Carney. -Der Vorfall ereignete sich in Terminal 3. Augenzeugen gaben an, einen Schützen mit einem Gewehr in einer der Abflughallen gesehen zu haben, wie mehrere Medien berichteten. -Ein Reisender erzählte dem Sender CNN, viele Menschen hätten in Panik Schutz gesucht. -Reisende hätten geschrien und Kinder geweint. -Unter den Wartenden sei Panik ausgebrochen, viele hätten sich aus Angst hinter Stühlen versteckt. -Er habe zwei Schüsse gehört, sagt ein Reisender der "Los Angeles Times". -Ein Mitarbeiter der Sicherheitsbehörde TSA habe eine Schussverletzung am Fuß erlitten. -Die Szene sei "wie in einem Film" gewesen. -Danach sei er selbst in einen Sicherheitsbereich gebracht worden, berichtete der Mann. -Die Schüsse sollen nahe eines Sicherheitskontrollbereichs gefallen sein. -Flughafen-Gebäude evakuiert -Auf Fernsehbildern war zu sehen, wie zahlreiche Kranken- und Polizeiwagen vor einem Terminal vorfuhren. -Hunderte Menschen wurden aus dem Gebäude in Sicherheit gebracht. -Nutzer auf Twitter veröffentlichten Fotos von einem Spezialeinsatzkommando und einem auf dem Boden liegenden Gewehr. -Wie die Flughafenleitung über Twitter mitteilte, begann der Vorfall um 9.30 Uhr morgens (Ortszeit). -Die Zufahrtsstraßen wurden gesperrt, wodurch sich laut CNN lange Rückstaus bildeten. -Bis auf Landungen sei der Flugverkehr vorübergehend eingestellt worden, hieß es. -Allerdings waren auf CNN auch mehrere Flieger beim Start zu beobachten. -Terminal 3 wird vor allem von kleineren US-Fluggesellschaften bedient. -Der LAX-Flughafen von Los Angeles ist einer größten Flughafen der USA. -Kokainabhängiger Anwalt, der Drogenboss vor polizeilicher Ermittlung warnte, muss ins Gefängnis -Basharat Ditta, 42, gab Informationen an Unterweltgröße Neil Scarborough weiter -Der Anwalt hatte Angst, seine heimliche Drogenabhängigkeit könnte ans Licht kommen -Er wurde vom Liverpooler Crown Court zu drei Jahren Gefängnis verurteilt -Ein bekannter Strafverteidiger, der einen Drogenbaron vor einer großen polizeilichen Ermittlung warnte, weil er Angst hatte, seine geheime Drogensucht könnte aufgedeckt werden, wurde zu drei Jahren Haft verurteilt. -Basharat Ditta, 42, versorgte Unterweltgröße Neil Scarborough mit sensiblen Informationen über Ermittlungen zu dessen Aktivitäten im Drogenhandel, nachdem er durch seine eigene Kokainsucht kompromittiert worden war. -Der von Kriminalisten als „erstklassig“ gepriesene Anwalt mit dem Spitznamen „Bash“ wurde 2011 in seinem Haus verhaftet, nachdem die Polizei Scarborough überwacht hatte, den er in einem früheren Drogenverfahren vertreten hatte. -Beamte beobachteten, wie Scarborough (32) drei Beutel Kokain am Haus des Anwalts in Blackburn, Lancashire, ablieferte, während dieser mit Kollegen bei einem Essen der Anwaltskammer war. -Die Ermittlungen brachten zutage, dass Ditta regelmäßig Drogen der Klasse A konsumierte, nachdem bei Tests Spuren von Kokain in seinen Haaren, auf seiner Brieftasche und an Kreditkarten gefunden worden waren. -Über einen Zeitraum von acht Monaten zwischen Januar und August 2011 versuchte er, unerlaubt Informationen über die Verhaftung zweier Männer im Auftrag von Scarborough und einem seiner Partner zu beschaffen. -Alle vier Verdächtigen wurden damals von der Polizei im Rahmen eines großen Ermittlungsverfahrens zum Heroin- und Kokainhandel in Lancashire, Cumbria, Merseyside, Berkshire und West Yorkshire überwacht. -Gemeinsam mit 32 anderen Personen wurden sie später verhaftet, nachdem die Polizei bei einer Reihe von Hausdurchsuchungen Heroin und Kokain im Wert von 1,5 Millionen Pfund sowie 200.000 Pfund an Bargeld sicherstellte. -Ditta, 42, gab Informationen an die Kriminellen weiter, weil er fürchtete, seine Drogenabhängigkeit könne publik werden. -Heute wurde Ditta, der für die Kanzlei Forbes Solicitors in Blackburn arbeitet, am Liverpooler Crown Court nach einem dreiwöchigen Verfahren in zwei Anklagepunkten wegen Behinderung der Justiz für schuldig befunden und verurteilt. -Er gab bei einer früheren Befragung den Kokainbesitz zu. -Der Anwalt wurde überführt, nachdem die Polizei bei ihren Ermittlungen gegen Scarborough herausfand, dass dieser im Februar 2011 in regelmäßigem telefonischen Kontakt mit Ditta gestanden hatte. -Zwei Ermittlungsbeamte folgten dem Verdächtigen und sahen, wie er zu Dittas Haus ging und dort die Drogen mit einer Reinheit von 60 Prozent in einem schwarzen Golfhandschuh unter die Mülltonne des Anwalts legte. -Kurz nachdem er die Drogen abgeliefert hatte, telefonierte Scarborough regelmäßig mit Ditta, der sich beim Abendessen im Fußballstadion der Blackburn Rovers in Ewood Park befand. -Der Anwalt kehrte nach Hause zurück, holte sich die Drogen und dann wurden neun Mitteilungen zwischen den beiden ausgetauscht. -Vor Gericht wurde bekannt, dass Ditta ein „regelmäßiger Konsument“ von Kokain war, nachdem bei Tests Drogen der Klasse A in seinen Haaren, seiner Brieftasche und auf seinen Kreditkarten entdeckt worden waren. -Ditta wurde später verhaftet, stritt aber ab, Kokain zu nehmen und sagte, er habe mit dem mutmaßlichen Dealer gesprochen, weil dieser sein Mandant sei, und behauptete, ihre Diskussion unterliege dem „Anwaltsgeheimnis“. -Während seiner Verhaftung nahm Ditta seine Brieftasche und versuchte, eine Reihe von Kreditkarten zu entfernen, doch sie wurden sichergestellt und ihm wurde eine Haarprobe entnommen. -Beim polizeilichen Verhör gab er an, er betreibe sowohl unter seiner Privatadresse als auch am Arbeitsplatz ein Büro und Mandanten würden ihn zu Hause wegen juristischer Angelegenheiten anrufen. -Vor Gericht wurde bekannt, dass er wichtige Akteure des Drogenhandels, von denen er einige zuvor vertreten hatte, nach bedeutsamen Verhaftungen angerufen und darüber informiert hatte, was die Ermittler über sie wussten. -Staatsanwältin Anne Whyte sagte: „Ein Strafrechtsanwalt sollte besser als alle anderen wissen, dass man das Gesetz nicht bricht.“ -Ditta wird des Missbrauchs seiner Position als Strafverteidiger angeklagt, da er sich zu sehr mit bestimmten Mandanten eingelassen hat. -Die fragliche Beziehung ist nicht einfach die eines Drogenhändlers, sondern eines Drogenhändlers, der seinen Anwalt mit Drogen versorgt. -Ein Teil seiner Mitteilungen war zweifellos legitim, denn er war der Anwalt dieser Personen. -Doch das ging weit über die Grenzen einer normalen Beziehung zwischen Anwalt und Mandant hinaus. -Er wirkte den polizeilichen Ermittlungen soweit wie möglich entgegen, sodass sie weiterhin ihren kriminellen Aktivitäten nachgehen konnten. -Herr Ditta brachte damit seinen Berufsstand in Verruf. -Er stand bestimmten Mandanten zu nahe, insbesondere Scarborough, und er ließ es zu, dass seine Unabhängigkeit kompromittiert wurde. -Ditta wies jedes Fehlverhalten von sich und behauptete: „Wenn ich ein korrupter Anwalt wäre, was ich nicht bin, und Informationen an Scarborough weitergeben wollte, dann würde ich keine 15 Stunden warten, sondern das sofort tun.“ -Doch nach der Anhörung sagte Superintendent Lee Halstead von der Polizei in Lancashire: „Herr Ditta hat den Schritt von einem Strafverteidiger zu einem Kriminellen in dem Moment getan, als er anfing, Drogen von organisierten Kriminellen zu erwerben.“ -Seine Kokainabhängigkeit führte zu einer hoffnungslosen Kompromittierung und machte ihn empfänglich für die Motive führender Mitglieder der organisierten Kriminalität, die ihn beauftragten, wertvolle Informationen über polizeiliche Ermittlungen einzuholen. -Anwälte müssen die höchsten Standards an Integrität aufrechterhalten und in der Öffentlichkeit für Vertrauen und Zuversicht sorgen. -Ditta hat dieses Vertrauen hintergangen und versucht, sich hinter der Fassade seines Berufs zu verschanzen. -Die Serious and Organised Crime Unit von Lancashire leitete die Ermittlungen gegen Ditta, die auch zu einer Verurteilung in drei Fällen wegen des Besitzes von Kokain und Justizbehinderung führten, was unsere Entschlossenheit unterstreicht, Kriminelle vor Gericht zu bringen. -Dieser Fall sollte Kriminellen eine Warnung sein und zeigt, dass sich keiner dem Arm des Gesetzes entziehen kann. -Wir finden Sie und bringen Sie vor Gericht. -Scarborough selbst wurde zu 14 Jahren Gefängnis verurteilt, nachdem er sich der Verschwörung zum Heroin-, Kokain- und Cannabishandel für schuldig erklärt hatte. -Fünfunddreißig weitere am Drogenhandel beteiligte´Personen wurden zu Gesamtstrafen von 153 Jahren wegen Drogendelikten verurteilt. -Auf seiner Website veröffentlichte Ditta einen Bereich mit Fragen und Antworten über sich, in dem er angab, sein Traumjob sei es, als Anwalt Mandanten in der Todeszelle in Amerika zu vertreten, sein ultimativer Gast beim Abendessen sei Mohammed Ali und Chancenungleichheit sei die Motivation für seine Arbeit. -Revolutionäres Werk der Kirchenmusik -Zum Abschluss der Feierlichkeiten aus Anlass seines 160-jährigen Bestehens wird der Liederkranz Dunningen zwei Gottesdienste mit der "Deutschen Messe" von Franz Schubert umrahmen. -Nach dem großen Erfolg des Musicals "Rock my Life", das die Zuschauer im Frühjahr diesen Jahres begeisterte, wird der Liederkranz zum Abschluss des Jubiläumsjahres zwei Gottesdienste mit der Aufführung der Deutschen Messe von Franz Schubert bereichern. -Am Samstag, 26. Oktober, wird diese außergewöhnliche Messe ab 19 während der Abendmesse in der St. Martinuskirche Dunningen und am Sonntag, 27. Oktober, ab 10.15 Uhr beim Gottesdienst in der St. Johannes Baptistkirche in Lackendorf zu hören sein. -Die so genannte Deutsche Messe mit dem Originaltitel "Gesänge zur Feier des heiliges Opfers der Messe", D 872, ist ein geistliches Musikwerk des Komponisten Franz Schubert aus dem Jahre 1826 und galt zur Zeit der Entstehung fast als revolutionär. -Die Messe wird im Rahmen der Gottesdienste aufgeführt, wie der Auftraggeber Johann Philipp Neumann es im Sinn hatte. -Unter der Leitung von Hermann Schneider wird der Chor von der Bläsergruppe des Musikvereins Frohsinn Tennenbronn und Noemi Lokodi an der Orgel begleitet. -Haslach: Skepsis weicht zusehends Zuversicht -Mit den Wahlen zum Pfarrgemeinderat 2015 wird sich beispielsweise in der Seelsorgeeinheit Haslach (SE) Grundlegendes ändern. -Es gibt dann nur noch einen gemeinsamen Pfarrgemeinderat für alle sechs Pfarreien. -In einer gemeinsamen Sitzung bereiteten sich am Samstag die Gremien aus Haslach, Hofstetten, Mühlenbach, Fischerbach, Steinach und Welschensteinach auf die Veränderungen vor. -Denn an den grundsätzlichen Beschlüssen der Diözese ist nichts mehr zu ändern und es ist an den Pfarrgemeinderäten, diese möglichst zufriedenstellend umzusetzen. -Im Laufe des Tages wurde besonders deutlich: die Skepsis gegenüber dem Neuen weicht zögernd aber zusehends einer Zuversicht. -Von den Vorstellungen und Erwartungen an die Neuerungen 2015 bis hin zu konkreten Vorschlägen über die künftige Zusammensetzung und die Größe des Pfarrgemeinderats reichten die äußerst konstruktiven Gespräche der sechs Gremien mit den Hauptamtlichen des Seelsorgeteams. -Regionaldekan Georg Schmitt erläuterte anhand einer Präsentation die neu gefassten Richtlinien für Seelsorgeeinheiten in der Erzdiözese Freiburg. -Danach bildet die SE Haslach als Kirchengemeinde künftig eine Körperschaft des öffentlichen Rechts, die in die Rechte und Pflichten der bisher sechs einzelnen Kirchengemeinden eintritt. -Es wird nur noch einen gemeinsamen Pfarrgemeinderat und einen Stiftungsrat geben, in dem der Pfarrer Kraft Amtes vertreten ist. -Aus jeder Pfarrei sollen mindestens zwei gewählte Mitglieder vertreten sein, in den sechs Pfarreien werden vor Ort so genannte Gemeindeteams gebildet. -Ihre Aufgabe wird es künftig sein, das kirchliche Leben im Ort zu fördern. -Bei der Frage nach den Finanzen der einzelnen Pfarrgemeinde zeigte sich dann erheblicher Gesprächsbedarf. -Die Neuerungen sehen vor, dass angesammelte Guthaben in den Pfarreien zweckgebunden erhalten bleiben. -Die Verbindlichkeiten gehen dagegen auf die Gesamtgemeinde über. -Gerade im Hinblick auf die großen geplanten Investitionen, deren Verpflichtung dann alle Pfarreien treffen, gab es große Bedenken. -"Auch in der Vergangenheit wurde nur in Projekte investiert, die von den einzelnen Pfarreien alleine bewältigt werden konnten", relativierte Haslachs Pfarrgemeinderatsvorsitzender Bruno Prinzbach. -Praktische Erfahrung mit einem gemeinsamen Gremium für fünf Pfarreien schilderte Barbar Ritter, die Vorsitzende des Gremiums in der Seelsorgeeinheit Schutterwald-Neuried. -Sie hatten sich 2006 zur Aufgabe gemacht, "Eins werden, fünf bleiben" und mussten feststellen, dass das gar nicht so einfach ist. -Die Gremien der Haslacher Seelsorgeeinheit machten sich dann daran, die Fragen zur künftigen Zusammensetzung und Größe des gemeinsamen Pfarrgemeinderats sowie der praktischen Arbeit der Gemeindeteams zu beantworten. -Die Ausarbeitungen werden in weiteren Sitzungen konkretisiert und in einer weiteren gemeinsamen Sitzung aller sechs Gremien im kommenden Frühjahr beschlossen. -Am Ende präsentierte Pfarrgemeinderat Michael Schöner aus Steinach das neue Logo der Seelsorgeeinheit, dem mit deutlicher Mehrheit zugestimmt wurde. -In Pfullendorf wird der Strom teurer -Die Bürger Pfullendorfs müssen sich auf höhere Stromkosten einstellen. -Wie der Geschäftsführer der Stadtwerke, Jörg-Arne Bias, gegenüber dem SÜDKURIER bestätigte, kommen auf eine vierköpfige Pfullendorfer Familie 70 bis 90 Euro Mehrkosten pro Jahr zu. -Wesentlicher Grund der anstehenden Preiserhöhung ist der Anstieg der sogenannten EEG-Umlage von 5,277 auf 6,3 Cent pro Kilowattstunde. -Diese Umlage wird jedes Jahr im Oktober von den vier Betreibern der der großen Stromtrassen neu festgesetzt. -Diese beziffern die ihnen aus der Energiewende entstehenden Mehrkosten, die sie durch eine Umlage über den Strompreis erheben können. -Die Stadtwerke Pfullendorf bilden das letzte Glied in dieser Kette. -Der Aufsichtsrat des Energieunternehmens war in seinen Prognosen sogar von einer höheren Umlage ausgegangen, wie Jörg-Arne Bias berichtet. -Nun müssten die konkreten zahlen errechnet werden, bevor die Stadtwerke nächste Woche ihre Kunden über die exakte Preiserhöhung informieren könnten. -"Wir gehen von einer Anhebung von 1 Cent plus x aus", so Bias, wobei die Zahl eher in der Nähe der 2 Cent liegen werde. -Ampel auf Dauerrot: Auf Wartezeit achten -Manchmal wird eine Ampel einfach nicht grün. -Die Kontaktschleife in der Straße reagiert nicht, oder das Lichtzeichen ist defekt. -Was soll man dann tun - bei Rot fahren? -Hat ein Betroffener lange genug an einer roten Ampel gewartet und die Kreuzung ist frei, kann er weiterfahren, erklärt der Stuttgarter Rechtsanwalt Ralf Becker in der Zeitschrift "Motorrad". -Die Wartezeit müsse aber "angemessen" sein, was von Fall zu Fall unterschiedlich ausgelegt werden könne. -Becker rät, mindestens fünf Minuten auszuharren, bevor von einer Funktionsstörung der Ampel auszugehen ist. -Wer die Fahrt trotz Rotlichts fortsetzt, müsse sich jedoch im Klaren darüber sein, dass er für einen dadurch verursachten Unfall die volle Schuld trägt. -Wer sich weniger als fünf Minuten geduldet, wartet unter Umständen nicht lange genug, warnt Becker und verweist auf einen Beschluss des Oberlandesgerichts Hamm. -In dem verhandelten Fall hatte ein Fahrer nach etwa drei Minuten Dauerrot die Ampel ignoriert und musste sich für einen fahrlässigen Rotlichtverstoß verantworten. -Die Ampel war nämlich nicht defekt, sondern sprang wenig später auf Grün um. -Ein Fahrverbot blieb ihm wegen der besonderen Situation erspart - die Geldstrafe aber nicht. -Gemeinderat freut sich über soliden Haushalt -Immer wenn es in Gemeinderatssitzungen um den Haushalt geht, hat Bürgermeister Ralph Gerster gut lachen. -Das Gemeindesäckl in Herdwangen-Schönach ist gut gefüllt und Schulden kennt die Verwaltung bereits seit 2005 nicht mehr. -Auch das Haushaltsjahr 2012 kann in diesem Sinne als voller Erfolg verbucht werden. -Dies wurde bei der Vorstellung der Jahresrechnung für 2012 deutlich. -Wie Andrea Rothmund erläuterte, sei der Verwaltungshaushalt mit rund 7,6 Millionen Euro über eine Millionen Euro höher ausgefallen als geplant. -Auch der Überschuss des Verwaltungshaushaltes liege mit etwa 1,8 Millionen Euro rund eine Millionen Euro über dem Ansatz. -Zu verdanken sei der Überschuss vor allem höheren Gewerbesteuereinnahmen und dem Gemeindeanteil an der Einkommenssteuer. -Auch der Vermögenshaushalt lag wieder deutlich über den geplanten Werten. -Statt der vorgesehenen 1,5 Millionen Euro betrug er im vergangenen Jahr 2,5 Millionen Euro. -"Dies liegt im Wesentlichen an der höheren Zuführung aus dem Verwaltungshaushalt", so Rothmund. -Da im Haushaltsjahr 2012 weniger Investitionen als geplant getätigt worden sind, ist auch die Rücklagenzuführung höher ausgefallen. -Insgesamt konnte die allgemeine Rücklage um rund 2,1 Millionen Euro auf jetzt insgesamt 4,9 Millionen Euro gesteigert werden. -Bürgermeister Ralph Gerster bedankte sich bei Rothmund und ihrem Team für die gute Arbeit. -"So ein Werk schüttelt man nicht einfach aus dem Ärmel", lobte Gerster mit Blick auf die Jahresrechnung. -Er und die Gemeinderäte freuten sich sichtlich über die guten Zahlen. -Es mag paradox klingen, doch genau diese guten Zahlen könnten die Bürger bald teuer zu stehen kommen. -Durch die gute Finanzlage konnte die Gemeinde in der Vergangenheit auf hohe Hebesätze verzichten. -Dadurch wiederum seien aber laut Gerster schon öfters Zuschüsse vom Land gekürzt worden. -Für diese Zuschüsse aus diversen Ausgleichsstöcken gibt es Voraussetzungen. -Unter anderem müssen die Städte und Gemeinden, die Zuschüsse beantragen, bestimmte Hebesätze vorweisen. -"Das Thema Hebesätze müssen wir uns anschauen, da in den kommenden Jahren Investitionen anstehen, für die wir gerne Zuschüsse hätten", kündigte Gerster in diesem Zusammenhang an. -Nagold: Hugo Wermter seit 60 Jahren im Chor -Mit 60 Jahren als Sänger ist Hubert Wermters Geschichte beim Männergesangverein Cäcilia fast doppelt so lange wie die des Herbsfestes, in dessen Rahmen er feierlich geehrt wurde. -Hubert Wermter stand wie gewohnt in den Farben seines Männergesangvereines auf der Bühne. -Auch nach 60 Jahren macht ihm das Singen mit dem inzwischen wohl mehr als lieb gewonnenen Chor immer noch großen Spaß. -Im Verein war Wermter ebenfalls aktiv, unter anderem 28 Jahre lang als Mitglied des Ausschusses. -Für sein Engagement und seine Vereinstreue erhielt er gleich mehrere Auszeichnungen: Jörg Kohr, Pastoralreferent im Auftrag des Cäcilienverbandes Rottenburg-Stuttgart, überreichte ihm - da der Chor auch ein Kirchenchor ist - eine Urkunde des Verbandes und einen von Bischof Gebhard Fürst unterzeichneten Ehrenbrief. -Seine Sängerkollegen überreichten ihm die Urkunde des deutschen Chorverbandes sowie einen gravierten Zinnteller. -Neben der Ehrung verlief das 31. Herbstfest in gewohnten Bahnen: Mit dem MGV Betra, dem Liederkranz Salzstetten, der Sängerabteilung Baisingen und dem örtlichen Musikverein hatten die Vollmaringer eine bunte Mischung verschiedener Chöre und Gesangsstile geladen, welche die rund 400 Besucher unterhielten. -Der MGV Vollmaringen eröffnete mit stimmungsvollen Liedern wie "Im Weinparadies" und "Lustig, ihr Brüder". -Ihrem Ehrensänger Hubert Wermter bereiteten sie zur Ehrung noch ein kleines Ständchen - Wolfgang Amadeus Mozarts "Bundeslied". -Der Männergesangverein Betra sang ebenfalls klassische Chorlieder wie "Jägerwerben" von Julius Wengert, "Weit, weit weg" von Hubert Goisern sowie das Volkslied "Wann du durchgehst durchs Tal". -Der Liederkranz Salzstetten sorgte mit seinem gemischten Chor und seiner Frauengruppe - dem "Impulschor" - für die Frauenquote des Festes. -Der gemischte Chor entführte mit der neapolitanischen Volksweise "Santa Lucia" und "Eviva Espana" von Antos Gus in fremde Länder, während der "Impulschor" ebenfalls auf fremde Sprachen zurück griff: "Liberatio" und "Hey Jude" gehörten zu ihrem Programm. -Die Sängerabteilung des Baisinger Sportvereins kam mit "Was isch der Schwob?" von Hans Süssmuth und Robert Papperts "Bierlied" wieder zurück in heimische Gefilde, wo der Vollmaringer Musikverein den krönenden musikalischen Abschluss bot. -Für die Kinder gab es ein kleines Nebenprogramm - "Oma Hanne" alias Hannelore Stollsteimer führte ein Kasperltheater auf und las ein paar Geschichten vor, welche die Kinder den Mittag über gut beschäftigten. -Kinderträume werden wahr -Die Kindergärten und Kindertagesstätten im Zollernalbkreis bekommen in diesen Tagen Post. -Der Schwarzwälder Bote möchte in Zusammenarbeit mit Bauhaus und der Sparkasse Zollernalb die Einrichtungen bei der Realisierung ihrer Bau- und Verschönerungswünsche unterstützen. -Braucht der Kindergarten einen neuen Sandkasten? -Mitmachen bei der Aktion können alle Kindergärten, Kindertagesstätten und Kinderkrippen aus dem gesamten Zollernalbkreis. -Wenn Sie einen Bau- oder Verschönerungswunsch für Ihre Einrichtung haben, können Sie sich mit diesem beim Schwarzwälder Boten bewerben. -Seien Sie mit Ihren Kindern kreativ und überraschen Sie die Jury der Aktion Kindergarten-Baustelle. -Eine Jury, bestehend aus Vertretern von Schwarzwälder Bote, Bauhaus und Sparkasse Zollernalb, wählt aus allen Einsendungen drei Wünsche aus. -Welche Voraussetzungen zur Teilnahme gibt es? -Der Wunsch sollte mit Baumaterial im Wert von maximal 2000 Euro und an einem (Aktions-) Tag zu realisieren sein. -Darüber hinaus muss der Gewinner selbst für viele fleißige Bauhelfer sorgen. -Was hat es mit dem Aktionstag und den Helfern auf sich? -Bauhaus stellt mit Unterstützung der Sparkasse Zollernalb das Material zur Verfügung. -Es wird am vereinbarten Tag in den Kindergarten geliefert und soll dann direkt von den Helfern verarbeitet werden. -Helfer können Eltern, Großeltern, Freunde, Vereine und natürlich die Erzieher und Kinder selbst sein. -Kenianische Presse empört über kontroverses Mediengesetz -„Wir befinden uns in einer beängstigenden Lage und man darf sich fragen: Was hält das Parlament davon ab, morgen einfach die richterliche Unabhängigkeit abzuschaffen?“, war in der Zeitung zu lesen, die den Gesetzesentwurf für nicht verfassungsgemäß hält. -„Das Gesetz ist drakonisch und sehr strafend und wir lehnen es ab“, sagte Cyrus Kamau, Geschäftsführer der Capital Group, zu der auch CapitalFM gehört, eine der angesehensten unabhängigen Radiostationen und Nachrichten-Websites Kenias. -Er sagte, das neue Medientribunal „wird immer voreingenommen sein, denn es ist ein verlängerter Arm der Regierung“, und Einschränkungen bei Inhalten und Werbung würden Kenias Stellung in der globalen Wirtschaft beschädigen. -„Ich hoffe, der Präsident hört auf uns und wir appellieren an ihn, diesen Gesetzesentwurf abzulehnen und an das Parlament zurückzuschicken“, sagte er. -Laut der Zeitung The Star nehme die Regierung durch das neue Gesetz „die Medien effektiv in einen Würgegriff“. Dagegen schreibt The Standard, Demokratie und Redefreiheit in Kenia hätten „einen schweren Schlag“ erlitten, und betitelte den Entwurf als „drakonisch“. -Die Verabschiedung des Gesetzes gehört zu einer Reihe von Maßnahmen, mit denen die nationale Sicherheit im Nachgang zu den Angriffen durch bewaffnete Islamisten auf das Westgate-Einkaufszentrum im September gestärkt werden soll. -Die kenianischen Medien zogen den Zorn der Behörden auf sich, weil sie Aufnahmen von Sicherheitskameras veröffentlichten, auf denen zu sehen war, wie die an den Tatort entsandten Einheiten angeblich das luxuriöse Einkaufszentrum ausraubten. -Polizeichef David Kimaiyo reagierte darauf mit der Einbestellung zweier Journalisten und eines führenden Medienvertreters zu Verhören, auch wenn die Vorladung nach einem Aufschrei der Medien zurückgezogen wurde. -Nach dem neuen Gesetz können Medienhäuser mit einer Strafe von bis zu 20 Millionen kenianischen Schillingen belegt werden und einzelne Journalisten mit bis zu einer Million sowie dem weiteren Risiko, ihre berufliche Anerkennung zu verlieren oder von einer offiziellen Presseakkreditierung ausgeschlossen zu werden. -Das Tribunal hat außerdem das Recht, den Besitz der Betroffenen zu beschlagnahmen, wenn eine Strafe nicht bezahlt wird. -Laut Daily Nation kann „schon eine einzige Strafe die meisten Radiosender in den Bankrott drängen“. -Darüber hinaus ist die Zeitung der Ansicht, diese Maßnahmen könnten eine vernichtende Auswirkung auf die ihrer Beschreibung nach „lebhafte Blogosphäre“ Kenias haben. -Wenn sie die Medien zum Schweigen brächten, könnten Politiker ungestraft tun, was immer sie wollen. -„Keiner wird es jemals erfahren“, schrieb Mutuma Mathiu, Journalist bei der Nation, der die kenianischen Medien als einen entscheidenden Kontrollfaktor im öffentlichen Leben bezeichnet. -„Überlässt man Politiker sich selbst, führen sie das Land in den Bankrott und uns zurück in die Zeit der Jäger und Sammler“, kommentierte er. -Die kenianischen Gesetzgeber zogen schon in der Vergangenheit den öffentlichen Ärger auf sich. -Im Mai stimmten sie ab, die von der nationalen Vergütungskommission angeordneten Kürzungen rückgängig zu machen und setzten ihre deftigen Gehälter von etwa 532.000 Schilling steuerfrei pro Monat wieder in Kraft – sie zählen zu den weltweit höchsten. -Bad-Dürrheim: Ein Traum wird wahr für den FC -An der Bedeutung für die Stadt und den Fußballclub büßte die offizielle Einweihungsfeier für den Kunstrasensportplatz trotz der kalten Witterung und dem Regen nichts ein. -Eine angenehmere Feierstunde hatten sich am vergangenen Samstag wohl alle gewünscht, doch Petrus wollte dabei nicht mitspielen. -So hatten all jene das Nachsehen, die ohne Regenschirm kamen oder solch einen aus irgend einem Grund nicht halten konnten. -Das ging den Musikern, die den Festakt umrahmten, genauso wie so manchem Redner. -Nass wurde deshalb auch der FC-Vorsitzende Albrecht Schlenker, der von der Erfüllung eines Traums sprach. -Sein Dank galt allen voran Bürgermeister Walter Klumpp, der sich für die Umsetzung des Projektes stark engagiert habe, dem Gemeinderat, den beteiligten Unternehmen und Vereinsmitgliedern, die sich einbrachten. -Hier ganz besonders Lothar Held, Paul Weizenegger, Heiner Gail und Peter Graf. -Die rüstigen Rentner verlegten 400 Quadratmeter Pflastersteine für die neuen Wege. -Dieses bemerkenswerte ehrenamtliche Engagement würdigte auch der Landtagsabgeordnete Karl Rombach. -Weitere Grußworte sprachen für den städtischen Sportausschuss Hubert Baier, der Bezirksvorsitzende des südbadischen Fußballverbandes, Kuno Kayan, und Friedrich Knorr vom Planungsbüro, der eine Spende für die Jugendabteilung des Clubs ankündigte. -Bürgermeister Klumpp brachte in Erinnerung, dass immer wiederkehrende witterungsbedingte Einflüsse im Frühjahr und Herbst dem ursprünglichen Rasenplatz so zusetzten, dass er nicht bespielt werden konnte. -Bereits vor 15 Jahren gab es die ersten Überlegungen zur Lösung des Problems, als an den Bau eines dritten Platzes beim Gelände des Fußballclubs oder bei der Realschule am Salinensee gedacht wurde. -Beides wurde wieder verworfen. -Vor sechs Jahren kamen Gedanken zur Anlegung eines Kunstrasensportplatzes auf, was allgemein als die ultimative Lösung angesehen wurde. -Konkrete Planungen wurden vor zwei Jahren aufgenommen, als der FC die Schabelstube in Erbpacht übernahm, eine Kostenbeteiligung von 100 000 Euro für den Bau des Platzes zusagte und das Land Fördermittel von 104 000 Euro billigte. -Der Gemeinderat beschloss im Oktober 2012 die Übernahme der restlichen 356 000 Euro an den Gesamtkosten von 560 000 Euro, fasste Klumpp zusammen. -Der Baubeginn des 68 mal 108 Meter großen Kunstrasenplatzes erfolgte am 4. Juni. -Während der über drei Monate andauernden Bauphase wurden 3000 Kubikmeter Erde abgetragen, wovon die Hälfte für die Anlegung einer Dirt-Bike-Strecke verwendet wurde. -Union und SPD haben ihre Koalitionsverhandlungen mit den Themen Inneres und Justiz fortgesetzt. -Dazu kam die zuständige Arbeitsgruppe am Morgen in Berlin zusammen. -Unter anderem geht es um direkte Demokratie, Abgeordnetenbestechung und die Bundespolizei. -Am Vormittag wollte auch die Arbeitsgruppe Migration und Integration ihre Beratungen fortsetzen. -Dabei dürfte es unter anderem um das Thema doppelte Staatsbürgerschaft gehen, bei dem beide Seiten gegensätzliche Vorstellungen haben. -Die SPD möchte den Zwang für in Deutschland geborene Zuwandererkinder, sich für eine zu entscheiden, beenden - die Union lehnt einen Doppelpass ab. -Beim Thema Verkehr, das an diesem Freitag nicht auf der Tagungsordnung steht, untermauerte die SPD ihr Nein zu einer von der CSU geforderten Pkw-Maut. -Eine Vignette sei eine "Flatrate fürs Vielfahren und damit ökologisch kontraproduktiv", erklärte der SPD-Verhandlungsführer für das Thema Verkehr, Florian Pronold, in einem Schreiben an seine Fraktion. -Es drohten Ausweichverkehre auf Landstraßen, die schon jetzt Unfallschwerpunkte seien. -Die Einführung einer Maut für alle Pkw wäre aus Sicht Pronolds zudem nur ein erster Schritt. -"Es besteht die Gefahr, dass zu einem späteren Zeitpunkt die Kompensation für Deutsche gestrichen wird", sagte er. -Dieter Thomas Kuhn in Stuttgart: Die singende Föhnwelle auf der Freilichtbühne -Stuttgart - "Wann wird's mal wieder richtig Sommer?" war eigentlich nicht die Frage am Freitagabend auf der Freilichtbühne Killesberg. -Es war wahrlich eine tropische Nacht in Stuttgart. -Die besten Voraussetzungen also für Schlagerstar Dieter Thomas Kuhn und seine Band. -Klicken Sie sich durch die Bildergalerie zum Aufrtitt der "singenden Föhnwelle". -Autofahrer bei Unfall schwer verletzt -Bei einem Unfall am Donnerstagabend ist ein 37-jähriger Pkw-Fahrer aus Aachen schwer verletzt worden. -Nach Angaben der Polizei war eine 41-Jährige aus Müsch gegen 21.15 Uhr mit ihrem Wagen auf dem Kempener Außenring in Richtung Grefrath unterwegs. -Als die nach links auf die St. Töniser Straße abbiegen wollte, übersah sie den entgegenkommenden Wagen des 37-Jährigen. -Die Fahrzauge kollidierten. -Der Aachener erlitt schwere Verletzungen und musste zur Behandlung ins Krankenhaus gebracht werden. -Konstanz: Sperrung nach Unfall am Schänzlekreisel -Zu dem Unfall war es laut Polizei gekommen, als ein 51 Jahre alter Fahrer eines Schweizer Seat Ibizas am Donnerstagabend, gegen 20 Uhr, auf der linken Fahrspur der zweispurigen Reichenaustraße stadtauswärts fuhr. -Kurz vor dem Kreisverkehr am Schänzle bemerkte er, dass er sich auf der Linksabbiegespur in Richtung Stromeyersdorfstraße befand und wechselte auf die rechte Fahrspur. -Hierbei kam es zum Zusammenstoß mit dem dort fahrenden BMW eines 23-jährigen Konstanzers. -Ebenfalls beschädigt wurde der hinter dem BMW fahrende VW Passat eines 19-Jährigen. -Insgesamt entstand rund 15000 Sachschaden, teilte die Polizei mit. -Die Fahrbahn musste während der Unfallaufnahme gesperrt werden, hieß es weiter. -Flughafen Wien schafft Passagier-Aufrufe ab -Herr Max Maier, bitte kommen Sie zu Gate 24. -Durchsagen wie diese wird es künftig am Flughafen Wien-Schwechat nicht mehr geben. -Ein Großteil der Lautsprecherdurchsagen fällt damit weg - der Lärmpegel soll so deutlich gesenkt werden. -Eine entspanntere Atmosphäre zu schaffen, ist das Ziel dieser Neuerung. -Wie Airport-Sprecher Peter Kleemann gegenüber Radio Wien bekannt gab, folgt der Flughafen Wien mit der Einstellung von Durchsagen, die einzelne Personen betreffen, einem internationalen Trend. -So verzichten bereits unter anderem die Flughäfen in Kopenhagen, Frankfurt oder Dubai auf die lärmenden Aufrufe. -Durch den Wegfall dieser Durchsagen - im Durchschnitt ertönen an einem Tag rund 200 persönliche Aufrufe durch die Lautsprecher - soll es künftig ruhiger am Flughafen zugehen. -"Es hat sich auch in der Erfahrung gezeigt, dass diese individuellen Durchsagen relativ wenig wahrgenommen wurden, gleichzeitig aber auch alle anderen Informationen zu geänderten Flugsteigen oder Abflugzeiten hier ein bisschen untergehen", so Kleemann im Gespräch mit dem Radiosender. -Highlander-Games auf dem Kaltenhof -Neben dem Bremswagenziehen mit Traktoren gibt es beim landwirtschaftlichen Herbstfest auf dem Kaltenhof vom 6. bis 8. September noch einen zweiten Wettkampf. -Am Samstag, 7. September, starten um 13.30 Uhr die Highlander-Games. -Die haben zwar eine schottische Tradition, sind aber auf dem Kaltenhof schwäbisch angehaucht. -Teams und Einzelkämpfer messen sich dabei in den Disziplinen Baumstammwerfen, Hufeisenwerfen und Wassereimertragen. -Viereinhalb Meter ist der Baumstamm lang und 25 bis 30 Kilogramm schwer. -"Es kommt nicht auf die Weite an", erklärt Organisator Peter Saile. -Der Stamm muss senkrecht abgeworfen werden, soll sich einmal überschlagen und dann gerade zum Liegen kommen. -Das wird mit den meisten Punkten bewertet. -Beim Hufeisenwerfen kommt es auf die Zielgenauigkeit an. -Aus acht Metern Entfernung wird auf einen Pflock geworfen. -Geschicklichkeit und Kraft müssen die Teilnehmer beim Wassereimertragen über 50 Meter unter Beweis stellen. -Zeit und Wassermenge in einem Bottich werden am Ende gemessen. -Aus jedem "Clan" beziehungsweise Team dürfen dann zwei am Einzelwettbewerb teilnehmen. -Die Gewinner des Team- und Einzelwettkampfs erhalten Preise. -Trainiert werden kann schon ab 10 Uhr. -Erwünscht ist eine passende Kleidung, beispielsweise können die Wettkämpfer im Schottenrock antreten. -Doch auch schwäbische Tracht ist erlaubt. -Fürs "Outfit" vergibt die Jury noch ein paar Bonuspunkte. -Anmelden kann man sich bis eine Stunde vor Wettkampfbeginn. -Mit den schottisch-schwäbischen Highlander-Games und dem Traktor-Pulling, das eine halbe Stunde früher beginnt, ist auf dem Festgelände am Samstag schon mal viel Action angesagt. -Abends kommen die Blasmusikfreunde auf ihre Kosten. -Um 19 Uhr gestaltet die biraböhmische Blasmusik aus Schömberg den großen Unterhaltungsabend. -Die Kapelle spielt flotte Polkas und Märsche. -Zu ihrem Repertoire gehören aber auch gefühlvolle Walzer und ein satter Big-Band-Sound. -Mit dabei sein wird der als singender Wirt und Schwarzwurstbaron bekannte Heinz Koch aus Weilen unter den Rinnen. -In Dornhan ist er beim Weihnachtsmarkt und bei der Narrenzunft bereits aufgetreten. -Am Sonntag unterhält der Musikverein Leinstetten zum Frühschoppen, ab 14 Uhr ist die Bauernkapelle Böffingen zu Gast, und die "Oldies" lassen dann das Fest ausklingen. -Auftakt ist bereits am Freitag die Stoppelackerparty mit DJ Ralf. -Weitere Programmpunkte sind am Samstag und Sonntag unter anderem das Oldtimer- und Schleppertreffen, Ausstellungen von Firmen zum Thema Land- und Forstwirtschaft. -Weitere Attraktionen am Sonntag sind das Maislabyrinth, der Handwerkermarkt und das Schafscheren. -Speziell für Kinder gibt es einen Streichelzoo, Strohhüpfburg, Traktorfahren (unter Anleitung) und Traktor-Surfing. -Veranstalter sind der Förderverein Mehrzweckhalle, der Skiclub und die Traktorfreunde "Zündkäpsele". -Milch- und Ei-Alternativen im Überblick -Vegane Ernährung erfordert pflanzliche Alternativen zu Eiern, Milch und Milchprodukten. -Pürierter Tofu mit Pflanzensahne ist beispielsweise ein Quarkersatz. -Weitere nennt der Vegetarierbund zum Weltvegantag am 1. November. -Der Vegetarierbund Deutschland schlägt anlässlich des Weltvegantags am 1. November eine Reihe von veganen Alternativen vor: -Reine Pflanzenmargarine sei eine gute Alternative zu Butter, Joghurt lasse durch Sojajoghurt ersetzen. -Statt Milch könne Soja-, Hafer-, Mandel- oder Reismilch zum Einsatz kommen. -Aufschlagbare Pflanzensahne ersetze herkömmliche Schlagsahne. -Auch zu Eiern gibt es pflanzliche Alternativen. -Eine halbe zerdrückte, reife Banane zum Beispiel kann ein Ei als Bindemittel in einem Kuchen ersetzen. -Auch 50 Gramm Apfelmus oder ein Esslöffel gemahlene Leinsamen plus drei Esslöffel Wasser eignen sich dafür. -Locker werden Teige statt mit Ei zum Beispiel mit einer Mischung aus einem Teelöffel Backpulver, einem Esslöffel Speisestärke und drei Esslöffeln Mineralwasser. -Aber auch 50 Gramm Sojajoghurt oder Seidentofu können diesen Zweck erfüllen. -Nach Angaben des Vebu ernähren sich derzeit in Deutschland rund 800 000 Menschen vegan, also rein pflanzlich. -Schlaflos in New York -Auf dem Weg zu ihren Gastfamilien in Weymouth, Massachusetts, lernten die Schüler des Schwarzwald-Gymnasiums die amerikanische Großstadt New York kennen. -Nach acht Stunden Flug ging es gleich los zur Brooklyn Bridge, über die zu Fuß das Herz der Stadt, Manhattan, erreicht wurde. -Hier bot sich bereits ein faszinierender Blick auf die weltbekannte Skyline, welche im Abendlicht einen besonderen Reiz ausmacht. -Durch die Zeitverschiebung waren die Schüler fast 24 Stunden auf den Beinen, aber getreu dem Motto "Die Stadt schläft nicht" wurde der Tag am Times Square beschlossen. -Trotz Shutdown konnte am zweiten Tag die Freiheitsstatue mit einer Fähre erreicht und zu Fuß erklommen werden. -Anschließend ging es über das Finanzzentrum um die Wall Street zur Gedenkstätte des 11. Septembers. -Am dritten Tag konnten die Schüler von der 373 Meter hoch gelegenen Aussichtsplattform des Empire-State-Buildings einen Überblick über die faszinierend große Stadt gewinnen und dabei die Aussicht genießen. -Danach ging es durch das hektische Treiben der New Yorker Straßen über das Rockefeller Center zum Central Park, der eine ideale Gelegenheit zum kurzen Verweilen in der Herbstsonne bot. -Nach einem kurzen Flug von New York nach Boston sind die Schüler inzwischen in ihren Gastfamilien angekommen und erleben an der Weymouth High School den Schulalltag. -Auch in diesem Jahr wird am Namenstag des Heiligen Hubertus in der Pfarrkirche St. Marien an der Hülsbergstraße eine Hubertusmesse zelebriert. -St. Hubertus war der Sage nach ein recht rücksichtsloser Jäger, der durch eine Vision bekehrt und später zum Bischof von Lüttich ernannt wurde. -Die Jagdhornbläsergruppe der Jagdreiter Westfalen unter der Leitung von Brigitte Kluth musiziert auf Parforcehörnern (Naturhörner in der Stimmung Es) nach alten, ursprünglich französischen Melodien. -Die Bläser tragen Reitkleidung, weil sie vornehmlich Reiter, Pferde und Hundemeute auf Schleppjagden musikalisch begleiten. -Die feierliche Messe findet statt am Sonntag, 3. November, um 11.30 Uhr. -Der neu gestaltete Opschlag glänzt mit einem neuen Cafe. -Eins, das zum Verweilen und Wiederkommen einlädt. -Eins, das den Namen zum Programm machen möchte. -Als Déjà Vu bezeichnet man laut Lexikon ein psychologisches Phänomen, das sich in dem Gefühl äußert, eine neue Situation schon einmal erlebt oder gesehen zu haben. -Im Falle des Besuchs des gleichnamigen Cafés am Opschlag in der Kreisstadt Kleve in Sichtweite zur Hochschule Rhein-Waal muss es nicht beim reinen Déjà Vu-Gefühl bleiben. -Schließlich hat das Café mit dem schönen Namen genau das zum Ziel: einen tatsächlichen Aufenthalt nicht nur gedanklich, sondern handfest immer wiederkehrend erlebbar zu machen. -Weil's eben Spaß macht und einfach schön ist. -Ein Blick ins Innenleben am Opschlag 8 gibt dem Betrachter auf Anhieb ein gutes Gefühl. -Das hochmoderne, aber nicht kühle Café-Ambiente sieht wie ein Ort zum Wohlfühlen aus. -Hier verwöhnt das Team mit den Gründern Mirjam van der Stelt und Daniel Büttner an der Spitze den Gast mit verschiedensten Kaffee- oder Cocktail-Varianten. -Dazu gibt's köstlichen Kuchen oder individuell belegte frische Baguettes. -Die Inhaber, beide 33 Jahre alt, haben sich bereits vor der Eröffnung des schmucken Cafés am Opschlag als Gastronomen einen Namen gemacht: In Kranenburg führten sie das "Art Lounge". -Vom Umzug nach Kleve an den Opschlag erhoffen sich die sympathischen Café-Liebhaber mehr Publikum. -Nicht zuletzt auch durch die Hochschule. -"Wobei wir keine bestimmte Zielgruppe haben", betont Daniel Büttner. -Vielmehr möchte die beiden hier junge Studenten genauso ansprechen wie Senioren und alle Altersklassen dazwischen. -Apropos Café-Liebhaber. -Das sympathische Inhaberpaar steht hinter dem Kaffee-Genuss. -Genießt das duftende heiße Gebräu in den verschiedensten Variationen selbst. -Expressofan -Daniel Büttner ist eher der Expresso-Fan und Mirjam van der Stelt die Cappuccino-Trinkerin. -"Dazu ein Baguette - das ist mein Ding", verrät sie der NRZ. -Zwei Events gab's auch schon im hellen Café. -Besser gesagt eins. -Denn das Hafenfest fand ja eher draußen statt - das Café Déjà Vu profitierte nicht davon. -Aber es gab auch den Ringelnatzabend. -"Schön, erfolgreich und witzig", fanden die beiden 33-Jährigen. -Aber eben nicht Jedermanns Sache. -Vielleicht ist Daniel Büttner auch deswegen kein ausgesprochener Fan von Event-Abenden: "Man kann's ja nun mal nicht allen Recht machen". -Und mit einem Event spreche ich immer nur die an, die sich auch für das spezielle Event interessieren. -Deswegen werden Events eher die Ausnahme bei uns sein. -"Wir möchten den ganz normalen Betrieb so wenig wie möglich stören". -Soll heißen: Déjà Vu steht auch für Verlässlichkeit. -In Bezug auf die Qualität der Produkte, die hier geboten werden, ebenso, wie auf das Team und die schöne Umgebung am Opschlag in Kleve. -Der Straße, die sich mehr und mehr zu einer Gastronomiemeile entwickelt. -Zur Freude vieler Klever, der Gäste aus nah und fern und der Studenten, die die Gastromeile und damit auch das Café Déjà Vu für sich entdecken. -Anke Gellert-Helpenstein -Ein Tag des Dankeschöns beim MGV in Dinker -Es war das 165. Stiftungsfest zu dem der MGV "Friedrich Wilhelm Dinker" in diesem Jahr eingeladen hatte und 52 Gäste, aktive Sänger und ihre Ehefrauen, waren gerne gekommen. -Im Vereinslokal der Gaststätte "Witteborg" in Dinker genossen alle das harmonische Miteinander ganz im Zeichen der Musik. -Traditionell ist das Stiftungsfest auch immer die passende Festivität, um Ehrungen vorzunehmen. -Über die besondere Auszeichnung "Sänger des Jahres" freute sich diesmal Schriftführer Rolf Wagener. -"Du hast nicht nur viel für uns aufgeschrieben, sondern hast auch so manche Runde für uns gedreht, dafür unser herzliches Dankeschön", so der Vorsitzende des Chores Erich Schlotmann. -Die Auszeichnungen für die Teilnahme an den Auftritten und Chorproben mittels Aktivitätssträußchen gingen in diesem Jahr an den Chorleiter Dieter Schulze mit 44 Teilnahmen, gefolgt vom Ehrenvorsitzenden Horst Pier-Ribbert, dem Vorsitzenden selbst, und dem letztjährigen "Sänger des Jahres" Friedrich Winkler mit jeweils 42 Teilnahmen. -Platz fünf belegte Notenwart Artur Brückner. -Ein besonders Dankeschön richtete der Vorstand an das Team von Vereinswirtin Ange Pier-Ribbert, das einmal mehr ein Menü der Extraklasse gezaubert hatte und an Gerda Pier-Ribbert für die Tischdekoration. -Der MGV "Friedrich-Wilhelm" trifft sich zu seinen regelmäßigen Chorproben immer dienstags um 19.45 Uhr im Vereinslokal. -"Neue sangesfreudige Stimmen sind uns jederzeit herzlich willkommen", so Schlotmann. -UN verkündet neue Ziele bei der Armutsbekämpfung -Die Vereinten Nationen beginnen ab sofort mit der Ausarbeitung einer Reihe neuer Ziele, mit denen die vor zwölf Jahren verkündeten Millenniumsentwicklungsziele zur Armutsbekämpfung ersetzt werden sollen. -Im Vorfeld des UN-Gipfels für nachhaltige Entwicklung, der am Abend in Rio de Janeiro begann, spielten australische Diplomaten eine Schlüsselrolle beim Vorstoß für „nachhaltige Entwicklungsziele“, die die 2015 auslaufenden Millenniumsziele ersetzen sollen. -Sie wurden in den abschließenden Entwurf des Dokuments aufgenommen, der von weltpolitischen Führern, einschließlich Frau Gillard, beim Gipfel verabschiedet werden wird. -UN-Generalsekretär Ban Ki-moon erklärte am Abend auf dem Gipfel, der Zeitpunkt sei gekommen, „über die nationalen Interessen hinauszudenken“. -„Ich freue mich, dass sich die Mitgliedsstaaten darauf verständigt haben, einen Prozess in Angriff zu nehmen, mit dem universelle, nachhaltige Entwicklungsziele vereinbart werden sollen“, sagte er. -„Diese Ziele werden auf unseren Fortschritten aus den Millenniumsentwicklungszielen aufbauen und einen integralen Bestandteil des Rahmenwerks für die Entwicklung nach 2015 bilden. -Ich werde keine Mühen scheuen, dieses mir von den Mitgliedsstaaten erteilte Mandat umzusetzen, um unsere Vision der nachhaltigen Entwicklungsziele zu verwirklichen, die auf dem Erfolg der Millenniumsziele aufbauen.“ -Auch wenn die SG Achim/Baden seit vier Spieltagen ohne Sieg ist, herrscht beim Handball-Oberligisten weiterhin gute Stimmung. -Daran lässt Trainer Tomasz Malmon überhaupt keine Zweifel. -Alle sind weiterhin voll motiviert bei der Sache. -Dennoch hoffe ich, dass wir endlich auch mal wieder gewinnen. -"Denn ich weiß ja schon gar nicht mehr, wie so ein Sieg schmeckt", erwartet Malmon ein Erfolgserlebnis von seiner Mannschaft beim VfL Fredenbeck II. -Auch wenn die Drittliga-Reserve aus Fredenbeck aktuell noch keine Bäume ausgerissen hat, hebt Malmon im Vorfeld warnend den Zeigefinger. -Viele Spieler habe ich selber noch in der A-Jugend trainiert. -Von daher werden sie gegen ihren Ex-Coach sicher ganz besonders motiviert sein. -"Zudem gilt es abzuwarten, ob dann noch Verstärkung aus der Ersten dabei ist", verdeutlicht der SG-Coach. -Um endlich wieder in die Erfolgsspur zurückzukehren, muss sich das Malmon-Team im Vergleich zu den vergangenen Auftritten auf jeden Fall steigern. -Vor allen Dingen in der Deckungsarbeit und beim Umschaltspiel haperte es zuletzt doch gewaltig. -Auch die Chancenverwertung muss definitiv besser werden. -Daher hoffe ich, dass meine Spieler während der kurzen Pause endlich den Kopf frei bekommen haben. -Sicherheitsbedenken in Mosambik wachsen angesichts von Zusammenstößen zwischen einflussreichen Persönlichkeiten -Tausende Menschen versammelten sich im Zentrum Maputos unter den wachsamen Augen der Statue von Samora Machel, dem Staatsgründer und ersten Präsidenten Mosambiks, um bei einer seltenen öffentlichen Demonstration Friedensrufe anzustimmen. -„Wir wollen wieder Frieden, wir wollen Stabilität“, sagte Vanessa de Sousa, Geschäftsführerin eines Investment-Unternehmens. -Aus Angst um die Zukunft des Landes hat sie ihre Geschäftskleidung gegen ein T-Shirt mit der Aufschrift „Wir fordern Sicherheit“ auf Portugiesisch eingetauscht und sich am Donnerstag der Menge auf dem Unabhängigkeitsplatz der Hauptstadt angeschlossen. -Seit zwei Wochen gibt es beinahe täglich Berichte über Zusammenstöße zwischen Regierungskräften und Renamo, die zu den schlimmsten Gefechten seit dem Friedensabkommen vor über 20 Jahren zählen. -Renamo war einstmals eine berüchtigte Rebellenbewegung, die anfänglich vom von Weißen regierten Rhodesien und später der Apartheitsregierung Südafrikas unterstützt wurde im Bemühen, die unabhängige Regierung des Landes zu destabilisieren. -Nach dem Friedensvertrag von 1992 wurde sie zu einer Oppositionspartei. -Analysten halten es für unwahrscheinlich, dass das Land wieder in einen ausgewachsenen Konflikt zurückfalle, doch haben die jüngsten Ereignisse ausländische Investoren und Einheimische beunruhigt. -Für die schnell wachsende Wirtschaft steht viel auf dem Spiel, denn die Entdeckung riesiger Gasvorkommen vor der Küste und Kohlelager im Nordwesten könnte in den nächsten Jahren Investitionen von über 50 Milliarden US-Dollar von Firmen wie Rio Tinto, Vale aus Brasilien, Eni aus Italien und Anadarko aus den USA ins Land bringen. -Die regierende Frelimo-Partei, seit 1975 die dominierende Kraft, und Renamo geben sich gegenseitig die Schuld für die Spannungen. -Renamo behauptet, die Regierung habe die jüngsten Zusammenstöße provoziert, indem sie am 17. Oktober ihre Mitglieder in der Provinz Sofala, einer traditionellen Hochburg Renamos, angegriffen hätte. -Die Übergriffe auf die früheren Rebellen eskalierten, als Regierungstruppen Renamo-Stützpunkte attackierten und versuchten, Afonso Dhlakama zu töten, den Anführer der Gruppe, erklärte Renamos Sprecher gegenüber der Financial Times. -Die Regierung gibt Renamo die Schuld für die Zusammenstöße und beschuldigt die Organisation, Soldaten angegriffen zu haben. -Präsident Armando Guebuza hat versucht, Bedenken über eine Instabilität herunterzuspielen. -Gegenüber AFP, der französischen Nachrichtenagentur, sagte er am Mittwoch, dass Dhlakama sich als „Verlierer“ betrachte, der „all seine noch verbleibenden Kräfte einsetzen will, um zu beweisen, dass er der Regierung seine Entscheidungen aufzwingen kann“. -Sowohl Frelimo als auch Renamo betonen, dass sie einen Krieg vermeiden möchten. -Doch die Bedenken haben zugenommen, seit Mazanga mit den Worten zitiert wurde, Renamo gebe das Friedensabkommen von 1992 auf. -Gegenüber der FT erklärte er, damit habe er gemeint, dass Frelimo die Vereinbarung nicht mehr respektiere. -„Unsere Vision ist es, wieder Verhandlungen aufzunehmen und zwar ernsthaft“, sagte Mazanga. -Frühere Gespräche zwischen den Parteien haben nur wenig zur Beilegung der Spannungen beigetragen, die durch eine Reihe von Zusammenstößen in diesem Jahr befeuert wurden. -„Wir haben hier zwei erwachsene Männer (Guebuza und Dhlakama), die die Köpfe zusammenstoßen“, meint Joseph Hanlon, Dozent an der Open University und Mosambik-Experte. -Keiner von ihnen ist gut im Verhandeln und keiner ist bereit, die nötigen Zugeständnisse zu machen. -Renamo, die auf Wahlreformen drängt, hat bereits erklärt, sie werde die Kommunalwahlen im November boykottieren. -Präsidentschafts- und Parlamentswahlen sind für nächstes Jahr geplant. -Einige Kommentatoren haben den Einsatz von Gewalt als den Versuch einer kränkelnden Bewegung interpretiert, Zugeständnisse und finanzielle Vergünstigungen von der Regierung zu erwirken. -Renamos Wahlerfolge schrumpfen seit 1992, während eine neuere Partei, die von einem früheren Renamo-Mitglied gegründete Movimento Democrático de Moçambique (MDM), sich vermutlich bei den Wahlen verbessern wird. -Mazanga behauptet, dass Guebuza – dessen Rücktritt ansteht, wenn seine zweite Amtszeit im nächsten Jahr endet – die Demokratie des Landes zerstören wolle. -„Er will keine Mehrparteien-Demokratie, er will keine transparenten Wahlen, er will keinen Frieden, weil er das Präsidentenamt nicht aufgeben will“, so Mazanga. -Es ist unklar, über welche Kapazitäten Renamo verfügt, doch sie hat Unruhe stiftende Angriffe auf Polizeistationen und Fahrzeuge auf einer wichtigen Nord-Süd-Verbindungen unternommen. -Die meisten Gefechte fanden in der Provinz Sofala statt, die einige Hundert Kilometer nördlich von Maputo liegt, in der sich aber Beira befindet, der Hafen, den Bergbaufirmen wie Rio Tinto und Vale zum Export von Kohle nutzen. -Im Juni unterbrach Rio einen Monat lang die Nutzung der Eisenbahn, nachdem Renamo einen Angriff auf die Strecke angekündigt hatte. -Mazanga antwortete zurückhaltend auf die Frage, ob Renamo diese Drohung wiederholen werde. -Renamo wollte „die internationale Gemeinschaft warnen, dass die Dinge in Mosambik nicht gut stehen“, sagte Mazanga. -Die Instabilität habe die Frustration über die Regierung noch gesteigert, sagt Fernando Lima, Leiter von Mediacoop, einem unabhängigen Medienunternehmen, denn viele Menschen sorgten sich bereits wegen der Korruption, den schleppenden Fortschritten bei der Entwicklung und einer kürzlichen Entführungswelle. -„Die Menschen denken, dass die Regierung und der Präsident für die Zukunft des Landes verantwortlich sind und er derjenige sein sollte, der Lösungen für die Probleme findet“, erklärt er. -Omar Sultane, ein Demonstrant, sagte, die Menschen wollten einfach nur Stabilität. -„Keinen interessieren Renamo und Frelimo, sie wollen einfach nur wieder Frieden und benutzbare Straßen“, erklärte er. -Kinder sollten Mythen und Legenden als „Vorbilder für das Leben“ beigebracht bekommen, meint ein Autor. -Die Geschichten von Thor zeigten, dass „brutale Kraft keine Chance hat gegen clevere Tricks“, und die Artus-Legende demonstriere, wie wichtig es sei, einen Traum zu haben. -Viele der Mythen seien allerdings „bei weitem zu wild, zu anstößig und in manchen Fällen zu schmutzig, um an Schulen gelehrt zu werden“, weshalb Crossley-Holland für eine „sorgfältige Auswahl“ altersgerechter Werke plädierte. -„Ich finde es wundervoll, dass in Amerika Mythen und Folklore bereits Teil der Bildung sind“, sagte er. -Ich habe das schon seit zwanzig Jahren als Plan vorgeschlagen. -Er fügte hinzu, bei Autoren und Lehrern, die „übermäßig didaktisch“ seien, würden Kinder „komplett abschalten“, denn in unterhaltsamen Geschichten würden die Botschaften „unterschwellig“ vermittelt. -Crossley-Holland, der Beowulf aus dem Angelsächsischen übersetzt hat und Autor des Penguin Book of Norse Myths und der British Folk Tales ist, erklärte: „Man kann bestimmte Absichten verfolgen, aber man sollte sie möglichst im Hintergrund halten.“ -Der vielleicht größte Unterschied zwischen einem erwachsenen Autor, der für Erwachsene schreibt, und einem Autor, der für Kinder schreibt, liegt in der Notwendigkeit, Hoffnung zu vermitteln. -Nicht alles muss vereinfacht werden oder zum Happy End führen, aber es gibt einen immanenten Sinn für Gut und Böse. -Dieser muss unterschwellig dargestellt sein, durch die Geschichte offenbart werden und nicht erklärt. -Das ist die alte Geschichte vom Zeigen statt Erzählen. -Hansjakob zieht noch einmal in Freihof -Vor 100 Jahren hat Heinrich Hansjakob seinen Altersruhesitz in Haslach bezogen, am Sonntag wurden seine letzten Jahre im "Freihof" lebendig. -Die vielen Zuschauer waren vom äußerst kurzweiligen Szenenspiel begeistert. -Von der Ankunft über die Verleihung der Ehrenbürgerschaft bis hin zu seinem Tod hatte Drehbuchautor und Hansjakobdarsteller Alois Kraftzcyk ein unterhaltsames Stück entworfen, das unter der Regie von Cornelia Volk viel Beifall bekam. -Marcus Zagermann nahm das Publikum als Sprecher mit durch die zehn verschiedenen Szenen, erklärte Zusammenhänge und überbrückte Zeitsprünge von der Jugend bis zur nahenden Pensionierung. -Am 22. Oktober 1913 kommt Hansjakob heim in seinen Kinderhimmel, in seinen Freihof. -Und dann begann das Szenenspiel, in dem Alois Krafczyk einmal mehr in seiner Paraderolle als großer Sohn der Stadt glänzte. -Stilecht fuhr er mit dem Zweispänner und "Schwarzwälder Füchsen" von Erich Becherer aus Mühlenbach vor. -Er wurde nicht nur vom Publikum mit viel Applaus empfangen, auch seine Schwester Philippine hieß ihn willkommen. -Billy Sum-Hermann verstand es unglaublich gut, sich in die Rolle der Schwester hineinzuversetzen und verlieh ihr in Mimik und Gestik ungeheures Leben. -Zwischen den einzelnen Spielszenen spielte die Mühlenbacher Bauernkapelle passend ausgewählte Musikstücke und rundete damit die Aufführung ab. -Zur Verleihung der Ehrenbürgerwürde gab sich dann Haslachs Bürgermeister Heinz Winkler die Ehre, der in Vertretung des damaligen Amtsinhabers Hättich zusammen mit einem Teil des Stadtrates seine Aufwartung machte. -Ihre Aufwartung machten anschließend die Dreikönig- singer, die Hansjakob zu dem Ausruf veranlassten: "Ach wie schön, da werden wieder Jugenderinnerungen an meine eigene Dreikönigzeit lebendig". -Mit ihrem Lied "O Jesulein" erfreuten sie das Publikum im Freihof ebenso wie die Storchentagskinder mit ihrem lauten "Heraus, Heraus". -Seine Kritik an der Amtskirche hatte Hansjakob damals genauso geäußert, wie seine Bedenken über die Auswüchse in der Landwirtschaft, die Folgen der Industrialisierung oder die Auswirkungen des Krieges. -In der Heimaterde, hinter seiner Grabkapelle auf der Brand bei Hofstetten, fand er seine Ruhestätte. -"Ein unruhiger Geist hatte endlich seine Ruhe gefunden und war für immer heimgegangen", hieß es am Ende des Szenenspiels. -Viel Applaus und viele lobenden Worten gab es vom Publikum für die Aufführung. -Basketball: Die Hoffnung der Neckar Riesen wächst -Die Chancen auf den Ligaverbleib der sportlich abgestiegenen Ludwigsburger Bundesliga-Basketballer sind gestiegen. -Denn sollte es ein Wildcard-Verfahren geben, dann gibt es für die Neckar Riesen nun einen Mitbewerber weniger. -Das Basketball-Projekt Hamburg Towers verzichtet auf eine Bewerbung für einen möglichen Nachrückerplatz. -"Wir werden nicht am Wildcard-Verfahren teilnehmen", sagte Ex-Nationalspieler Pascal Roller, der seit 2012 das Konzept für einen Proficlub in der Hansestadt aufstellt. -Ein erstes Aufatmen war aus Ludwigsburg dennoch nicht zu vernehmen. -"Wir beschäftigten uns nicht mit dem Wildcard-Verfahren, so lange nicht feststeht, dass Düsseldorf keine Lizenz erhält", sagte Neckar-Riesen-Boss Alexander Reil unserer Zeitung. -Bis zum 23. Mai kann Aufsteiger Düsseldorf Baskets noch Unterlagen beim Schiedsgericht einreichen und seine wirtschaftliche Bundesliga-Tauglichkeit nachweisen. -Bisher wurde den Rheinländern die Lizenz aber zweimal verweigert. -Das Schiedsgericht ist die letzte Instanz. -Haigerloch: Abendmahlskirche rückt in den Blickpunkt -Als Beitrag der Stadt zum 150-jährigen Bestehend der Evangelischen Kirche in Haigerloch widmet das Kultur- und Tourismusbüro der Stadt die letzte ihrer diesjährigen öffentlichen Themenführungen am Sonntag, 27. Oktober, der Abendmahlskirche. -Nach dem Besuch des Atomkeller-Museums soll die Haigerlocher Zeit nach 1850 betrachtet werden, als Haigerloch unter preussische Herrschaft kam. -Krönender Schlusspunkt ist dabei ein Besuch in der Abendmahlskirche. -Dort bekommen die Führungsteilnehmer die Geschichte vom Bau der Kirche erzählt und gewinnen Einblicke in die Entwicklung der evangelischen Gemeinde in einer durch und durch katholischen Gegend. -Nicht zuletzt fällt in der Kirche auch der Blick auf das von Friedrich Schüz mit Unterstützung von Walter Kröll und Georg Halbritter geschaffene Abendmahlgemälde. -Es ist eine orginalgetreue Reproduktion des berühmten Mailänder Vorbildes von Leonardo da Vinci. -Treffpunkt zu der andert­halbstündigen Führung ist um 15 Uhr am Atomkeller-Museum. -Karten gibt es an der Kasse des Atomkellermuseums. -Für Fragen und Informationen steht das Tourismusbüro der Stadt Haigerloch zur Verfügung. -Snowden könnte auch in Russland aussagen -Der frühere US-Geheimdienstexperte Edward Snowden könnte in der US-Spähaffäre auch in seinem russischen Asyl Aussagen machen. -Vertreter der Bundesgeneralstaatsanwaltschaft könnten entweder schriftlich Fragen stellen oder den 30-Jährigen auch persönlich in Russland treffen. -Das meldete die Agentur Interfax unter Berufung auf nicht näher benannte Kreise, die mit der Lage vertraut seien. -Eine solche Frage könne zwischenstaatlich geklärt werden, hieß es. -Eine Ausreise Snowdens aus Russland ist praktisch ausgeschlossen. -"In diesem Fall verliert er seinen Flüchtlingsstatus", zitierte die Agentur ihre Quelle. -Weil Deutschland Verbündeter der USA sei, drohe Snowden dort die Auslieferung, hieß es weiter -Langer Atem macht sich bezahlt -Sechs Männermannschaften kämpften beim Tennisturnier der Vereine um den Siegertitel. -Am Ende entschied die Ausdauer vom Team Maier/Bronner das letzte Match. -Ein Traumwetter hatte die Tennisabteilung der Sportfreunde Rohrdorf zum Turnier der örtlichen Vereine und Freunde erhalten. -Der Spaß am Tennisspielen stand im Vordergrund des Turniers. -Es standen fünf Spiele pro Team auf dem Plan, wobei jeder gegen jeden spielte. -Gespielt wurde im Doppelmodus mit einem langen Satz bis zu neun Punkten oder maximal 45 Minuten Dauer. -Anja Schlichter leitet das Turnier -Die Turnierleitung lag in den bewährten Händen von Anja Schlichter, unterstützt wurde sie von Carmen Müller und Inga Kronemeyer. -Nach den ersten drei Spielen und einer Mittagspause mit Pasta ging das Turnier in die entscheidende Phase. -Die Favoriten Andre Maier/Matthias Bronner lagen punktgleich mit Michael Klippel und Sadmin Osmicic (beide Teams der SG Rohrweiler), so dass die letzten beiden Matches die Entscheidung bringen mussten. -Bei der Siegerehrung konnte Carmen Müller den Wanderpokal schließlich an Andre Maier/Matthias Bronner überreichen, die auf der Zielgerade den längsten Atem hatten. -Den zweiten Platz belegte das Doppel Michael Klippel/Sadmin Osmicic. -Das Bronzetreppchen erreichten Rainer und Bernd Maier des Teams Asphaltriecher. -Bei Spätnachmittagsonne und Kaffee und Kuchen fand der gelungene Ausklang des Turniers statt. -Am letzten Augustwochenende ist das Schwarzwälder Freilichtmuseum Vogtsbauernhof in Gutach bei Hornberg wieder fest in Kinderhand. -Traditionell heißt das Museum die Besucher am Samstag und am Sonntag, den 24. und 25. August, im Rahmen des Sommerferienprogramms zu einem bunten Kinder- und Familienfest mit zahlreichen Mitmachaktionen und Veranstaltungen willkommen. -"Groß und Klein können an beiden Tagen verschiedene alte Handwerke erlernen", heißt es in der Pressemitteilung. -Ob Seife herstellen, Kerzen drehen, filzen oder Seile fertigen, für jedes Alter ist das Passende dabei. -Den Kindern stehen regionale Handwerker von 11 bis 17 Uhr helfend zur Seite. -In der Museumswerkstatt wird ebenfalls gewerkelt. -Beim Bau von Kuckuckspfeifen, Wasserrädern oder Kreiseln können die jungen Gäste ihr handwerkliches Geschick unter Beweis stellen. -Viel Spaß versprechen auch die vielen alten Spiele, wie Schubkarren-Rennen oder Stelzenlaufen. -Schnelligkeit und Koordination sind hier gefragt. -Für alle kleinen Detektive bietet das Freilichtmuseum am Samstag und am Sonntag, jeweils um 12 und 14 Uhr, eine Rätselführung durch das Museum an. -Auf einem Geländerundgang müssen die Kinder erraten, welche Geschichten der Wahrheit entsprechen oder doch gelogen sind. -Darüber hinaus haben die Besucher die besondere Gelegenheit, das Freilichtmuseum während einer Kutschfahrt mit Schwarzwälder Füchsen kennenzulernen. -Nicht nur Pferde, sondern auch viele weitere Tiere wie Schafe, Ziegen, Kühe und Hühner sind auf dem Gelände anzutreffen und zu bestaunen. -Zusätzlich dürfen sich alle Kinder am Sonntag auf den Clown Otsch freuen, der seinen Schabernack ab 11 Uhr mit den Museumsgästen treibt. -Gemütlich und märchenhaft geht es unterdessen bei Hermann Büttner zu. -Der Geschichtenerzähler nimmt die kleinen Gäste um 11, 13 und 15 Uhr mit in die wunderbare Welt der Märchen. -Zudem führt die Kindertrachtentanzgruppe aus Gutach um 11.30 Uhr traditionelle Tänze vor. -Des Weiteren lassen sich an beiden Veranstaltungstagen von 11 bis 17 Uhr zahlreiche Handwerker wie der Besenbinder, die Näherin, die Spinnerin, die Weberin und die Bäckerin bei ihrer traditionellen Arbeit über die Schulter schauen. -Noch bis zum Ende der Sommerferien in Baden-Württemberg erwartet die Besucher des Freilichtmuseums ein abwechslungsreiches Ferienprogramm. -In einer neuseeländischen Kleinstadt gilt Burt Muro als liebenswerter Kauz, weil er sich vorgenommen hat, mit seinem Motorrad, Baujahr 1920, an einem Rennen in Amerika teilzunehmen. -Burt ist trotz eines Herzfehlers nicht von seinem Traum abzubringen. -Mit seinem Ersparten, einpaar Spenden und einer Hypothek auf seinen alten Werkstattschuppen setzt er per Schiff nach Los Angeles über. -Nach dem Film gibt es Kaffee, Tee und Kuchen. -Schrilles Piepsen stört Anwohner -Rhenus Midgard hat auch an Land kräftig investiert. -Sie verteilen die von den Schiffen kommende Kohle auf die Halde. -Das Unternehmen hat am Standort gleich mehrere Eisen im Feuer. -Neben dem Kohleimport setzt Rhenus Midgard unter anderem auf Windanlagen-Logistik. -Schwarz schimmert die Steinkohle auf dem Lagerplatz vor dem Deich. -Zwei gewaltige blaue Haldenschütt- und Rückladegeräte verteilen oder verladen die Kohle hier nach Bedarf. -Bei jeder Bewegung der Umschlaggeräte tönt ein schrilles Warngeräusch übers Gelände. -"Die Signale dienen der Sicherheit und sind gesetzlich vorgeschrieben", sagt Matthias Schrell, Geschäftsführer der Rhenus Midgard in Wilhelmshaven. -Bei ungünstig stehendem Wind ist das leider weithin zu hören - und so gab es bereits einige Beschwerden von Leuten aus dem Stadtnorden. -Er nehme die Klagen sehr ernst und habe deshalb Kontakt aufgenommen mit den Betroffenen, sagt der 40-Jährige. -In enger Absprache mit den Behörden sei man nun dabei, die Warnsignale mit technischen Maßnahmen leiser zu halten. -Auch künftig setze er auf den offenen Dialog mit den Nachbarn. -Denn Matthias Schrell hat noch einiges vor am Standort. -Mit dem Ausbau der Niedersachsenbrücke setzt Rhenus Midgard weiterhin auf den Kohleimport für Kraftwerke und ist auf diesem Sektor einer der Großen in Europa. -Dank der auf 18,5 Meter vertieften Liegewanne vorm Terminal können längst auch Capesize-Bulkcarrier hier abgefertigt werden. -"In diesem Jahr wollen wir beim Umschlag die 3-Millionen-Tonnen-Marke knacken", so Schrell. -Neben dem Eon-Kraftwerk vor Ort geht die Importkohle zu Kraftwerken im Binnenland. -Wenn dann noch das GDF-Suez-Kraftwerk ans Netz gehe, seien gut 5 Millionen Tonnen Kohleimport pro Jahr realistisch. -Die drei Schiffsentlader auf der Brücke sowie das zweite Transportband könnten bis zu 10 Millionen schaffen. -Bayerns Basketballer trotz erster Niederlage optimistisch -Selbst die erste Niederlage in der Euroleague konnte bei den Basketballern des FC Bayern München den riesigen Optimismus nicht schmälern. -"Niemand kann uns verbieten daran zu glauben, auch gegen das beste Team Europas der vergangenen beiden Jahre zu gewinnen", bilanzierte Bayern-Coach Svetislav Pesic nach dem unglücklichen 83:88 (39:47) am Donnerstagabend bei Titelverteidiger Olympiakos Piräus. -Wir haben die Message gesendet: Auch in München existiert Basketball! -Bei ihrem erst dritten Auftritt in der europäischen Königsklasse verkauften sich die Münchner lange sehr gut, waren in den entscheidenden Phasen aber einfach nicht abgeklärt genug. -Die Gäste schafften zunächst das Kunststück, einen 15-Punkte-Rückstand 13 Minuten vor Schluss noch in eine Führung umzuwandeln, ehe Piräus dem Spiel erneut eine Wende gab. -"Wir haben uns unglaublich zurückgekämpft gegen den zweimaligen Euroleague-Champion, aber dann wieder leichtsinnige Fehler gemacht", sagte Bayern-Profi Yassin Idbihi. -Wir sind stolz auf unsere Leistung, aber wir wollen jedes Spiel gewinnen. -Malcolm Delaney und Nihad Djedovic waren die erfolgreichsten Werfer der Münchner, die in den ersten beiden Partien gegen den italienischen Serienmeister Montepaschi Siena und den polnischen Titelträger Zielona Góra deutliche Siege gefeiert hatten. -Ben Greenman: Zehn Jahre New York Comedy Festival: The New Yorker -Man könnte sagen, dass New York City die Geburtsstätte von Stand-up Comedy in Amerika ist: Vor knapp hundert Jahren fing der Varietekünstler Frank Fay, der als Conférencier im Palace Theatre am Broadway arbeitete, damit an, der Menge direkt Witze in Form eines Gesprächs zu erzählen. -Im Laufe der Jahre wurde Fays Innovation ausgebaut, so wie zuletzt auf dem New York Comedy Festival. -Ins Leben gerufen und geleitet von Caroline Hirsch, der Gründerin der Stand-up-Institution Carolines, feiert das Festival in diesem Jahr sein zehnjähriges Jubiläum mit über sechzig Shows in kleinen Clubs und großen Theatern. -„Die meisten dieser Headliner fingen bei Carolines an und wurden dann immer erfolgreicher, bis sie irgendwann zu bekannt waren, um in einem Club aufzutreten“, sagte Hirsch. -Wir haben dieses Festival konzipiert, damit wir weiter mit ihnen arbeiten können. -Bei der diesjährigen Veranstaltung gibt es Auftritte von Wanda Sykes, Kathy Griffin und Bill Maher sowie auch von „Stand Up for Heroes“, einer jährlichen Musik- und Comedy-Benefizveranstaltung für Armeeveteranen im Madison Square Garden, bei der unter anderem Bruce Springsteen, Jon Stewart, Roger Waters und Bill Cosby auftreten. -Mit dem Festival ist auch die Welt der Comedy gewachsen. -Eine Reihe der Comedians, die dieses Jahr am Festival teilnehmen, sind über eher unkonventionelle Wege bekannt geworden, wie beispielsweise Sendungen auf kleineren Fernsehkanälen wie Comedy Central, FX und Spike. -Nick Kroll wurde mit einer Sitcom im Kabelfernsehen bekannt (die ironisch-schlüpfrige Fantasy-Football-Serie „The League“ auf FXX) und hat jetzt seine eigene Sketch-Show auf Comedy Central. -Jenny Slate war sowohl bei „Saturday Night Live“ als auch bei „Parks and Recreation“ Teil der Besetzung, obwohl sie am bekanntesten für ihre virale Videoserie „Marcel the Shell with Shoes On“ ist. -Sowohl Kroll als auch Slate und andere junge Comedians mit charakteristischen Stimmen (der surreal pessimistische Anthony Jeselnik, der ironische, auf Rassenthemen konzentrierte W. Kaumau Bell) sind Produkte der dezentralisierten Welt der amerikanischen Comedy. -Zu den größten Besuchermagneten des Festivals gehört ein Interview: David Steinberg spricht mit Larry David. -Steinberg begann als Stand-up Comedian, hat sich aber zu einem angesehenen Fernseh- und Filmregisseur sowie zu einem inoffiziellen Comedy-Historiker entwickelt. -Von 2005 bis 2007 moderierte er eine Fernsehsendung mit dem Titel „Sit Down Comedy with David Steinberg“ bei TV Land. -Das Treffen findet in der Town Hall mitten in Manhattan statt. -„Die Stadt ist definitiv in der Comedy-DNA von Larrys gesamter Arbeit“, sagte Steinberg. -Er hat mir erzählt, wenn er hier ist, dann gehe er manchmal durch die Gasse zwischen zwei Gebäuden und denkt sich: Hey, wenn ich mein ganzes Geld verliere, dann lebe ich vielleicht hier. -Iran zufrieden mit Prozess der Atomverhandlungen -Der Iran zeigt sich eine Woche vor dem nächsten Atomtreffen mit den fünf UN-Vetomächten und Deutschland zufrieden mit dem Verhandlungsprozess. -"Nach Jahren habe man nun eine Einigung mit der Internationalen Atomenergiebehörde, um die Differenzen der letzen Jahre auszuräumen", schrieb Außenminister Mohammed Dschawad Sarif auf seiner Facebookseite. -Es wäre zwar noch ein langer Weg, aber der Atomchefunterhändler sei mit dem Verhandlungsprozess zufrieden und auch optimistisch, dass am Ende beide Seiten zu einer Lösung kommen. -Drama in Uruguay: Jungen töten Spielkamderaden mit Machete -Zwei Kinder haben in Uruguay den Mord eines 11-Jährigen eingestanden. -Die beiden 14 und 12 Jahre alten Jungen hatten ihr Opfer eingeladen, mit ihnen auf Vogeljagd zu gehen. -Mit Macheten und Messern bewaffnet erstachen sie am Dienstag den 11-Jährigen und warfen den Leichnam in einen Brunnen, berichtete die Zeitung "El País" am Donnerstag. -Anschließend hätten sie vor ihren Wohnungen in einem Arbeiterviertel Fußball gespielt. -Der Leichnam wurde in der folgenden Nacht in der Ortschaft Cerro Pelado, 15 Kilometer nördlich vom exklusiven Strandort Punta del Este aufgefunden. -Als Alibi sollte eine fünfjährige Halbschwester des Jüngeren, die sie mitgenommen hatten, aussagen, sie sei von dem Jungen sexuell angegriffen worden. -Als das Mädchen aber vor der Polizei den Tathergang ohne Erwähnung eines Angriffs gegen sie beschrieb, gaben die zwei Jungen die vorsätzliche Tötung des 11-Jährigen zu. -Als Grund nannte der Ältere unter anderem eine Rivalität im Fußballspiel. -Er wurde vorläufig in eine psychiatrische Jugendanstalt eingeliefert. -Zuhälter muss ins Gefängnis -Wegen ausbeuterischen Menschenhandels, Zuhälterei, Bedrohung, Beleidigung und gefährlicher Körperverletzung hat das Landgericht Konstanz einen 33-jährigen Mann aus St. Georgen zu dreieinhalb Jahren Haft verurteilt. -Der ehemalige Bundeswehrsoldat, der nach zwei Einsätzen in Afghanistan als Türsteher arbeitete, legte im Rahmen einer Prozessabsprache ein Geständnis ab. -Demnach hat er in fünf Fällen Frauen über Internet- oder Telefonkontakte kennengelernt, die er nach einigen Wochen gegen ihren Willen auf den Strich schickte. -Widerstand pflegte er mit Gewalt und Drohungen zu brechen. -Die Einnahmen der Frauen, die zum Teil jahrelang für ihn anschafften, behielt er ein. -Durch Konfiszierung von Handys und SIM-Karten unterband er Kontakte der Frauen zur Außenwelt. -Zusammen mit einem Kollegen, der die Frauen vermittelte, brachte er sie in verschiedene Bordelle im Südwesten. -Dort ließ er sie zum Teil überwachen, um ihre Einnahmen zu kontrollieren. -Vor Gericht behauptete der Angeklagte zunächst, er leide unter einem schweren Trauma, nachdem Kollegen in Afghanistan neben ihm bei einer Explosion ums Leben gekommen seien. -Nachdem ein psychiatrischer Sachverständiger große Zweifel an der psychischen Einschränkung und damit an einer eingeschränkten Schuldfähigkeit andeutete, legte der 33-Jährige ein umfassendes Geständnis ab. -Das Gericht musste keine der geschädigten Frauen mehr vernehmen. -Wie ein Kripo-Beamter berichtete, konnten nach einer ersten Anzeige durch Telefonüberwachungsmaßnahmen mindestens vier weitere Fälle von ausbeuterischem Menschenhandel ermittelt werden. -Die abgehörten Gespräche seien von hoher Aggression geprägt gewesen. -In späteren Vernehmungen hätten die Geschädigten ihr Martyrium bestätigt. -Eine weitere Zeugin, bei der eine der Frauen am ganzen Körper blaue Flecken entdeckt hatte, habe bis heute keine Aussage gemacht. -Es handle sich um "eine völlig gebrochene Persönlichkeit", die Gewalt und Ausbeutung in diesem Metier als normal ansehe. -Der Angeklagte bedauerte sein Verhalten gestern. -"Er habe sich aus diesen Kreisen gelöst und wolle nach der Haft ein ganz normales Leben führen", meinte er. -Ölpreis gibt weiter nach bis auf 96 USD pro Barrel -Der Ölpreis ist auch am Freitag weiter gefallen, nachdem Sorgen über ein hohes Angebot einen Bericht überschatteten, wonach der energiehungrige Fertigungssektor Chinas wächst. -Die Referenzrohölsorte für Lieferungen im Dezember war am späten Morgen in Europa im elektronischen Handel an der New York Mercantile Exchange um 14 Cent auf 96,24 USD pro Barrel gefallen. -Der Vertrag fiel um 39 Cent am Donnerstag, was einen Preisverfall von 5,8 Prozent im Oktober bedeutete. -Das große Rohölangebot hat die Preise in den vergangenen Wochen belastet. -Das Energieministerium der USA erklärte am Mittwoch, das US-Angebot habe letzte Woche um 4,1 Millionen Barrel zugenommen. -Im Laufe von fünf Wochen war das Angebot um 25 Millionen Barrel gestiegen. -Doch am Freitag gab es Hinweise auf stärkere Nachfrage durch zwei Berichte über einen Aktivitätszuwachs in der chinesischen Fertigung. -Dies deutet auf eine fortgesetzte Erholung der Wirtschaft Chinas hin, deren Wachstum auf 7,8 Prozent im dritten Quartal angestiegen war, nachdem es im vorherigen Quartal auf dem niedrigsten Stand seit zwei Jahrzehnten angelangt war. -Rohöl von der Sorte Brent, ein Referenzwert für internationales Rohöl, das auch von US-amerikanischen Raffinerien verwendet wird, fiel an der ICE Exchange in London um 26 Cent auf 108,58 USD pro Barrel. -Gericht blockiert Entscheidung zur Stop-and-Frisk-Richtlinie des NYPD -Ein Bundesberufungsgericht in den USA blockierte am Donnerstag die Anordnung einer Richterin, die Änderungen am Stop-and-Frisk-Programm („Anhalten und durchsuchen“) des New Yorker Polizeidepartments gefordert hatte, und entband die Richterin von dem Fall. -Das 2. US-Berufungsgericht erklärte, die Entscheidungen von Richterin Shira Scheindlin würden ausgesetzt, bis über eine Berufung der Stadt entschieden sei. -Die Richterin hatte im August geurteilt, die Stadt verstoße gegen die Verfassung mit der Art und Weise, wie das Programm für das Anhalten und Befragen von Personen umgesetzt werde. -Die Stadt legte Berufung gegen ihre Einschätzung und die angeordneten Abhilfemaßnahmen ein, einschließlich der Entscheidung, eine Überwachungsstelle einzurichten, die die Polizei bei der Änderung ihrer Richtlinie und des entsprechenden Schulungsprogramms unterstützen solle. -Das Berufungsgericht hörte am Dienstag die Argumente zur geforderten Aussetzung. -Das Berufungsbericht erklärte, die Richterin müsse von dem Fall entbunden werden, da sie den Verhaltenskodex für US-amerikanische Richter verletzt habe, indem bestimmt ist, dass ein Richter den Anschein der Parteilichkeit vermeiden soll; dies wurde teilweise mit einer Reihe von Medieninterviews und öffentlichen Aussagen begründet, in denen sie öffentlich auf die Kritik des Gerichts reagiert hatte. -Die Richterin hatte geurteilt, Polizeibeamte verletzten die Bürgerrechte von Zehntausenden von Menschen, weil sie in unangemessener Weise gezielt schwarze und lateinamerikanische Männer mit dem Stop-and-Frisk-Programm ins Visier nähmen. -Sie ernannte eine externe Stelle zur Überwachung der Umsetzung wesentlicher Änderungen, einschließlich Reformen bei Richtlinien, Schulung und Dienstaufsicht, und ordnete ein Pilotprogramm mit am Körper getragenen Kameras in Vierteln an, in denen die meisten Kontrollen durchgeführt wurden. -Im August erklärte sich die Stadt New York bereit, die Praxis der Speicherung von Namen und Adressen von Personen zu beenden, die nach einer Kontrolle durch die Polizei wieder entlassen wurden. -Eine mündliche Verhandlung zur Berufung der Stadt ist nach dem 14. März 2014 geplant. -Die Stop-and-Frisk-Taktik wurde von einer Reihe von Bürgerrechtsverfechtern kritisiert. -Kontrollen dieser Art gibt es seit Jahrzehnten in verschiedenen Formen, doch stieg die Anzahl der aufgezeichneten Kontrollen während der Amtszeit des parteilosen Bürgermeisters Michael Bloomberg auf ein Allzeithoch von 684.330 im Jahr 2011, die meisten davon schwarze und lateinamerikanische Männer. -2004 wurde von vier Männern, die alle Minderheiten angehörten, Klage eingereicht, die sich zu einer Sammelklage entwickelte. -Befürworter von Änderungen am Stop-and-Frisk-Programm des NYPD sagen, die Veränderungen werden unfaire Praktiken beenden und eine vertrauenswürdigere und effektivere Polizei schaffen sowie die Art und Weise beeinflussen, wie andere Polizeipräsidien die Regelung einsetzen. -Gegner sind der Ansicht, die Veränderungen würden die Moral der Polizei dämpfen, aber nicht die Kriminalität; auch würde Geld verschwendet und das breitere Problem einer Polizei nicht gelöst, die nach dem Wegfall Tausender von Stellen im letzten Jahrzehnt unter Druck stehe. -Die Richterin wies darauf hin, dass sie die Stop-and-Frisk-Praxis nicht beende, die verfassungsmäßig sei, sondern nur die Art der Umsetzung der Kontrollen durch das NYPD reformiere. -Vom Glück der träumenden Kamele -Glück ist eine Oase, die zu erreichen nur träumenden Kamelen gelingt -Mit dieser Beduinenweisheit beschrieb der erste Vorsitzende Wolfgang Henne die Geschichte und Faszination des Vereins "Helfende Hände". -Am Samstagnachmittag wurde der zehnte Geburtstag des Vereins gefeiert, der schon beachtliche Erfolge aufweisen kann. -Der erste Vorsitzende Henne berichtete ausführlich von der Arbeit des Vereins. -Er selbst war im Jahr 2004 das erste Mal in Mauretanien im Cheijk-Zajed-Krankenhaus in Nouakchott. -In der Folge entwickelte sich eine weitere Kooperation, und der Gynäkologe Henne führte vor Ort auf mehreren Reisen zahlreiche Operationen durch. -Während der Reden wurden Bilder von den Einsätzen auf einer großen Leinwand gezeigt, so dass sich die Gäste der Jubiläumsveranstaltung von den erwähnten Aktionen ein Bild machen konnten. -Das Wüstenfahrzeug des Vereins wurde auch gezeigt. -Es handelt sich um ein ehemaliges Bundesgrenzschutzfahrzeug. -Rainer Prewo, der ehemalige OB von Nagold, hatte den Vorschlag gemacht, dieses Fahrzeug mit einer Fotovoltaikanlage auszustatten und plant nun, laut Henne, beim Verein in beratender Funktion tätig zu werden. -Im Jahr 2008 hat die Zimmerei Schleeh aus Baiersbronn in der Rekordzeit von knapp einer Woche die Krankenstation in Socogim, ein Elendsviertel am Rande der Hauptstadt erstellt - ehrenamtlich. -Henne erwähnte auch ein neues Mutter-Kind-Krankenhaus in Nouakchott -Besonders interessant in Zeiten in denen in Nagold die Geburtsklinik wegrationalisiert werden soll. -Man lerne viel in Afrika als Mediziner, so Henne, zum Beispiel Entschleunigung gegen Burn-out. -Aufgrund der politischen Unruhen im Land konnte man nicht so viele Reisen wie geplant unternehmen, aber dafür kamen viele Mediziner nach Deutschland, um hier in verschiedenen Bereichen zu hospitieren. -Außerdem werden regelmäßig Container mit Material nach Afrika geschickt, so im März diesen Jahres medizinische Geräte, Medikamente, Verbandsmaterial, Krankenhausbetten und Brillen. -Mitinitiator Hans-Joachim Fuchtel selbst lieferte reichlich Lokalkolorit, indem er aus Mauretanien von zum mobilen Ziegenstall umgebauten Mercedes-Fahrzeugen erzählte und die Erlebnisse der Stammheimer Musiker, welche den Verein traditionell unterstützen, bei ihrer Mauretanienreise schilderte. -Als seine persönlichen Beweggründe nannte er seine Arbeit im Bereich Rechnungsprüfung als junger Abgeordneter. -Als ihm klar geworden sei, wie viel Geld unterwegs verloren ging, habe er beschlossen, den Afrikanern bei der Kontrolle eines Budgets durch das Volk zu helfen. -Außerdem betonte Fuchtel, dass globale Wirtschaft auch globale Menschenliebe verlange. -Es solle auch keineswegs vergessen werden, dass Afrika näher liegt als mancher denke. -Von Gran Canaria sei es nicht weit nach Afrika. -Coulson setzte Telefon-Hacking für das Überprüfen eines Tipps ein -Der frühere News of the Word-Herausgeber Andy Coulson setzte angeblich „Telefon-Hacking, Überwachung und Konfrontation“ beim Versuch ein, einen falschen Tipp über eine Affäre des damaligen Innenministers Charles Clarke zu bestätigen. -Staatsanwalt Andrew Edis QC erklärte dem Gericht im Old Bailey, News of the World habe im Mai 2005 ein falsches Gerücht dahingehend gehört, dass Clarke ein Verhältnis mit seiner „attraktiven Sonderberaterin“ Hannah Pawlby hätte. -Die Zeitung beauftragte den Privatdetektiv Glenn Mulcaire damit, Pawlbys Voicemails zu hacken, und verfolgte sie, aber Coulson rief sie auch an und hinterließ Nachrichten, wie das Gericht erfuhr. -„Nach Auffassung der Staatsanwaltschaft ist Coulson, der jetzt Herausgeber der NotW ist, niemand, der vor der Wohnung von Leuten steht und hofft, sie auf frischer Tat zu ertappen, sondern einer, der Leute gern mit einer Geschichte konfrontiert, um ihre Reaktion zu sehen“, erklärte Edis. -Er sagte, NotW verwende drei Verfahren, um Geschichten investigativ zu recherchieren: Telefon-Hacking, Überwachung und Konfrontation. -Der Herausgeber ist persönlich an der dritten Methode beteiligt. -Offensichtlich weiß er von der zweiten, der Überwachung, anders kann es nicht sein. -Wie sieht es mit der ersten aus? -Weiß er über das Telefon-Hacking Bescheid? -Er streitet das ab, aber wir sagen: „Und ob er davon wusste.“ -Gerüchte über eine Affäre Clarkes wurden zuerst von der Feature-Redaktion von NotW aufgegriffen, als einer Quelle, die sexuell an Pawlby interessiert war, gesagt wurde: „Verschwenden Sie nicht Ihre Zeit, sie ist mit Charles zusammen.“ -Ein Band mit Voicemails von ihrem Telefon von mindestens drei Anlässen wurde im August 2006 in Mulcaires Wohnung sichergestellt. -Die Ermittler fanden außerdem Einträge auf dem Computer des Detektivs, auf dem Pawlby und ihre Schwester als „Projekte“ gespeichert waren. -In der Zeit, als die Recherchen liefen, erhielten die Großeltern von Pawlby anonyme Anrufe, in denen sie nach Informationen über sie gefragt wurden, erklärte Edis. -Unterdessen beaufsichtigten der ehemalige Chefreporter Neville Thurlbeck und der frühere Reporter James Weatherup die Überwachung der Bewegungen von Pawlby. -Am 18. Juni 2005 hinterließ Coulson ihr eine Voicemail mit folgendem Inhalt: „Ich habe eine Story, die wir morgen herausbringen wollen, über die ich gern mit Charles sprechen möchte.“ -Edis erklärte, Coulsons Aktivitäten bei dieser Story folgten demselben Muster wie bei anderen wichtigen Persönlichkeiten, etwa dem früheren Innenminister David Blunkett. -Die Geschworenen hörten am Donnerstag, dass Coulson Blunkett wegen einer Affäre mit einer verheirateten Frau konfrontiert habe, als er selbst mit der Mitangeklagten Rebekah Brooks liiert war, die damals verheiratet gewesen war. -Coulson und Brooks streiten ab, sich mit anderen verschworen zu haben, zwischen dem 3. Oktober 2000 und dem 9. August 2006 Telefone zu hacken. -Mulcaire, Thurlbeck und Weatherup haben das Telefon-Hacking zugegeben. -Aus Protest gegen die geplante Reichensteuer wird der französische Fussball tatsächlich den ersten Streik seit 1972 durchziehen. -Ein Schlichtungstreffen geht ergebnislos zu Ende. -Im Konflikt um die Reichensteuer haben sich die Regierung und der Profi-Fussball in Frankreich nach einem gescheiterten Schlichtungstreffen in eine Sackgasse manövriert. -Präsident François Hollande empfing zwar am Donnerstag im Pariser Élysée-Palast Vereins- und Verbandsvertreter und hörte sich auch deren Klagen an. -Er weigerte sich aber, den Fussball von der geplanten 75-prozentigen Abgabe auf Einkommen von mehr als einer Million Euro pro Jahr, die alle Firmen des Landes ab 2014 zwei Jahre lang zahlen sollen, zu verschonen. -Auch die Gegenseite blieb hart. -Den für Ende November angekündigten Streik werde man nun auch durchziehen, teilte der Chef Vereinigung der Profiklubs (UCPF), Jean-Pierre Louvel, mit. -Die zwischen dem 29. November und dem 2. Dezember angesetzten Spieltage in der Ligue 1 und Ligue 2 fallen somit aus. -Gechingen: Kein absoluter Schutz möglich -"Wir wollen die Einwohner frühzeitig in die Planung einbinden", eröffnete Bürgermeister Jens Häußler die Bürgerinformationsveranstaltung zum Hochwasserschutzkonzept in Gechingen. -Rund 80 Teilnehmer waren in die Gemeindehalle gekommen, um sich die geplanten Maßnahmen erklären zu lassen, Fragen zu stellen, Bedenken zu äußern und Ideen einzubringen. -Häußler stellte klar: "Die letztendlichen Entscheidungen fällen die Mitglieder des Gemeinderats". -Das Hochwasser, das Gechingen am 15. Mai 2009, unvorbereitet traf, sei ein einschneidendes Ereignis gewesen, das die Frage nach Schutzmaßnahmen für die Zukunft aufwarf, so Häußler. -Eine Flussgebietsuntersuchung war mit der Nachbargemeinde Aidlingen in Auftrag gegeben worden, die nun Grundlage für das Hochwasserschutzkonzept in Gechingen ist. -"Der Schutz möglichst vieler bebauter Grundstücke ist das Ziel", so Häußler. -Zugrunde gelegt ist der Planung der Schutz vor einem laut Deutschen Wetterdienst statistisch alle 100 Jahre zu erwartenden Hochwasser. -Der Klimaveränderung wird durch einen 15-prozentigen Zuschlag Rechnung getragen. -In Fachkreisen wird dies mit "HQ 100aKlima" bezeichnet. -Die Wassermassen, die 2009 in Gechingen zu beträchtlichen Schäden geführt hatten, entsprachen einem 1000-jährigen Hochwasser. -"Einen absoluten Schutz gibt es nicht, aber eine relative Verbesserung können wir erzielen", so Häußler. -Um den Schutz HQ 100aKlima zu erzielen, müssen in Gechingen rund vier Millionen Euro investiert werden, wobei die Gemeinde mit Landeszuschüssen in Höhe von rund 70 Prozent rechnen kann. -Häußler wies aber darauf hin, dass es die Förderung nur gebe, wenn ein Gesamtkonzept umgesetzt werde. -Dazu sei die Gemeinde auf die Mitwirkung der Bürger angewiesen. -Ein aufwändiges Planungs- und Genehmigungsverfahren müsse absolviert werden. -Im besten Fall könne 2016 mit der Umsetzung begonnen werden. -Unter anderem sieht das Schutzkonzept, das bereits in der vergangenen Woche im Gemeinderat präsentiert wurde (wir berichteten) lokale Maßnahmen im Zufluss des Flüsschens Irm aus dem Stammheimer Tal vor. -Im Anschluss an die Präsentation des Hochwasserschutzkonzepts nutzten die Teilnehmer die Gelegenheit, ihre Anliegen einzubringen. -Unter anderem wurde in Frage gestellt, warum nicht mehr Dämme und Rückhaltebecken zum Schutz des Ortes geplant seien. -Gregor Kühn, Fachplaner des beauftragten Ingenieurbüros Wald und Corbe aus Hügelsheim, der das Konzept vorstellte, betonte, dass es das Zusammenspiel aller vorgestellten Maßnahmen benötige, um in der Summe das gewünschte Ziel zu erreichen. -Darüber hinaus sei man gehalten die wirtschaftlichste Lösung zu finden, so Joachim Wald von Wald und Corbe. -Unmut herrschte bei Anliegern des Altorts, die selbst bei normalen Gewitterregen immer wieder unter einer Überlastung des Kanalnetzes zu leiden haben. -Das Problem ist bei der Verwaltung bekannt und erste Maßnahmen wurden bereits ergriffen. -Häußler wies jedoch darauf hin, dass die Richtlinien für Kanalsysteme andere seien, als für den Hochwasserschutz und dies nicht vermischt werden dürfe. -Die Gemeinde sei verpflichtet, das Kanalnetz so auszulegen, dass zwei- bis dreijährige Regenereignisse verkraftet werden könnten. -Die Abwasserkanäle größer zu dimensionieren, würde Millionen verschlingen. -Schwerverletzte nach Zusammenstoß -Am frühen Freitagnachmittag sind auf der Landesstraße 44 nahe Revensdorf zwei Autofahrer bei einem Frontalzusammenstoß schwer verletzt worden. -Die Polizei sperrte die Straße sofort komplett, konnte aber zum Unfallhergang zunächst wenig sagen. -Allerdings gebe es verschiedene Zeugenaussagen, die erst ausgewertet werden sollten. -Fest steht, dass die 19 Jahre alte Lenkerin eines VW Golf in Richtung Revensdorf fuhr und der 38-jährige Mann aus Gettorf ihr mit seinem Hyundai entgegen kam. -Beide Fahrzeuge prallten zusammen, wobei die Frau so schwer eingeklemmt war, dass die Feuerwehr knapp eine halbe Stunde benötigte, um sie aus dem Fahrzeugwrack zu befreien. -Die eigentliche Unfallursache blieb zunächst ungeklärt. -Beide Verunglückten kamen in nahe gelegene Krankenhäuser. -Die Strecke blieb etwa zwei Stunden voll gesperrt. -Man könnte ihn den Dostojewski der USA nennen. -Philip K. Dick war ein Gottsucher, ein Metaphysiker, seine Romane und Erzählungen haben auch etwas Serielles. -Verblüffend die Verwandtschaft im Äußeren: die stechenden Augen, der Bart, die hohe Stirn. -Und so, wie Fjodor M. Dostojewski mit seinem massiven Oeuvre als Ergründer und Apologet der russischen Seele zu verstehen ist, liest man Philip K. Dick als amerikanischen Propheten, der im eigenen Land durchaus etwas gilt. -Einige Titel sind im Heyne Verlag erschienen, die Haffmans-Kassette mit den 118 Erzählungen vertreibt Zweitausendeins. -Dick erklärt Amerika - das Reich des atemberaubenden technischen Fortschritts, gepaart mit Paranoia, Sicherheitswahn und dem Glauben, auserwählt zu sein. -Dick hatte Gotteserlebnisse, seine späten Bücher lesen sich wie eine Mischung aus der Offenbarung des Johannes und einem Computer-Manual. -Vielleicht war er verrückt, hatten Drogen seinem Hirn zugesetzt. -Er hat kapiert, früh schon, dass die Rechner Divinitäten sind. -Er schrieb eine Theologie des Computers und fragte, was einen Menschen von einer Maschine unterscheidet, den Schöpfer von seinem Geschöpf. -Das ist das "Blade Runner"-Problem. -Das Töten. -Ein Meister der Science Fiction. -Ein fantastischer Schriftsteller. -Seine Storys haben Filmregisseure wie Ridley Scott, Paul Verhoeven und Steven Spielberg inspiriert. -Diese Geschichte ist jetzt besonders signifikant. -Der "Minderheiten-Report" stammt aus dem Jahr 1956, er verströmt den Geruch des Kalten Kriegs und der McCarthy-Tribunale. -Es wird hier das Prinzip des "Prä-Verbrechens" entwickelt, "Pre-Crime", also das, was inzwischen mehr oder weniger offizielle Doktrin des Weißen Hauses ist. -In Dicks Welt, die der unseren erschreckend gleicht, schauen Mutanten in die Zukunft - und die Polizei greift zu. -"Wir erfassen Individuen, die gegen keinerlei Gesetz verstoßen haben", sagt der Chef der Behörde. -"Wir schnappen sie uns, noch bevor sie ein Gewaltverbrechen begehen können". -Und: "In unserer Gesellschaft gibt es keine Schwerverbrecher, dafür haben wir ein Straflager voller Pesudoverbrecher". Sie lesen Zeitströme. -Sie nehmen an, dass Schlimmes geschieht, und verhindern, dass der kriminelle Gedanke in die Tat umgesetzt wird. -Dicks Erzählung wirkt wie die Blaupause für die Anti-Terror-Gesetze und den Abhörrausch der NSA. -Antizipation des Anschlags: So funktioniert auch Obamas Drohnen-Philosophie und -Praxis. -Aber Dicks Story ist noch nicht zu Ende. -Der Polizeichef beklagt, dass es keine Gewaltenteilung mehr gebe, das Militär kontrolliert den Alltag - und was vom Staat übrig ist. -Dann wird der Überwacher selbst überwacht und eines zukünftigen Verbrechens überführt. -Das System dreht durch. -Es ist perfekt, aber es lügt. -Es erfindet seine eigenen Voraussetzungen. -Nimmt Irrtümer und Opfer nicht nur in Kauf, sondern baut sich darauf auf. -Das System schafft die Gefahr, die es bekämpft. -Philip K. Dick lesen heißt, immer wieder, der Wahrheit näher zu kommen. -Spähaffäre um Merkel in Russland aussagen -Der frühere US-Geheimdienstexperte Edward Snowden könnte in der US-Spähaffäre um Bundeskanzlerin Angela Merkel auch in seinem russischen Asyl Aussagen machen. -Vertreter der deutschen Bundesanwaltschaft könnten entweder schriftlich Fragen stellen oder den 30-Jährigen auch persönlich in Russland treffen, meldete die Agentur Interfax. -Zuvor hatte der Grünen-Bundestagsabgeordnete Hans-Christian Ströbele gestern als erster deutscher Politiker Snowden in Moskau getroffen, um mit ihm über die Affäre zu sprechen. -NSA-Enthüllungen verstärken Firmenparanoia wegen staatlicher Überwachung -An einem milden Tag Ende August flog ein deutscher Polizeihubschrauber tief über das US-Konsulat in Frankfurt am Main, der Finanzhauptstadt Deutschlands. -Auf Anweisung des Bundesamtes für Verfassungsschutz und Terrorismusbekämpfung (BVT), des deutschen Innengeheimdienstes, sollte er das Dach des US-Außenpostens fotografieren, der sich weniger als 5 km von der Europäischen Zentralbank und der Bundesbank befindet. -Laut deutschen Medien wollte das BVT das Vorhandensein von Überwachungsantennen nachweisen und die Aktion führte zu einem Wortwechsel zwischen den USA und dem deutschen Außenministerium in Berlin. -James Clapper, US-amerikanischer Director of National Intelligence, bestand im September erneut darauf, dass die USA keine Möglichkeiten der Auslandsspionage einsetzten, „um die Betriebsgeheimnisse ausländischer Unternehmen im Auftrag US-amerikanischer Firmen zu stehlen, damit diese ihre internationale Wettbewerbsfähigkeit oder ihren Gewinn ausbauen könnten.“ -Doch seit Edward Snowden, der zum Whistleblower gewordene einstige Mitarbeiter, mit der Veröffentlichung seines Füllhorns an US-amerikanischen Überwachungsgeheimnissen begonnen hat, sind sich europäische Regierungen und Unternehmenschefs nicht mehr sicher, ob sie den Direktor beim Wort nehmen können. -Berichte, wonach die amerikanische National Security Agency die brasilianische Ölfirma Petrobas ausspioniert und sich Daten amerikanischer Cloud-Anbieter einschließlich Google und Yahoo beschafft hat, haben die Firmenparanoia wegen staatlicher Überwachung auf einen neuen Höchststand gebracht. -Das Fass zum Überlaufen brachte die Enthüllung, dass Kanzlerin Angela Merkels Telefon verwanzt war, möglicherweise schon seit zehn Jahren. -Wenn die mächtigste Person in Europa ins Visier geraten kann, dann sind mit Sicherheit auch Unternehmensleitungen potenzielle Ziele. -Snowden hat die intensive Zusammenarbeit zwischen US-Geheimdiensten und Unternehmen transparent gemacht. -Ich glaube, es ist denkbar, dass diese Daten zum gegenseitigen Nutzen verwendet werden. -„Deutschland muss aufwachen“, erklärt Oliver Grün, Präsident des BITMi, der kleine und mittlere IT-Firmen in Deutschland vertritt. -Deutsche Unternehmen glauben, die USA stellten nun ein beinahe ebenso großes Risiko dar wie China, wenn es um Industriespionage und Datendiebstahl gehe, so eine Umfrage, die im Juli von der Beraterfirma EY veröffentlicht wurde. -In den von Snowden durchgesickerten Dokumenten gab es allerdings bisher keinen Beleg, dass die USA die Betriebsgeheimnisse ausländischer Unternehmen an eigene Firmen weitergegeben haben. -Politiker haben ihre Sorge darüber zum Ausdruck gebracht, dass der EU bestimmte IT- und Internet-Möglichkeiten fehlten und sie ihre Abhängigkeit von den USA reduzieren sollte. -Die Unternehmen sind diesbezüglich skeptisch. -Im Bundestag wurde vorgeschlagen, wir sollten ein deutsches Google aufbauen. -Ich kann nur meine Augen schließen und sie langsam wieder öffnen ... -„So funktioniert das nicht“, sagt Hasso Plattner, Vorsitzender des deutschen Softwareherstellers SAP. -Wenn man eine starke europäische IT-Industrie gewollt hätte, dann hätte man sie nicht vor 20 Jahren aussterben lassen sollen. -Alles in Deutschland wird subventioniert, von der Kohle über Autos bis zur Landwirtschaft. -Alles, bis auf die IT-Branche. -Dennoch haben Reichweite und technische Raffinesse der von den Snowden-Enthüllungen aufgedeckten US-Spionagebehörden Firmen schockiert, die vorher das größte Spionagerisiko in China gesehen hatten. -Beim Cloud Computing findet eine große Veränderung statt, weil europäische Unternehmensleiter sich nun bewusster sind, dass in den USA gespeicherte Daten der dortigen Rechtsprechung unterliegen und deshalb potenziell gefährdet sind. -Laut einer von der Cloud Security Alliance, einer Handelsinstitution, durchgeführten Umfrage verwarfen nach den Enthüllungen über die US-amerikanischen Data-Mining-Aktivitäten mit Prism etwa zehn Prozent der nicht in den USA ansässigen Mitglieder Pläne, einen amerikanischen Anbieter zu nutzen. -Jim Snabe, stellvertretender Geschäftsführer von SAP, sagt: „Wir sehen eine neue Frage von Kunden, die es vor einem Jahr noch nicht gab – sie lautet: Wo werden meine Daten gespeichert und können Sie garantieren, dass diese physisch in diesem Rechtsraum bleiben?“ -Viele deutsche Unternehmensleiter vertreten die Ansicht, die jüngsten Berichte bestätigen einfach nur, was sie sowieso schon wussten, nämlich dass mächtige Staaten ihre wertvollsten Geheimnisse stehlen wollen und diese deshalb um jeden Preis bewacht werden müssen. -Dass Wirtschaftsspionage stattfindet, ist keine Überraschung. -Das war schon immer so. -„Es war viele Jahre lang ein Thema und hat sich durch die aktuelle Diskussion nicht fundamental geändert“, sagt Kurt Bock, Vorsitzender des Chemieunternehmens BASF. -Die Amerikaner spionieren uns auf kommerzieller und industrieller Ebene aus, so wie wir das auch machen, denn es ist im nationalen Interesse, unsere Unternehmen zu verteidigen. -Die Unternehmensführung prahlt normalerweise nicht über die Gegenmaßnahmen, die man ergriffen hat, denn das verschafft Angreifern Vorteile. -In großen Unternehmen wird Mitarbeitern schon lange eingebläut, dass das Mitnehmen eines kostenlosen USB-Sticks auf einer Messe oder das unbewachte Liegenlassen eines Laptops im Hotelzimmer nicht ratsam sei, gelinde gesagt. -Ulrich Hackenberk, Vorstandsmitglied bei Autohersteller Audi, sagt, dass es seit Jahren zur Standardpraxis gehöre, vor einem Vorstandstreffen die Handys einzusammeln, damit sie nicht als Abhörgeräte verwendet werden können. -Das deutsche BVT rät Führungskräften, einfache Prepaid-Handys zu verwenden, wenn sie auf Auslandsreisen gehen, weil Smartphones möglicherweise kompromittiert sein können. -Die Prepaid-Handys werden hinterher weggeworfen. -Es besteht allerdings die Sorge, dass kleine und mittlere Unternehmen angreifbar gegenüber Hacking und Überwachung bleiben. -In Deutschland sind viele dieser Unternehmen weltweite Marktführer in der jeweiligen Nische. -„Kleinen und mittleren Unternehmen fehlen oft die Erfahrung, das Personal und die finanziellen Ressourcen, um Betriebsgeheimnisse effektiv gegen nicht autorisierten Zugriff zu schützen“, warnt das BVT in einem Bericht. -Die USA warnen eigene Firmen vor Wirtschaftsspionage durch andere Länder. -Laut Washington Post wurden im US National Intelligence Estimate im Februar Frankreich, Russland und Israel hinter China auf Rang zwei der Länder eingeordnet, die sich zur Beschaffung von Wirtschaftsdaten des Hackings bedienten. -Ein Vorstandsmitglied eines deutschen Blue-Chip-Unternehmens stimmte zu, wenn es um Wirtschaftsspionage gehe, dann „sind die Franzosen die Schlimmsten“. -Bernard Squarcini, ehemaliger Leiter des französischen Innengeheimdienstes DCRL, wurde diesen Monat in einem Interview mit folgenden Worten zitiert: „Die Geheimdienste wissen sehr genau, dass alle Länder, selbst wenn sie im Kampf gegen den Terror zusammenarbeiten, ihre Verbündeten ausspionieren.“ -Hechingen: Messe gibt Antwort auf Hochzeits-Fragen -"Die Ehe ist die wichtigste Entdeckungsreise, die der Mensch unternehmen kann", sagte der Philosoph Sören Kierkegaard. -Tipps, wie zumindest die Hochzeitsfeier gelingt, gab nun eine Spezialmesse in der Domäne in Hechingen. -Vertreten waren wichtige Anbieter rund um eine Hochzeit. -Ein Trend: Der Retro-Look hat wieder Einzug gehalten. -Das beginnt etwa bei den Hochzeitskleidern. -Weiß ist Muss, am besten gedeckt oder in Creme. -Vorne sind die vielfach gerüschten und mit Stickereien versehenen Kleider geschlossen, oft mit einem Schleier, hinten hingegen gehen sie den Rücken hinab und enden oft in einer Schleppe. -Da allerdings scheiden sich die Geister, wie Stefanie Koch vom Modehaus Kleidermüller betont. -Bei Herren dominiert der Anzug mit Weste und Plastron, aber es können auch cremefarbige oder braune Kombinationen gewählt werden. -Wichtig sei die Auswahl des Lokals, wurde erklärt. -Schmucklose Nebenzimmer sind nicht optimal, das Ambiente sollte besonders sein. -Hier machte die Domäne auf ihr eigenes Angebot in der Remise aufmerksam. -Wer dann mit der Kutsche vorfahren will, für den hat Uwe Link ein Angebot. -"Kutschen sind aber auch für Jungesellinnenabschiede beliebt", meint er. -Romantik ist auch für Einladungskarten, Blumenarrangements und auch für Hochzeitsbilder gefragt. -Immer mehr in Mode komme das "After-Wedding-Foto" - also Aufnahmeserien an besonderen Orten, vorzugsweise etwa Wasserfälle, einige Tage nach dem Fest, aber in Hochzeitskleidung, erklärte Elisabeth Keidel. -Viele weitere Details einer gelungenen Hochzeit wurden am Sonntag in der Domäne präsentiert. -Blumenarrangement, Ringe, Hochzeitstafel, Kuchen und auch ein Zauberer, wie etwa Marko Ripperger, der Gäste stilvoll unterhalten kann. -Nicht zu vergessen die richtige Kosmetik und Nagelpflege. -Alles für ein unvergessliches Fest. -Hundefreunde erfolgreich -Zum wiederholten Mal waren die Sportler der Hundefreunde Bitz erfolgreich auf einem Rally-Obedience-Turnier. -Achim Scherrenbacher startete in Kandel mit zwei Hunden; mit seiner Hündin Sandy erzielte er in der Klasse Beginner 94 Punkte und sicherte sich den fünften Platz, und mit dem 15 Monate alten Marley brachte er es mit 87 Punkten auf Platz zwölf. -Susi Höpp stellte sich mit ihrem Woody in der Klasse 1 den kritischen Blicken des Leistungsrichters. -Eltern eines Teenagers aus Georgia, der bei tragischem Unfall starb, glauben an Mord -Die Eltern des Teenagers, dessen Leiche in einer zusammengerollten Ringermatte in der Turnhalle seiner High School gefunden wurde, sind überzeugt, dass ihr Sohn ermordet wurde, erklärte der Anwalt der Familie am Donnerstag. -Kendrick Johnson aus Valdosta im US-amerikanischen Bundesstaat Georgia wurde am 11. Januar in einer aufrecht aufgestellten Matte gefunden, die hinter der Tribüne in der Schulturnhalle stand. -Die Untersuchungsbeamten des Sheriffs von Lowndes County kamen zu dem Schluss, dass Johnson bei einem tragischen Unfall starb, was die Familie des 17-Jährigen jedoch anzweifelt. -„Sie sind davon überzeugt, dass ihr Sohn ermordet wurde“, erklärte Benjamin Crump, der Anwalt von Kenneth und Jacquelyn Johnson, gegenüber FoxNews.com. -Sie hätten nie geglaubt, er sei so gestorben, wie es der Sheriff geschlussfolgert habe. -„Sie sind der Ansicht, dass es jeder Logik entbehre, gegen die Gesetze der Physik wie auch gegen den gesunden Menschenverstand verstoße“, so Crump. -Vielmehr seien sie überzeugt, die Wahrheit werde vertuscht, um die Person oder Personen zu schützen, die für den Tod ihres Sohnes verantwortlich seien. -„Ihr Sohn ging zum Lernen in die Schule und kam im Leichensack zurück“, kommentiert er. -US-Staatsanwalt Michael Moore sagte am Donnerstag, es werde eine formelle Ermittlung zum Tod Johnsons geben, da eine Reihe wichtiger Fragen noch ungeklärt seien. -Was war die Todesursache? -Ist sein Tod auf ein Verbrechen zurückzuführen? -Das erklärte Moore bei einer Pressekonferenz am Donnerstagnachmittag. -Ich folge den Fakten, in welche Richtung sie auch führen. -Mein Ziel ist es, die Wahrheit herauszufinden. -„Ich vertrete die Auffassung, dass eine hinreichende Grundlage für eine formelle Ermittlung besteht“, sagte er. -Moore erklärte gegenüber Reportern, die anfängliche Autopsie deute darauf hin, dass Johnson an den Folgen einer „positionsbedingten Erstickung“ verstorben sei. -Eine zweite Autopsie habe allerdings eine andere Todesursache ergeben, so Moore. -„Es gibt eine Reihe von Fragen, die beantwortet oder bestätigt werden müssen“, sagte er. -Moore fügte hinzu, falls es ausreichende Beweise für eine strafrechtliche oder zivilrechtliche Untersuchung zum Tode Johnsons gebe, werde er das FBI mit der Durchführung der Ermittlungen beauftragen. -Ein Vertreter der Behörde des County Sheriffs war auf unsere Anfrage am Donnerstag hin nicht sofort zu einem Kommentar bereit. -Ein Gericht im Süden Georgias ordnete am Mittwoch an, dass die Behörden alle Überwachungsvideos freigeben müssen, die die Ermittler überprüft haben. -Die Eltern des Teenagers sagten, sie hofften, dass die Videoaufzeichnungen Hinweise darauf enthielten, wie er gestorben sei. -CDC veröffentlichen Allergierichtlinien für Schulen -Am Mittwoch veröffentlichten die Centers for Disease Control and Prevention (CDC) eine Reihe von Richtlinien für den Umgang mit Lebensmittelallergien von Kindern an Schulen. -Dies sind die ersten derartigen Richtlinien, die die US-amerikanische Regierung herausgegeben hat, da die Anzahl der schulpflichtigen Kinder mit Lebensmittelallergien steigt. -Eines von 20 Kindern in den Vereinigten Staaten hat inzwischen eine Lebensmittelallergie. -Die Verbreitung von Lebensmittelallergien unter Kindern hat laut CDC zwischen 1997 und 2007 um 18 Prozent zugenommen. -Die Leitlinien enthalten Informationen für Schulen, die beim Lehrkörper und den Mitarbeitern ein Bewusstsein für Lebensmittelallergien wecken sollen und wie mit Kindern umgegangen werden soll, die allergische Reaktionen zeigen. -Es wird auch empfohlen, dass Schulen Epinephrin vorrätig haben – die Auto-Injektion der Marke EpiPen wird am häufigsten verwendet –, um einer potentiell tödlichen Überempfindlichkeitsreaktion begegnen zu können. -Die Gesetzgeber in den Bundesstaaten haben kürzlich die Vorschriften geändert, damit Schulen Epinephrin einfacher vorrätig halten können. -Im Bericht ist auch eine Liste typischer Symptome enthalten, die von Kindern genannt werden, die eine allergische Reaktion aufweisen. -Kinder sagen vielleicht: „Es fühlt sich an, als ob etwas auf meine Zunge drückt“, „Meine Zunge fühlt sich an, als ob ein Haar darauf liegt“ oder „Meine Zunge kribbelt“. -Eltern von intersexuellen Kindern können „Geschlecht unbestimmt“ wählen -Deutschland ist die erste europäische Nation, in der ein drittes Geschlecht für Kinder anerkannt ist, die mit nicht eindeutigen Genitalien geboren wurden. -Neugeborenen wird nicht mehr starr männlich oder weiblich zugewiesen. -Nach dem neuen Gesetz müssen Eltern kein Geschlecht mehr für solche Kinder erklären, sondern können „unbestimmt“ oder „nicht festgelegt“ auf der Geburtsurkunde angeben. -Ziel des Gesetzes ist es, den Druck von Eltern zu nehmen, damit sie keine übereilten Entscheidungen in Bezug auf Operationen für eine Geschlechtszuweisung bei Neugeborenen treffen müssen, und die Diskriminierung von intersexuellen Menschen zu bekämpfen. -Eine intersexuelle Person sagte laut BBC Jahre später: „Ich bin weder Mann noch Frau.“ -Ich bleibe das Flickwerk, das von Ärzten erschaffen wurde, verstümmelt und voller Narben. -Schätzungsweise kommt eines von 2.000 Kindern jedes Jahr weder als Junge noch als Mädchen zur Welt. -Sie gehören zu den Intersexuellen, einer Gruppe, die unter die Diagnose „Sexualdifferenzierungsstörung“ (DSD) fallen, ein Sammelbegriff für Menschen mit atypischen Chromosomen, Keimdrüsen (Eierstöcken oder Hoden) oder ungewöhnlich entwickelten Genitalien. -Wallis Simpson war möglicherweise intersexuell. -Das Verständnis der Geschlechteridentifikation ist noch immer nicht sehr gut entwickelt, doch raten die meisten Experten in den USA dazu, man solle die besten verfügbaren Informationen zur Geschlechtszuweisung verwenden, falls eine Bestimmung nicht möglich sei, und nicht auf die psychologische und körperliche Entwicklung des Kindes warten, ehe man sich – wenn überhaupt – für eine Operation entscheide. -Der New Yorker Psychiater Dr. Jack Drescher, der sich auf die Problematik der Geschlechteridentifikation spezialisiert hat, sagte, das neue deutsche Gesetz „klinge nach einer guten Sache“. -Intersexuelle Kinder stellen ein ethisches Dilemma dar. -„Manche Menschen haben lebensbedrohliche Erkrankungen, die einen chirurgischen Eingriff erforderlich machen, doch für die meisten Kinder gilt das nicht“, sagte er. -Man könne eine Geschlechtszuweisung ohne Operation vornehmen und dann abwarten, wie sich die Identität entwickle. -Die wissenschaftlichen Daten darüber, wie ein Kind überhaupt seine geschlechtliche Identität entwickelt, sind nicht sehr genau. -Niemand weiß, warum es geschieht. -Es ist wie das Geheimnis, weshalb Menschen homosexuell sind. -In einem Bericht an die Europäische Kommission aus dem Jahr 2011 heißt es, intersexuelle Menschen seien von transsexuellen oder Transgender-Personen zu unterscheiden, denn ihr Status sei nicht geschlechtsbezogen, sondern durch ihre biologische Konstitution bedingt, die weder ausschließlich männlich noch weiblich ist, sondern typischerweise beides gleichzeitig oder nicht klar als eines davon definiert. -Diese Eigenschaften zeigten sich in nachrangigen sexuellen Charakteristiken wie Muskelmasse, Behaarung, Brüste und Statur, vorrangigen sexuellen Charakteristiken wie Fortpflanzungsorgane und Genitalien oder in Chromosomstrukturen und Hormonen. -Der Bericht gibt auch einen Überblick über die Diskriminierung, der sich intersexuelle und Transgender-Personen im Berufsleben ausgesetzt sehen sowie über das Ausmaß von Belästigung, Gewalt und Vorurteilskriminalität. -Nicht mit dem Geschlechterbild übereinstimmende Jungen haben jetzt ein spezielles Camp. -Inzwischen ist es in Australien und Nepal für Erwachsene möglich, auf offiziellen Dokumenten männlich, weiblich oder „drittes Geschlecht“ anzugeben. -Im Juni wurde Australier Norrie May-Welby (52) als weltweit erste „geschlechtslose“ Person anerkannt, nachdem ein Gericht die lebenslange Beibehaltung eines „nicht festgelegten“ Geschlechtsstatus zugestanden hatte. -Deutsche Ausweise haben neben M und W künftig eine dritte Zuweisung, X für intersexuell, so das Innenministerium. -Im benachbarten Frankreich sind Geschlechtsthemen immer noch kontrovers, so eine Nachrichtenmeldung auf France 24. -2011 unterzeichneten Dutzende französische Abgeordnete des streng katholischen Landes eine Petition, dass die „Geschlechtertheorie“ aus Schulbüchern entfernt werden solle. -Die US-amerikanische Website Catholic Online hat sich ebenfalls gegen das deutsche Gesetz ausgesprochen, mit der Begründung: „Wenn die Welt in einen neuen Zustand gezerrt wird, wo das Geschlecht eine Entscheidung darstellt, sexuelle Aktivität aber nicht, dann verdrehen wir zwei weitere Säulen der Zivilisation.“ -Die Mutter eines Neugeborenen aus Maryland erklärte gegenüber Baby Zone, sie fände es besser, wenn Babys bei der Geburt ein Geschlecht zugewiesen werde. -„Das Elterndasein ist auch ohne weitere Einschränkungen schon stressig genug, besonders wenn man das Geschlecht des eigenen Kindes nicht kennt“, sagte sie der Elternwebsite. -Kinder brauchen Stabilität und Sicherheit. -Historisch gesehen wurden Kindern, die sowohl mit männlichen als auch weiblichen Geschlechtsteilen geboren wurden, als Hermaphroditen bezeichnet, nach dem attraktiven griechischen Gott, der beide Geschlechter hatte. -Noch vor zehn Jahren betrachtete die Medizin das Geschlecht als etwas, das man auslöschen und dann neu aufbauen könne. -Doch inzwischen werden die ethischen Grundlagen einer Operation angezweifelt, nachdem nun bekannt ist, wie komplex die geschlechtliche Identität ist, und dass Ärzte auch falsch liegen können, weil sie nicht wissen, wie sich das Kind beim Aufwachsen mit dem von ihnen zugewiesenen Geschlecht fühlt. -„Mitte des 20. Jahrhunderts nannte man das einen ,psychiatrischen Notfall‘“, sagte Drescher. -Wenn ein solches Kind geboren wurde, dann rief man nicht den Psychiater, sondern den Chirurgen. -Die herrschende Theorie über die Behandlung von Kindern mit nicht eindeutigen Genitalien geht auf Dr. John Money von der Johns Hopkins University zurück, der die Ansicht vertrat, das Geschlecht sei formbar. -Er prägte den Begriff „geschlechtliche Identität“ und argumentierte, soziale und umweltbedingte Anstöße – wie die Eltern ein Kind erziehen – interagierten mit den Genen und Hormonen eines Kindes und prägten so, ob sich eine Person als männlich oder weiblich betrachte. -Doch in einem unter dem Titel „John/Joan“ bekannt gewordenen Fall aus dem Jahr 1966 wurden seine Theorien kontrovers. -Er riet den Eltern eines Jungen, dessen Penis bei einer verpfuschten Beschneidung abgetrennt worden war, das Kind ganz zu kastrieren und auch seine Hoden zu entfernen und ihn dann als Mädchen großzuziehen. -„Money präsentierte diesen Fall als erfolgreiches Beispiel für eine Umwandlung, was er aber nicht war“, so Drescher. -Als der Junge etwa 15 war, wechselte er zurück zum männlichen Geschlecht und heiratete eine Frau. -Mit 38 aber beging er Selbstmord. -Laut Drescher gibt es noch immer Ärzte, die dieses Modell praktizieren würden. -Doch in den 1990er Jahren, mit dem Einzug des Internet, sind Überlebende dieser Geschlechtsoperationen an die Öffentlichkeit getreten, die „nicht glücklich mit dem Ergebnis“ sind. -So etwa bei Jim Bruce, einem 36 Jahre alten Autor aus Montana, der mit XY-männlichen Chromosomen geboren wurde, aber zweideutigen Genitalien. -Die Ärzte waren sich nicht klar darüber, ob er eine große Klitoris oder einen kleinen Penis hatte und waren überzeugt, er könne niemals ein „befriedigendes Leben“ als Mann führen. -Also wurden sein externes Organ und die Hoden kurz nach der Geburt 1976 chirurgisch entfernt und er wurde als Mädchen großgezogen. -Im Alter von 12 erhielt er weibliche Hormone. -„Ich wusste, dass ich kein Mädchen bin“, erklärte er gegenüber ABCNews.com -Ich war unglücklich, aber es war wirklich schwer, Fragen zu stellen. -Im Alter von 18 sollte er eine Vaginalplastik erhalten. -Doch da er depressiv war und wusste, dass etwas nicht stimmte, verlangte er seine medizinischen Akten. -Was er herausfand, war schockierend. -Ich wurde bei meiner Geburt sterilisiert – und keiner hat mir das jemals gesagt. -Bruce wurde mit einer DSD geboren, durch die sein Körper nicht genug Testosteron zur Entwicklung der Genitalien produzieren konnte. -Nachdem er die Wahrheit erfahren hatte, wechselte er wieder zur männlichen Identität, erhielt Testosteron-Injektionen und ließ sich seine Brüste entfernen. -Durch die Operation ist er unfruchtbar geworden. -Heute kämpft er für andere in einer Organisation namens Interface Project dafür, dass intersexuelle Menschen als normal wahrgenommen werden. -Doch Anne Tamar-Mattis, Geschäftsführerin der in Kalifornien ansässigen Rechtsgruppe Advocates for Informed Choice, sorgt sich, dass das deutsche Gesetz „zum Abstempeln und Stigmatisieren ermutigen“ könne. -„Viele Aktivisten sind besorgt, dass die deutsche Entscheidung Eltern ermutigen könnte, schnelle Entscheidungen zu treffen und dem Kind ein ,nicht festgelegt‘ zuzuweisen“, erklärte sie. -Wir fürchten, das begünstigt Interventionen. -Wir denken, es wäre besser, das männliche oder weibliche Geschlecht zuzuweisen und dann abzuwarten. -Doch noch wissen wir nicht, wie sich das Gesetz auswirken wird, also können wir nur spekulieren. -Tamar-Mattis sagte, ihre Organisation unterstütze das australische Gesetz, denn „es ermöglicht Erwachsenen die Anerkennung als drittes Geschlecht“. -„Erwachsene sollten in der Lage sein, eigene Entscheidungen über ihr rechtliches Geschlecht zu treffen“, erklärte sie. -Bei dem deutschen Gesetz geht es um die Zuweisung bei der Geburt. -Das ist kein Kampf, zu dem man kleine Kinder zu diesem Zeitpunkt zwingen sollte. -Wenn sie erwachsen sind, können sie selbst über ihren Körper entscheiden. -Doch Dr. Arlene Baratz, eine Brustradiologin aus Pittsburg, die eine Tochter mit einer sexuellen Differenzierungsstörung hat und Hunderte andere in einer Selbsthilfegruppe unterstützt, sagt, das deutsche Gesetz „bevollmächtige“ sowohl Eltern als auch Kinder. -Katie, die Tochter von Baratz, wurde mit männlichen Chromosomen geboren, hat aber eine DSD namens komplette Androgenresistenz. -Wegen des Rezeptordefektes der Zielzellen für Testosteron entwickelte Katie weibliche Charakteristiken. -Sie hat eine Vagina, aber keinen Uterus und keine Eierstöcke. -Heute ist Katie 29 und verheiratet; sie arbeitet an der University of Pennsylvania als Mitarbeiterin in der Kinderpsychiatrie. -Zwar ist sie unfruchtbar, hofft allerdings, durch Adoption oder eine Leihmutter ein Kind bekommen zu können. -„Das Gesetz gibt Eltern Raum, wodurch sie nicht überstürzt Entscheidungen treffen müssen“, sagte Baratz. -Es verschafft ihnen die Zeit, verschiedene Tests zu machen und Dinge zu klären und gibt ihnen so Zeit, ehe sie „männlich“ oder „weiblich“ schreiben. -Auf diese Weise ist alles in Ordnung – man zieht das Kind groß und liebt es. -Man hat ein wunderbares Baby und genießt das Elternglück. -Und man muss nicht übereilt eine Operation vornehmen, die sich nicht mehr rückgängig machen lässt. -„Das schließt die Kinder in die Entscheidung mit ein und beseitigt die Angst, die Eltern umtreibt, weil sie der Meinung sind, sie würden nicht das Richtige tun“, erläuterte sie. -Letztendlich entscheidet das Kind über das Geschlecht, das besser zu ihm passt – und das ist wunderbar. -Dadurch werden Kinder in die Lage versetzt, die Entscheidung selbst zu treffen. -Unbekannte verletzen Mädchen durch Schuss aus Auto -Unbekannte haben bei Schüssen aus einem Auto eine Zwölfjährige in Sachsen-Anhalt verletzt. -Die Täter hatten am Donnerstagabend in Genthin vermutlich mit einer Luftdruckwaffe auf eine Gruppe von Kindern gefeuert. -Das teilte die Polizei in Burg mit. -Das Mädchen sei leicht an der linken Wade verletzt worden. -Rettungskräfte brachten das Mädchen ins Krankenhaus. -Der Schuss kam aus einem Kleinwagen, der an einer Gruppe von sechs Kindern vorbeigefahren war und plötzlich angehalten hatte. -Zeugen sahen zwei Menschen in dem Auto sitzen. -Die Täter schossen außerdem auf eine Bushaltestelle. -Eine Scheibe zerbrach. -Anschließend fuhren sie davon. -Die Kriminalpolizei ermittelt, hieß es am Freitag. -Bis zum jetzigen Zeitpunkt kann die Polizeiinspektion Kempten auf eine ruhige Halloween-Nacht zurückblicken. -Die Beamten wurden lediglich zweimal zu eierwerfenden Kindern gerufen, welche diese gegen Hausfassaden warfen. -Da die Hauseigentümer dies rechtzeitig erkannten und die Eierreste gleich entfernten, dürfte auch kein Sachschaden entstanden sein. -Desweiteren wurden im Stadtgebiet wohl auch einige Sylvesterböller gezündet, aber auch hier ist kein Sachschaden entstanden. -Ein Anwohner im Bischof-Freundorfer-Weg meldete, dass sein Pkw mit Klopapier eingewickelt und seine Radzierblenden entwendet wurden. -Bis zum Eintreffen der Polizei hat der Mann seine Radzierblenden jedoch in unmittelbarer Nähe alle wieder aufgefunden, weswegen die Polizei nicht weiter tätig werden musste. -Wohnen mit Zukunftsperspektive -Eigentlich wohnt Waltraud Ries traumhaft. -Ihr Haus liegt in einer ruhigen Wohngegend von Stuttgart mit viel Grün, einem alten Baumbestand, netten Nachbarn und auch nicht allzu viel Verkehr. -In die Stadt sind es nur wenige Minuten mit öffentlichen Verkehrsmitteln. -"Sie haben hoffentlich keine Angst vor Spinnen?", fragt Waltraud Ries und zeigt auf eine fette Spinne am Türrahmen. -Ich suche schon seit einiger Zeit eine neue Wohnung für meinen Mann und mich. -Aber Sie wissen ja, wie schwierig das in Stuttgart ist. -Der Grund ist allerdings keine Spinnenphobie, sondern die Sorge, im Alter die vielen Treppenstufen zum Haus und in der Wohnung nicht mehr bewältigen zu können, erklärt sie. -Zudem: Seit einer Meniskus-Operation hat die Mittfünfzigerin am eigenen Leib erfahren müssen, was es bedeutet, sich mit Handicap von Stockwerk zu Stockwerk zu bewegen. -Vor kurzem erst ist ein Buch von ihr erschienen zum Thema 'Glücklich wohnen im Alter - Welche Wohnform ist die beste für mich?'. -'Unsere Wohnung ist das beste Beispiel dafür, was im Alter gar nicht geht', beginnt sie zu erzählen. -Als sie vor mehr als 20 Jahren mit ihrem Mann in die Maisonette-Wohnung zog, war es einfach nur eine schöne Wohnung mitten im Grünen. -Heute mit Mitte fünfzig denkt Waltraud Ries anders darüber. -Sie weiß aber auch, dass sie zu den wenigen Menschen gehört, die sich überhaupt mit dem Thema 'Wohnen im Alter' auseinandersetzen. -Die meisten verdrängen es, solange es geht. -Alt werden immer nur die anderen', sagt sie schmunzelnd. -Erst wenn der Leidensdruck wirklich groß ist, mache man sich Gedanken. -'Dann kann es aber auch schon zu spät sein', so Ries. -In ihrem Buch stellt die Innenarchitektin 17 Wohnmodelle für ein selbstbestimmtes Wohnen im Alter vor. -'Welche Wohnform man aber wählt, ist immer eine Individualentscheidung', erklärt sie. -D i e Lösung gebe es nicht für das Alter. -Selbst wer sich den Altersruhesitz in der Toskana oder der Bretagne leisten könne, sollte sich immer überlegen, dass er auch krank werden könne. -Und: 'Ohne gute Kenntnisse der Landessprache vereinsame man auch im Dolce Vita,' gibt die Fachbuchautorin Aussiedlungswilligen zu bedenken. -Unter ihren Modellen finden sich klassische Betreuungsmodelle, alternative Wohnformen oder die oft zitierte Rentner-WG. -Im Alter hat jeder so seine Ticks und Macken -'Für mich wäre so eine Wohngemeinschaft aber nichts', meint sie mit einem Augenzwinkern. -Schon gar nicht wie in einer Studenten-WG mit einem gemeinsamen Bad und einer Küche. -'Im Alter braucht man einfach auch seine Rückzugsräume', so Ries. -Dass eine Senioren-Wohngemeinschaft dennoch funktionieren kann, will die Buchautorin dennoch nicht ausschließen. -Das müsse dann aber anders verlaufen als in der klassischen Studenten-WG. -Und manchmal will man einfach auch nur seine Ruhe', glaubt sie. -Mit dieser Meinung steht sie zumindest in der Landeshauptstadt nicht allein da. -'Die meisten alten Menschen in Stuttgart wollen so lange es geht in ihrer eigenen Wohnung bleiben', ist auch die Erfahrung von Theresa Rütten, der Leiterin des Bürgerservices Leben im Alter der Landeshauptstadt. -Ihre Dienststelle berät Menschen rund um das Thema 'Älter werden'. -Deshalb würden gerade ältere Menschen häufig auch Unbequemlichkeiten und Einschränkungen in Kauf nehmen, nur um in ihrer gewohnten Umgebung bleiben zu können, das ist auch die Erfahrung der Stiftung Warentest. -Dabei gibt es heute viele Möglichkeiten, das Haus oder die Wohnung barrierefrei zu gestalten. -Ries empfiehlt allen, die sich mit dem altersgerechten Umbau ihres Hauses oder ihrer Wohnung beschäftigen, sich erst einmal gründlich zu informieren und nicht den erstbesten Handwerker zu beauftragen. -Heute gibt es auch für den altersgerechten Umbau Spezialisten. -Selbst in der Mietwohnung lasse sich das eine oder andere realisieren, und wenn es nur die Toilettensitzerhöhung ist, klärt die Innenarchitektin auf. -Aber nicht immer lässt sich die bestehende Wohnung altersgerecht und damit barrierefrei umgestalten, auch wenn sie noch so schön liegt. -Diese Erfahrung musste Waltraud Ries machen. -Zwar könnte man innerhalb der Wohnung einen Treppenlift installieren, der ebenfalls mit Treppen gepflasterte Weg bis an die Wohnungstür wäre aber das weitaus größere Hindernis. -Sie wird weitersuchen, bis sie das ideale Objekt für sich und ihren Mann gefunden hat. -Vor dem Umzug graut es ihr mehr als vor der Spinne an ihrer Wohnungstür. -USA wollen Snowden weiter strafrechtlich verfolgen -Die USA haben ihre Haltung zum früheren US-Geheimdienstmitarbeiter Edward Snowden nicht geändert. -Ihm werde weiterhin vorgeworfen, unerlaubt geheime Informationen weitergegeben zu haben. -Daher müsse er sich in den USA einem Strafverfahren stellen, sagte die Sprecherin des US-Außenministeriums, Jennifer Psaki, in Washington. -Auch die jüngsten Äußerungen von Snowden würden daran nichts ändern. -Zuvor hatte der deutsche Grünen-Politiker Hans-Christian Ströbele nach einem Treffen mit Snowden in Moskau an die USA appelliert, ihn nicht weiter mit Strafe zu bedrohen. -Psaki betonte, dass es sich bei Ströbeles Äußerungen um die eines Parlamentariers handele und nicht um die eines deutschen Regierungsmitglieds. -Geheimdienstenthüller Edward Snowden ist grundsätzlich zu einem Gespräch mit deutschen Behörden bereit. -Zuerst will er jedoch seine Situation geklärt wissen. -Das hat der von den USA gesuchte ehemalige NSA-Mitarbeiter in einem nicht adressierten Schreiben klargemacht, das der Grünen-Politiker Hans-Christian Ströbele am Freitag nach einem Treffen mit Snowden veröffentlichte und an Bundesregierung, Bundestag und Generalbundesanwalt weiterleitete. -Das Schreiben hat nach einer von Ströbele verbreiteten Übersetzung folgenden Wortlaut: -An die Zuständigen -Ich wurde gebeten, Ihnen bezüglich Ihrer Untersuchung zur Massenüberwachung zu schreiben. -Ich heiße Edward Joseph Snowden und war früher vertraglich bzw. über eine Direktanstellung als technischer Experte bei der National Security Agency (NSA), der Central Intelligence Agency (CIA) und der Defense Intelligence Agency (DIA) der Vereinigten Staaten beschäftigt. -Im Zuge meiner Beschäftigung in diesen Einrichtungen wurde ich Zeuge systematischer Gesetzesverstöße meiner Regierung, die mich aus moralischer Pflicht zum Handeln veranlassten. -Als Ergebnis der Veröffentlichung dieser Bedenken sah ich mich ich einer schwerwiegenden und anhaltenden Hetze ausgesetzt, die mich zwang, meine Familie und meine Heimat zu verlassen. -Ich lebe derzeit im Exil und genieße befristetes Asyl, das mir die Russische Föderation gemäß internationalem Recht gewährt. -Ich bin ermutigt von der Resonanz auf mein politisches Handeln, sowohl in den USA als auch anderswo. -Bürger auf der ganzen Welt und auch hohe Amtsträger - einschließlich der Vereinigten Staaten - haben die Enthüllungen zu einem System der allumfassenden Überwachung, das niemandem Rechenschaft schuldig ist, als einen Dienst an der Öffentlichkeit beurteilt. -Diese Spionage-Enthüllungen zogen viele Vorschläge zu neuen Gesetzen und Richtlinien nach sich, die auf den vormals verdeckten Missbrauch des öffentlichen Vertrauens abzielten. -Der Nutzen für die Gesellschaft aus diesen gewonnenen Erkenntnissen wird zunehmend klarer; gleichzeitig wurden die in Kauf genommenen Risiken sichtlich vermindert. -Obwohl das Ergebnis meiner Bemühungen nachweislich positiv war, behandelt meine Regierung Dissens nach wie vor als Treuebruch und strebt danach, politische Meinungsäußerung zu kriminalisieren und unter Anklage zu stellen. -Dennoch: Die Wahrheit auszusprechen ist kein Verbrechen. -Ich bin zuversichtlich, dass die Regierung der Vereinigten Staaten mit Unterstützung der internationalen Gemeinschaft diese abträgliche Haltung ablegen wird. -Ich hoffe, dass ich, wenn die Schwierigkeiten dieser humanitären Lage beigelegt sind, in der Lage sein werde, mich an der verantwortungsvollen Aufklärung der Sachverhalte bezüglich der in den Medien getätigten Aussagen, insbesondere im Hinblick auf Wahrheit und Authentizität der Berichte, angemessen und gesetzesgemäß zu beteiligen. -Ich freue mich auf ein Gespräch mit Ihnen in Ihrem Land, sobald die Situation geklärt ist und danke Ihnen für ihre Bemühungen, das internationale Recht zu wahren, das uns alle beschützt. -Edward Snowden bezeugt durch Hans-Christian Ströbele -Kindergarten hat einen privaten Käufer gefunden -"Das Gebäude ist in guten Händen", sagt Winterlingens Bürgermeister Michael Maier. -Den ehemaligen Kindergarten in der Gartenstraße hat die Kommune an eine Privatperson verkauft. -Dem Verkauf stimmte der Gemeinderat in seiner jüngsten Sitzung im nicht öffentlichen Teil zu. -Das Gebäude wechselte seinen Besitzer zu einem "angemessenen" Preis. -"Die Gemeinde ist zufrieden", betont Maier. -Im Haushalt waren 100 000 Euro angesetzt. -Noch nicht klar ist, wie der neue Eigentümer den früheren Kindergarten nutzen will. -Dem Bürgermeister ist es Recht, dass Winterlingen die Verantwortung für das Gebäude vor dem Winter an jemand anderen übergeben hat und somit nicht mehr für die Unterhaltung des Hauses aufkommen muss, wie zu heizen, nach dem Garten zu schauen und den Gehweg zu räumen. -"Dieser Aufwand ist nun weg", freut sich Maier. -Platzkonzert bei hochsommerlichen Temperaturen -Der Musikverein Hammereisenbach gibt ein Platzkonzert auf der Terrasse des Gasthauses Hammer. -Als Dirigentin Bianca Willmann um 19.30 Uhr den Taktstock hebt, zeigt das Thermometer immer noch 25 Grad. -Etwa 50 Zuhörer erfreuen sich an den Klängen der Hammricher Musiker. -Die Vorsitzende Manuela Honeck erläutert die Musikstücke. -Honeck dankte in diesem Rahmen allen, die geholfen hatten, den ersten Musikerhock erfolgreich zu bewältigen. -Bericht: Obama-Kampagne überlegte, Biden durch Hillary Clinton zu ersetzen -Die engsten Berater von Präsident Barack Obama haben im Geheimen überlegt, Vizepräsident Joe Biden im Wahlkampf 2012 durch Hillary Clinton zu ersetzen, so die New York Times. -Die Enthüllung ist die beachtenswerteste Überraschung des lang erwarteten Werkes von Mark Halperin und John Heilemann über den Wahlkampf 2012, „Double Down: Game Change 2012“. -Der Times liegt ein Exemplar des demnächst erscheinenden Buchs vor und sie berichtete am Donnerstagabend, dass die Topberater des Präsidenten „umfangreiche Gruppensitzungen und Umfragen Ende 2011“ durchgeführt hätten, um herauszufinden, ob es für Obamas schwindende Hoffnung auf Wiederwahl besser aussehe, wenn er Biden fallen lasse. -Laut Jonathan Martin, nationalpolitischer Korrespondent der Times, zeichnet das Buch gründlich die Geschichte der Bemühungen führender Mitarbeiter der Kampagne und des Weißen Hauses nach, namentlich des früheren White House Chief of Staff Bill Daley, zu messen, welchen Effekt es auf die Umfragewerte habe, die ehemalige Außenministerin Clinton gegen den Vizepräsidenten zu tauschen. -Der potenzielle Wechsel war ein streng gehütetes Geheimnis innerhalb der Kampagneninfrastruktur in Chicago und im Oval Office. -Nur ein halbes Dutzend der engsten Berater des Präsidenten – einschließlich Daley, dem früheren Kampagnenleiter Obamas, Jim Messina, und der früheren leitenden Berater des Weißen Hauses, David Axelrod und David Plouffe – wussten über den angedachten Wechsel Bescheid. -In „Double Down“ wird behauptet, Daley habe an der Spitze der Bemühungen gestanden, Biden trotz ihres „engen, persönlichen Verhältnisses“ auszutauschen, ehe man sich schließlich gegen einen solchen Schachzug entschied, nachdem die Daten zeigten, dass die Kandidatur Clintons „die Chancen Obamas nicht maßgeblich verbessern“ würde. -In einem Interview mit Martin bestätigte Daley, dass die Regierung tatsächlich überlegt habe, Biden durch Clinton zu ersetzen. -„Ich äußerte eine ganze Reihe von Dingen, die man in Erwägung ziehen sollte, und das war eines davon“, erklärte Daley dem Magazin. -Man muss sich in Erinnerung rufen, dass der Präsident damals furchtbare Umfragewerte hatte, also dachten wir: „Um Gottes Willen, was machen wir?“ -Während Daley die Untersuchungen als „gebotene Sorgfalt“ bezeichnete, sagte Martin gegenüber Anderson Cooper von CNN, dass während der Kampagne für die Wiederwahl erheblicher Aufwand betrieben worden sei, um herauszufinden, ob ein solcher Zug sich im Hinblick auf die Wahlurnen auszahle. -„In Kampagnen wird nicht so viel Geld für Umfragen und Fokusgruppen ausgegeben, wenn man nicht ernsthaft über etwas nachdenkt“, erklärte Martin auf AC360. -Es ist allerdings nicht klar, ob Obama wusste, dass sein Team über einen Austausch nachdachte. -Martin sagte gegenüber CNN, er habe Daley gefragt, ob sein damaliger Boss über den potenziellen Wechsel Bescheid gewusst hätte. -Wenngleich Daley erklärte, er glaube nicht, dass sich der Präsident der möglichen Veränderung „bewusst war“, gab der frühere Stabschef zu, es sei „möglich“ gewesen, dass Obama davon gewusst habe. -Martin fügte hinzu, dass „Double Down“ keine definitive Antwort auf die Frage liefere, ob die politischen Sondierungen Obamas Schreibtisch erreicht hätten. -Cooper fragte Martin, ob er ernsthaft denke, Obama habe nichts von den Untersuchungen darüber gewusst, ob man Biden bei den Wahlen fallen lassen solle. -„Möglich“, erwiderte Martin. -Was passiert unterwegs mit meiner E-Mail? -E-Mail heißt ja eigentlich elektronischer Brief. -Wurde beim Entwurf des Internet-Dienstes E-Mail ähnlich wie bei der herkömmlichen Post technisch das Briefgeheimnis umgesetzt? -Bei der Konzeption der frühen Internetdienste stand im Vordergrund, Kommunikation möglich zu machen. -Daher hat man bei den grundlegenden technischen Protokollen nicht darauf geachtet, ein stabiles Fundament für einen anhörsicheren Briefwechsel zu schaffen. -Daher entspricht eine herkömmliche E-Mail eher einer offenen Postkarte als einem versiegelten Brief. -Wenn Nutzerin "Anna" an "Benni" eine Mail schickt, sind die Rechner der beiden dann direkt verbunden? -Das E-Mail-Programm oder der Browser von Anna schickt im ersten Schritt die Mail an den Mail-Server ihres Service-Providers. -Wenn sie zum Beispiel ein GMail-Konto hat, wäre das Google. -Dieser Mail-Server schickt den Inhalt an den Provider, den Benni nutzt. -Auf der Strecke kann die Mail über etliche andere Server im Internet laufen. -Bennie kann den Inhalt dann bei seinem Provider anrufen. -Werden herkömmliche E-Mails auf ihrem Weg durchs Netz mitgelesen? -Die meisten Mails werden unterwegs mehrfach von Software-Robotern gelesen. -Beim Provider des Absenders wird in der Regel überprüft, ob die Mail ein Schadprogramm als Anhang mit sich führt. -Auf den Empfänger-Systemen wird ebenfalls ein Virus-Check vorgenommen. -Außerdem überprüfen die Provider, ob es sich um eine lästige und unerwünschte Spam-Mail handelt, die gleich gelöscht oder zumindest in einen Spam-Ordner wegsortiert wird. -Haben die Roboter noch eine weitere Aufgabe? -Bei Systemen wie GMail ermittelt der Roboter auch die Informationen, die Google zum Platzieren von Kontext-Werbung benötigt. -Wenn Anna und Benni per GMail über ihre kommenden Ferienreise kommunizieren, kann Google entsprechende Links zu Urlaubsangeboten einblenden. -Lesen auch unbekannte Menschen die E-Mails mit? -Die Wahrscheinlichkeit, dass unbefugte Personen eine Mail mitlesen, ist äußerst gering. -Theoretisch ist das aber möglich. -In Firmen haben häufig Administratoren die Möglichkeit, elektronische Post mitzulesen. -Auch die Strafverfolgungsbehörden und Geheimdienste haben legale Möglichkeiten, E-Mails abzufangen oder zur Kenntnis zu nehmen. -Dazu kommen mögliche illegale Ausspäh-Aktionen. -Ist es möglich, E-Mails gegen das Mitlesen zu schützen? -Mit einer Verschlüsselung nach dem OpenPGP-Standard kann eine E-Mail wirksam gegen Mitlesen verschlüsselt werden. -Mit hohem technischen Aufwand ist es auch möglich, die Metadaten einer E-Mail-Kommunikation zu verschleiern, so dass nicht einmal ohne weiteres erkannt werden kann, wer mit wem kommuniziert hat. -Wie wichtig sind Yahoo und Google auf dem E-Mail-Markt in Deutschland? -Bei den hauptsächlich privat verwendeten Postfächern spielen Yahoo und Google laut einer Untersuchung von Convios Consulting aus dem August 2013 nur eine untergeordnete Rolle. -Während die Anteile von Yahoo-Mail zuletzt gefallen waren, konnte GMail von Google deutlich zulegen. -Mutter im Wald verscharrt und Rente kassiert -Als seine betagte Mutter starb, schmiedete ein Mann aus Wolfsburg einen Plan. -Er begrub die Tote in einem Wald - und kassierte weiterhin ihre Rente und Pflegegeld. -Um weiter Rente und Pflegegeld kassieren zu können, hat ein 67-jähriger Wolfsburger seine tote Mutter nach Ermittlungen der Polizei im Wald begraben. -Zunächst habe er den Tod der 89-Jährigen vertuscht, dann habe er das Geld mehr als anderthalb Jahre lang eingestrichen. -Nun muss sich der 67-Jährige wegen Betrugs verantworten, wie ein Sprecher am Freitag sagte. -Demnach versteckte der Mann die Frau in einem Wald in der Nähe von Helmstedt. -Verdächtigt wurde der Mann bereits im Mai: Damals fiel auf, dass für die pflegebedürftige Mutter keine Medikamente mehr benötigt wurden. -Den Ermittlern erzählte der Rentner, seine Mutter sei nach Spanien gereist. -Weil den Beamten dies merkwürdig vorkam, ermittelten sie weiter. -Dabei kam heraus, dass die beiden seit mehr als 15 Jahren in einer gemeinsamen Wohnung gelebt hatten und er sie zuletzt gepflegt hatte. -Der 67-Jährige habe schließlich den Tod seiner Mutter eingeräumt. -Eine Obduktion ergab laut Polizei keine Anhaltspunkte für ein Kapitalverbrechen. -Lust auf Eiscreme, die im Dunkeln leuchtet? -Ein britischer Unternehmer hat das erste im Dunkeln leuchtende Eis entwickelt – mithilfe von Quallen. -Charlie Francis machte sich die fluoreszierenden Eigenschaften der Meerestiere zunutze und schuf daraus eine leuchtende Leckerei. -Die Idee dazu kam ihm, nachdem er eine Forschungsarbeit über Quallen gelesen hatte und chinesische Wissenschaftler überzeugen konnte, das leuchtende Protein künstlich herzustellen. -Die Eiscreme reagiert beim Verzehr mit der Zunge, wodurch der pH-Wert im Protein angehoben und das Leuchten ausgelöst wird. -Chris erklärt, das Speiseeis reagiere auf die Wärme im Mund und leuchte – je mehr man also leckt, desto heller wird es. -Charlie, Gründer der Eiscremefirma „Lick Me I'm Delicious“, sagt: „Das Produkt ist unglaublich, aber noch in einer sehr frühen Produktionsphase, und so bekommt man zwei Gramm davon für 200 Pfund.“ -Das in der Eiscreme verwendete Protein reagiert mit der Zunge bei einem neutralen pH-Wert. -Wenn der Mund das Protein erwärmt, steigt der pH-Wert und das Eis leuchtet. -Wir haben es in den letzten Monaten getestet und es erschien uns ideal, es an Halloween auf den Markt zu bringen, weil es diesen wunderbaren Leuchteffekt hat. -Es ist wahrscheinlich die teuerste Eiscreme, die ich je hergestellt habe, denn der Quallenleuchtstoff ist viermal teurer als Gold. -Jede Kugel kostet mich etwa 140 Pfund. -Aber dafür schmeckt es lecker. -Charlies experimentelle Firma mit Sitz in Bristol ist berühmt für ihre ungewöhnlichen Geschmacksrichtungen, zu denen Bier, Käse, Rindfleisch und Blattgold gehören. -Doch seine neueste Schöpfung soll noch ambitionierter werden. -Er sagt: „Ich möchte gern eine unsichtbare Eiscreme entwickeln.“ -Das ist an und für sich unmöglich aufgrund der Lichtbrechung, die durch die Eiskristalle hervorgerufen wird, aus denen Eiscreme besteht; aber ich vermute, wir finden eine Möglichkeit, es zu schaffen. -Die Eiscreme nutzt die fluoreszierenden Eigenschaften einer Qualle, die von chinesischen Wissenschaftler künstlich hergestellt werden -Der TSV Morsum ist nach sechs Spieltagen noch immer ohne jeden Punkt in der Handball-Verbandsliga. -Ausgerechnet jetzt kommt es für das Team von Ingo Ehlers morgen zum Derby gegen den TSV Daverden, der am vergangenen Wochenende seiner ersten Sieg gefeiert hat. -Mit diesem Spiel beginnt die Saison neu für uns. -"Ich hoffe, dass wir endlich mal den Kopf frei haben", gibt sich Ehlers verhalten optimistisch. -Dabei hat er endlich mal, mit Ausnahme des dauerverletzten Hendrik Blohme, seinen kompletten Kader zur Verfügung. -Auch Daverden setzt wie wir auf Tempospiel. -Daher müssen wir unsere Fehlerquote gering halten. -In erster Linie gilt es sich jedoch in der Deckung zu steigern, die zuletzt alles andere als einen sicheren Eindruck hinterlassen hat. -Deutlich entspannter gibt sich Daverdens Coach Thomas Panitz nach dem ersten Saisonsieg gegen Nordhorn. -Das Spiel war natürlich eine Hausnummer. -Aber deswegen sind wir noch lange nicht der Favorit, denn im Derby kann immer alles passieren. -Gleichwohl will Panitz nun nachlegen in Morsum, um eine Serie zu starten. -Fehlen wird Jan-Malte Jodat. -Dafür feiert der A-Jugendliche Joost Windßuß, der mit Doppelspielrecht beim A-Jugend-Bundesligisten HC Bremen aktiv ist, sein Saisondebüt. -Wer in so einem Spiel nicht heiß ist, soll zuhause bleiben. -Halloween 2013: In Zahlen -Als ich klein war, hatte Halloween etwas Magisches. -Meine Schwester und ich durften Süßigkeiten essen, spät aufbleiben und uns für die Nachbarschaft verkleiden. -Heute bin ich eher zum Halloween-Muffel geworden. -In den letzten zwei Jahren habe ich mich nicht bereit erklärt, von meiner Wohnung aus Süßigkeiten zu verteilen, und werde es wohl auch dieses Jahr nicht tun. -Aber laut Statistik bin ich ein schwarzes Schaf, was Halloween angeht. -Die Mehrheit der Amerikaner – 158 Millionen, um genau zu sein – feiern dieses Jahr Halloween und geben dabei insgesamt 6,9 Milliarden US-Dollar für Süßigkeiten, Kostüme und Dekorationen aus, so der US-amerikanische Verband der Einzelhändler (NRF). -Eine Sache, auf die ich mich jedes Halloween dann doch wieder freue, sind die Trendwerte. -Die Kostüme werden etwa 1,2 Milliarden der 6,9 Milliarden ausgegebenen US-Dollar ausmachen, so der NRF. -In diesem Jahr sind aufreizende unbelebte Gegenstände der letzte Schrei. -Frauen müssen sich nicht mehr in sexy Kleidung hüllen, sie können jetzt auch verführerisches Essen wie etwa Pizza, Hamburger oder Karotten verkörpern. -Was Männer angeht, erwarte ich jede Menge Zombies dank The Walking Dead, und ich wette, die Daft-Punk-Weltraummänner werden es dieses Jahr in die Instagram-Feeds schaffen. -Laut Google sind die meistgesuchten Kostüme Zombies, Batman, Piraten und Hexen. -Ich vermute, es ist nichts verkehrt an Tradition. -Wir haben letztes Jahr unsere Hunde verkleidet und waren damit zu meinem Erstaunen nicht allein. -Tatsächlich geben Amerikaner 330 Millionen Dollar dieses Jahr für Kostüme ihrer Haustiere aus, so der NRF. -Das sind eine Menge „Hot Dogs“. -Was Süßigkeiten angeht, sind wir nicht zimperlich. -Amerikaner werden dieses Jahr 1,9 Milliarden Dollar dafür ausgeben, so The Nielsen Company. -Das sind etwa 270 Millionen Kilogramm an Schokoriegeln, Lutschern, Bonbons und Gummibärchen. -Also gute Nachrichten für die laut US-Handelsministerium 41 Millionen Menschen, die mit dem Spruch „Süßes oder Saures“ durch die Viertel ziehen. -Tatsächlich werden wir über 40 Millionen Kilogramm Schokolade für Halloween kaufen und – machen wir uns nichts vor – essen. -Das Einzige, was wir nicht konsumieren möchten, ist das zuckersüße Candy Corn; dennoch werden 15 Millionen Kilogramm davon um Halloween verkauft, so der Nationale Verband der Süßwarenhersteller. -Das sind etwa neun Milliarden Maiskörner. -Es ist ein Rätsel, das ich noch nicht lösen konnte. -Nichts gehört mehr zu Halloween als Häuser, in denen es spukt. -Sie haben großartige Namen wie „Terror Behind the Walls“ (was übrigens in einem echten Gefängnis steht), „Howl-O-Scream“ und „The House of Shock“. -Es gibt tatsächlich 1.200 offiziell anerkannte Spukhäuser in den Vereinigten Staaten, die 500 Millionen Dollar Erlös erzielen, so America Haunts, und das schließt die wunderbaren Fotos ein, auf denen man sich gerade in die Hose pinkelt und die Freunde dann auf Facebook stellen, wo man sie nicht wieder loswird, und einem dann der Typ, auf den man steht, einen Kommentar hinterlässt wie „netter Gesichtsausdruck“. -Aber kommen wir endlich zu den Kürbissen. -Charlie Brown machte uns mit dem Großen Kürbis vertraut, als wir Kinder waren, und das Schnitzen einer Kürbislaterne ist wie das Schmücken des Weihnachtsbaums – etwas, das wir von klein auf gemacht haben. -Glücklicherweise begann der „Baby im Kürbis“-Trend erst letztes Jahr dank Pinterest, sodass die meisten von uns damit aufgewachsen sind, den Kürbis zurechtzuschnitzen und nicht, darin zu sitzen. -Dieses Jahr werden Amerikaner etwa 106 Millionen Dollar für Kürbisse ausgeben, so das US Census Bureau. -Die Kürbislaterne, die langsam im Vorgarten verrottet, stammt vermutlich aus Illinois, wo dieses Jahr 245 Millionen Kilogramm Kürbisse geerntet wurden. -Wenn Sie richtig punkten möchten, dann rufen Sie Tim und Susan Mathisdon in Napa, Kalifornien an und schnitzen Sie etwas Hübsches aus ihrem 921 Kilo schweren Kürbis. -Wohnungsbrand in Helmbrechts: Rettungskräfte bergen verkohlte Leiche -Die Feuerwehr musste am späten Donnerstagabend in Helmbrechts ausrücken. -Ein Dreifamilienhaus im Landkreis Hof fängt in der Nacht zum Freitag Feuer. -Die Rettungskräfte bergen eine verkohlte Leiche aus dem Haus. -Rettungskräfte haben bei einem Brand in Helmbrechts eine verkohlte Leiche in einem Dreifamilienhaus gefunden. -Ob es sich um einen Bewohner handle sei noch unklar, wie ein Sprecher der Polizei in Bayreuth sagte. -Am späten Donnerstagabend hatte es im Erdgeschoss des Hauses gebrannt. -Als die Feuerwehr eintraf, schlugen die Flammen bereits aus einem Fenster. -Die Einsatzkräfte gingen in die Wohnung und fanden die Leiche in einem Zimmer. -Dieses und ein weiteres Zimmer brannten vollständig aus. -Das gesamte Erdgeschoss wurde beschädigt. -Alle anderen Bewohner seien während des Brands nicht zu Hause gewesen, erklärte der Polizeisprecher weiter. -Das Gebäude sei vorerst nicht mehr bewohnbar. -Die genaue Ursache für das Feuer war zunächst nicht klar. -Deutschlands größte Landeskirche hat eine Offensivegestartet, um für den Beruf des Pastors zu werben. -Hintergrund ist ein möglicher Pfarrermangel: Die Evangelisch-lutherische Landeskirche Hannover rechnet damit, dass sich die Zahl ihrer derzeit rund 1.800 Pastorinnen und Pastoren nach jetziger Lage bis 2030 halbieren wird. -Für junge Leute von heute sei der Beruf sehr attraktiv und biete gute Zukunftsaussichten, sagte Pastor Mathis Burfien (43) im Gespräch mit dem Evangelischen Pressedienst. -Es ist attraktiv, den Arbeits- und Tagesablauf selbst bestimmen zu können. -Mit Burfien hat die Landeskirche erstmals einen Pastor auf einer vollen Stelle beauftragt, junge Leute für das Theologiestudium zu begeistern. -Zurzeit entscheiden sich immer weniger junge Menschen nach dem Abitur, Theologie zu studieren. -Burfien führt dies auf den Prozess der Entkirchlichung zurück: "Gottes Stimme ist leise, die Welt ist laut". -Dabei sei der Beruf von großer Freiheit und Vielfalt geprägt. -Ich bin Herr meines Terminkalenders und kann dort meine Schwerpunkte setzen, wo es mir wichtig ist. -Pastoren könnten als Seelsorger ganz nah bei den Menschen sein. -Sie verdienten so viel wie Lehrer und könnten gut davon leben. -Sicherlich werde in der freien Wirtschaft mehr bezahlt, aber dafür hätten Theologen einen sicheren Arbeitgeber. -Das komme den jungen Leuten von heute entgegen, denen es nicht nur um Karriere, sondern um eine sinnerfüllte Arbeit gehe. -Nach Angaben der Landeskirche, die drei Viertel Niedersachsens umfasst, gehen derzeit pro Jahr rund 60 Pastoren in den Ruhestand. -Gleichzeitig beginnen etwa 40 Absolventen eines Theologiestudiums ihr Vikariat. -In dünn besiedelten Randgebieten wie dem Harz, dem Emsland oder dem Wendland könne künftig es schwer werden, Stellen zu besetzen. -Burfien will unter anderem Studientage für junge Leute veranstalten und dazu auch Prominente einladen, die Theologie studiert haben. -Das Theologiestudium biete eine breite Ausbildung. -Man kann damit sogar Bundespräsident werden. -Land Rover Rally Series angekündigt -Im Interieur finden sich Rennsitze und Sechs-Punkt-Gurte sowie eine Freisprechanlage. -Die Optionen reichen von verstärkten Bremsen, einem Servicepaket mit Betreuung von Bowler-Works-Mechanikern und logistischer Unterstützung bis hin zu Fahrzeugeinlagerung zwischen Veranstaltungen. -Drew Bowler, Geschäftsführer von Bowler Motorsport, erklärt: „Die Rally-Kunden, die zu Bowler kommen, haben sich verändert.“ -Sie sind nicht alle erfahrene Rennfahrer, sondern Leute, die auf der Suche nach Spannung und Abenteuer sind sowie nach einem erreichbaren Weg zu Weltklasse-Veranstaltungen. -Wir freuen uns, diesen Weg in Partnerschaft mit Land Rover und der MSA eröffnen zu können und glauben, dass das Format neue Möglichkeiten erschließt, die verschiedenen Rally-Disziplinen in Großbritannien und dem Ausland zu erleben und Neulinge auf die Strapazen und die Wirklichkeit von Rally Raid vorzubereiten. -Wir hatten wirklich Spaß daran, den Defender Challenge-Wagen zu entwickeln – das wird eine unterhaltsame Meisterschaft. -Darüber hinaus gibt es bei der Defender Challenge einen Trainings- und Testtag im Februar sowie die Option, an Wüstenrennen in Nordafrika und dem Nahen Osten teilzunehmen. -Feuerwehr zur Rettung eines Hündchens gerufen, das 15 Meter über dem Boden auf einem gefährlichen Felsvorsprung in einem Steinbruch festsaß -Cockerspaniel-Hündin Ruby war nach einem kleineren Verkehrsunfall davongelaufen -Drei Tage später wurde sie von einem Spaziergänger im Steinbruch in ihrer misslichen Lage entdeckt -Feuerwehrleute seilten sich an Felswand ab und retteten den Hund vor dem sicherem Tod -Der Feuerwehr gelang es, ein Hündchen in Sicherheit zu bringen, das auf einem 15 Meter hohen Vorsprung in einer Felswand festsaß. -Die neun Monate alte Cockerspaniel-Hündin Ruby war nach einem Verkehrsunfall am Sonntagnachmittag davongelaufen und überlebte drei Tage allein, ehe sie am Mittwoch aus einem Steinbruch gerettet wurde. -Scott Alderson, 25, und seine Freundin Becky Hall, 20, denen der Hund gehört, kamen zum Steinbruch Flappit in Denholme, West Yorkshire, um Ruby wieder in Empfang zu nehmen und sich bei den Feuerwehrleuten des West Yorkshire Fire and Rescue Service zu bedanken. -Sie hatten verzweifelt nach ihrer vermissten Hündin gesucht und Hilferufe in sozialen Netzwerken veröffentlicht, als sie nach dem kleinen Verkehrsunfall in den Steinbruch entlaufen war. -Etwa gegen 14:15 Uhr am Mittwoch sah ein Spaziergänger, der seinen Hund ausführte, die gestrandete Ruby auf dem 15 Meter hohen Absatz im Steinbruch. -Das technische Rettungsteam der Cleckheaton Fire Station eilte zur Stelle und seilte sich zur Rettung Rubys ab, die dann in einer Hundebox nach oben transportiert wurde. -Andy Clayton, Spezialist des technischen Rettungsteams, sagte: „Sie war in einer gefährlichen Lage. -Sie befand sich in der Mitte der Felswand – 15 Meter von oben und dem Boden entfernt. -Sie bewegte keinen Muskel während ihrer Rettung – sie war wie steifgefroren. -Aber jetzt geht es ihr gut. -Sie hat anschließend ein paar Hundekuchen gefressen. -Das war ein ziemlich ungewöhnlicher Einsatz. -Die Tatsache, dass der Hund entdeckt wurde, ist unglaublich. -Peter Lau, Spezialist des technischen Rettungsteams, sagte: „Ruby hatte ein Riesenglück.“ -Es bestand die Möglichkeit, dass sie sehr schwer verletzt war oder Schlimmeres. -Ruby wurde zum Tierarzt zur Untersuchung gebracht, der aber außer Erschöpfung und Dehydrierung nichts feststellte. -Becky Hall aus Halifax, West Yorkshire, sagte: „Bei der Rettung zuzusehen war beängstigend.“ -Ich konnte gar nicht glauben, dass sie wirklich da oben war. -Es war wundervoll, sie wieder in den Armen zu halten. -Der Tierarzt erklärte, wenn die Erschöpfung zu groß geworden und sie zusammengebrochen wäre, wäre sie vermutlich abgestürzt. -Die Feuerwehrleute haben Großartiges geleistet. -Es war wirklich mutig, was sie getan haben. -Wir sind einfach so dankbar und jeder von ihnen war absolut unglaublich. -Scott Alderson aus Keighley fügte hinzu: „Wir hatten Angst, dass sie herunterfallen würde, aber sie blieb oben.“ -Die Feuerwehrleute waren großartig. -Ich kann kaum glauben, wo sie war. -Mick Jagger erklärt, er habe Katy Perry nie angemacht, als sie 18 war. -Bei einem Interview mit einem australischen Radiosender diese Woche sagte der Pop-Star, sie habe als Background-Sängerin an Jaggers Song „Old Habits Die Hard“ aus dem Jahr 2004 mitgearbeitet. -Perry erklärte, sie habe mit dem Altrocker zu Abend gegessen, und dass „er mich angebaggert hat, als ich 18 war“. -Sie fügte hinzu: „Das ist schon lange her und er war sehr nett zu mir.“ -In einer Erklärung sagte ein Vertreter Jaggers, 70, am Donnerstag, dass er „kategorisch verneint, dass er jemals Annäherungsversuche bei Katy Perry unternommen habe“. -Der Vertreter ergänzte: „Vielleicht hat sie ihn mit jemand anderem verwechselt.“ -Perry war eine der Sängerinnen, die bei der Rolling-Stones-Tour dieses Jahr einen Gastauftritt hatten. -Ihr neues Album „Prism“ landete auf Anhieb auf Platz 1 diese Woche. -George Kerevan: Europäische Entflechtung gibt den Schotten die Wahl -Ein neuer Tag, eine neue Schreckensgeschichte zur Unabhängigkeit. -Diesmal werden wir gewarnt, dass ein unabhängiges Schottland erforderlich sei, um dem Schengen-Raum als Bedingung für die EU-Mitgliedschaft beizutreten. -Das liefert das Stichwort für Geschichten über Passkontrollen in Berwick und eine mit Stacheldraht gesicherte Grenze entlang des Hadrianswalls. -In der Tat wurde in der Strathclyde-Vorlage auf die möglichen wirtschaftlichen Vorteile hingewiesen, die mehr Reisefreiheit im restlichen Europa mit sich bringen würde, doch spielte dies – erwartungsgemäß – in den Schlagzeilen keine Rolle. -Auch hat niemand erwähnt, dass sich die EU-Mitgliedsstaaten häufig ihre strikten Regeln zurechtbiegen, wenn es für sie günstiger ist. -Da Schottland derzeit nicht zum Schengen-Raum gehört, wäre die fortgesetzte Nichtbeachtung für Brüssel nur ein kleines Zugeständnis im Gegenzug für das, was man wirklich von den Schotten will. -Also eigentlich keine Story. -Und eine, die schon so in der Jahre gekommen ist, dass sie bereits einem Fossil gleicht: Das erste Mal habe ich die „Unabhängigkeit bedeutet Passkontrollen“-Ente vor mindestens 40 Jahren gehört. -Dabei geht ein interessanter Punkt beim ständigen Wiederholen dieser uralten Geschichte verloren. -Weshalb sollte man von einem unabhängigen Schottland erwarten, dass es nach der europäischen Pfeife tanzt? -Warum das Londoner Joch gegen das von Brüssel eintauschen, besonders jetzt? -Das sind die wirklichen europäischen Neuigkeiten: Der große, nach dem Krieg gefasste Plan zur Vereinigung Europas ist ins Stocken geraten. -Mit der Eurokrise ist das Projekt Europa offiziell gestorben. -Überall in der EU gewinnen Parteien an Boden, die EU-feindlich sind oder den Euro als Gemeinschaftswährung über Bord werfen möchten. -Selbst in Deutschland konnte die euroskeptische Partei Alternative für Deutschland – erst dieses Jahr gegründet – knapp fünf Millionen Stimmen bei der Bundestagswahl im September für sich ergattern und damit effektiv die Freien Demokraten (das Äquivalent zu den britischen Liberal Democrats) aus dem Bundestag vertreiben. -Es gab schon immer heimischen Widerstand gegen den Plan, ein föderalistisches Europa zu schaffen. -Die aktuelle Wirtschaftskrise stellt sich allerdings als Wendepunkt heraus. -Wegen der von Berlin und der Europäischen Zentralbank verhängten strengen Sparpolitik in Verbindung mit der Zwangsjacke, in die die jeweilige nationale Wirtschaft durch das Festhalten an der gemeinsamen Währung genötigt wird, sind viele Menschen der Ansicht, das Projekt Europa sei zu weit gegangen. -Die Krise des Euro hat wenig damit zu tun, dass nationale Regierungen exzessive Haushaltsdefizite verursachen – das war nur in Griechenland der Fall. -Stattdessen hat das Eurosystem seine Mitglieder auf Wechselkurse festgenagelt, die für deutsche Exporteure vorteilhaft sind – und das möchten deutsche Politiker gern so lassen. -Ohne die Möglichkeit einer Abwertung der lokalen Währung findet sich Südeuropa mit einem eingebauten Produktivitätsnachteil gegenüber Deutschland wieder. -Die einzige Möglichkeit besteht darin, Löhne und öffentliche Ausgaben zu kürzen – angespornt von Berlin. -Hinter den aktuellen Etat- und Währungsproblemen liegt ein tiefer gehendes europäisches Produktivitätsproblem. -Als Folge der von Brüssel verordneten „grünen“ Energierichtlinien – ein Code für die Subventionierung französischer und deutscher Energieunternehmen auf Kosten der Verbraucher – zahlt die europäische Industrie verglichen mit den Vereinigten Staaten das Doppelte für Elektrizität und das Vierfache für Gas. -Das ist ein lähmender Kostennachteil, wie wir bereits in Grangemouth sehen konnten. -Alle Lohnfestschreibungen der Welt schützen die europäische petrochemische Industrie nicht davor, von billigem amerikanischem Schiefergas in die Ecke gedrängt zu werden. -Als Folge zieht eine Revolte herauf, besonders in Frankreich, dem einstigen Cheerleader der EU. -Nach dem Krieg sah die politische Elite Frankreichs die EU als ein Vehikel, mit dem man Deutschland in Schach halten und Paris auf Augenhöhe mit Washington heben konnte. -Doch Berlin braucht Paris nicht mehr als Tor zur politischen Legitimation und hat Europa seine eigene Wirtschaftspolitik aufgedrückt, womit die angeschlagene französische Wirtschaft zu kämpfen hat. -Das Ergebnis: Marine Le Pens rechtsgerichteter, EU-feindlicher Front National hat gerade eine entscheidende Nachwahl gewonnen und damit die regierenden Sozialisten auf Platz drei verwiesen. -Der Front National ist nun die beliebteste Partei in Frankreich mit 24 Prozent der Wählerstimmen – eine zeitgerechte Warnung für die britische Labour-Partei, dass sie nicht davon ausgehen kann, eine Spaltung im rechten Spektrum würde automatisch Vorteile für die Linke bedeuten. -Was macht Le Pen mit ihrer neu gewonnen Popularität unter den weißen Franzosen der Arbeiterklasse? -Sie möchte die EU-Wahlen nächstes Jahr dazu nutzen, einen Anti-EU-, Anti-Gemeinschaftswährungsblock im Europäischen Parlament zu schmieden. -Falls die Anti-EU-Parteien bei diesen Wahlen gut abschneiden – und das ist sehr gut möglich –, dann könnte erstmals ein solcher Block das Europäische Parlament dominieren. -Dies ist der Punkt, auf den ich hinaus will: In nicht allzu ferner Zukunft wird das wachsende Ressentiment gegen die EU und die gemeinsame Währung sich zur Abschaffung des Euro zusammenfinden. -Die EU wird nicht verschwinden, aber sie wird eher so etwas wie das „Europa der (souveränen) Nationen“ werden, das General de Gaulle favorisierte. -Deutschland und ein paar seiner Satelliten-Wirtschaften halten vielleicht am Euro fest, aber Frankreich und Südeuropa werden zu ihren eigenen Währungen zurückkehren. -Ich erwarte, dass sich Großbritannien von diesem Projekt distanziert in der Hoffnung, sich bei den USA einzuschmeicheln. -Allerdings legt Washingtons wachsendes Interesse am Pazifikraum nahe, dass Großbritannien allein im atlantischen Regen stehen wird. -Wo bleibt Schottland bei all dem? -Wir können entscheiden, eine Region von (in letzter Konsequenz) Klein-England zu sein. -Oder wir können unsere eigenen wirtschaftlichen Interessen verteidigen – wozu auch gehört, Berlin und Brüssel zu sagen, was sie sich wo hinstecken können. -Ich vermute, Schottland könnte in einem lockereren europäischen Verbund ganz gut fahren, sofern wir unsere eigene Währung behalten. -Die Kooperation mit anderen, ähnlich denkenden Ländern ist in einem nicht föderierten Europa der Nationen einfacher. -Ansonsten sollten wir erwägen, es Norwegen gleichzutun und unsere wirtschaftliche Unabhängigkeit behalten. -Die SNP-Regierung in Schottland ist – bemerkenswerterweise – die erfolgreichste politische Bewegung gegen die Sparpolitik in Europa und hat 2011 eine spektakuläre Mehrheit aufgrund der Ablehnung der vorgeschlagenen (und vorgenommenen) Kürzungen gewonnen, die der Labour-Finanzminister Alistair Darling und die nachfolgende Tory-Lib Dem-Koalition zu verantworten hatten. -Es wäre für Schottland lächerlich, sich jetzt für eine Unabhängigkeit zu entscheiden, nur um dann von Berlin und Brüssel eine Sparpolitik aufgezwungen zu bekommen. -Frühe Pubertät: Schneller erwachsen werden -Afroamerikanische und hispanische Mädchen erreichen tendenziell eher die Pubertät als ihre weißen Altersgenossinnen, wie Untersuchungen zeigen. -Körperliche Veränderungen bedeuten noch nicht unmittelbar die Pubertät -Es gibt keinen Beleg dafür, dass Hormone oder andere Chemikalien verantwortlich sind -Experten glauben, die epidemische Fettleibigkeit sei ein Auslöser der frühen Pubertät -Der Trend zur frühen Pubertät ist weniger ausgeprägt bei Jungen -Die frühere CNN-Korrespondentin Pat Etheridge ist eine Journalist, die sich auf Kindergesundheit und Familienthemen spezialisiert hat. -Sollte eine Mutter besorgt sein, wenn ihrer Tochter schon im Alter von sieben oder acht Jahren Brüste und Schamhaare wachsen? -Bei der jährlichen Konferenz des Kinderärzteverbandes American Academy of Pediatrics diese Woche in Orlando, Florida, erklärte der pädiatrische Endokrinologe Dr. Paul Kaplowitz, dass diese frühen körperlichen Veränderungen relativ normal unter amerikanischen Mädchen seien und eine neue Norm darstellten. -„Ich verbringe viel Zeit damit, Eltern zu beruhigen – normalerweise ist das kein Anzeichen dafür, dass nun schnell die Pubertät eintritt“, erläuterte Kaplowitz. -Offensichtliche Anzeigen der Entwicklung, wie zum Beispiel wachsende Brüste, Scham- und Achselhaare sowie Körpergeruch, zeigen sich bei Mädchen früher. -Dennoch es gab nur eine leichte Verschiebung beim Alter der Menarche (Auftreten der ersten Regelblutung) in den letzten 40 Jahren. -In den Vereinigten Staaten liegt das Durchschnittsalter bei 12,5 Jahren, etwas weniger als 12,75 im Jahr 1970. -„Wenn die Entwicklung der Brüste beginnt, dauert es noch mindestens zwei bis drei Jahre bis zur Menarche“, sagte Kaplowitz, auch Autor des Buchs „Frühe Pubertät bei Mädchen: Der essentielle Ratgeber zum Umgang mit diesem häufig auftretenden Problem“. -Zeit ist der genaueste Test, wie die Pubertät voranschreitet. -Es ist umstritten, was tatsächlich den Beginn der Pubertät darstellt, doch gilt es „frühreif“, wenn die Brustvergrößerung von einem Wachstumsschub vor einem Alter von acht Jahren begleitet wird. -In den meisten Fällen verlangsamt sich der Prozess oder stagniert – etwas, das ein Kinderarzt genau überwachen kann. -Bei schnellerem Voranschreiten könnten Tests durch einen Endikronologen angezeigt sein, damit ernste Probleme wie Tumore oder Zysten ausgeschlossen werden können. -Es gibt Behandlungsmethoden zum Verzögern einer frühen Menstruation und der Abwehr einer anderen Folge: der vorzeitigen Alterung der Knochen, die ultimativ zu Wachstumshemmung und damit kleinem Körperwuchs bei Erwachsenen führen kann. -Die Empfehlungen für eine medikamentöse oder hormonelle Therapie orientieren sich am Alter des Kindes, der Entwicklungsgeschwindigkeit, der Wachstumsrate und der emotionalen Reife. -Psychosoziale Aspekte sind ebenfalls wichtig. -Kaplowitz ist zurückhaltend, was Medikamente angeht, aber er räumt ein: „Das Unterdrücken der Pubertät kann Verhaltensprobleme und die Gefühle von Mädchen mildern, anders als ihre Altersgenossinnen zu sein.“ -Das andere große Problem ist verständlich: Eltern möchten einfach nicht, dass ihre sehr jungen Töchter die Periode haben. -„Sie sorgen sich wegen des Schwangerschaftsrisikos oder darüber, wie sie die hygienische Seite handhaben“, sagte Kaplowitz. -„Es war ein Schock“, erinnert sich eine Frau, deren Tochter mit zehn ihre Tage bekommen hat. -Auch wenn es erste Anzeichen gab und wir über Menstruation gesprochen hatten, war sie emotional nicht vorbereitet. -Sie kam verängstigt und aufgeregt von der Schule nach Hause, weil sie die erste in ihrem Freundeskreis war. -Es gibt viele umfassend publizierte Theorien über die Ursachen frühzeitiger Pubertät. -Doch es liegen keine schlüssigen Beweise dafür vor, dass Hormone in Milch oder anderen Nahrungsmitteln, Chemikalien in der Umwelt oder sexuelle Botschaften in den Medien ursächlich wären. -Jungen kommen ähnlich wie Mädchen früher in die Pubertät. -Kaplowitz ist der Ansicht, die überzeugendste Prämisse sei die Epidemie der Fettleibigkeit. -Er unterstützte die Durchführung einer Studie im Jahr 2001 über Mädchen im Alter von sechs bis neun Jahren, die einen Zusammenhang zwischen Körperfett und dem Eintritt in die Pubertät herstellt. -Andere Untersuchungen unterstützen diese Schlussfolgerung, aber es gebe viele andere Faktoren, die eine Rolle spielten. -In diesem Land erreichen afroamerikanische und hispanische Mädchen tendenziell eher die Pubertät als ihre weißen Altersgenossinnen. -Es gibt dafür unterschiedliche Erklärungen. -Global scheint eine frühe Pubertät von allen möglichen Dingen beeinflusst zu sein, von Wirtschaftsbedingungen über das Klima bis zu den Genen. -Ein weiteres Rätsel: Obwohl Jungen die Gesichts- und Schambehaarung schon früher wächst, ist der Trend zu einer vollumfänglichen früheren Pubertät nicht so ausgeprägt wie bei Mädchen. -Andere Ärzte auf der AAP-Konferenz unterstrichen die Komplexität des Themas. -Das Auftauchen von Akne und Schamhaar ist selbst bei Babys und Kleinkindern verbreitet. -„Wir müssen vorsichtig sein, wie wir den wahren Beginn der Pubertät identifizieren“, sagte Dr. Lawrence Silverman, ein pädiatrischer Endokrinologe am Goryeb Children's Hospital in Morristown, New Jersey. -Eltern sollten nicht zögern, sich von ihrem Kinderarzt beraten zu lassen, wie sie mit ihrem Kind sprechen sollten. -„Das kann bedeuten, dass man früher als erwartet ein Gespräch führen muss“, riet Kaplowitz. -Wenn man ruhig bleibt, reagiert das Kind normalerweise gut. -Mädchen, die früh erblühen, brauchen die Versicherung, das der Prozess ein normaler Teil des Lebens ist, selbst wenn er früher als gedacht eintritt. -Jumbo-Hersteller streiten im Angesicht großer Bestellungen über Sitzbreite -Zwischen führenden Flugzeugherstellern ist es zu einer Auseinandersetzung über die Breite von Sitzen in der Touristenklasse bei Langstreckenflügen gekommen, was den Ton für einen bitteren Zusammenstoß auf der Dubai Airshow diesen Monat angibt. -Der Streit dreht sich um die Sitzbreite, die Passagieren bei langen Flügen in der Touristenklasse angeboten wird – nicht immer diejenigen, um die sich Fluggesellschaften am meisten bemühen, doch dieser ausgewiesene Raum stellt den Schlüssel für die Effizienzbehauptungen bei den neuesten Flugzeugen von Airbus SAS und Boeing Co. dar. -Airbus forderte diese Woche einen Branchenstandard, der eine Sitzbreite von mindestens 46 cm (18 Zoll) in der Touristenklasse vorschreibt, doch der US-amerikanische Erzrivale Boeing sagt, das sollten die Fluggesellschaften entscheiden. -Die Auseinandersetzung kommt zu einem Zeitpunkt, da die Flugzeugbauer darum wetteifern, noch größere Versionen ihrer zweimotorigen Langstreckenflieger zu verkaufen, und bei der Veranstaltung vom 17. bis 21. November Rekordaufträge erwarten. -Wie der hintere Teil des Flugzeugs ausgestaltet ist – insbesondere ob es Reihen mit neun oder zehn Sitzen gibt –, ist entscheidend für die angegebenen wirtschaftlichen Ergebnisse, die das neue „Mini-Jumbo“-Design liefern soll. -Boeing behauptet, seine modernisierte „777X“ fasse 406 Personen bei Sitzen in der Touristenklasse, die über 17 Zoll breit sind und so zehn pro Reihe erlauben. -Airbus erklärt, die konkurrierende Version des A350 befördere 350 Personen in 18 Zoll breiten Sitzen in der Touristenklasse, wobei es neun pro Reihe gibt. -Flugzeuggiganten teilen häufig Hiebe über technische Details mittels Werbung in der Fachpresse aus. -Jetzt appelliert Airbus vor der Dubai Airshow, wo die 777X mit über 100 Bestellungen voraussichtlich das Rennen machen wird, direkt an die Öffentlichkeit. -Kürzlich stellte das Unternehmen eine Anzeige vor, die der Beginn eines neuen Anzeigenkriegs sind könnte und Geldgebern ein Bild zeigte, in dem drei Personen zusammengequetscht in einem Restaurant sitzen, und dazu der Titel: „Würden Sie das akzeptieren?“ -„Boeing schlägt Langstreckenflüge in Sitzen vor, die enger sind als regionale Turbo-Props“, sagt Airbus-Vertriebschef John Leahy. -Angesichts geänderter Ernährungsgewohnheiten werden die Leute dicker, doch Flugzeugsitze haben sich nicht radikal verändert. -Zwischen den frühen 1970ern, als die Boeing 747 die moderne Langstreckenreise definierte, und dem Ende des Jahrhunderts hat das Gewicht des männlichen Durchschnittsamerikaners zwischen 40 und 49 Jahren um zehn Prozent zugenommen, so die Daten des US-Gesundheitsministeriums. -Der Taillenumfang des männlichen Durchschnittsamerikaners im 21. Jahrhundert beträgt laut US-amerikanischen Gesundheitsstatistiken 100,8 cm. -Airbus sagt, sein Rivale halte sich an ein Sitzkonzept aus den 1950ern, als der durchschnittliche Körperumfang des frisch getauften „Jet Set“ schmaler war. -Man habe, so Airbus, Untersuchungen durchgeführt, die belegten, dass ein zusätzliches Zoll (2,54 cm) pro Sitz die Schlafqualität um 53 Prozent steigere. -Boeing bestreitet die Zahlen von Airbus zu den Sitzmaßen und sagt, es stehe nicht im Ermessen der Hersteller zu entscheiden, wie Fluggesellschaften die Balance zwischen Flugtarifen und Einrichtung gestalten. -Das Unternehmen erklärt auch, laut Untersuchungen hänge das Erlebnis im Flugzeug von mehr als der Sitzbreite ab. -„Es läuft wirklich darauf hinaus, Fluggesellschaften Flexibilität zu geben, damit sie das tun können, was sie ihrer Ansicht nach müssen, um erfolgreich zu sein“, erläutert der Flugkabinenexperte Kent Craver von Boeing. -Sie möchten nicht, dass wir ihnen diktieren, was sie profitabel macht. -Sie kennen ihr Geschäft besser als jeder andere. -Für Fluggäste geht es um mehr Ellenbogenfreiheit, aber für Hersteller wird es in zunehmendem Maße ein Thema, das sich auf die Umsätze auswirken kann. -Hinter der Auseinandersetzung steht ein Rennen um Flugzeugbestellungen mit einem geschätzten Volumen von mindestens 700 Milliarden US-Dollar nach Listenpreisen in den kommenden Jahrzehnten, was sich nachhaltig auf die Exportbilanz der USA oder Europas auswirken könnte. -Wie Reuters im Juli erstmals berichtete, ist das Sitzlayout die treibende Kraft hinter der Auseinandersetzung um die neuen Jets. -Sowohl Airbus als auch Boeing behaupten 20 Prozent mehr Effizienz pro Sitz bei ihren neuesten zweimotorigen Langstreckendesigns als der Marktführer in diesem Segment, die 365-sitzige Boeing 77-300ER. -Die Aussagen Boeings zu den Ergebnissen hängen teilweise davon ab, dass die 777X mit ihren zehn Sitzen pro Reihe mit dem Originaldesign von neun Sitzen in der 777 verglichen wird. -Die gestiegenen Kosten pro Einheit sind immer noch geringer als bei derzeit verwendeten Maschinen mit zehn Sitzen pro Reihe. -„Boeing quetscht deshalb mehr Sitze in sein Flugzeug, um es wettbewerbsfähiger zu unseren Produkten zu machen“, sagt Kevin Keniston, Leiter für Passagierkomfort bei der europäischen Airbus. -Andererseits sagen Analysten, volle Kabinen mit zehn Sitzen pro Reihe bei vorhandenen 777s deuteten darauf hin, dass sich viele Passagiere für die engere Anordnung entschieden, was mit billigeren Flugtarifen einhergehen könnte. -„Achtzehn Zoll Sitzbreite wären toll für Passagiere, aber die Wirklichkeit ist: von einem wirtschaftlichen Gesichtspunkt aus ist das Airbus-Angebot von der Bedrohung durch die 777 gesteuert“, sagt Mary Kirby, Expertin für Kabineneinrichtung, Gründerin und Herausgeberin von Runway Girl Network. -Airbus und Boeing bieten keine Sitze an, sondern einen Katalog von Zulieferern, aus denen die Fluggesellschaften auswählen können. -Die weltreisenden Jet-Verkäufer tragen sogar Bandmaß bei sich, um die Einrichtung bei der Konkurrenz nachzumessen. -Selbst wenn sie den Komfort anpreisen, so bieten doch alle Hersteller auch Jets mit hochverdichteter Ausgestaltung für Billigfluglinien und regionale Flüge. -Airbus bietet einen A350 mit zehnsitzigen Reihen, hat aber laut eigener Aussage noch keinen verkauft. -Bis vor kurzem hat Airbus betont, wie wichtig mehr Anpassbarkeit der Kabinen sei und für einige Jets breite Sitze an den Gängen angeboten. -Ohne die Unterstützung des einzigen anderen Herstellers großer moderner Jets sagen Experten, dass der Ruf nach einem neuen Branchenstandard vermutlich verpuffen werde, aber von der Welle von 777X-Verkäufen ablenken könnte. -Neuer Anti-Nikotin-Impfstoff könnte dem Vergnügen beim Rauchen den Garaus machen -Wissenschaftler haben einen Anti-Nikotin-Impfstoff entwickelt, der den Spaß am Rauchen verderben könnte. -Eine einzige Dosis des Impfstoffs konnte Mäuse ein Leben lang vor Nikotinsucht schützen. -Vor Tests am Menschen werden noch weitere Versuche benötigt, was noch einige Jahre dauern kann, doch Professor Ronald Crystal vom Weill Cornell Medical College in New York sagte, die ersten Ergebnisse seien gut. -„Wir sind sehr hoffnungsvoll, dass diese Art von Impfstrategie einmal Millionen von Rauchern helfen kann, die versucht haben aufzuhören und dabei alle auf dem Markt erhältlichen Methoden ausprobiert haben, aber erleben mussten, dass ihre Nikotinsucht stärker ist als diese Ansätze“, so Cornell. -Der neue Impfstoff enthält einen harmlosen Virus, der so verändert wurde, dass er die genetischen Informationen zur Herstellung von Nikotin-Antikörpern enthält. -Der Virus infiziert selektiv die Leberzellen, die dann einen ständigen Strom von Antikörpern produzieren. -Die Antikörper jagen die Nikotinmoleküle im Blut und neutralisieren sie, ehe sie das Gehirn erreichen, wodurch ein Raucher kein Nikotin-High erfährt. -In Versuchen behielten geimpfte Mäuse, denen Nikotin verabreicht wurde, ihr normales Aktivitätsniveau bei. -Mäuse ohne Impfung dagegen hätten eine „Auszeit“ genommen, erklärten die Forscher, ein Zeichen dafür, dass das Nikotin ihr Gehirn erreicht habe. -Die Experimente sind im Fachblatt Science Translational Medicine beschrieben. -Frühere Tabakimpfstoffe schlugen fehl, weil sie Antikörper enthielten. -Die Impfungen mussten zur Aufrechterhaltung des Antikörperpegels so häufig gegeben werden, dass sie teuer und unpraktisch waren. -Doch die Kosten des neuen Impfstoffs sind wahrscheinlich deutlich niedriger, denn er verwandelt Leberzellen in Antikörperfabriken. -Wie Professor Crystal sagte, könnte ein zukünftiger Impfstoff für Menschen, wenn er ganz sicher sei, Kindern verabreicht werden, ehe sie in Versuchung gerieten, eine Zigarette zu probieren, und so Nikotinsucht verhindern. -Wahrscheinlicher aber ist, dass Raucher ihn einsetzen würden, um aufzuhören. -„Sie wissen dann: Wenn sie wieder anfangen zu rauchen, empfinden sie wegen des Nikotinimpfstoffs kein Vergnügen mehr dabei, und das hilft ihnen, die Gewohnheit abzulegen“, erläutert er. -Britische Wissenschaftler sagten, die Ergebnisse seien interessant, warnten aber gleichzeitig, es sei noch weitere Forschung notwendig. -Tripodi bestreitet, von Obeid beeinflusst worden zu sein -Gegen den früheren Minister der Labor-Partei Joe Tripodi aus New South Wales wird eine Ermittlung durch die staatliche Korruptionsaufsicht eingeleitet. -Der ehemalige NSW-Minister bestreitet, die Regelungen zur Maritimverpachtung auf Bitte seines politischen Mentors Eddie Obeid geändert zu haben, der verdeckte Beteiligungen an drei Grundstücken auf staatlich kontrolliertem Land besaß. -Die Independent Commission Against Corruption (ICAC ­ Unabhängige Kommission gegen Korruption) hat am Freitag ihre Untersuchungen zur Frage ausgeweitet, ob Obeid verschiedene Staatsminister dahingehend beeinflusst hat, Pachtverträge am Circular Quay, wo die Obeids zwei Restaurants und ein Café betrieben, ohne Ausschreibung nach deren Auslaufen im August 2005 zu verlängern. -Jetzt wird wegen der Anschuldigung ermittelt, Tripodi habe von Obeids verdecktem Interesse an den Grundstücken gewusst, nachdem Tripodis frühere stellvertretende Stabschefin Lynne Ashpole am Donnerstag entsprechend ausgesagt hatte. -In jahrelangen Gesprächen, die 2005 begannen, hatte die Regierung auf öffentliche Ausschreibungen für die Pachtverträge gedrängt. -Die Pächter waren dagegen und wollten zudem längere Laufzeiten. -2009 wurden die Pachtverträge für die Unternehmen am Circular Quay, die den Obeids jährlich 2,5 Millionen Dollar einbrachten, ohne öffentliche Ausschreibung verlängert. -Tripodi, der von Februar 2006 bis November 2009 Hafenminister war, hatte sich ursprünglich für öffentliche Ausschreibungen ausgesprochen. -Doch er stritt ab, die Änderungen auf Bitten von Obeid vorgenommen zu haben, der, wie Tripodi bestätigte, auf eine Umgestaltung der Pachtregelungen der Regierung gedrängt hatte. -Eine der ICAC vorgelegte Telefonniederschrift zeigte Gespräche aus dem August und September 2007 zwischen Obeid, Tripodi und Steve Dunn, einem leitenden Beamten, der ins Hafenministerium gewechselt war, nachdem er unter Obeid in der Fischereibehörde gearbeitet hatte. -„War das in diesen Telefongesprächen besprochene Thema die Ausarbeitung der kommerziellen Verpachtungsregelungen?“, fragte das stellvertretende Kommissionsmitglied Anthony Whealy Tripodi. -„Nein“, antwortete Tripodi. -Ich kann mich nicht erinnern, was besprochen wurde, aber das war es definitiv nicht. -Definitiv nicht zwischen mir und Herrn Obeid. -Israelische Kampfflugzeuge greifen Ziel in Syrien an, so offizielle Quellen -Israelische Jagdflugzeuge hätten Donnerstagnacht ein Ziel im syrischen Hafen Latakia angegriffen, bestätigte ein leitender Regierungsmitarbeiter gegenüber Fox News. -Der Beamte erläuterte nicht, was das Ziel gewesen sei, sagte aber, es sei mindestens eines gewesen. -Laut Associated Press handelte es sich bei dem Ziel um russische SA-125-Raketen. -Mindestens zweimal hat Israel dieses Jahr schon Luftschläge auf Raketenlieferungen in Syrien durchgeführt. -Ausländische Arbeitnehmer mit 457-Visa könnten „Echtheitstest“ unterzogen werden -Die Regierung denkt im Rahmen erweiterter Maßnahmen über einen „Echtheitstest“ für ausländische Arbeitnehmer mit 457-Visum nach. -Sollte der Test eingeführt werden, käme er über ein Kriterium zum Einsatz, das verhindern soll, dass das 457 zur Besetzung unqualifizierter Arbeitsplätze oder als Hintertür für den Zuzug von Familie und Freunden nach Australien benutzt wird. -Heute wurde ein Diskussionsvorschlag der Regierung veröffentlicht, nachdem die frühere Abgeordnete der Labor-Partei, Maxine McKew, die Kommentare der Regierung im Hinblick auf ausländische Arbeitnehmer verurteilt hatte, weil diese von den Nachbarn Australiens als Beleidigung aufgefasst werden könnten. -„Lautstarke Erklärungen wie ,Ausländer sollen sich hinten anstellen‘ und ,Australier zuerst einstellen‘ sind ein sehr unangenehmer Rückfall in Zeiten, in denen Gewerkschaften einen geschützten Arbeitsmarkt forderten“, sagte sie dem Australia India Institute heute. -Historisch betrachtet hieß das, dass weiße Arbeitskräfte geschützt werden sollten – und wenn manche in der Region ein Echo dieses historischen Artefakts vernähmen, würde mich das nicht wundern. -Der Diskussionsvorschlag umreißt zwölf Maßnahmen, die einst vom ehemaligen Einwanderungsminister Chris Bowen angedacht wurden. -Einwanderungsminister Brendan O'Connor, der gestern zu Gesprächen über Menschenschmuggel in Sri Lanka weilte, hat fünf der empfohlenen Änderungen umgesetzt, die anderen werden noch erwogen. -Wenn das „Echtheitskriterium“ übernommen wird, dann könnte jemand, der ein Visum beantragt, dahingehend überprüft werden, ob „der Antrag in den Fällen echt ist, in denen der Antragsteller mit dem Inhaber oder einer relevanten Person des unterstützenden Unternehmens verwandt oder persönlich verbunden ist.“ -Firmen könnten außerdem verpflichtet werden, Rechenschaft über die Anzahl der 457-Visumsinhaber abzulegen, nachdem in der Vergangenheit Unternehmen, die zunächst nur eine kleine Anzahl von Arbeitern unterstützen wollten, schließlich Hunderte eingestellt haben. -Unterdessen starb ein 35-jähriger sri-lankischer Asylant vermutlich an einem Herzinfarkt, nachdem er diese Woche mit einem Flüchtlingsboot auf der Weihnachtsinsel angekommen war. -Der verstörte neunjährige Sohn des Mannes, der mit ihm nach Australien gekommen war, wird seit dem Tod seines Vaters am Mittwoch von einem erwachsenen Cousin betreut, der ebenfalls auf dem Boot war. -Australische Behörden brachten den Mann in das Christmas Island Hospital, wo er starb. -Von der britischen Regierung wurde eine Studie angekündigt, die die Vorteile des HS2-Eisenbahnprojekts für Schottland erhöhen soll. -In der Arbeit von HS2 Ltd wird vorgeschlagen, schon mit Beginn von Phase 1 im Jahr 2026 Hochgeschwindigkeitsverbindungen zwischen Schottland und dem Norden Englands zu verwirklichen. -Verkehrsministerin Baroness Kramer sagte, das Projekt „wird Großbritannien näher zusammenbringen“. -Der schottische Verkehrsminister Keith Brown erklärte, er sei sehr erfreut, mit der britischen Regierung an diesem Plan zu arbeiten. -Phase 1 besteht aus einer neuen Hochgeschwindigkeits-Bahnverbindung zwischen London und den West Midlands. -Nach Abschluss von Phase 2 wird es Verbindungen nach Manchester und Leeds geben. -Im Juni korrigierte die Regierung die geschätzten Kosten für den Bau der Hochgeschwindigkeitstrasse zwischen London und Nordengland von 32,7 Mrd. Pfund auf 42,6 Mrd. Pfund. -Die britische Regierung, die in Gesprächen mit Transport Scotland steht, hat HS2 Ltd beauftragt, nach weiteren Streckenkapazitäten und Reisezeitverbesserungen für Nordengland und Schottland zu suchen. -Dazu gehört die Möglichkeit einer Reisezeit von letztendlich drei Stunden oder weniger von Glasgow und Edinburgh nach London. -Baroness Kramer sagte: „Unser Ziel für HS2 ist ein wirklich nationales Streckennetz, das Großbritannien und seine Städte näher zusammenbringt.“ -Wir gehen mit HS2 voran, weil es enorme Vorteile mit sich bringt. -Ohne es stehen wir vor einer Kapazitätskrise in unserem Bahnnetz. -Doch es geht auch um die Verbindung zwischen 18 britischen Städten, einschließlich Glasgow und Edinburgh, die dank HS2 verbessert wird. -Der schottische Staatssekretär Alistair Carmichael kommentierte: „Die heutige Ankündigung ist eine gute Nachricht für Schottland.“ -Im Namen der schottischen Regierung forderte Keith Brown Carmichael auf, sich „unmissverständlich“ für die schottische Einbindung in das HS2-Netzwerk einzusetzen. -Brown sagte: „Die Hochgeschwindigkeits-Bahnverbindung birgt das Potenzial enormer wirtschaftlicher Vorteile für Schottland, gleichzeitig liefert aber auch die Wirtschaftskraft Schottlands ein Argument für schnelle Bahnverbindungen in ganz Großbritannien.“ -Deshalb sind wir erfreut, als Partner der britischen Regierung zu arbeiten und die Optionen für eine Hochgeschwindigkeits-Bahnverbindung in Schottland zu untersuchen, die Vorteile für alle schafft und die Verbindung Glasgow-Edinburgh ergänzt, die die schottische Regierung bereits plant. -Ich freue mich darauf, den Untersuchungsbericht mit den britischen Ministern im nächsten Jahr zu prüfen und gemeinsam die nächsten Schritte zu beschließen. -Vorschriften zu elektronischen Geräten in Flugzeugen bleiben in Australien vorläufig in Kraft -Australische Flugpassagiere müssen auch weiterhin Tablets und Smartphones bei Start und Landung abschalten, trotz Bemühungen in den USA, die Regelungen für derartige Geräte zu lockern. -Die US-amerikanische Federal Aviation Administration (FAA) ermöglicht US-Fluggesellschaften, ihre Vorgaben zu ändern, sodass Passagiere auf ihren Geräten während kritischer Flugphasen E-Books lesen, Videos sehen oder spielen können, sofern diese sich im „Flugmodus“ befinden. -Während des Großteils des Flugs ist Passagieren dies bereits erlaubt, doch finden es viele Leute ärgerlich, dass sie beim Start und der Landung ihre E-Books nicht lesen dürfen. -Australische Gesellschaften beobachten die Entscheidung, zu deren Umsetzung die US-amerikanischen Fluggesellschaften Unmengen an Arbeit erledigen müssen, verfolgen jedoch keine unmittelbare Pläne zur Änderung ihrer Vorschriften. -Die Flugsicherungsbehörde CASA erklärte ebenfalls, man werde sich die Ankündigung ansehen, betonte aber, dass die Einschränkungen bei der Nutzung elektronischer Geräte in kritischen Flugphasen in Australien nach wie vor gelten. -„Die CASA hat derzeit keine spezifischen Regelungen hinsichtlich des Gebrauchs elektronischer Geräte im Flugzeug“, wurde verlautbart. -Das Thema unterliegt Bestimmungen, denen zufolge Fluggesellschaften die Sicherheit jederzeit aufrechterhalten und Passagiere die vom Kabinenpersonal gegebenen Sicherheitsanweisungen befolgen müssen. -Virgin hat bereits mit der CASA Gespräche über eine Nutzungsausweitung seines Wi-Fi-Unterhaltungssystems auf Flügen geführt und erklärt, man sei offen für Veränderungen, diese müssten aber vom Gesetzgeber initiiert werden. -„Wir würden eine Überprüfung der Zulassung elektronischer Geräte durch die CASA begrüßen, denn wir sind überzeugt, dass dies das Kundenerlebnis verbessern würde, nachdem wir nun (drahtlose Unterhaltung) während des Flugs anbieten“, sagte ein Sprecher. -Qantas erklärte, man werde bis auf Weiteres bei den aktuellen Vorschriften bleiben. -„Unsere derzeitigen Richtlinien sehen vor, dass elektronische Geräte beim Start und der Landung nicht verwendet werden dürfen, und wir haben zurzeit keine Pläne, das zu ändern“, erklärte das Unternehmen. -Die FAA-Regelungen gelten für US-amerikanische Fluggesellschaften. -Wir sind allerdings immer an Entwicklungen bei Regelungen interessiert, von denen Passagiere profitieren können, und werden uns die FAA-Entscheidung sowie die Gründe dahinter sicherlich genau ansehen. -Die Entscheidung wirkt sich bei US-amerikanischen Gesellschaften je nach Alter der Flotte unterschiedlich aus. -Sie müssen nachweisen, dass ihre Flugzeuge die Funkstörungen durch Mobilgeräte tolerieren können, und Handbücher, Schulungsmaterialien, Handgepäck-Programme sowie Kundenunterweisung ändern. -„Nachdem eine Fluggesellschaft die Toleranz ihrer Flotte bestätigt hat, kann sie Passagieren das Verwenden tragbarer, leichter elektronischer Geräte wie Tablets, E-Book-Lesegeräte und Smartphones in jeder Höhe erlauben“, gab die FAA bekannt. -In seltenen Fällen bei schlechter Sicht wird die Crew die Passagiere anweisen, während der Landung die Geräte abzuschalten. -Die Gruppe empfahl außerdem, schwerere Geräte bei Start und Landung sicher unter dem Sitz oder in den Fächern über den Sitzen zu verstauen. -Vor dem Revierderby gegen Schalke hatten mitgereiste BVB-Anhänger mit Leuchtraketen für hässliche Szenen in der Veltins-Arena gesorgt. -Borussia Dortmund kündigte daraufhin harte Konsequenzen an. -Vor dem Freitagsspiel der Fußball-Bundesliga gegen den VfB Stuttgart reagierten die Ultras mit einem Schweigen - zunächst. -Es war eine ungewohnte, fast schon gespenstische Akustik, die sich dem Betrachter bis kurz vor dem Anpfiff des Bundesligaspiels zwischen dem deutschen Vizemeister Borussia Dortmund und dem VfB Stuttgart im Signal Iduna Park bot. -Zu hören waren überwiegend die Gästefans mit ihren Gesängen. -Die Südtribüne dagegen, wo die treuesten der treuen - und lautesten - BVB-Fans stehen, präsentierte sich zunächst außergewöhnlich leise: Keine Gesänge, keine Schlachtrufe. -Das Podest des Capo, der sonst die gemeinsame Anfeuerung koordiniert, blieb unbesetzt. -Und ganz vorne auf der Tribüne wehte einsam eine große Fahne mit Aufschrift "Sektion Stadionverbot". -Nur als Torhüter Roman Weidenfeller wie immer als erster BVB-Spieler den Platz betrat, brandete kurzzeitiger Jubel auf. -Als seine Mannschaftskollegen folgten, gab es lautstarken Gesang und wehende Schals - aber längst nicht überall. -In den zentralen Blöcken 12 und 13 der Südtribüne regte sich nichts. -Auch dass Stadionsprecher Norbert Dickel noch einmal die positiven Erlebnisse der letzten Tage - Derbysieg, Sieg über Arsenal, Vertragsverlängerung von Trainer Jürgen Klopp - aufzählte, sorgte nicht für Feierlaune. -Erst fünf Minuten vor dem Anpfiff erklomm der Capo sein Podest - und die bis dahin geradezu mit Händen greifbare Anspannung entlud sich in einem lauten Schrei, als Dickel wie gewohnt die Stimmung auf den Rängen im Stadion abfragte. -Als letztes war wie immer die Südtribüne an der Reihe - und endlich war hier wieder wie gewohnt der lauteste Ort im Stadion. -Und spätestens als mit Anpfiff von der Südtribüne die lautstarke Aufforderung "Derbysieger steht auf" kam, war wieder alles wie immer. -Das Schweigen zuvor war wohl eine Reaktion auf die Geschehnisse der vergangenen Tage. -Unmittelbar vor Revierderby beim FC Schalke 04 hatten einige der mitgereisten BVB-Anhänger Leuchtraketen in die Zuschauerränge und auf den Platz gefeuert - und dabei fast Torhüter Roman Weidenfeller getroffen. -Das Spiel wurde wegen der chaotischen Szenen verspätet angepfiffen. -BVB-Boss Hans-Joachim Watzke hatte die Spitzen der Dortmunder Ultras daraufhin in seinem Büro empfangen - und glaubt man Watzkes Worten, war dies für die Fanvertreter ein eher ungemütlicher Termin. -"Es wird für die gesamte Ultrabewegung massive Einschnitte geben", verkündete der BVB-Boss am Donnerstagabend auf einer Podiumsdiskussion in Frankfurt. -Als Sofortmaßnahme untersagte Watzke den Ultras eine Choreographie für das Heimspiel gegen den VfB Stuttgart. -"Das habe ich verboten", erklärte er. -Die Null-Toleranz-Politik in Sachen Pyrotechnik können wir nicht aufgeben. -Zudem wurden den Ultras verschiedene Möglichkeiten einer Bestrafung aufgezeigt - bis zum Dienstag haben diese nun Zeit, sich für eine zu entscheiden. -Andernfalls, so Watzke, werde der BVB bis Ende der kommenden Woche eine Sanktion festlegen. -"Das wird einschneidend", versicherte Watzke . -Man darf gespannt sein, wie die Reaktionen auf der Südtribüne dann ausfallen. -Pfandhäuser boomen in Singapur, da die Krise in der Mittelschicht angekommen ist -In einem Pfandhaus im Einkaufszentrum Bendemeer in Singapur tauscht Janani Amirthalinga einen goldenen Armreif, einen Ring und ein Paar Ohrringe ein, um die Schulgebühren ihrer Töchter bezahlen zu können. -„Mein Ehemann und ich haben gerade ein Haus gekauft, da steckt mein ganzes Geld drin“, sagt Amirthalinga. -Obwohl sie jeden Monat 3.000 Singapur-Dollar (ca 1.730 Euro) als Verwaltungsmitarbeiterin verdiene und ihr Mann auch arbeite, reiche das monatliche Familieneinkommen nicht aus, erläutert sie. -Tatsächlich ist die Nachfrage in Teilen Südostasiens – wo die Verschuldung der Haushalte zunimmt – so groß, dass ValueMax, wo sie ihren Tausch vorgenommen hat, diese Woche das dritte Pfandhaus wurde, das an der singapurischen Börse gelistet ist. -Das Verpfänden von Schmuck ist nicht nur ein schneller Weg, um Geld zu bekommen – 1.300 Singapur-Dollar im Fall von Janani Amirthalinga –, sondern auch fast so günstig wie ein ungesichertes Bankdarlehen. -Typischerweise verlangen Pfandhäuser in Singapur einen effektiven Jahreszins von 17 Prozent, der nur knapp über den 15,4 Prozent liegt, die die United Overseas Bank verlangt, ein lokaler Kreditgeber mit einer Filiale im selben Einkaufszentrum. -Pfandhäuser bieten aber den Vorteil, dass hier keine Überprüfung der Kreditwürdigkeit oder Einkommensnachweise erforderlich sind, weshalb Darlehen schneller als bei Banken möglich sind. -Aus diesem Grund wenden sich Millionen von Menschen in der Region an Pfandhäuser, nachdem Familien den Druck durch steigende Lebenshaltungskosten sowie eine zunehmende Haushalts- und Verbraucherverschuldung spüren. -Nach fünf Jahren robusten Wachstums seit der globalen Finanzkrise und billigen Krediten aufgrund einer lockeren Finanzpolitik in den entwickelten Wirtschaftsräumen suchen Familien mit geringeren und mittleren Einkommen Pfandhäuser auf, um so bei stotternder Wirtschaft den Unterschied auszugleichen. -Diese Woche bezeichnete die Rating-Agentur Standard & Poor's wachsende Haushaltsverschuldung, hauptsächlich durch steigende Hypotheken, als einen Risikofaktor für die Kreditwürdigkeit asiatischer Banken. -Malaysia, Thailand und Singapur haben nach Einschätzung der Agentur die höchste Haushaltsverschuldung in Asien im Verhältnis zum Bruttoinlandsprodukt. -Malaysia steht mit 80 Prozent des BIP an der Spitze der Liste, ein Anstieg von 60 Prozent noch im Jahr 2008. -Volkswirtschaftler sorgen sich auch wegen der hohen Verbraucherverschuldung in Thailand, das diese Woche knapp aus einer technischen Rezession herausgekommen ist. -Am Donnerstag zeigten die Daten eine weiterhin bestehende Exportschwäche und ein Nachlassen der Verbrauchernachfrage. -„Im Endeffekt bedeutet das, dass angesichts steigender Kosten die Menschen im mittleren bis unteren Segment (der Einkommensskala) versuchen werden, ihr Einkommen zu ergänzen, wo immer das möglich ist“, sagt Song Seng Wun, Ökonom bei CIMB, einer malaysischen Bank. -Historische Höchststände bei den Goldpreisen in den letzten zwei Jahren haben den Trend zur Verpfändung persönlicher Gegenstände noch verstärkt, da die Menschen die Gelegenheit nutzen, den Schmuck der Familie in Bargeld zu verwandeln. -In Singapur sind etwa 70 Prozent der verpfändeten Gegenstände in den 200 Pfandhäusern des Stadtstaats Gold. -Die Leute sagen sich: „Der Goldpreis sieht gut aus, lasst uns Großmutters Goldkette verpfänden und nächsten Monat zurückholen.“ -Der größte thailändische Pfandhausbetreiber, EasyMoney, hat in seinen Filialen einen 20-prozentigen Kundenzuwachs in den letzten Monaten erlebt. -Das Wachstum im Pfandgeschäft ist so stark, dass ValueMax, Betreiber der Filiale in Bendemeer und 15 weiterer in Singapur, nicht nur in das benachbarte Malaysia expandieren möchte – wo das Unternehmen vier Häuser betreibt –, sondern auch außerhalb Asiens, so Yeah Lee Ching, Geschäftsführerin von ValueMax. -Das Unternehmen finanziert das mit 60 Prozent der 66 Millionen Singapur-Dollar, die es diese Woche mit dem Börsengang in Singapur einnahm. -Während einige Kreditgeber wegen hoher Zinssätze ins Kreuzfeuer geraten seien, erklärt Yeah, dass Pfandhäuser nicht nur günstigere Zinsen als andere Verleiher böten, sondern auch nicht direkt die Schulden erhöhten. -„Kunden beleihen Gegenstände, die sie bereits besitzen, und das Verflüssigen persönlicher Wertgegenstände vergrößert nicht die Haushaltsverschulung“, erläutert sie. -Das Pfandleihgeschäft wird gesellschaftlich zunehmend als kurzfristige, abgesicherte Finanzierung akzeptiert. -Auch sind nicht alle Menschen, die Pfandleihe nutzen, in finanziellen Schwierigkeiten. -Reiche Leute aus Singapur kommen auch in die Filialen von ValueMax, um Goldbarren und Rolex-Uhren zu verpfänden, wofür sie bis zu 60 Prozent des Kaufpreises in bar erhalten. -Zu uns kommen Kunden aus jeder sozialen Schicht. -„Dazu gehören Wohlhabende, die kurzfristig etwas für ein Geschäft oder eine Investition leihen, oder kleine Unternehmen, die Engpässe im Cash-Flow überbrücken müssen“, sagt Yeah. -Manchmal brauchen sie auch einfach nur sehr schnell Geld. -NSA saugt Daten von Google und Yahoo ab - Snowden will helfen -Der Geheimdienst-Enthüller Edward Snowden hat prinzipielles Interesse, Deutschland bei der Aufklärung der immer brisanter werdenden NSA-Spähaffäre zu helfen. -Nach Darstellung des Grünen-Bundestagsabgeordneten Hans-Christian Ströbele ging es bei seinem überraschenden Treffen mit Snowden in Russland darum, unter welchen Bedingungen der Ex-Geheimdienstmitarbeiter bei einer deutschen Staatsanwaltschaft oder vor einem Untersuchungsausschuss aussagen würde. -Snowden habe auf seine komplizierte juristische Situation verwiesen, sagte Ströbele dem ARD-Magazin "Panorama". -Rettungspaket über 325 Millionen australische Dollar für tasmanisches Gesundheitswesen -Die australische Regierung besteht auf den harten Bedingungen, die mit dem Rettungspaket über 325 Millionen australische Dollar für das tasmanische Gesundheitswesen verknüpft sind, um sicherzustellen, dass die Regierung des Bundesstaates das Geld nicht verschwenden kann. -Bundesgesundheitsministerin Tanya Plibersek hat angekündigt, der Commonwealth werde „unverzügliche Maßnahmen“ ergreifen, um eine Krise abzuwenden, die durch eine alternde Bevölkerung des Inselstaats, eine höhere Quote an chronischen Erkrankungen und Systemeinschränkungen verursacht worden sei. -Die Finanzierung für einen Zeitraum von vier Jahren wurde nach Regierungsberatungen mit dem unabhängigen tasmanischen Abgeordneten Andrew Wilkie beschlossen. -„Die Regierung hat ein Notfallrettungspaket geschnürt, das unserer Ansicht nach die einzigartigen Herausforderungen angeht, denen sich der Bundesstaat gegenüber sieht“, erklärte Plibersek heute. -Das 325-Millionen-Dollar-Paket umfasst 31 Millionen Dollar Soforthilfe für Wahlleistungen. -Durch weitere 2600 Operationen, einschließlich orthopädischer Eingriffe und Behandlungen wegen grauem Star, sollen Rückstände beseitigt werden. -Darüber hinaus gibt es Geld für Tageskliniken in Hobart und Launceston, bessere Nachsorge nach stationären Aufenthalten, die Facharztausbildung, Dienstleistungen im Bereich psychische Gesundheit und die Einführung eines Systems zur elektronischen Aufzeichnung von Gesundheitsdaten in lokalen Krankenhäusern. -„Diese Investitionen entsprechen den Ideen, von denen mir betroffene Klinikmitarbeiter gesagt haben, sie seien die beste Möglichkeit, das tasmanische Gesundheitssystem zu verbessern“, sagte Tanya Plibersek. -Die Ministerin bestand auf einer strengen Berichts- und Rechenschaftspflicht der tasmanischen Regierung. -Der Bundesstaat müsse das aktuelle Finanzierungsniveau beibehalten, um Geld aus dem Commonwealth erhalten zu können, und monatlich darüber berichten, wo die zusätzlichen Gelder ausgegeben wurden. -Eine dreiköpfige Kommission wird eingerichtet und soll sicherstellen, dass der Bundesstaat Gesundheitsdienste so effektiv und effizient wie möglich bereitstellt. -Wie Wilkie heute erklärte, würden die 325 Millionen Dollar nur wenig nützen, wenn „sie nicht von echten Reformen begleitet werden, die das öffentliche tasmanische Gesundheitssystem auf einen nachhaltigeren Kurs bringen.“ -Dessen ungeachtet lobte er die Regierung dafür, dass sie auf seine Bitte um dringende Hilfe reagiert habe, die er zuerst gegenüber dem Premierminister Anfang Mai zum Ausdruck gebracht habe. -„Ich bin zuversichtlich, dass die Bundeshilfen einen großen Beitrag dazu leisten werden, das öffentliche Gesundheitssystem des Bundesstaates von der Sorgenliste zu nehmen“, sagte Wilkie. -Laut der Regierung des Bundesstaates werden diese Wahlleistungen weitgehend die kürzlichen Einschnitte wettmachen. -Doch der gesundheitspolitische Sprecher der Opposition auf Bundesebene, Peter Dutton, ist der Ansicht, die heutige Ankündigung sei nur eine „Pflasterlösung“. -„Wir sind deshalb in der heutigen Lage, weil die Labor-Staatsregierung 430 Millionen Dollar aus dem Gesundheitssystem abgezogen hat“, sagte er gegenüber ABC TV. -Es kann nicht sein, dass eine Staatsregierung beinahe eine halbe Milliarde Dollar aus dem System reißt und der Commonwealth 300 Million hineinsteckt und dann so tun, als seien das gute Nachrichten. -Dutton forderte Plibersek auf zu garantieren, dass nicht ein einziger Dollar des Rettungspakets für zusätzliche Bürokratie ausgegeben werde. -Guillaume Nicloux' Adaption des Romans von Denis Diderot überzeugt mit außergewöhnlicher Produktionsausstattung und zeitgenössischen Details, ist allerdings auch schwerere Kost als nötig. -Die im Frankreich der 1760er Jahre spielende Handlung erzählt die ergreifende Geschichte von Suzanne, einer jungen Adligen, die von ihrer Familie ins Kloster geschickt wird. -Als sie rebelliert, ist sie mit der extremen Grausamkeit einer äußerst sadistischen Oberin konfrontiert und wird zum Objekt erotischer Faszination einer anderen. -Der Film gleitet niemals ab in Lüsternheit oder Sensationslust – und darin liegt sein Problem. -Der feierliche Ernst, mit dem der Film erzählt wird, macht ihn auch für das Publikum zum quälerischen Büßerhemd. -Syrien hat seine Möglichkeit zur Herstellung von Chemiewaffen zerstört, so eine Überwachungsgruppe -Syrien habe wichtige Einrichtungen zur Herstellung von Chemiewaffen und Gitftgasmunition zerstört, erklärte die globale Überwachungsgruppe für Chemiewaffen am Donnerstag, während es im Norden des Landes zu schweren Zusammenstößen in der Nähe eines der Standorte kam, an denen mutmaßlich giftige Substanzen gelagert werden. -Ebenfalls am Donnerstag verkündete eine syrische Aktivistengruppe, dass mehr als 120.000 Menschen seit Beginn des Bürgerkrieges vor nun schon bald drei Jahren getötet worden seien. -Die Ankündigung der Organisation für das Verbot chemischer Waffen (OPCW) kam einen Tag vor dem Termin am -1. November, den die in Den Haag ansässige Organisation Damaskus gesetzt hatte, um alle Einrichtungen zur Herstellung von Chemiewaffen zu zerstören oder „inoperabel“ zu machen, einschließlich aller Maschinen für das Mischen von Chemikalien zu Giftgas und das Befüllen von Munition. -Der Abschluss dieses Vorgangs, der im Grunde genommen die erste Stufe der Zerstörung darstellt, ist ein wichtiger Meilenstein eines ambitionierten Zeitplans, der auf die Vernichtung aller Chemiewaffen Syriens bis Mitte 2014 abzielt. -Die Zerstörung der Einrichtungen bedeutet, dass Syrien keine neuen Chemiewaffen mehr produzieren kann. -Damaskus muss allerdings noch damit beginnen, die vorhandenen Waffen und Vorräte zu vernichten. -Annahmen zufolge gibt es im Land etwa 1.000 Tonnen an Chemikalien und Waffen, wozu auch Senfgas und das Nervengas Sarin zählen. -Die Ankündigung kam zu einer Zeit, als am Donnerstag Kämpfe in der Stadt Safira stattfanden, von der Experten sagen, dass es hier eine Produktionseinrichtung für Chemiewaffen sowie Lagerstätten gebe, wie die in Großbritannien ansässige Syrische Beobachtungsstelle für Menschenrechte mitteilte. -Die Aktivistengruppe, die über ein Netzwerk von Aktivisten in Syrien die Zahl der Toten verfolgt, erklärte am Donnerstag, 120.296 Menschen seien gestorben. -Davon seien 61.067 Zivilisten, einschließlich 6.365 Kinder. -Auf Seiten der Regierung gehörten 29.954 von ihnen der Armee von Präsident Bashar Assad an, 18.678 seien regierungsfreundliche Kämpfer und 187 libanesische Hisbollah-Milizen. -Ebenfalls unter den Toten seien 2.202 desertierte Armeesoldaten und 5.375 Kämpfer der Opposition, viele von ihnen Ausländer. -Am 25. Juli schätzten die UN die Zahl der Toten in diesem Konflikt seit März 2011 auf 100.000. -Diese Zahl wurde bisher nicht aktualisiert. -Der Konflikt hat etwa zwei Millionen Menschen zur Flucht aus dem Land gezwungen. -Assads Truppen kämpfen seit Wochen in Safira gegen die Rebellen, von denen viele Verbindungen zu al-Qaida-Gruppen haben. -Die Beobachtungsstelle sagte, es habe am Donnerstag Verluste auf beiden Seiten gegeben, nannte aber keine Details. -Die Kämpfe unterstreichen die Gefahr, der die Chemiewaffeninspektoren ausgesetzt sind, denn ihre Mission zur Befreiung Syriens von seinem giftigen Waffenarsenal ist ein Rennen gegen die Zeit inmitten eines tobenden Bürgerkriegs. -Nach Aussage der OPCW, die eng mit den Vereinten Nationen zusammenarbeitet, sei das Team „nun überzeugt, dass es alle von Syrien genannten wichtigen Produktions- und Misch-/Befüllungseinrichtungen gesehen und ihre Zerstörung miterlebt hat.“ -Deshalb „sind derzeit keine weiteren Inspektionsaktivitäten geplant.“ -Anfang der Woche hatten die Inspektoren erklärt, sie hätten die erste Runde der Überprüfungen abgeschlossen und 21 von 23 der von Damaskus angegebenen Stätten besucht. -Wegen Sicherheitsbedenken sei der Besuch zweier Einrichtungen nicht möglich gewesen, sagten die Inspektoren. -Am Donnerstag erklärte die OPWC, die beiden Standorte seien laut syrischer Aussage „aufgegeben worden ... und die dort vorhandenen Objekte des Chemiewaffenprogramms seien an andere genannte Einrichtungen verlagert worden, die inspiziert wurden.“ -Es war nicht sofort klar, ob die Stätte in Safira eine der beiden Einrichtungen war, die die OPCW-Inspektoren nicht besuchen konnten. -Syrien hat einen Plan für die komplette Zerstörung seiner Chemiewaffen vorgelegt, der nächsten Monat vom Exekutivkomitee des OPWC genehmigt werden muss. -„Ich ziehe den Hut vor der Tapferkeit und dem Mut, den Sie alle beim Erfüllen der herausforderndsten Mission, die je von dieser Organisation ausgeführt wurde, gezeigt haben“, erklärte Ahmed Uzumcu, der Generaldirektor der Überwachungsorganisation in einem von OPCW veröffentlichten Kommentar. -Seit inzwischen drei Jahren kämpfen die hauptsächlich sunnitischen Muslimrebellen in diesem Bürgerkrieg gegen Assads Regierung und ihre Sicherheitskräfte, der viele Mitglieder der Alawiten angehören, einer Abspaltung des schiitischen Islam. -Im Hinblick auf weitere Entwicklungen sagte der Leiter der Beobachtungsgruppe, Rami Abdurrahman, es habe am Mittwoch eine starke Explosion in einer Luftverteidigungseinrichtung in der syrischen Küstenprovinz Latakia gegeben. -Die Ursache der Explosion sei nicht bekannt, erklärte er. -Zorn über Urteil für Bombenleger von Bali -Überlebende und Verwandte der 202 Menschen, die 2002 bei dem Bombenanschlag in Bali ums Leben gekommen waren, reagierten verärgert über das Urteil für den letzten der Anstifter, der vor Gericht gestellt wurde, da Umar Patek ihrer Ansicht nach standrechtlich erschossen werden sollte. -Patek, der knapp zehn Jahre als eine der meistgesuchten Personen Südostasiens auf der Flucht war, wurde gestern für seine Rolle beim Bau von Sprengkörpern für den Bombenanschlag zu einer Haftstrafe von 20 Jahren verurteilt. -Mit Strafaussetzung könnte er nach 15 Jahren entlassen werden. -Der 45-jährige wurde des Massenmords für schuldig befunden wegen des Angriffs auf zwei Nachtclubs im beliebten Touristenviertel von Kuta, bei dem 202 Menschen starben, darunter 88 Australier, und viele weitere verletzt wurden. -Ein Schuldspruch erfolgte außerdem in einer Reihe terrorismusbezogener Anklagepunkte, wozu eine Welle von Sprengstoffanschlägen auf Kirchen in Indonesien an Heiligabend im Jahr 2000 zählten. -Die Staatsanwaltschaft hatte eine lebenslange Haftstrafe gefordert, auch wenn sie für den Mann, der wegen seines Rufs als meisterlicher Bombenbauer auch als „Demolition Man“ bezeichnet wird, die Todesstrafe hätte beantragen können. -Die Entscheidung hat schmerzhafte Erinnerungen bei June Corteen geweckt, einer Mutter aus Perth, die ihre 39 Jahre alten Zwillingstöchter Jane und Jenny in der von Patek und seinen Mitverschwörern vor fast zehn Jahren angerichteten Verwüstung verloren hat. -Mit Tränen kämpfend sagte sie, Patek hätte zum Tode verurteilt werden sollen. -Ich meine wirklich, er sollte in den Fußstapfen der anderen Kerle folgen. -„Man sollte ihn vor ein Erschießungskommando stellen“, sagte Corteen gegenüber AAP. -Ich muss jeden Tag damit leben, dass ich keine weiteren Enkel bekommen werde und ich meine Töchter nie mehr sehen werde. -Der Sari Club wurde dem Erdboden gleichgemacht, als am 12. Oktober 2002 kurz nach 23 Uhr eine gewaltige Bombe in einem Kleinlaster explodierte, der vor dem Nachtclub geparkt war. -Peter Hughes war in Paddy's Bar, wo nur 20 Sekunden früher ein Selbstmordattentäter einen mit Sprengstoff gefüllten Rucksack explodieren ließ. -Er fiel nach dem Anschlag einen Monat lang ins Koma und „starb“ dreimal, während er an einem Lebenserhaltungssystem hing. -Hughes sagte, Patek habe dasselbe Schicksal verdient wie die drei anderen Mitglieder der Terrorzelle Jemaah Islamiah – Amrozi, Mukhlas und Imam Samudra –, die für das Blutbad verantwortlich waren und vor vier Jahren hingerichtet wurden. -Wirklich, dieser Typ sollte vor allen anderen die Todesstrafe bekommen. -Ihn am Leben zu lassen – nein, es gibt keinen Grund, ihn am Leben zu lassen. -20 Jahre zu bekommen, nachdem er 202 Menschen getötet und viele Hunderte verletzt hat, ist nicht viel. -Patek ist der letzte der Bali-Bomber, der vor Gericht stand. -Er entzog sich beinahe ein Jahrzehnt der Festnahme, wurde aber schließlich im Januar 2011 in der pakistanischen Stadt Abbottabad aufgegriffen, wo US-Streitkräfte knapp vier Monate später den früheren al-Qaida-Chef Osama bin Laden töteten. -Während der Verhandlung sagte ein FBI-Agent aus, dass Geheimdienstberichte den Hinweis erbracht hätten, dass sich Patek in Pakistan befand, um bin Laden zu treffen mit dem Ziel, die Verbindung zwischen südostasiatischen Terrorgruppen und al-Qaida wieder aufzubauen. -„Er hat sich nicht gestellt“, sagte Corteen. -Bis vor kurzem hat er keine Reue dafür gezeigt, wie viel Leid er anderen Menschen zugefügt hat. -Das Urteil erging vor dem zehnjährigen Jahrestag des Anschlags in diesem Jahr, der mit Gedenkfeiern in Bali und Australien begangen wird. -„Es wird dieses Jahr viele Tränen geben“, sagte Corteen. -Patek kann gegen sein Urteil noch Berufung einlegen. -FAA: Fluggäste können jetzt elektronische Geräte in Flugzeugen verwenden (aber keine Handygespräche führen) -Flugreisende können jetzt vom Abflug bis zur Landung ihre elektronischen Geräte zum Lesen, Arbeiten, Spielen, Musik hören und dem Ansehen von Filmen verwenden, nicht aber mit dem Handy telefonieren, so die neuen Richtlinien, die am Donnerstag von der Federal Aviation Administration (FAA) in den USA herausgegeben wurden. -Doch sollten die Passagiere nicht erwarten, dass diese Änderungen sofort umgesetzt werden. -Wie schnell sie implementiert würden, hinge von der jeweiligen Fluggesellschaft ab, erklärte FAA-Mitarbeiter Michael Huerta auf einer Pressekonferenz. -Die Fluggesellschaften müssen der FAA nachweisen, dass Flugzeuge den neuen Richtlinien entsprechen und dass sie die Schulungshandbücher des Flugpersonals sowie die Regelungen für das Verstauen der Geräte entsprechend der neuen Vorschriften aktualisiert haben. -Die FAA erklärte, sie habe bereits von einigen Fluggesellschaften Pläne erhalten, die den Einsatz tragbarer elektronischer Geräte in Flugzeugen ausbauen möchten. -Unter diesen Fluggesellschaften waren auch Delta und JetBlue. -„Je nach Zustand des Plans könnten wir die erweiterte Nutzung elektronischer Geräte sehr bald genehmigen“, gab die FAA in einer Verlautbarung bekannt. -Derzeit müssen Passagiere Smartphones, Tablets und andere Geräte abschalten, wenn die Flugzeugtüren geschlossen werden. -Sie dürfen sie erst dann wieder starten, wenn das Flugzeug eine Höhe von drei Kilometern erreicht hat und der Kapitän grünes Licht gibt. -Beim Eintritt in den Landeanflug müssen die Fluggäste sie wieder abschalten und dürfen sie nicht wieder starten, ehe das Flugzeug gelandet ist. -Nach den neuen Richtlinien könnten Fluggesellschaften, deren Flugzeuge gegen elektronische Störungen gesichert seien, ihren Passagieren erlauben, die Geräte auch bei Start, Landung und auf dem Rollfeld zu verwenden, erklärte die FAA. -Die meisten neuen Verkehrsflugzeuge und andere Flieger, die für die Verwendung von Wi-Fi in größeren Höhen umgerüstet wurden, sollten den Kriterien entsprechen. -Laura Glading, Präsidentin des Berufsverbands professioneller Flugbegleiter, begrüßte die Änderungen. -„Wenn die neue Regelung sicher umgesetzt ist – und wir arbeiten diesbezüglich eng mit der Fluggesellschaft zusammen –, werden beide Seite davon profitieren“, kommentierte Glading. -Wir sind es ehrlich gesagt leid, uns bei diesem Thema wie Aufpasser fühlen zu müssen. -Dennoch blieben Internetverbindungen zum Surfen, Austauschen von E-Mails, Textnachrichten oder Herunterladen von Daten unter drei Kilometern weiterhin verboten, erläuterte die Behörde. -Passagiere werden aufgefordert, Smartphones, Tablets und andere Geräte in den Flugmodus zu versetzen. -Also immer noch kein „Words With Friends“, das Scrabble ähnelnde Online-Spiel, das der Schauspieler Alec Baldwin 2011 auf seinem Smartphone spielte, als er öffentlichkeitswirksam einen Jet von American Airlines verlassen musste, weil er sich weigerte, das Gerät abzuschalten, während das Flugzeug am Flugsteig geparkt war. -Und schwerere Geräte wie Laptops müssen auch weiterhin verstaut werden, da hier eine Verletzungsgefahr besteht, falls sie durch die Kabine fliegen. -Handygespräche während des Flugs bleiben ebenfalls weiterhin verboten. -Die Regulierungskompetenz für Telefongespräche liegt bei der Federal Communications Commission (FCC), nicht bei der FAA. -FAA hebt möglicherweise Verbot für bestimmte elektronische Geräte bei Start und Landung auf -Letzten Monat sagte Mark Rosenker von der Verkehrssicherheitsbehörde National Transportation Safety Board, Sicherheitsexperte für nationale Verkehrsfragen auf CBS News, Handys würden noch immer als Risiko betrachtet. -„Handy sind wirklich ein Problem, nicht nur weil sie potenziell Störungen bei Navigationsgeräten verursachen könnten, sondern weil wir von der FCC wissen, dass die Übertragung aus der Luft auch Sendemasten stören kann“, so Rosenker. -Ein Beratungskomitee der Branche, das von der FAA zur Untersuchung dieses Themas ins Leben gerufen wurde, empfahl letzten Monat, dass die Regierung einen breiteren Einsatz persönlicher elektronischer Geräte erlaube. -In den letzten Jahren hat sich der Druck auf die FAA erhöht, Einschränkungen für den Gebrauch zu lockern. -Kritiker wie die demokratische Senatorin Claire McCaskill aus Missouri vertreten die Ansicht, es gebe keine gültigen Sicherheitsgründe für das Verbot. -Die Einschränkungen sind auch zunehmend schwerer durchzusetzen, weil die Nutzung der Geräte allgegenwärtig geworden ist. -In einigen Studien ist davon die Rede, das bis zu ein Drittel der Passagiere das Abschalten des Gerätes vergisst oder ignoriert. -Die FAA begann 1966, die Nutzung elektronischer Geräte durch Flugreisende einzuschränken, nachdem Berichte über Störungen von Navigations- und Kommunikationsgeräten durch von Passagieren mitgeführte UKW-Radios bekannt geworden waren, die damals den neuesten Techniktrend darstellten. -Neue Flugzeuge sind weitaus abhängiger von elektrischen Systemen als frühere Generationen von Luftfahrzeugen, aber sie sind auch so konstruiert und von der FAA genehmigt, dass ihnen elektronische Interferenzen nichts ausmachen. -Fluglinien bieten Passagieren schon seit einigen Jahren Wi-Fi, wenn die Flughöhe erreicht ist. -Die an Wi-Fi-Systeme angepassten Flieger sind auch unempfindlicher gegenüber Interferenzen. -Die überwiegende Mehrheit der Passagierflugzeuge sollte für den stärkeren Gebrauch elektronischer Geräte nach den neuen Richtlinien geeignet sein, sagte Huerta. -Die heutigen elektronischen Geräte geben im Allgemeinen wesentlich weniger Funkstrahlung ab als frühere Generationen. -E-Reader beispielsweise geben nur minimale Strahlung ab, wenn man umblättert. -Doch beim Herunterladen oder Senden von Daten sind die Übertragungswerte stärker. -Zu den Verfechtern einer Lockerung der Einschränkungen für die Gerätenutzung durch Passagiere zählt Amazon.com. -2011 luden Firmenmitarbeiter ein Passagierflugzeug voller Kindle E-Reader und machten einen Testflug, um Probleme zu ermitteln, konnten aber keine feststellen. -Mitglieder des FAA-Beratungskomitees äußerten gemischte Gefühle, was das Risiko bei der Nutzung von Geräten angeht. -Douglas Kidd vom Passagierverband National Association of Airline Passengers sagte, er sei der Ansicht, die Störung durch die Geräte sei echt, auch wenn das Risiko minimal sei. -Andere Komiteemitglieder sagten, es gebe nur vereinzelte Berichte von Piloten, die für eine Störung von Flugzeugsystemen durch die Geräte sprächen, und die meisten davon seien sehr alt. -Das Komitee empfahl der FAA allerdings, es Piloten zu erlauben, bei Instrumentenlandung unter schlechten Sichtverhältnissen das Abschalten der Geräte durch die Passagiere anzuordnen. -Eine Gruppierung aus der Reisebranche begrüßte die Änderungen und bezeichnete sie als vernünftige Anpassung für Reisende, die heute umfangreich mit Technik ausgestattet wären. -„Wir freuen uns, dass die FAA erkennt, dass ein angenehmes Passagiererlebnis nicht im Widerspruch zur Sicherheit steht“, sagte Roger Dow, CEO der U.S. Travel Association. -Per Luftrettungsdienst von Nordsee-Bohrinsel geretteter Vogel wieder freigesetzt -Ein per Luftrettungsdienst an die Küste geflogener Vogel, der erschöpft auf einer Nordsee-Bohrinsel gefunden worden war, wurde wieder in die Natur entlassen. -Die Wasserralle wurde letzten Monat mit einem Hubschrauber nach Aberdeen geflogen, wo sie von der schottischen Tierschutzorganisation SPCA in deren Rettungscenter in Alloa aufgepäppelt wurde. -Colin Seddon, Leiter des Zentrums, sagte: „Diese Wasserralle war wahrscheinlich ein winterlicher Zugvogel aus Nordeuropa, der durch die starken Winde über der Nordsee aufgehalten wurde.“ -Anscheinend suchte der erschöpfte Vogel Zuflucht auf der Bohrinsel. -Er fügte hinzu: „Er konnte nicht mehr weiterfliegen, weshalb wir mit der Bitte um Hilfe kontaktiert wurden.“ -Bei ihrer Freilassung war die Wasserralle wieder fit und gesund. -Ist Europas Elite bereit, mit Großbritannien Geschäfte zu machen? -Business for Britain wurde im April mit der Absicht gestartet, die Wirtschaft an einen Tisch zu bringen und zu definieren, was diejenigen, die in Großbritannien für Wohlstand und Arbeit sorgen, an unseren Beziehungen zur EU ändern möchten. -Zu diesem Zweck haben wir die größte und umfassendste Umfrage unter britischen Wirtschaftsführern in Auftrag gegeben und sie gefragt, was sie über Großbritannien, die Wirtschaft und die EU denken. -YouGov befragte über 1.000 Führungskräfte aus der Wirtschaft, die in etwa die britischen Unternehmensgrößen, -sektoren und Regionen abbilden. -Die Ergebnisse der Umfrage werden viele überraschen. -Wir haben herausgefunden, dass die überwiegende Mehrheit der Unternehmen inzwischen außerhalb Europas exportiert und sich auf Länder konzentriert, die modernisieren und wachsen, während die Staaten in der EU stagnieren. -Sie wünschen sich, dass von Regierungsseite neue Handelsverbindungen mit Ländern wie China, Indien und Brasilien favorisiert werden, statt sich mit dem langen und anstrengenden Prozess aufzuhalten, die obskuren EU-Institutionen zu reformieren. -Gefragt nach ihrer Ansicht zu bestimmten gesetzlich geregelten Themen – vom Kartellrecht bis zur Produktgesetzgebung – ist die Mehrheit der Wirtschaftsführer der Ansicht, die Kontrolle über diese Schlüsselkompetenzen sollte nach Westminster zurückgeholt werden. -Es herrschte allgemeine Unzufriedenheit über den Binnenmarkt, da laut Aussage der Unternehmen die Kosten der Brüsseler Vorschriften inzwischen die Vorteile überwiegen würden, Teil von Europas Handelszone zu sein – selbst 40 Prozent der Großunternehmen stimmten dem zu, die traditionell am europafreundlichsten sind. -Schließlich, und das sagt vermutlich am meisten, ergab unsere Befragung von Wirtschaftsführern, dass eine deutliche Mehrheit sich von Großbritannien einen Kurs in Richtung Veränderung des Abkommens wünscht und eine Beziehung zur EU, die auf Handel basiert, nicht auf Politik. -Diese Erkenntnis, die sich bei Unternehmen aller Größen und führenden Konzernen findet, zeigt, dass die Wirtschaft auf eine „sinnvolle Veränderung“ drängt, die das Heft des Handelns wieder nach Großbritannien bringt. -Es geht um viel – eine Änderung des Abkommens und bessere Bedingungen für Großbritannien würden bei 16 Prozent zu einer Meinungsänderung zugunsten des Verbleibs in der EU in einer Volksabstimmung führen. -Der Premierminister sollte keine Zweifel haben: Diese Umfrage zeigt, dass die britische Wirtschaft hinter seinem Plan für die Neuverhandlungen der britischen Bedingungen für die EU-Mitgliedschaft steht. -Sie zeigt auch, dass die Unternehmen von einer Neuverhandlung eine deutliche Verschiebung der Machtverhältnisse zurück nach Großbritannien erwarten. -Ein besserer Deal für die britische Wirtschaft ist möglich und in zunehmendem Maße notwendig, nachdem die Eurozone sich nun in Richtung einer engeren Wirtschafts- und Fiskalunion bewegt. -Die Priorität müssen Arbeitsplätze und Wachstum in Großbritannien sein, wie das Ergebnis unserer Umfrage zeigt, was für Unternehmen ein verstärktes Augenmerk auf Handel und eine fundamentale Veränderung im Brüsseler Ansatz für Vorschriften bedeutet. -Alexei Miller von Gazprom bezeichnet Pipeline in Bulgarien als Beginn einer neuen Gasära -Der Baubeginn der South-Stream-Pipeline in Bulgarien stelle den Start eines der größten europäischen Energieprojekte dar, erklärte der Gazprom-Vorstand. -„Das heutige Ereignis ist ein Meilenstein: Die Bauarbeiten am bulgarischen Abschnitt der South-Stream-Gaspipeline, dem größten und wichtigsten Projekt in Europa, haben begonnen“, sagte der Vorstandsvorsitzende von Gazprom, Alexei Miller, am Donnerstag in einer Erklärung. -Das Projekt ist ein Schlüsselelement für die Energiesicherheit des gesamten europäischen Kontinents. -South Stream soll für mehr Vielfalt bei Russlands Exportrouten durch Europa sorgen. -Ein Vertragsstreit zwischen Gazprom und seinen Pendants in der Ukraine, in dem der Großteil des russischen Gases für Europa gelagert ist, sorge für ein erhöhtes Risiko bei konventionellen Routen, wie von offizieller Seite zu hören ist. -Miller sagte, die direkte Verbindung nach Bulgarien, einem Mitglied der Europäischen Union, bedeute, dass die mit Transitländern verknüpften geopolitischen Risiken „für immer“ ausgeschaltet seien. -Bulgarische Verbraucher erhalten Gas von South Stream zu vergünstigten Tarifen, wenn das gesamte Projekt 2015 in Betrieb geht. -Laut Gazprom sollen die Bauarbeiten in anderen nachgeordneten Ländern auf der geplanten Route zum Jahresende beginnen. -Die Pipeline ist auf eine jährliche Kapazität von 62,3 Milliarden Kubikmetern Erdgas ausgelegt. -Für nationale und internationale Investoren führt kein Weg vorbei am Immobilienstandort Deutschland. -Die wirtschaftlichen Rahmendaten stimmen, und die Finanzierungsmöglichkeiten sind hervorragend. -Dies ist der Tenor des 9. Immobilientages der Börsen-Zeitung gewesen. -Die deutschen Immobilienmärkte profitieren von der wirtschaftlichen Stärke und den im europäischen Vergleich guten konjunkturellen Rahmenbedingungen im Land. -Darauf hat Christian Ulbrich, CEO Europa, Naher Osten und Afrika von Jones Lang LaSalle, auf dem 9. Immobilientag der Börsen-Zeitung hingewiesen. -Wie gesucht deutsche Gewerbeimmobilien seien, zeige der Blick auf das Transaktionsvolumen, so Ulbrich. -In den ersten drei Quartalen dieses Jahres legte es im Vergleich zum Vorjahresabschnitt um 31% zu. -In Großbritannien lag das Plus nur bei 6%, in Frankreich bei 19%. -"Immobilieninvestments bieten eine attraktive Verzinsung", sagte Ulbrich. -Der Renditeabstand zwischen Immobilien und Bundesanleihen sei auf einem historisch hohen Niveau. -Die FAA lockert Einschränkungen für die Nutzung elektronischer Geräte in Flugzeugen – auch wenn das Telefonieren mit dem Handy weiterhin verboten bleibt. -Kampfflugzeuge greifen ein Depot russischer Raketen in der Hafenstadt Latakia an, wie offiziell verlautbart wurde. -Dies ist offenbar eine Fortsetzung der israelischen Kampagne, mit der die Verbreitung von Waffen im Nahen Osten verhindert werden soll. -Bundesberufungsgericht blockiert Entscheidung einer Richterin, wonach die umstrittene Taktik des NYPD Minderheiten diskriminiere. -Knapp 100 afrikanische Einwanderer, die nach Algerien einreisen wollten, verdursten, nachdem zwei LKWs mitten in der Sahara liegen bleiben. -Experten sagen, dass das Gewaltverbrechen, bei dem 14 Erwachsene und sieben Kinder starben, nichts als reiner Zufall sei und kein Zeichen für wachsende Gewalt in Amerika. -Statt sich von der Zahlungsunfähigkeit der US-Regierung verunsichern zu lassen, konzentrierten sich Investoren auf das, was vermutlich mehr zählt: die Federal Reserve. -Die Kalifornierin plant, die möglicherweise erste Vorladung dieser Art anzufechten, mit der Begründung, die mit dem Internet verbundene Brille erleichtere die Navigation. -Die Polizei gibt an, sie sei im Besitz eines Videos, auf dem angeblich zu sehen sei, wie Bürgermeister Rob Ford eine Crack-Pfeife rauche. -Selbst enge Verbündete haben Geheimnisse voreinander – und versuchen alles, um herauszufinden, was zurückgehalten wird. -Der Vatikan möchte wissen, wie katholische Pfarreien auf der ganzen Welt mit sensiblen Themen wie Verhütung, Scheidung und schwule Paare umgehen. -Anhörung vor der Königlichen Kommission: Zwei YMCA-Mitarbeiter vor Anschuldigungen gegen Jonathan Lord wegen Sexualdelikten angeklagt -Zwei YMCA-Mitarbeiter aus New South Wales (NSW) seien wegen Sexualdelikten im Zusammenhang mit Kindern angeklagt worden, ehe 2011 Vorwürfe gegen den Kinderpfleger Jonathan Lord aus Caringbah erhoben wurden, wie die Königliche Kommission für sexuellen Missbrauch von Kindern erfuhr. -Doch bei der Eröffnungserklärung vor der Kommission hatte der YMCA erklärt, man habe „in der Organisation niemals mit einem Vorfall von sexuellem Kindesmissbrauch zu tun gehabt“. -Geschäftsführer Phillip Hare wurde über einen Fall befragt, bei dem ein YMCA-Mitarbeiter wegen Vergehen mit Kinderpornografie angeklagt worden war, und einen anderen, bei dem ein Sportlehrer der YMCA Caringbah Hall 1991 wegen des Missbrauchs ihm anvertrauter Kinder verurteilt worden war. -Hare sagte Gail Furness, einer der Kommission assistierenden Anwältin, dass er von dem ersten Fall gewusst habe, den zweiten aber nicht gekannt habe. -Er räumte ein, die Eröffnungserklärung des YMCA gegenüber der Kommission sei ebenfalls ungenau in der Behauptung gewesen, „es habe externe Überprüfungen des YMCA gegeben, in denen der YMCA als an vorderster Front bei der Kindersicherheit anerkannt wurde“. -Beweise vor der Kommission zeigen vielmehr, dass der YMCA darüber unterrichtet wurde, dass er die zweitniedrigste Bewertung von vier möglichen in einem Qualitätsaudit des Department of Education and Communities (Erziehungsministerium) im August des Jahres erhalten hat. -Hare, der mit 21 beim YMCA angefangen hat, räumte ein, die Geschäftsleitung habe „von mir an abwärts“ bei der Einstellung von Lord versagt und nicht sichergestellt, dass Mitarbeiter sich über ihre Verpflichtungen im Klaren gewesen seien, Verstöße gegen die Richtlinien zur Kindersicherheit zu melden. -Lord war Anfang des Jahres wegen Sexualvergehen an zwölf Jungen während seiner zweijährigen Tätigkeit für den YMCA verurteilt worden. -Er wurde zu mindestens sechs Jahren Gefängnis verurteilt. -Hare stritt aber ab, dass der YMCA ein kulturelles Problem hätte, das Mitarbeiter davon abgehalten habe, Lords Verstöße in Bezug auf die Sicherheit von Kindern zu melden. -Die Mitarbeiter sagten aus, sie hätten Verstöße beobachtet, unter anderem, dass Lord allein mit Kindern war, sie privat als Babysitter betreute, sie auf seinem Schoß sitzen ließ, zu einem sagte, dass er es lieb habe, und Kinder mit seinem Handy spielen ließ. -Danielle Ockwell, deren Vorgesetzter Lord war und die um eine Schulung zum Kinderschutz gebeten hatte, weil sie wegen seines Verhaltens besorgt war, sagte aus, dass die Kinderdienstleiterin Jacqui Barnat, die im YMCA Caringbah die Vorgesetzte Lords war, „sehr einschüchternd und die meiste Zeit sehr unnahbar war“. -Der Geschäftsführer erklärte, er würde Aussagen von Mitarbeitern nicht akzeptieren, wonach diese sich nicht getraut hätten, ihren Vorgesetzten Meldung zu erstatten. -Stattdessen, so sagte er, habe vielmehr ihre Freundschaft mit Lord ihr Urteilsvermögen beeinträchtigt, weshalb sie ihn nicht gemeldet hätten. -Hare erklärte, er habe dem Vorstand des YMCA NSW seine Ansicht mitgeteilt, dass die Lektion, die die Organisation aus dem „Jonathan-Lord-Zwischenfall“ zu ziehen habe, „nicht das Melden“ durch Mitarbeiter betreffe, und der Vorstand habe ihm zugestimmt. -Hare sagte, die Entscheidung, die Mitarbeiter zur Unterzeichnung von Geheimhaltungsverpflichtungen kurz nach Auftreten der Anschuldigungen zu drängen, sei vom Geschäftsführer der Kinderdienste des YMCA, Liam Whitley, getroffen worden. -Er sagte, damit sollte eine Kontaminierung der Beweise verhindert werden, was aber „übereifrig“ und schlecht umgesetzt worden sei. -Der YMCA NSW sei in der Zeit, als Jonathan Lord zwischen 2009 und 2011 dort beschäftigt war, keine für Kinder sichere Organisation gewesen, erklärte Professor Stephen Smallbone von der Griffith University, Experte für sexuellen Kindesmissbrauch, der Kommission. -Er erläuterte, dass es „ernsthafte Probleme“ bei der Einstellung, Überprüfung, Einarbeitung, Schulung und Beaufsichtigung der Mitarbeiter gegeben habe. -Die Anhörung wurde bis 20. Dezember vertagt. -Tony Blair erklärte, er würde die Chance zu einer Rückkehr als britischer Premierminister nutzen – räumt aber ein, dass ein Comeback unwahrscheinlich sei. -In einem Interview heute Abend anlässlich des fünften Jahrestages seines Ausscheidens aus dem Amt verkündete der 59-Jährige seine Ansichten zu verschiedenen innenpolitischen Themen. -Seit seinem Rücktritt im Juni 2007 nach einem Jahrzehnt als Staatschef hat Blair es weitgehend vermieden, sich zu britischer Politik zu äußern, und seine Kommentare meistens auf die Außenpolitik und seine Rolle als Gesandter des Quartetts der Friedensstifter im Nahen Osten beschränkt. -Auf die Frage, ob er gerne in das Amt des Premierministers zurückkehren würde, wurde Blair vom Evening Standard aus London mit den Worten zitiert: „Ja, sicher, aber es ist unwahrscheinlich, dass das passiert, also ...“ -Als sich die Pferdeschau-Experten in Scharen in Cardiff einfanden, um das Pferd des Jahres zu küren, war ihnen bewusst, dass ein harter Konkurrenzkampf bevorstand. -Doch keiner war auf den drei Jahre alten Fenton Kirkland vorbereitet. -Der Knirps, der noch nicht einmal im Kindergarten ist und erst vor ein paar Monaten seine ersten Schritte gemacht hat, und sein Shetland-Pony Toffee trabten locker durch die drei Runden und holten den ersten Preis – wobei sie 30 erwachsene Gegner hinter sich ließen. -Das unzertrennliche Paar, beide gleich groß, wurden wegen ihres Auftretens, Verhaltens und Stils beim alljährlichen Wettbewerb gelobt, der vom Sunnybank Equestrian Centre in Rudry nahe Cardiff veranstaltet wird. -Bei seinem Auftritt in Konkurrenz gegen Männer und Frauen mit eleganten Melonen hatte er seine Tellermütze in einem frechen Winkel aufgesetzt und führte den zwei Jahre alten Toffee durch den Ring. -Fenton wurde von den Juroren für seine natürlichen Fähigkeiten beim Umgang mit dem Pony gepriesen, die weit über seinem Alter lägen. -Und Toffee erhielt Bestnoten für sein Auftreten und seine Persönlichkeit. -Fenton hatte Toffee letzten März als Geschenk zu seinem dritten Geburtstag bekommen und seitdem jeden Tag mit dem Shetland-Pony geübt. -Seine Mutter, Donna, 30, sagte: „Fenton und Toffee sind ein großartiges Gespann.“ -Sie mussten gegen das gesamte Teilnehmerfeld antreten und sind mit Goldpokal und Rosette vom Platz gegangen. -Es war erst das zweite Mal, dass er mit Toffee an einem Wettbewerb teilgenommen hatte, und wir waren alle begeistert, als er gewonnen hat. -Vollkommen Fremde in der Arena hielten ihn für so phänomenal, dass sie mit ihm fotografiert werden wollten. -Der kleine Junge aus dem Dorf Nantyglo in der Nähe von Ebbw Vale, South Wales, folgt in den Fußstapfen seiner Tante Sharon Howells, die seit über zehn Jahren Pferde vorführt. -Howells erzählte: „Das ganze Publikum war wie elektrisiert und alle haben ihn angefeuert und geklatscht.“ -Er lief auf Sand die gesamte Länge der Arena und obwohl er so winzig aussah, hat er phantastische Arbeit geleistet. -Fenton ist verrückt nach Tieren – er liebt Pferde, Traktoren und Bauernhöfe und hat zwei Hühner, um die er sich kümmert. -So wie er gestartet ist, wird es nicht lange dauern, bis er auf der „Pferd des Jahres“-Schau ist – und ich bin mir sicher, dass er gut abschneiden wird. -Ein Sprecher der jährlichen Pferdeschau sagte: „Fenton ist erst drei, aber er weiß, wie er mit seinem Pony umgehen muss.“ -Zusammen sind sie ein tolles Team. -Die Juroren bewerteten Fenton und Toffee danach, wie gut sie sich geschlagen und im Vorführring präsentiert hätten. -Sie achten auf gute Zusammenarbeit zwischen Pony und Führer – und da waren Fenton und Toffee die Besten im Ring. -Ich bin mir sicher, dass Fentons süße Kleidung ihren Teil dazu beigetragen hat, das passte wirklich perfekt zum Anlass. -Chinesische Zeitung soll nach Aufruf „reformiert“ werden -Eine chinesische Zeitung, die auf der Titelseite die Freilassung eines Reporters gefordert hatte, der wegen Diffamierung angeklagt worden war, solle reformiert werden, erklärte ein Presseregulator. -Der in Guangzhou ansässige Neue Express hatte einen seltenen öffentlichen Aufruf zur Freilassung des Journalisten Chen Yongzhou abgedruckt. -Doch Chen gab später im Fernsehen zu, dass er Bestechungsgelder angenommen habe, um Berichte über eine teilstaatliche Firma zu fälschen. -Jetzt werde der Neue Express einer „umfassenden Rektifizierung“ unterzogen, erklärte die Regulierungsbehörde. -Die „Rektifizierunganordnung“ erging von der Behörde für Presse und Publikationen, Radio, Film und Fernsehen in Guangdong. -Vorläufige Ermittlungen ergaben, dass der Neue Express, der zur Yangcheng Evening News Group gehört, zwischen September 2012 und August 2013 mehrere unzutreffende Berichte über das börsennotierte Unternehmen Zoomlion veröffentlicht hatte. -„Das Redaktionsmanagement des Neuen Express war ungeordnet“, gab die Regulierungsbehörde in einer Erklärung bekannt. -Sie sagte, man habe entschieden, „eine administrative Strafe für Chen Yongzhou zu verhängen und ihm seine Reporterlizenz zu entziehen“. -Außerdem habe man „die Yangcheng Evening News Group angewiesen, eine vollständige Rektifizierung des Neuen Express vorzunehmen und empfohlen, die relevanten Verantwortlichen beim Neuen Express zu ermitteln und unverzüglich das Führungsteam des Neuen Express zu überprüfen.“ -Chen hatte mehrere Artikel für den Neuen Express geschrieben, in denen er über angebliche finanzielle Unregelmäßigkeiten bei einem Hersteller von Baumaschinen namens Zoomlion berichtete. -Nachdem er verhaftet worden war, veröffentlichte die Zeitung zwei Aufrufe für seine Freilassung auf der Titelseite und stellte sich hinter seine journalistische Arbeit. -Doch dann trat Chen im Staatsfernsehen auf und gab zu, dass er gegen Bezahlung falsche Berichte veröffentlicht habe. -„In diesem Fall habe ich Zoomlion geschädigt ebenso wie die gesamte Nachrichtenbranche und ihre Fähigkeit, das Vertrauen der Öffentlichkeit zu gewinnen“, erklärte er gegenüber dem staatlichen Nachrichtensender CCTV. -Ich habe es in erster Linie getan, weil ich auf Geld und Ruhm aus war. -Mir ist klar geworden, dass ich falsch gehandelt habe. -Im Anschluss an Chens Entschuldigung veröffentlichte der Neue Express eine Entschuldigung auf der Titelseite und erklärte, man habe es versäumt, die Berichte korrekt zu überprüfen. -Verschiedene bekannte Verdächtige haben in jüngster Zeit im Fernsehen Geständnisse abgelegt. -Experten sagen, dass Geständnisse immer noch routinemäßig erzwungen werden, trotz einer Gesetzesänderung Anfang des Jahres, in der den Behörden verboten wurde, jemanden zu zwingen, sich selbst zu beschuldigen. -Drogentunnel mit eigener Eisenbahn zwischen den USA und Mexiko entdeckt -Einer der raffiniertesten Tunnel für den Drogenschmuggel zwischen den USA und Mexiko, der komplett mit Beleuchtung, Belüftung und elektrischem Bahnbetrieb ausgestattet war, wurde jetzt entdeckt. -Die US-amerikanischen Behörden beschrieben den 120 mal 90 Zentimeter messenden Tunnel als eine der ausgefeiltesten Passagen, die sie jemals gefunden hätten. -Der Tunnel, der im Zickzackmuster über eine Länge von knapp sechs Fußballfeldern verläuft, verbindet Lagerhallen in der Nähe von Tijuana, Mexiko, und San Diego, USA. -In der Gegend gibt es zahllose unscheinbare Lagerhallen, was es leichter macht, mit Drogen beladene LKWs zu verbergen. -Der Tunnel sei geschlossen worden, bevor Drogen unerkannt darin transportiert werden konnten, erklärten die Behörden. -Bei der Entdeckung des Tunnels seien achteinhalb Tonnen Marihuana und 148 Kilogramm Kokain sichergestellt worden, so die Aufzeichnungen des Gerichts. -Drei Männer, die laut Aussage der Behörden als Fahrer arbeiteten, wurden wegen des Besitzes und des beabsichtigten Verkaufs von Marihuana und Kokain angeklagt. -Ihnen drohen bei einem Schuldspruch Gefängnisstrafen zwischen zehn Jahren und lebenslang. -In Nogales, Arizona, setzen Schmuggler auf ein ausgedehntes unterirdisches Netzwerk von Entwässungskanälen. -Der Tunnel ist die achte große Passage, die seit 2006 in San Diego entdeckt wurde. -Einige der größten Tunnel wurden nach der Marihuana-Ernte in Zentralmexiko im Oktober entdeckt, was die Drogenkartelle vor die Herausforderung stellt, wie sie ihr Produkt schnell zu den Konsumenten bringen können. -Im Jahr 2010 fanden Behörden eine knapp 640 Meter lange Passage mit Schienen, die von einer Küche in einem Wohnhaus in Tijuana zu zwei Lagerhäusern in San Diego verlief. -Orlando Bloom und Miranda Kerr lieben sich noch immer -Schauspieler Orlando Bloom und Model Miranda Kerr wollen künftig getrennte Wege gehen. -In einem Interview sagte Bloom jedoch, dass er und Kerr sich noch immer lieben. -Miranda Kerr und Orlando Bloom sind Eltern des zweijährigen Flynn. -Schauspieler Orlando Bloom hat sich zur Trennung von seiner Frau, Topmodel Miranda Kerr, geäußert. -In einem Interview mit US-Journalistin Katie Couric, das am Freitag (Ortszeit) ausgestrahlt werden sollte, sagte Bloom, "das Leben verläuft manchmal nicht genau so, wie wir es planen oder erhoffen". -Kerr und er selbst liebten sich noch immer, betonte der 36-Jährige. -"Wir werden uns gegenseitig unterstützen und lieben als Eltern von Flynn". -Kerr und Bloom sind seit 2010 verheiratet, im Jahr 2011 wurde ihr Söhnchen Flynn geboren. -Der Veganismus ist laut Lexika dem Vegetarismus unterstellt, unterscheidet sich von diesem aber doch auf ganz erhebliche Weise. -Denn während Vegetarier sich den Verzehr von Sahnetorte, Eis oder einem kräftigen Edamer erlauben, stehen diese Schlemmereien nicht auf der Speisekarte eines Veganers. -Milch, Käse, Ei, neben den Verzicht auf Fleisch und Fisch lässt der Veganer vom sämtlichen Verzehr tierischer Produkte ab - auch Honig wird verneint. -Was bleibt, mag man sich fragen, doch bei näherer Betrachtung zeichnet sich die vegane Ernährungswelt durch einen beachtlichen Einfallsreichtum aus. -"Brotaufstriche sind sehr beliebt", erklärt Ute Henkelmann, die Inhaberin des hiesigen Reformhauses, die eine Vielzahl spezieller Produkte für Veganer bereithält. -Auch Reuben Proctor besucht den Laden in der Domgasse gerne. -Seit 13 Jahren lebt der gebürtige Neuseeländer vegan, in Lampertheim findet er (fast) alles vor, was er zum Leben braucht. "1997 stellte ich meine Ernährung um und wurde Vegetarier". -Um die Jahrhundertwende erfolgte dann der nächste Schritt und ich wurde Veganer. -"Die Überzeugung kam aber nicht nur wegen der Ernährung, sondern aufgrund der Schuhwahl", sagt Proctor, der Kunstleder normalem Leder vorzieht. -Er erkannte, dass es auch in anderen alltäglichen Belangen ohne tierische Produkte geht. -In erster Linie ist vegan sein eine Ethik. -"Ich will nicht, dass Tiere für mich sterben müssen", spricht Proctor die Maxime der Veganer aus, die sich auf das gesamte Konsumverhalten erstreckt. -Statt Burgern, Rühreiern oder Gummibären hinterherzutrauern, habe der Veganer schnell ganz neue Produkte entdeckt, die Begeisterung stieg an. -"Es ist ein doppelter Bonus", sagt Proctor heute, "tatsächlich stellt vegan sein keinen Verzicht, keine Askese dar, sondern es ist eine Bereicherung". Die Annahme, dass man mit der Ernährungsumstellung die Auswahl an Speiseoptionen reduziere, führt Proctor eher auf die Macht der Gewohnheit zurück. -Und genau diese Konditionierungen ließen auch manchen noch so gut gemeinten Versuch scheitern. -"Der Suchtfaktor ist ein ganz entscheidender Faktor, der viele schnell aufgeben lässt, ähnlich wie beim Rauchen", stellt Proctor einen Vergleich auf. -Und wendet ein: "Bestimmte Geschmäcker sind nicht am Tier festzumachen". -Oft sind es die Gewürze oder die Art der Zubereitung, die den Geschmack ausmachen. -"Man kann aber auch auf andere Weise den gleichen Effekt erzeugen", verdeutlicht der Veganer. -Zu einem Grillfest bringt Proctor beispielsweise Auberginen, Champignons und auch ein paar Veganwürste mit. -Er achtet auf eine ausgewogene, abwechslungsreiche Ernährung, bei der es durchaus auch einmal deftig schmecken soll. -Natürlich möchte man etwas zwischen den Zähnen haben, das ist legitim. -"Es soll schmecken, aber das ist nicht nur auf tierische Nahrung zu reduzieren", so Proctor. -Gesundheitliche Schäden bis hin zu Essstörungen, eine weitere weitverbreitete Annahme, habe er nicht erlitten. -Für mich hatte die Umstellung einen positiven Aspekt. -Ich nehme kein fremdes Cholesterin auf und habe einen erhöhten Vitaminanteil. -Es gibt Hochleistungsathleten, die sich ausschließlich vegan ernähren. -"Meine Blutwerte sind bestens, ich lasse mich oft checken", verrät Proctor. -Insbesondere vor dem Mangel an Vitamin B12 warnen viele Vegansimus-Kritiker. -"Da Tiere mit Futtermitteln genährt werden, statt auf der Weide zu grasen, ist es fraglich, ob in dem Fleisch, das man in der Kühltruhe kauft, der Vitamin-B12-Anteil hoch ist", findet Proctor. -Für Veganer gebe es zudem die Möglichkeit, das Coenzym in Form von Tabletten zu sich zu nehmen. -"Meine B12-Werte sind in Ordnung", erklärt er. -Weite Wege müsse Proctor für die Nahrungsmittelbeschaffung nicht gehen. -Grundnahrungsmittel gibt es schließlich überall und jeder Supermarkt hat mittlerweile Sojamilch und andere Produkte. -Alles, was ich brauche, finde ich vor Ort. -Nur ab und an kaufe ich etwas Spezielles. -"Viele Leute übersehen, dass eben nur die Fertigprodukte teuer sind, aber so verhält es sich bei "gewöhnlichen" Fertigprodukten bekanntermaßen auch". -Ohnehin sei die Zubereitung veganer Gerichte weitaus günstiger und weniger zeitaufwendig, als gemeinhin angenommen. -"In der Hausküche gibt es viel zu entdecken, mit ein paar kleinen Kniffen kann man gute, wenn nicht teilweise bessere Ergebnisse erzielen", sagt Proctor. -Margarine statt Butter, etwas mehr Backpulver und Kohlensäure statt Ei, mit seinen veganen Käsekuchen konnte Proctor auch seine Schwiegereltern zum Staunen bringen. -Familienfesten verleiht der Veganer eine neue Würze. -Das hat Dynamik, wenn man zusammen kocht, hat das einen neuen Stellenwert. -"Die Situationen, die dabei entstehen können, sind so unterschiedlich wie die Menschen selbst", sagt Proctor. -Natürlich wurde ihm aber auch schon Skepsis entgegengebracht. -Schmeckt dir mein Essen nicht mehr? -"Soll ich mich jetzt schlecht fühlen, weil ich Milch trinke?" - bei solcherlei Fragen müsse man als Veganer mit Fingerspitzengefühl agieren. -"Ich lehne die Menschen ja nicht ab, ich bin Teil dieser Gesellschaft", möchte Proctor kein Enklavendasein fristen. -Und genauer betrachtet erscheint die Alltagswelt dann gar nicht so veganfremd wie vermutet - und der Veganer nicht so im Alltag eingeschränkt wie gemeinhin angenommen. -Auch Fast Food ist erlaubt. -Pommes gibt es überall! -"Viele Dönerläden bieten Falafel an, oder eben einen vegetarischen Yufka", sagt Proctor. -In einer Pizzeria weicht er auf Pastagerichte aus, oder bestellt eben eine Pizza ohne Käse, alles eine Frage der Gewohnheit. -Die Restaurants stellen sich so langsam darauf ein. -"Als ich vor 13 Jahren angefangen habe, mich vegan zu ernähren, war alles noch viel schwieriger", kann der Neuseeländer eine Entwicklung ausfindig machen: "Man muss mit den Leuten reden, sie darauf aufmerksam machen". -"Wenn genügend Nachfragen kommen, sind sie vielleicht dazu geneigt, die Speisekarte zu ergänzen", hofft er. -Vegane Restaurants entwickeln sich, wie zum Beispiel die Kombüse in Mannheim oder das Café Vogelfrei. -Proctor hegt den Wunsch, dass auch andere Restaurants zumindest ein veganes Gericht auf der Speisekarte stehen haben. -Im Grunde genommen sind vegane Gerichte für alle da. -Jeder kann sie essen, sie sind also genau das Gegenteil von exklusiv. -"Fleischgerichte sind dagegen exklusiv, und zwar im wahrsten Sinne des Wortes, denn sie schließen andere aus", stellt Proctor fest. -"Wenn man Lebensmittelskandale vermeiden will, dann ist Veganismus ein sicherer Weg, den allergrößten Teil von vornherein unmöglich zu machen", sagt Proctor. -Der Einkauf veganer Produkte könne aber zu Beginn etwas schwierig sein. -Insbesondere wenn man zu verstehen lernt, das auch in Erdnüsse tierische Zusatzstoffe als Farbgeber enthalten seinen können, scheint man mit der Befolgung der Ethik bei so vielen Hürden und Fallen schnell der Mut zu verlassen. -Nicht nur deshalb haben Reuben Proctor und Lars Thomsen ein Buch geschrieben: "Veganissimo - Tierische Inhaltsstoffe und ihre Alternativen" erklärt die vielen Inhaltsstoffe, die Nahrungs- aber auch Reinigungsmitteln beigesetzt werden. -"Nach und nach entwickelt man einen Scannerblick und weiß, auf welche Zeichen und Logos man zu achten hat "Es ist manchmal besser kleine Schritte zu tun als stehenzubleiben oder zu stolpern", weiß Proctor. -Der Veganismus, er ist ein wichtiger moralischer Fingerzeig, der auf das Bewusstsein schlägt. -Vielleicht schafft er es nicht ganz, dass man seine Gewohnheiten ablegt, doch zumindest kann er den Konsumenten lehren, seine Gewohnheiten kritisch zu hinterfragen. -Der Börsenbetreiber Nasdaq OMX hält seine Kunden weiter in Atem. -Der Handel am Nasdaq Options Market wurde am Freitagnachmittag deutscher Zeit unterbrochen. -In einer Mitteilung machte der Betreiber technische Probleme verantwortlich. -Die anderen elf US-Optionsmärkte, darunter auch zwei der Nasdaq OMX, setzten ihr Geschäft ungehindert fort. -Der jüngste Zwischenfall setzt eine ganze Serie von kleineren und größeren Pannen bei dem Börsenbetreiber fort. -Erst am Dienstag waren die Indizes der Nasdaq wegen Fehlern im Datentransport eine Stunde lang nicht berechnet worden. -Im August dieses Jahres hatte es allein in einer Woche zwei Pannen gegeben. -Zuerst hatte die US-Investmentbank Goldman Sachs wegen technischer Probleme massenhaft fehlerhafte Kaufaufträge an die Optionsmärkte geschickt. -Daraufhin brauchten die Börsenbetreiber fast einen ganzen Tag, um die Orders durchzuschauen und zu streichen. -Zwei Tage darauf kam die Hälfte des gesamten Aktienhandels in den USA wegen einer Computerpanne an der Nasdaq-Börse für mehrere Stunden zum Erliegen. -Für Aufsehen hatte zudem im vergangenen Jahr der Börsengang von Facebook gesorgt. -Die Nasdaq-Systeme waren der Flut von Kauf- und Verkaufsaufträgen nicht gewachsen gewesen, stellte später die Börsenaufsicht SEC fest und verdonnerte das Unternehmen zu einer Rekordstrafe von 10 Millionen Dollar. -Konstanz: Radler fährt 63-Jährigen um -Zu dem Unfall war es nach Angaben der Polizei gekommen, als ein 26 Jahre alter Mann am Donnerstagabend, gegen 22 Uhr, mit einem Damenfahrrad ordnungswidrig auf dem linken Gehweg vom Bahnhofplatz in Richtung Marktstätte unterwegs war. -Als plötzlich ein 63 Jahre alter Mann von einem Lokal auf den Gehweg trat, konnte der Fahrradfahrer laut Polizei nicht mehr rechtzeitig bremsen. -Bei dem folgenden Zusammenstoß wurde der Fußgänger gegen die Hauswand gedrückt, ging im weiteren Verlauf zu Boden und zog sich eine rund 15 Zentimeter lange Platzwunde am Kopf zu. -Ein Rettungswagenteam brachte den Verletzten zur ärztlichen Behandlung ins Klinikum. -Britische Polizei stellt Assange einen Auslieferungsbescheid zu -Die britische Polizei hat heute einen Auslieferungsbescheid an WikiLeaks-Gründer Julian Assange übergeben, der in der ecuadorianischen Botschaft in London Zuflucht gesucht und Asyl beantragt hat. -Scotland Yard erklärte, man habe dem 40-jährigen Australier eine „Aufforderung, sich zu stellen“ zugestellt, die ihn zum Erscheinen in einer Polizeiwache auffordere, und fügte hinzu, sollte er dem nicht nachkommen, drohe ihm die Verhaftung. -Assange droht die Auslieferung nach Schweden wegen der Anklage in einem Sexualdelikt, nachdem seine Möglichkeiten nach britischem Recht ausgeschöpft sind, seit der Oberste Gerichtshof seinen Einspruch gegen die Auslieferung Anfang des Monats abgelehnt hat. -Da er befürchtet, Stockholm werde ihn an die USA ausliefern, hatte er am 19. Juni in der ecuadorianischen Botschaft in London Zuflucht gesucht und das südamerikanische Land um politisches Asyl gebeten. -Scotland Yard habe „einem 40-jährigen Mann eine Aufforderung zugestellt, sich zu einem von uns gewählten Zeitpunkt bei einer Polizeiwache zu stellen“, erklärte ein Sprecher. -Er verletzt weiterhin die Bedingungen seiner Freilassung auf Kaution. -Die Botschaft gab keinen Kommentar zur Zustellung der polizeilichen Aufforderung ab. -Assange fürchtet, er werde von Schweden an die Vereinigten Staaten ausgeliefert und dort wegen möglicher Spionagevorwürfe angeklagt, nachdem er über 250.000 diplomatische Nachrichten auf der Enthüllungswebsite WikiLeaks veröffentlicht hatte. -Fahrer, der mit 210 km/h und heißem Getränk zwischen den Beinen gerast war, erhält 1.000 £ Strafe -Einem Autofahrer wurde eine Strafe in Höhe von 1.000 £ auferlegt, weil er mit bis zu 210 km/h und einem Heißgetränk zwischen seinen Beinen gefahren war. -Andrew Howie, 35, aus Tiptree, Essex, wurde am 27. Mai mit seinem Mercedes Benz auf der A120 bei Braintree gestoppt. -Nachdem ihn die Polizei angehalten hatte, entdeckten sie den Getränkebecher zwischen seinen Beinen. -Vor dem Amtsgericht Colchester bekannte sich Howie schuldig, ohne die nötige Sorgfalt und Aufmerksamkeit gefahren zu sein. -Er erhielt sieben Punkte und ein sechsmonatiges Fahrverbot. -Howie wurde außerdem zur Zahlung von Kosten in Höhe von 90 £ sowie eines Opferzuschlags von 100 £ verurteilt. -Steuer für ausländische Immobilienbesitzer gegen Londons Spekulationsblase -Das Finanzministerium hat provisorisch die CGT-Maßnahme durchkalkuliert, wartet aber noch auf die abschließende Entscheidung von Schatzkanzler George Osborne, der im Haushalt 2012 eine Stempelsteuer in Höhe von 7 % auf Wohnungen verhängt hatte, die über zwei Millionen Pfund kosten sowie jährliche Gebühren für Käufer, die Wohnungen als Firma und nicht als Einzelpersonen halten. -Bereits jetzt übersteigen die Einnahmen aus der Stempelsteuer für Wohneigentum in den Bezirken Westminster und Kensington & Chelsea, die im Steuerjahr 2012/13 bei 708 Millionen Pfund lagen, die Erlöse für Nordirland, Wales, Schottland, den Nordosten, Nordwesten, Yorkshire und die Region Humber zusammengenommen. -Cook sagte: „Nach den Erhöhungen bei der Stempelsteuer auf hochpreisige Wohnungen und der Einführung der damit verbundenen Gesetzgebung gegen ein Umgehen kann man schwerlich behaupten, hochwertige Immobilien seien zu niedrig besteuert, ungeachtet der Auswirkungen des veralteten Gemeindesteuersystems.“ -„Doch dieser Schritt könnte einige ausländische Investoren zurückhaltender machen, was den Kauf von Immobilien in London angeht, oder Eigentümer davon abhalten zu verkaufen“, fügte er hinzu. -Erstklassige Immobilien – die obersten fünf bis zehn Prozent des Wohnungsmarkts nach Preis – im wohlhabenden Londoner Südwestgürtel, der sich von Fulham bis Wimbledon erstreckt, haben sich um den Rekordwert von 11,8 % im letzten Jahr verteuert. -Die Preise in Central London wiesen ein gleichmäßiges jährliches Wachstum von 5,6 % auf, wurden aber von einem aufstrebenden „Inlandsmarkt“ überschattet, auf dem der südwestliche Teil der Stadt, der Norden (7,4 %) und der Osten (6,5 %) einen Aufwärtstrend erlebten, so eine Untersuchung von Savills. -Wissenschaftler konnten mehr Licht darauf werfen, in welchem Zusammenhang das Schwanzwedeln eines Hundes mit seiner Stimmung steht. -Frühere Forschungen hatten gezeigt, dass glückliche Hunde mit dem Schwanz mehr nach rechts wedelten (aus Sicht des Hundes), während es bei nervösen Hunden die linke Seite war. -Doch jetzt sagen Wissenschaftler, dass andere Hunde diese subtilen Unterschiede wahrnehmen und darauf reagieren können. -Professor Georgio Vallortigara, ein Neurowissenschaftler der Universität Trient, sagte: „Beim Menschen ist gut bekannt, dass die linke und rechte Gehirnhälfte unterschiedlich auf Stimuli reagieren, die positive oder negative Emotionen hervorrufen.“ -Hier haben wir versucht, uns dies bei anderen Spezies anzusehen. -Er bemerkte außerdem, dass bei Hunden genau wie beim Menschen die rechte Gehirnhälfte für die Bewegung der linken Seite zuständig sei und umgekehrt, und dass die beiden Hemisphären unterschiedliche Rollen bei den Gefühlen spielten. -Um mehr darüber zu erfahren, wie Hunde auf das seitliche Schwanzwedeln ihrer Artgenossen reagieren, überwachten die Forscher die Tiere, während sich diese Filme anderer Hunde ansahen. -Sie maßen die Herzfrequenz der Vierbeiner und analysierten das Verhalten. -Wahrscheinlich werden wir bald verstehen, warum sich der Schwanz mal in die eine, mal in die andere Richtung bewegt -Professor Vallortigara sagte: „Wir haben den Hunden Filme mit Hunden gezeigt – entweder eine naturgetreue Version oder eine Silhouette, um ablenkende andere Aspekte zu eliminieren, und wir konnten damit die Bewegung des Schwanzes mehr nach links oder rechts steuern.“ -Wenn die Tiere sahen, wie ein ansonsten ausdrucksloser Hund den Schwanz nach rechts bewegt hat (von der Perspektive des wedelnden Hundes aus gesehen), blieben sie völlig entspannt. -War aber die Schwanzneigung vornehmlich nach links (wieder aus Perspektive des Hundes, der wedelt), so stieg ihre Herzfrequenz und sie sahen unruhig aus. -Nach Ansicht von Professor Vallortigara kommunizieren die Hunde nicht absichtlich miteinander durch diese Bewegungen. -Er ist vielmehr überzeugt, dass die Hunde aus Erfahrung gelernt hätten, bei welchen Bewegungen sie sich Sorgen machen sollten und wann nicht. -Er sagte: „Wenn man verschiedene Begegnungen mit anderen Hunden hat und das Wedeln mit dem Schwanz in eine Richtung eher mit freundlichem Verhalten verknüpft ist und die rechte Seite eher ein weniger freundliches Verhalten produziert, dann reagiert man auf Basis dieser Erfahrungen.“ -Die Forscher sagen, dass die Erkenntnisse Hundebesitzern, Tierärzten und Trainern zu einem besseren Verständnis der Emotionen ihres Tieres verhelfen könnten. -John Bradshaw, Experte für Hundeverhalten und Gaststipendiat an der Fakultät für Tierheilkunde der Universität von Bristol erklärte, dies sei nicht die erste Studie gewesen, bei der untersucht wurde, ob links und rechts für Hunde wichtig sei. -Letztes Jahr fand ein Team der Universität von Lincoln heraus, dass Hunde den Kopf nach links drehen, wenn sie einen aggressiven Hund ansehen, und nach rechts, wenn es sich um einen zufriedenen Hund handelt. -In einer anderen Forschungsarbeit an der Universität von Victoria in Kanada stand Folgendes: „Hunde gehen eher auf einen Roboterhund zu, wenn sein ,Schwanz‘ links wedelt und nicht rechts, statt unruhig zu werden – genau andersherum als in der italienischen Studie.“ -Laut seiner Aussage könnten die Unterschiede daher rühren, dass die Hunde in den verschiedenen Studien die Tiere in den Filmen bzw. die Roboterhunde nicht als Hunde interpretierten. -Eine Studie, wie Hunde auf echte Hunde reagieren, könnte helfen, erklärte er. -„Zwar gibt es beträchtliche Belege für viele verschiedene Säugetiere, dass die beiden Gehirnhälften für verschiedene Zwecke verwendet werden, doch müssen viele der Details noch bestimmt werden – und Hunde bilden da keine Ausnahme“, so Bradshaw. -Da sich ihr Verhalten aber einfach aufzeichnen lasse, werde es vermutlich nicht lange dauern, bis wir verstehen, weshalb sich der Schwanz manchmal in die eine, manchmal in die andere Richtung bewegt. -Arctic Monkeys verschieben Auftritt in Glasgow wegen der Erkrankung Alex Turners -Die Rockband Arctic Monkeys hat einen Auftritt in Glasgow verschoben, nachdem ihr Leadsänger an Kehlkopfentzündung erkrankt ist. -Die Band aus Sheffield sollte am Freitag im Hydro spielen. -Wegen der Krankheit des Leadsängers Alex Turner muss das Konzert nun allerdings verlegt werden. -Die Ankündigung der Band kam, nachdem sie zuvor schon einen Auftritt in der LG Arena in Birmingham am Donnerstag verschieben musste. -In einer Erklärung auf ihrer offiziellen Website teilten die Arctic Monkeys mit: „Nach der Entscheidung, die Show in der LG Arena in Birmingham heute Abend abzusagen sowie auf ärztlichen Rat müssen die Arctic Monkeys auch die Show im Hydro in Glasgow am Freitag, dem 1. November absagen.“ -„Bei Alex Turner wurde eine Kehlkopfentzündung diagnostiziert, weshalb er leider nicht auftreten kann.“ -Die Show in der LG Arena in Birmingham findet nun am 20. November statt, die im Hydro in Glasgow wurde auf den 21. November verlegt. -Alle Tickets behalten für diese Shows Gültigkeit. -Wir entschuldigen uns bei allen Ticketinhabern für die hierdurch entstandenen Unannehmlichkeiten. -Bitte wenden Sie sich an den Kundenservice an den Verkaufsstellen, an denen Sie Ihr Ticket gekauft haben, falls Sie weitere Unterstützung benötigen. -Papst Franziskus ernennt im Februar erste Kardinäle -Papst Franziskus werde am 22. Februar erstmals neue Kardinäle der katholischen Kirche berufen, wie der Vatikan am Donnerstag bekannt gab. -Kardinäle sind die ranghöchsten Kleriker in der katholischen Kirche nach dem Papst und sie sind es auch, die einen Papst wählen, sodass Franziskus damit die erste Gruppe von Männern ernennen wird, die letztendlich bei der Wahl seines Nachfolgers mitwirken werden. -Derzeit gibt es 201 Kardinäle. -Jedoch kann ein Kardinal, der ein Alter von 80 Jahren erreicht hat, nicht mehr an der Papstwahl teilnehmen – das fällt einer Gruppe von 120 „wahlberechtigten Kardinälen“ zu. -In einer Erklärung, in der die Nachricht bekannt gegeben wurde, sagte Pater Federico Lombardi, ein Sprecher des Vatikans, es werde ein Treffen aller Kardinäle stattfinden, ehe die Zeremonie zur Ernennung der neuen Kardinäle abgehalten werde, die auch als Konsistorium bekannt sei. -„Papst Franziskus hat beschlossen, seine Entscheidung zur Einberufung des Konsistoriums im Februar im Voraus bekannt zu geben, damit andere Treffen, die die Teilnahme von Kardinälen aus aller Welt erfordern, geplant werden können“, sagte Lombardi. -Jack Valero von Catholic Voices sagte, dass die Zahl der wahlberechtigten Kardinäle bis Februar wahrscheinlich gesunken sein werde. -Er erklärte, ein Papst ernenne normalerweise so viele Kardinäle wie nötig, damit die Zahl der wahlberechtigten Kardinäle wieder 120 betrage, und so viele Kardinäle über 80, wie er möchte. -Das Konsistorium im nächsten Jahr sei deshalb bedeutsam, weil es das Erste seit der Wahl von Franziskus im März diesen Jahres sei, so Valero. -Derzeit gebe es einen gewissen Überhang aufseiten Europas und insbesondere Italiens. -„Es wird interessant sein zu sehen, ob der neue Papst Kardinäle aus dem Rest der Welt ernennen wird, um das Gleichgewicht wiederherzustellen“, sagte er. -Vierzig Prozent der Katholiken leben in Südamerika, doch sie haben nur eine winzige Anzahl von Kardinälen. -Die Kardinäle werden auch die Ersten sein, die seit der Bildung des von Franziskus gegründeten Rates der Kardinäle berufen werden, eine Gruppe von acht Kardinälen aus der ganzen Welt, die den Auftrag haben, Möglichkeiten zur Reformierung der Kirche zu finden. -In der Vergangenheit entschied der Papst alles allein. -„Nun hat Franziskus diese acht Kardinäle ausgewählt, die ihm helfen sollen“, sagte Valero. -Er erklärte, es sei „gut möglich“, dass Franziskus die Kardinäle um Rat bitte. -Doch bisher waren wir nicht in dieser Lage – das ist alles ganz neu. -Valero sagte, dass typischerweise die Bischöfe aus großen Orten zu Kardinälen berufen würden, doch stecke Franziskus „voller Überraschungen – wir wissen also nicht, wen er ernennt“. -GM ruft neue Pickup-Trucks in den USA zur Reparatur der Sitzlehne zurück -General Motors rufe knapp 19.000 seiner brandneuen Chevrolet Silverado- und GMC Sierra-Pickup-Trucks mit Baujahr 2014 zurück, um ein Problem mit der manuell verstellbaren Sitzlehne zu beheben, so eine Mitteilung der US-amerikanischen Regulierungsbehörde für Fahrzeugsicherheit am Freitag. -Bei einigen der Wagen kann es einen Defekt im Mechanismus für das Zurückstellen der Vordersitzlehne geben. -Aus diesem Grund erfüllen sie nicht die Bundesrichtlinien für die Fahrzeugsicherheit bei Kopfstützen. -„Wenn von hinten auf das Fahrzeug aufgefahren wird, ist es möglich, dass die Kopfstütze die Insassen nicht richtig schützt, sodass ein erhöhtes Verletzungsrisiko besteht“, war in der Nachricht auf der Website der National Highway Traffic Safety Administration (NHTSA) zu lesen. -Die zurückgerufenen Modelle wurden zwischen dem 1. August und 10. September hergestellt. -Die Lieferung der GM-Trucks begann im Juni und stellt den wichtigsten Modellstart des führenden US-amerikanischen Autoherstellers seit der insolvenzbedingten Umstrukturierung 2009 dar. -GM informierte die Fahrzeugbesitzer in der ersten Oktoberhälfte über den Defekt. -Die NHTSA konnte wegen des 16-tägigen Government Shutdowns das Benachrichtigungsschreiben nicht prüfen, was sich negativ auf die Autoverkäufe im Oktober auswirkte. -Der Verkauf der Silverado- und Sierra-Trucks, die für das Modelljahr 2014 neu entwickelt wurden, sei in den ersten zehn Monaten des Jahres um 20 Prozent gestiegen, erklärte GM am Freitag. -Im Oktober verkaufte GM 42.660 Silverado- und 16.503 Sierra-Pickup-Trucks. -Die GM-Aktie stieg an der New Yorker Börse um 1,4 Prozent auf 37,47 US-Dollar am Freitagnachmittag. -Verzweiflungsschrei eines Obama-Wählers -Ich habe Präsident Obama zweimal gewählt in der Hoffnung darauf, dass Veränderung möglich sei -Er sagt, Obama habe lobenswerte Bemühungen unternommen, die von der Blockadetaktik der Republikaner vereitelt worden seien -Die Blockadepolitik entschuldige nicht die Website-Probleme von Obamacare, die Drohnenangriffe -Obamas Memoiren über den Wahlkampf 2008 seien eine traurige Erinnerung an das, was möglich gewesen wäre -Nathaniel P. Morris ist Student im zweiten Jahr an der Harvard Medical School. -Ich lese derzeit ein furchtbar trauriges Buch. -Es ist ein Buch, von dem ich geglaubt habe, es würde mich während des trübsinnigen zweiten Jahres meines Medizinstudiums aufheitern und mir wieder Hoffnung geben. -Es heißt „The Audacity to Win“ (Mut zu gewinnen) und schildert die Memoiren Barack Obamas zur Präsidentschaftswahl 2008. -Wenn ich abends mit meinen Patientenniederschriften fertig bin und zu Bett gehe, dann führt mich das Buch zurück in eine Zeit, als die Politik Millionen inspirierte und einem Reden den Atem rauben konnten. -Die Wahl wurde zu einem Erdrutschsieg und Nachrichtensprecher hielten inne, um die historische Bedeutung dieser Stunde vor Augen zu führen. -Meine Klassenkameraden weinten vor Freude und meine Eltern sammelten alle Zeitungen, die sie finden konnten. -Ein junges Team von Visionären war auf dem Weg ins Weiße Haus und die Nation war bereit für Veränderung. -Als damals 2008 Obama ins Amt wechselte, hatte er Zustimmungswerte von 82 %. -Und dann schließe ich das Buch. -Wieder umzuschalten auf die Gegenwart ist ein böses Erwachen, als ob man aus einem Traum gerissen wird. -Es fällt schwer, sich an diese optimistischen Zeiten zu erinnern – sie scheinen eine vage Erinnerung zu sein, eine traurige Mahnung an verpasste Möglichkeiten. -In den Jahren, seit ich erstmals meine Stimme abgegeben habe, hat sich tatsächlich etwas verändert. -Aber es ist einfach nichts, was ich mir hätte vorstellen können. -Ich erkenne die großartigen und vielseitigen Dinge an, die Obama erreicht hat, von der Verabschiedung des Affordable Care Act für Krankenversicherung bis zum militärischen Abzug aus dem Irak, dem Ende des erzwungenen Verschweigens von Homosexualität in der Armee und der Tötung Osama bin Ladens. -Auch ich bin der Ansicht, dass die parteipolitische Blockadetaktik zu viele Bestrebungen gekippt habe, mit denen unsere Nation vorwärts gebracht werden sollte: Einwanderungsreform, eine öffentliche Option für Krankenversicherung und das Schließen von Guantanamo Bay, um nur einige zu nennen. -Doch nachdem ich zahllose Male die Obama-Regierung gegenüber Kollegen und Bekannten verteidigt habe, bin ich mit meinen Erklärungsversuchen am Ende angelangt. -Ich habe einen Punkt politischer Verzweiflung erreicht. -Die republikanische Blockadepolitik kann nicht erklären, weshalb man das Abhören ausländischer Staatsoberhäupter zulässt oder die Tötung unschuldiger Kinder durch Drohnen in Übersee. -Es kann nicht erklären, weshalb die National Security Agency Daten über das Privatleben von Amerikanern sammelt und warum Whistleblower bestraft werden, die staatliches Fehlverhalten offenlegen. -Es kann nicht erklären, weshalb Anwar al-Awlaki, ein US-amerikanischer Staatsbürger, ohne Verfahren ermordet wurde, noch weshalb die öffentlichen Finanzierungs- und Ausgabegrenzen während der Präsidentschaftswahlen umgangen werden. -Es rechtfertigt nicht die Ergebnisse eines Berichts, in dem es heißt, die Anstrengungen des Weißen Hauses zur Ruhigstellung der Medien seien die „aggressivsten ... seit der Nixon-Regierung“. -Und jüngstens kann es nicht die Unfähigkeit entschuldigen, in den mehr als drei Jahren seit der Verabschiedung des Affordable Care Act eine einfache Website zu entwerfen. -Ich weiß nicht, ob es das ist, was ich hätte erwarten sollen. -Ob ich mit 18 hätte verstehen sollen, dass die Regierungsarbeit den ihr vorangehenden politischen Kampagnen widersprechen könnte. -Offensichtlich ist das Abgeordnetenamt kein vorhersehbarer Kurs, da eine Oppositionspartei und zufällige Ereignisse wie das Newtown-Massaker die öffentliche Diskussion prägen. -Doch wenn ich mir die oben genannten Beispiele ansehe, dann scheinen sie weitgehend von der Regierung selbst gewählt zu sein. -Und das stört mich am meisten. -Ich habe 2012 wieder Obama gewählt, nicht weil ich über seine Kandidatur begeistert war. -Mitt Romney stellte eine verwirrende und unausgegorene Alternative dar, er schien seine Richtung und Positionen nicht festlegen zu können. -Ich hatte den Eindruck, eine zweite Amtszeit für Obama, frei vom Druck künftiger Wahlen, würde die Hoffnung erfüllen, von der wir schon so lange gehört hatten. -Doch mit dem Sinken der Zustimmungswerte für Obama auf unter 45 % diese Woche ist die Rückkehr ins Jahr 2008 durch dieses Buch um so härter geworden. -Es weckt in mir die Sehnsucht nach den vielen Versprechen, die sich in Luft aufgelöst haben. -Diese Woche las ich einen Abschnitt in dem Buch, der beschreibt, wie Obama einen schweren Verlust gegen Clinton bei den Vorwahlen in Pennsylvania einstecken musste. -In einem Treffen mit den Wahlhelfern nach der Entscheidung sagte er seinen Mitarbeitern, sie müssten zurück auf Kurs kommen und dem Zweck ihrer Sache folgen. -„Ich möchte, dass wir unseren Zauber wiederfinden“, sagte er. -Wir müssen uns daran erinnern, wer wir sind. -Es sind fünf Jahre später, Mr. President, und ich könnte Ihnen nicht stärker zustimmen. -Die in diesem Kommentar ausgedrückten Ansichten sind ausschließlich die von Nathaniel Morris. -Clive Palmer behauptet, Premierminister Tony Abbott habe Interessenkonflikt beim Elternurlaub -Der Milliardär und Abgeordnete Clive Palmer sagt, Premierminister Tony Abbott stünde in einem Interessenkonflikt bei seinen Plänen zum Elternurlaub, weil seine Töchter schwanger werden und deshalb davon profitieren könnten. -Der Bergbaumagnat, der sich in einer gerichtlichen Auseinandersetzung über die Zahlung von 6 Millionen Dollar an CO2-Steuer befindet, stellte die Behauptung auf, als er Fragen vom Tisch wischen wollte, ob er sich in einem Konflikt befände. -Die Palmer United Party könnte bis zu vier Stimmen im Senat kontrollieren, die wichtig für die Entscheidung sind, ob die CO2- und Bergbausteuern gestrichen oder gekürzt werden. -Doch Palmer behauptete, dass nur Minister Interessenkonflikte haben könnten und die Töchter von Abbott persönlich von den Gesetzen profitieren würden. -„Er befindet sich in einem großen Interessenkonflikt, wenn es um bezahlten Elternurlaub geht, denn wenn eine seiner Töchter schwanger wird, hat er ein direktes Interesse daran, ob sie Urlaub erhält oder nicht“, sagte Palmer. -Zwei Monate nach der Wahl erklärte die Wahlkommission Palmer mit 53 Stimmen Vorsprung nach der Neuauszählung offiziell zum Gewinner des Sunshine-Coast-Sitzes von Fairfax. -Palmer forderte eine Überarbeitung des Prozesses zur Stimmauszählung, damit dieser schneller vonstatten gehe. -Tony Abbotts Töchter Frances und Bridget. -Sollten diese Wahlen zwei Monate nach dem Ende der Stimmabgabe entschieden werden? -„Wir brauchen ein besseres System“, sagte er. -Weshalb sollten wir kein System haben, bei dem man hereinmarschiert, die Daten in einen Computer eingibt, sofort wählt und dann um 6:30 Uhr abends das Ergebnis hat? -Palmer kritisierte auch die Verwendung von Bleistiften beim Markieren der Stimmzettel. -Verwendet man sie, weil man dann die Ergebnisse ausradieren kann, wenn sie einem nicht gefallen? -Heutzutage mit Bleistiften abzustimmen ist unglaublich. -Die Wahlkommission hat die Optionen für eine elektronische Wahl geprüft und kürzlich einen gemeinsamen Gesetzesvorschlag mit Neuseeland veröffentlicht. -Palmer, 59, erklärte, dass zu seiner Politik ein internationaler Flughafen für die Sunshine Coast gehöre und dass er sein neues Amt „sehr ernst“ nehmen werde. -Bei einem öffentlichen Amt gehe es um den Dienst an der Öffentlichkeit. -„Wir streben nach keiner Belohnung außer der der Geschichte, nämlich, dass wir in einer kritischen Zeit dieser Gemeinschaft dienen können“, sagte er. -RBS suspendiert zwei Devisenhändler -Die Royal Bank of Scotland hat zwei Händler aus ihrer Devisenabteilung suspendiert, so zwei Personen, die mit der Situation vertraut sind; dies ist ein weiteres Indiz dafür, dass die weltweite behördliche Ermittlung zu den mutmaßlichen Manipulationen am Währungsmarkt rasant an Fahrt aufnimmt. -Einige der größten Banken der Welt, einschließlich UBS, Barclays, Deutsche Bank und RBS, bestätigten, dass sie mit den Behörden bei den Ermittlungen im weltweit größten Finanzmarkt zusammenarbeiten, auf dem täglich 5,3 Billiarden US-Dollar den Besitzer wechseln. -Die beiden Händler wären die ersten RBS-Mitarbeiter, die in der sich ausweitenden Untersuchung suspendiert wurden, die der Skandal um das Libor-Interbankengeschäft nach sich zieht. -Die Bank, die keinen Kommentar zu den Suspendierungen abgab, bestätigte diesen Monat, dass sie Informationsanfragen von Behörden erhalten habe. -„Unsere Ermittlungen zu diesem Thema gehen weiter und wir kooperieren voll mit der FCA und anderen Regulierungsbehörden“, sagte die Bank vor zwei Wochen. -Letzten Monat gaben der Situation nahestehende Personen an, dass die RBS die Aufzeichnungen von E-Mails und Instant Messages, die von und an einen früheren Händler gesandt wurden, an die britische Aufsichtsbehörde, die Financial Conduct Authority, weitergeleitet habe. -Dieser Händler, Richard Usher, hatte die RBS 2010 verlassen und soll inzwischen von seiner derzeitigen Position als europäischer Leiter des Forex-Spot-Handels bei JPMorgan beurlaubt worden sein. -Rohan Ramchandani, Leiter des europäischen Spot Trading bei Citi, wurde diese Woche beurlaubt und Matt Gardiner, früher leitender Währungshändler bei Barclays und UBS, wurde diese Woche bei Standard Chartered suspendiert. -Keinem dieser Händler wurde irgendein Fehlverhalten vorgeworfen. -Zur Instant Message-Gruppe von Usher gehörten Banker bei Barclays und der Citigroup, sagten der Situation nahestehende Personen. -UBS gab diese Woche bekannt, dass sie Schritte gegen einige ihrer Mitarbeiter unternommen habe, nachdem die Schweizer Regulierungsbehörde Finma erklärt hatte, dass sie eine mutmaßliche Manipulation des Devisenmarkts bei einer Reihe schweizerischer Banken untersuche. -Mindestens sechs Behörden weltweit – die Europäische Kommission, Finma, die Schweizer Wettbewerbsbehörde Weko, die FCA, das US-amerikanische Justizministerium und die Hong Kong Monetary Authority – beschäftigen sich derzeit mit Vorwürfen, wonach Banker sich bei Transaktionen am Währungsmarkt abgesprochen haben sollen. -HSBC, Citigroup, JPMorgan und Credit Suisse haben außerdem interne Untersuchungen eingeleitet oder Informationsanfragen von Behörden erhalten, sagten mit der Situation vertraute Personen. -Banken durchkämmen Instant Messages und E-Mails aus mehreren Jahren auf der Suche nach Fällen von Fehlverhalten. -Die Nachrichten über diese Untersuchungen hat die Händler in einem Bereich in Aufruhr versetzt, der in den letzten Jahren zu einem der profitabelsten der Trading-Einheiten von Investmentbanken gehört hat, aber dieses Jahr unter Druck stand, weil niedrige Volatilität bei Währungen die Chancen für Spekulanten einschränkt. -Einige Banker haben versucht, die Affäre herunterzuspielen und sagten, dass es nahezu unmöglich sei, den ausgedehnten und hochgradig liquiden Devisenmarkt zu manipulieren, doch führende Händler sind der Ansicht, dass dies nicht unbedingt wahr sei. -Ein führender Händler bemerkte, dass trotz des enormen täglichen Volumens im Devisenhandel die Fragmentierung der Liquidität zwischen den verschiedenen Handelsplattformen und der zunehmende Einsatz eigener interner Plattformen durch Banken dazu führe, dass „man schon mit sehr geringen Ticket-Preisen den Markt beeinflussen kann“. -Die Nachricht kam am selben Tag heraus, als Credit Suisse bekannt gab, sie habe diese Woche einem Händler ihres London Exchange Traded Funds-Büros gekündigt, nachdem er Ende letzten Jahres einen Verlust von knapp sechs Millionen Dollar verursacht hatte. -Die Bank informierte prompt die zuständigen Behörden und arbeitet mit ihnen zusammen. -„Wir sind sicher, dass der Händler allein gehandelt hat und dass die Angelegenheit eingedämmt ist“, erklärte Credit Suisse. -Den Bauzaun auf dem Bechtle-Grundstück bemalt -Eine Gruppe der Bürgerinitiative "Schweizer Wiese" hat den Bauzaun auf dem Bechtle-Grundstück an der Kurpromenade in Bad Herrenalb verschönert. -Außerdem säuberte und verbreiterte sie den Fußweg, heißt es in einer Pressemitteilung. -Das geplante Abdecken des zerfallenden Hauses im hinteren Teil des Grundstücks war aus Sicherheitsgründen nicht erlaubt worden, denn das Gelände droht abzurutschen. -Alfred Abel, der das Grundstück derzeit verwaltet, hatte die Verschönerungsaktion mit seinem Kollegen Reinhard Domke von der BI abgesprochen. -Vor der Aktion der BI war ein Transparent mit der Botschaft "Wir sind dafür" angebracht worden. -Das störte die etwa 20 Mitglieder der Bürgerinitiative jedoch wenig bei ihrer Aktion. -Ihr Motto auf dem großen Plakat hieß "Wir sind gegen Bauruinen". -Damit wollte sie auf die Gefahr von noch größeren Ruinen auf der Schweizer Wiese hinweisen - sollte das Riesenprojekt eines Tages scheitern. -Im Gespräch mit vielen Passanten vor der "Baustelle" wurde deutlich, dass manche Bürger glauben, dass es mit der Verwirklichung des Projektes der Stadt vielen besser gehen werde, obwohl nicht wenige die gleichen Fragen an die Dimension des vorgestellten Projekts haben wie die BI. -Aber sie fangen an zu resignieren, weil die Stadt diese Antworten weiter hinauszögert, heißt es vonseiten der BI. -Wie zu hören war, wirken sich auch die öffentlichen Äußerungen des Bürgermeisters aus. -Mai hatte in der Gemeinderatssitzung gesagt, dass er das Aus für das Thermalbad befürchtet, sollte die Bevölkerung beim Bürgerentscheid gegen die Pläne für einen Bade- und Wellnesskomplex auf der Schweizer Wiese stimmen. -Manche erscheinen deswegen geradezu verängstigt, andere empfinden sie als "plumpen Erpressungsversuch" an der Bevölkerung, heißt es in der Pressemitteilung der BI weiter. -Die BI dankte den Spendern für die Farbe und allen Helfern. -Rangnick-Schelte für Schiris: Mane kein Schwalbenkönig -So zufrieden Ralf Rangnick mit der aktuellen Hochform von Bundesliga-Tabellenführer Red Bull Salzburg auch ist, so enttäuscht zeigte sich der Sportdirektor der "Bullen" von den Schiedsrichtern. -"Es gibt derzeit eine gefährliche Tendenz, dass Mane und Alan in die Kategorie Schwalbenkönige gedrängt werden", nahm er die beiden Offensivwirbler am Donnerstag auf einer Pressekonferenz in Salzburg in Schutz. -Jüngster Anlass für Rangnicks Kritik war Gelb für Mane beim 3:0 gegen Grödig am vergangenen Sonntag, bei dem dem Senegalesen eine angebliche Schauspielerei zum Verhängnis wurde - die freilich keine war, wie die TV-Bilder zeigten. -"Eine krasse Fehlentscheidung", meinte Rangnick, der Schiedsrichter Harkam als "völlig überfordert" bezeichnete. -Harkam hätte zudem "seinen Teil" zur Eskalation zwischen Salzburg-Coach Roger Schmidt und Grödig-Trainer Adi Hütter nach der Partie beigetragen. -Seinen deutschen Landsmann verteidigte er dezidiert: "Da hätte ich mich in jeder einzelnen Situation gleich verhalten wie Roger Schmidt". -Schmidt selbst will nach dem Scharmützel mit Hütter keinen Kontakt mit seinem Kollegen gehabt haben. -Ich wüsste nicht, wieso. -"Denn ich bin derjenige, der beleidigt wurde", betonte der 46-Jährige. -Angesichts des Erfolgslaufs stimmte Rangnick aber eine Lobeshymne an. -"Dafür gibt es nur zwei Worte: Richtig gut", konstatierte er. -Es gibt nicht viel auszusetzen. -Vor allem die Art und Weise, wie die Mannschaft spielt, ist beeindruckend. -Das soll ruhig so weitergehen. -Kaderänderungen in der Winter-Transferzeit werde es kaum geben. -Es gibt keine großartigen Gründe, etwas zu verändern. -"Außer ein Spieler tritt mit dem Wunsch heran, dass er den Verein verlassen will", stellte Rangnick klar. -Supreme Court bestätigt Obamas Gesundheitsgesetz -Es ist ein großer Sieg für die Regierung Obama: Der US-amerikanische Supreme Court entschied heute, dass das eng mit Barack Obama verknüpfte Gesundheitsgesetz verfassungsmäßig ist. -Mit fünf zu vier Stimmen urteilten die Richter, dass das Einzelmandat des Patient Protection and Affordable Care Act – es zwingt US-amerikanische Bürger, bis 2014 eine Krankenversicherung abzuschließen oder eine Strafe zu zahlen – im Rahmen der Steuerautorität des Staates verfassungsmäßig ist. -Der oberste Richter John Roberts schlug sich auf die Seite der vier liberaleren Mitglieder der Kammer, während die Richter Scalia, Thomas, Alito und Kennedy dagegen entschieden. -Das Gericht bestätigte auch die restlichen Abschnitte des 2700 Seiten umfassenden Gesetzes und vertrat außerdem die Ansicht, die Vorgabe des Gesundheitsgesetzes, wonach Bundesstaaten entweder die Zugangsvoraussetzungen für Medicaid erweitern müssten oder ansonsten sämtliche staatlichen Medicaid-Förderungen gestrichen würden, sei nicht verfassungswidrig einschränkend. -Das Klageverfahren gegen das Gesetz war von 26 Bundesstaaten und der National Federation of Independent Business angestrengt worden. -Das Gesetz war von allen führenden republikanischen Präsidentschaftskandidaten, einschließlich des letztendlichen Herausforderers Mitt Romney, im Vorwahlkampf 2012 entschieden abgelehnt worden. -Norwegen: Norwegischer Ort macht sich mit Riesenspiegeln Licht -Mit riesigen Spiegeln haben die Einwohner eines norwegischen Örtchens Licht in ihr düsteres Tal gebracht. -Wegen des niedrigen Sonneneinfallgrades versinkt das im Vestfjord-Tal gelegene Rjukan von Herbst bis Frühling normalerweise im Schatten der umliegenden Berge. -Mit drei gigantischen Reflektoren in 45 Metern Höhe wurde am Mittwoch ein Jahrhunderttraum wahr. -"Endlich!", schwärmte Bürgermeister Steinar Bergsland bei der Startzeremonie im Sender TV2. -Einige Talbewohner rückten sich Sonnenstühle zurecht, andere setzten sicherheitshalber Sonnenbrillen auf. -Bisher mussten Sonnenhungrige im Winter mit einer Seilbahn auf einen nahen Gipfel fahren. -Vor zehn Jahren hatte der lokale Künstler Martin Andersen den Vorschlag präsentiert, die Strahlen mit Spiegeln ins Tal zu lenken. -Diese Grundidee gab es aber schon seit 1913 im Ort. -Nach mehrjähriger Debatte hatte der Stadtrat schließlich das 5 Millionen Kronen (rund 615 000 Euro) teure Projekt verabschiedet. -Eine ähnliche Konstruktion verschafft seit einigen Jahren dem italienischen Alpenort Viganella winterlichen Sonnenschein. -Post leert kaum noch sonntags -"Viele Bürger werden es noch nicht gemerkt haben, die roten Punkte für die Sonntagsleerung befinden sich noch an verschiedenen Briefkästen der Kernstadt und den Stadtteilen, aber liest man das Kleingedruckte an den gelben Behältern der Post AG, findet man schnell heraus, dass die Leerung an Sonn- und Feiertagen nicht mehr stattfindet", teilt die FDP mit. -Eine Anfrage der FDP habe das bestätigt. -An Sonn- und Feiertagen werde nur noch vor dem Postgebäude in der Ernst-Ludwig-Straße 36 und in der Jakob-Müller-Straße 1 in Hüttenfeld geleert. -Ob in Hofheim und Rosengarten auch noch Sonntag geleert wird, werde in der Antwort auf die Anfrage der FDP nicht beantwortet. -Der sozial- und jugendpolitische Sprecher der FDP, Fritz Röhrenbeck, will hier nachhaken. -Die Sonntagsleerung ist gerade bei Terminpost ein wichtiger Postservice. -"Diesen dünnt diese Unternehmung trotz der Preiserhöhung für Briefe am Januar 2013 radikal und unzumutbar aus". -Thomas Bittner, Fraktions- und Stadtverbandsvorsitzender der Liberalen, unterstützt seinen Fraktionskollegen: "Was mich ärgert, ist die Tatsache, dass hier klammheimlich die Leerungszeiten geändert oder auch gestrichen werden". -Von Bürgernähe ist die Post dabei meilenweit entfernt. -"Mindestens müssen die Briefkästen in den Stadtteilen Neuschloß und Rosengarten wieder sonntags geleert werden", so Bittner und Röhrenbeck unisono. -Auch für die Kernstadt gelte, besonders nicht mobile Mitbürger müssten einen Briefkasten mit Sonntagsleerung auch fußläufig erreichen können. -Röhrenbeck fragte im Stadtparlament, ob die Stadtverwaltung Gespräche mit der Post über diese Sache plant. -Bürgermeister Erich Maier antwortete, die Post habe nicht mit Lampertheim kommuniziert. -Seines Wissens gebe es aber in Hofheim auch eine Sonntagsleerung. -Ihre Partei hat sich ja für Privatisierungen ausgesprochen. -"Dann werden Entscheidungen an anderer Stelle getroffen", sagte Maier. -Frankfurter Parkgebühren sollen kräftig steigen -Parken in Frankfurt könnte bald empfindlich teurer werden. -Der Magistrat der Stadt berät heute Vormittag über eine Vorlage von Verkehrsdezernent Stefan Majer (Grüne). -Demnach sollen die Parkgebühren um 50 Prozent erhöht werden. -Dafür sollen aber nicht die Preise angehoben, sondern das Zeitintervall von Parkuhren und Parkscheinautomaten von 30 auf 20 Minuten verkürzt werden. -Im Dezember muss dann noch die Stadtverordnetenversammlung entscheiden. -Der Einzelhandelsausschuss der IHK Frankfurt hält das "für keine gute Idee". -Die Parkmöglichkeit unmittelbar vor dem Geschäft sei "ein nicht zu unterschätzender Vorteil für kleinere Fachhändler". -Der Verkehrsclub Deutschland ist der Meinung, dass eine Erhöhung nach zwei Jahrzehnten "durchaus angemessen und eigentlich längst überfällig" ist. -Der öffentliche Nahverkehr werde auch teurer. -Freudenstadt: Schnelle Aktionen überrumpeln Gastgeber -Mit einem schmalen Kader mussten die Herren I des TSV Freudenstadt in der Schiller-Schulturnhalle in Reutlingen gegen die TG Gönningen von der Schwäbischen Alb antreten. -Die Älbler standen zwar mit einer erfahrenen Mannschaft auf der anderen Netzseite. -Bald zeigte sich aber, dass sie, auch altersbedingt, mit den schnellen Aktionen der Freudenstädter nicht ganz mithalten konnten . -Mit den harten und platzierten Aufschlägen taten sich die Gönninger äußerst schwer. -Wiederholt verpassten sie die von Simon Schenk und Eduard Schulz sauber über die Netzmitte zugespielten Pässe und Angriffe und waren bei der Blockabwehr nicht zur Stelle. -Zwei hohe erste Satzgewinne drückten deutlich die Überlegenheit der Freudenstädter in allen Belangen aus. -Angesichts dieser erdrückenden und spielbestimmenden Leistung wurde dann der dritte Satz etwas lasch und lustlos zum 3:0-Endstand (25:13, 25:14, 25:22) gewonnen. -Da wird am 2. November mit dem TV Baiersbronn, der sich in Nagold klar durchsetzen konnte, ein wesentlich leistungsstärkerer Gegner auf dem Feld stehen. -TSV Freudenstadt I: Dominik Bäuerle, Yannik Büchle, Sebastian Dölker, Felix Pälchen, Stefan Räller, Simon Schenk und Eduard Schulz. -In der B-Klasse standen sich in der Murgtalhalle in Baiersbronn die zweiten Mannschaften aus Baiersbronn und Freudenstadt gegenüber. -Sie lieferten ein hart umkämpftes Zwei-Stunden-Match ab. -Nervosität auf beiden Seiten verursachte vor allem bei der Ballannahme Fehler und Punktegeschenke; auch bei den Pässen war die Präzision zuweilen Mangelware und in einigen kritischen Spielszenen verhinderte der Übereifer den Blick für mögliche Punktgewinne. -In dieser leistungsmäßig sehr ausgeglichenen Begegnung stimmte im TSV-Team von Marcus Blasutto aber durchweg der große körperliche Einsatz und ein erfreuliches Engagement beider Teams. -Pflegetag an der kleinen Engländerhütte -Zum mittlerweile fünften Pflegetag in der Nachfolge des Natura 2000-Projektes oberer Hotzenwald traf sich eine Gruppe engagierter Teilnehmer bei der kleinen Engländerhütte in Ibach. -Aufgrund von Anregungen aus der Bevölkerung sollte an diesem Tag im Bereich der Hütte der Blick ins Tal freigeschnitten werden, sagte Bürgermeister Helmut Kaiser. -Viele Wanderer bezeichneten, so Kaiser, diesen Bereich als das Glanzstück des Schluchtensteigs, auf dem die Landschaft den idealen Rahmen für herrliche Ausblicke biete. -Er dankte den Anwesenden für ihre Bereitschaft zur Mitarbeit und betonte, der Pflegetag sei ein geeignetes Instrument, um die Verbindung von Mensch und Natur zu stärken. -Er biete jedem Bürger die Möglichkeit, in der Natur selbst Akzente zu setzen. -Zugleich mit dem touristischen Aspekt könne auch der ökologische Berücksichtigung finden. -Diesen ökologischen Aspekt erläuterte Friederike Tribukait von der Abteilung Naturschutz des Regierungspräsidiums Freiburg im Anschluss an die Begrüßung durch Landrat Tilman Bollacher, der die Schirmherrschaft übernommen hatte und der Aktion ein gutes Gelingen wünschte. -Das touristische Ziel des Weitblicks deckt sich laut Tribukait mit dem Ziel, das inzwischen stark zugewachsene alte Allmendweidfeld mit seinen spezifischen geschützten Lebensraumtypen zu erhalten durch Öffnung und Verbindung zu den noch bestehenden Weiden hin. -Bestimmte Arten wie etwa seltene Schmetterlinge und Grashüpfer können Barrieren in Form von Waldflächen nicht überwinden. -Sie benötigen diese freien Verbindungen zur Arterhaltung. -Tribukait sagte, sie wolle es an dieser Stelle nicht versäumen, eine Lanze zu brechen für das geplante Biosphärengebiet. -In diese Planung füge sich die aktuelle Maßnahme nicht nur nahtlos ein. -Ein Biosphärengebiet biete auch die Chance zu nachhaltiger ökonomischer wie ökologischer Entwicklung auf der Basis des international anerkannten Unesco-Status und somit eine kontinuierliche Ergänzung des mit dem Life-Projekt sowie den bisherigen Pflegetagen Erreichten für die Zukunft. -Nach diesen verschiedenen Ausführungen teilten Revierleiter Christoph Wehle und Life-Projektmanagerin Cornelia Bischoff die Helfer in Gruppen ein. -Die größeren Bäume waren im Vorfeld bereits geschlagen worden, der Forstschlepper stehe gegebenenfalls abrufbereit. -Eine Gruppe von Helfern sollte sich um die Freistellung von Wacholderbüschen und um die Pflege eines Trockensteinriegels kümmern. -Ziel war die freie Sicht am Aussichtspunkt. -Löffingen: Köpfler immer noch einziger Kandidat -Informationen im Internet: Obwohl das Baarstädtchen Löffingen mit seinen 7514 Einwohnern, einer intakten Infrastruktur, einer zukunftsweisenden Schul-, Kinder-, Jugend- und Seniorenpolitik, sowie eigenen Stadtwerken als attraktive Stadt bezeichnet werden kann, hat sich bisher noch kein zweiter Bürgermeisterkandidat gemeldet. -Als einziger Kandidat um die Nachfolge des aus Altersgründen nicht mehr kandidierenden Bürgermeisters Norbert Brugger steht bisher der 46-jährige Bankkaufmann und Betriebswirt Dieter Köpfler aus Löffingen fest. -Die Bewerbungsfrist läuft am Montag, 11. November, um 18 Uhr ab. -Google, Samsung, Huawei wegen Nortel-Patenten verklagt -Die Gruppe, die Tausende früherer Nortel-Patente besitzt, strengte am Donnerstag eine Lawine von Klagen wegen Patentverletzung gegen Handyhersteller an, darunter auch Google, das Unternehmen, das bei der Insolvenzversteigerung von Nortel von ihm überboten wurde. -Rockstar, das Konsortium, das die Nortel-Patente für 4,5 Milliarden US-Dollar gekauft hatte, verklagte Samsung Electronics Co Ltd, HTC Corp, Huawei und vier andere Unternehmen wegen Patentverletzungen vor dem US-Bezirksgericht in Texas. -Rockstar befindet sich im Gemeinschaftsbesitz von Apple, Microsoft, BlackBerry, Ericsson und Sony. -Google wird in sieben Fällen der Patentverletzung bezichtigt. -Die Patente beträfen Technologie, die Suchbegriffe im Internet mit relevanter Werbung in Übereinstimmung bringe, so die Klage, die damit den Kern von Googles Suchmaschinengeschäft trifft. -Vertreter von Samsung, Huawei, HTC und Rockstar waren nicht unmittelbar erreichbar. -Samsung, Huawei und HTC stellen Handys her, die mit Googles Betriebssystem Android arbeiten, das in scharfem Wettbewerb zu den Mobilprodukten von Apple und Microsoft steht. -Im Jahr 2011 gab Google ein anfängliches Gebot über 900 Millionen Dollar für die Nortel-Patente ab. -Google steigerte sein Angebot mehrmals und bot zuletzt 4,4 Milliarden US-Dollar. -Nachdem es bei den Nortel-Patenten von Rockstar überboten wurde, erwarb Google Motorola Mobility für 12,5 Milliarden US-Dollar, ein Geschäft, das auch zum Teil von Motorolas Bibliothek an Patenten motiviert war. -„Obwohl Google mit seinem Versuch gescheitert ist, die Klagepatente bei der Auktion zu erwerben, hat Google die Patente verletzt und tut dies auch weiterhin“, so das Klageschreiben. -Rockstar fordert einen hohen Schadensersatz von Google, da es behauptet, Googles Patentverletzung sei absichtlich, so die Anschuldigung. -Ägypten vereidigt ersten frei gewählten Präsidenten -Mohammed Mursi legt den Amtseid ab, doch sein Tag des Triumphs dürfte nicht das Ende der politischen Streitigkeiten in Ägypten bedeuten. -Der Islamist Mohammed Mursi versprach ein „neues Ägypten“, als er als erster frei gewählter Präsident des Landes vereidigt wurde und damit die Nachfolge von Husni Mubarak antrat, der 16 Monate zuvor aus dem Amt vertrieben worden war. -Mit seiner Amtseinführung vor dem obersten Verfassungsgericht wurde Mursi auch zum ersten frei gewählten islamistischen Präsidenten der arabischen Welt und Ägyptens fünftes Staatsoberhaupt seit dem Sturz der Monarchie vor ungefähr 60 Jahren. -Er legte den Eid vor den 18 in schwarze Roben gekleideten Richtern des Gerichts ab, das seinen Sitz am Nil hat und einem antiken ägyptischen Tempel nachempfunden ist. -„Wir streben nach einer besseren Zukunft, einem neuen Ägypten und einer zweiten Republik“, sagte Mursi bei der feierlichen Zeremonie, die live im staatlichen Fernsehen übertragen wurde. -„Heute hat das ägyptische Volk das Fundament für ein neues Leben gelegt – absolute Freiheit, eine echte Demokratie und Stabilität“, sagte der 60-jährige Mursi, ein in den USA ausgebildeter Ingenieur und Mitglied der Muslimbruderschaft, einer fundamentalistischen Gruppe, die seit ihrer Gründung vor 84 Jahren meist als verbotene Organisation von der jeweiligen Regierung scharf ins Visier genommen worden war. -Hunderte Soldaten und Polizisten bewachten das Gebäude, als Mursi kurz nach elf Uhr Ortszeit in einem kleinen Autokonvoi eintraf. -Nur ein paar Hundert Anhänger hatten sich vor dem Gericht versammelt, um dem neuen Präsidenten zuzujubeln, und im Gegensatz zum präsidialen Prunk aus der Mubarak-Zeit wurde der Verkehr nur kurzzeitig angehalten, damit der Autokonvoi die üblicherweise belebte Straße überqueren konnte, die das Stadtzentrum mit den südlichen Vorstädten verbindet. -Als uncharismatisches „Ersatzrad“ der Bruderschaft verspottet hat sein persönliches Prestige seit seinem Sieg und einer Freitagsrede zugenommen, bei der er sich nicht nur als Kandidat der Islamisten, sondern all derer präsentierte, die die Bestrebungen des Aufstands aus dem Jahr 2011 gegen den autoritären Mubarak ans Ziel bringen wollten. -„Ägypten ist heute ein ziviler, nationaler, verfassungsmäßiger und moderner Staat“, erklärte der in einen blauen Anzug mit roter Krawatte gekleidete Mursi den Richtern in der holzgetäfelten Kammer, in der er den Amtseid ablegte. -Später fuhr Mursi zur Universität von Kairo, wo er seine Antrittsrede halten sollte. -Er wurde offiziell von einer Armeekapelle begrüßt, die die Nationalhymne spielte, der er in Habachtstellung zuhörte. -Der Militärführer Feldmarschall Hussein Tantawi war anwesend. -Bei seiner Ankunft wurde er von Hunderten Anwesenden im größten Hörsaal der Universität mit Sprechchören von „Die Armee und das Volk sind eine Hand“ begrüßt. -Die im Jahr 1908 als Bastion säkularer Bildung errichtete Universität von Kairo hat sich in den 1970er Jahren zu einer Hochburg islamistischer Studentengruppen entwickelt. -Mursi legte am Freitag einen symbolischen Eid auf dem Tahir-Platz ab, der Geburtsstätte des Aufstands, der letztes Jahr zum Ende der autoritären Herrschaft Mubaraks führte, und er gelobte, präsidiale Machtbefugnisse zurückzufordern, die der Militärrat vom aus dem Amt gejagten Staatschef übernommen hatte. -Doch mit dem Ablegen des offiziellen Eides vor dem Gericht und nicht wie üblich vor dem Parlament beugt er sich dem Willen des Militärs, was darauf hinweist, dass der Machtkampf weitergehen wird. -Mursis Rede auf dem Tahir-Platz war voller dramatischer populistischer Gesten. -Falkenberger Discothek sorgt für zwei tollen Abende -Die neue Saison in der Falkenberger Discothek "Blue Velvet" hat begonnen. -Am heutigen Freitagabend heißt es "Pump this party", am morgigen Samstagabend geht es mit einer Ü 25-Party weiter. -Fette Beats und coole Sounds werden von den Veranstaltern natürlich an beiden Tagen versprochen. -Arbeiter stürzte von Leiter: schwer verletzt -Beim Auswechseln eines defekten Außenfühlers stürzte am Donnerstag ein 51-jähriger Arbeiter in Eggelsberg von einer Leiter und verletzte sich schwer. -Der Mann aus Lamprechtshausen wollte an der Außenfassade eines Gasthauses einen defekten Heizungsfühler auswechseln. -Als er auf die an die Dachrinne angelehnte Leiter stieg, rutschte diese weg und der Mann stürzte auf den Betonboden. -Er erlitt schwere Verletzungen und wurde mit dem Rettungshubschrauber "Christophorus 6" in das Landeskrankenhaus Salzburg gebracht. -Vergewaltiger in Südafrika: zweimal lebenslange Haft -Neun Monate nach der brutalen Vergewaltigung und Verstümmelung eines 17-jährigen Mädchens in Südafrika ist der Täter am Freitag zu zweimal lebenslänglich verurteilt worden. -Dies berichtete der Sender SABC. -Das Opfer war später an den Folgen der schweren Verletzungen gestorben. -Johannes Kana war bereits Anfang der Woche des Verbrechens für schuldig befunden worden. -Die Tat hatte landesweit Entsetzen ausgelöst. -Der damals 21-Jährige hatte Anene Booysen im Februar in einem Industriegelände in Bredasdorp nahe Kapstadt vergewaltigt. -14 Stunden kämpften die Ärzte um das Überleben des Opfers, jedoch vergeblich. -Der Anblick der Jugendlichen war so entsetzlich, dass die Pfleger und Schwestern psychologischen Beistand brauchten. -Kana hatte während des Prozesses zugegeben, in der Tatnacht mit Booysen in einer Bar gewesen zu sein. -Er gestand auch, sie geschlagen und vergewaltigt zu haben, jedoch bestritt er, für den Tod des Mädchens verantwortlich zu sein. -Die Polizei war zunächst von mehreren Tätern ausgegangen, entließ jedoch zwei Verdächtige aus der Haft, weil die Beweise gegen sie nicht ausreichten. -Auch die Staatsanwaltschaft betonte während des Verfahrens, sie sei überzeugt, dass Kana zum Zeitpunkt der Attacke allein war. -Vergewaltigungen gehören in Südafrika schon fast zum Alltag: Jährlich gibt es etwa 64 000 Anzeigen wegen sexueller Gewalt. -Das sind fast zehnmal so viel wie in Deutschland, das aber mit 82 Millionen Menschen deutlich mehr Einwohner hat als Südafrika (50 Millionen). -Polizei und Frauenverbände in Südafrika schätzen die Dunkelziffer bei sexueller Gewalt aber auf das 10- bis 25-fache. -Jumbo besucht den Schwarzwälder Schinken -Jumbo auf der Suche nach dem "besten Schinken der Welt": Für die TV-Sendung "Galileo" besuchte der Pro7-Star Bonndorf. -Der Sprecher stellt gleich zu Beginn klar: Das wichtigste für den Schinken sind die Schweinekeulen, Turbomast ist dabei keine gute Voraussetzung. -Kann Schinken, den man in jedem Supermarkt bekommt, also ein Qualitätsprodukt sein? -Uli als Chef der großen Schinkenfabrik im Schwarzwald erklärt Jumbo die Arbeitsweise der Firma "Adler" in Bonndorf. -"Deutschlands liebster Schinken", wie Jumbo den Schwarzwälder Schinken ankündigt. -Nur mit Haar-Schutz und Kittel kommt er ins Innere der Fabrik. -Auch Jumbo muss sich erstmal die Hände desinfizieren, bevor er zum Mikro greifen darf. -Jumbo zur Stärkung bei Schnellimbiss in Waldshut gesichtet -"Hier wird mir rießigen Mengen gearbeitet", staunen die Fernseh-Macher, Schwarzwälder Schinken als Massenprodukt. -16.000 Keulen pro Woche sprechen für sich. -Gewicht, Fettanteil, Farbe: Uli erklärt worauf es bei der Qualität ankommt. -Die meisten Schweine werden mit sechs Monaten geschlachtet. -Fernsehen mit Niveau? -Auch Einzelheiten wie der Ph-Wert des Fleisches spielen eine Rolle. -Je dunkler das Fleisch, desto höher der ph-Wert. -"Da bin ich gespannt, wenn ihr Roberto Blanco messt, was dann passiert", so Jumbo zynisch. -Hier wird das Niveau des Privatfernsehens in Reinkultur präsentiert. -Aber das Zuschauen lohnt sich trotzdem. -Der Zuschauer sieht, dass das Zerlegen der Keulen weiterhin Handarbeit ist. -Denn: Schwarzwälder Schinken ist traditionell ohne Knochen im Regal zu finden. -Das Geheimnis des Schinkens ist die Gewürzmischung mit dem Salz. -Rauchturm und Rauchfeuerung erinnern Jumbo optisch an ein "U-Boot". -Der Trick: Tannenzweige sorgen hier im Rauch für den besonderen Geschmack des Schwarzwälder Schinkens. -20 Tage im Rauch machen den Schinken schließlich fast schwarz. -"Jetzt verschwindet die Scheibe im Jumbo", kündigt er die Test-Verspeisung an. -"Der ist richtig lecker", so das Fazit des TV-Stars. -Damit werden alle Beteiligten leben können. -Sie dachten, Reisebüros gehören dank Internet der Vergangenheit an? -Flight Centre scheint sich dem Trend zu widersetzen. -Das Unternehmen hat seine Gewinnaussichten für das Gesamtjahr erhöht und erwartet Rekordeinnahmen von Urlaubern in Australien und Großbritannien. -Das Reiseunternehmen geht jetzt von einem bereinigten Jahresgewinn vor Steuern zwischen 325 und 340 Millionen Dollar aus, verglichen mit 305 bis 315 Millionen Dollar in seiner früheren Prognose. -Wenn die aktuellen Zielwerte erreicht werden, dann bedeutet das ein Wachstum von 12 bis 17 Prozent im Vergleich zum Rekordgewinn von 290,4 Millionen Dollar im Zeitraum 2011/12. -Wie Geschäftsführer Graham Turner bekannt gab, erzielte Flight Centre im ersten Halbjahr acht Prozent Gewinn und verbuchte einen starken Start ins zweite Halbjahr, besonders im australischen und britischen Privatgeschäft. -„Seit Jahresbeginn sind unsere zehn Länder profitabel und einige davon befinden sich auf gutem Wege in Richtung Rekordjahreseinnahmen vor Zinsen und Steuern“, erklärte er. -Dazu gehören Australien und Großbritannien, die typischerweise zu unseren stärksten Ergebnisträgern gehören. -In Australien hat sich die Freizeitbranche in der zweiten Jahreshälfte erholt, was einen leicht schwächeren Markt für Geschäftsreisen ausglich. -Ähnlich gut lief es für das Freizeitgeschäft von Flight Centre in Großbritannien, während auch hier die Geschäftskunden weniger ausgaben. -Das US-Geschäft erholte sich gegenüber dem saisonbedingt schwächeren ersten Halbjahr, sodass für das dritte Jahr in Folge ein Gewinn für das Gesamtjahr erwartet wird. -Die Aktien von Flight Centre stiegen gestern um 3 Cent auf 38,20 Dollar. -Schulen werden zu größerem Fokus auf Mathematik, Rechtschreibung und Grammatik angehalten -In Kursen zu englischer Literatur müssen Schüler künftig mindestens ein Stück von Shakespeare, einen Roman des 19. Jahrhunderts, romantische Lyrik und zeitgenössische britische Romane ab 1914 behandeln. -In die Prüfung finden auch „ungesehene Texte“ Eingang, um zu breiterem Lesen anzuregen. -Der kombinierte Kurs aus englischer Literatur und Sprache wird abgeschafft. -Ab 2015 müssen Schüler eine eigenständige GCSE-Prüfung für Sprache ablegen, wobei es starke Anreize dafür gibt, englische Literatur als separate Qualifikation zu wählen. -Das Bildungsministerium wird morgen die neuen Lehrpläne für Englisch und Mathematik veröffentlichen – die ersten Fächer, die radikal umgestaltet wurden. -Andere Kernfächer werden nächstes Jahr geändert. -In einer separaten Initiative wird Ofqual, die Aufsichtsbehörde für Prüfungen, eine Neuorganisation der GCSE-Strukturen bekannt geben, einschließlich eines neuen Benotungssystems und weniger Kursarbeit. -In einer Rede im Sommer sagte Bildungsminister Michael Gove, dass es einen „breiten Konsens gibt, dass wir unser Prüfungssystem reformieren müssen, um das öffentliche Vertrauen wiederherzustellen“, wobei er betonte, die GCSEs würden „herausfordernder, ambitionierter und strenger“ werden. -Studien zeigen, dass in englischen Schulen weniger Zeit für Mathematik aufgewendet wird – 116 Stunden pro Jahr oder drei Stunden wöchentlich pro Schuljahr – als in den meisten anderen Ländern. -Demgegenüber unterrichten australische Schulen durchschnittlich 143 Stunden jährlich und Schüler in Singapur erhalten 138 Stunden. -Zwar wird es keine formelle Anforderung geben, im Stundenplan mehr Zeit für Mathematik vorzusehen, doch aus Koalitionskreisen heißt es, die umfassende Matheprüfung – kombiniert mit einer stärkeren Gewichtung des Fachs in Ranglisten – werde Schulen vermutlich dazu ermutigen, mehr Stunden anzusetzen. -Im Lehrplan wird mehr Augenmerk auf „Probleme aus dem richtigen Leben“ gelegt, einschließlich Finanzmathematik. -Schöne Tiere und leckere Torten locken -Die schönsten Kaninchen verschiedener Rassen und Farbenschläge präsentiert die Kreisverbandsleistungsschau an diesem Wochenende im Bürgerhaus. -Die Bewertungen wurden bereits am Donnerstag vorgenommen -Erneut richtet der Ortsverein W514 diese große Ausstellung aus. -Züchter der im Kreis Soest bestehenden Vereine zeigen hier ihre besten Tiere wie auch junge Wickeder Hobbyhalter ihre Kaninchen. -Am Donnerstag nahmen sechs Preisrichter die Bewertung der zahlreichen Kaninchen vor und konnten dabei herausragende Tiere auszeichnen. -Die Besucher der Schau werden bestens informiert, da die Beurteilung aller Tiere an den Käfigen angebracht ist. -Die offizielle Eröffnung der Ausstellung nimmt Bürgermeister Hermann Arndt Samstag um 14.30 Uhr vor. -Für alle Besucher geöffnet ist die Kaninchenschau am Samstag bereits ab 10 Uhr sowie am Sonntag von 10 bis 15.30 Uhr, woran sich die Siegerehrung anschließt. -Die Gäste sind auch zu einer mit stattlichen Sachpreisen bestückte Tombola und zu einer lecker bestückten Cafeteria eingeladen. -Pamela Anderson trennt sich von ihren legendären blonden Locken und tritt erstmals mit neuem Bubikopf auf. -Pamelas blonde Locken sind seit ihrer Rolle in der Fernsehserie Baywatch berühmt. -Pamela Anderson ist der neueste Star, der seine Fans mit einem drastischen neuen Haarschnitt schockiert. -Die ehemalige Baywatch-Schönheit hat sich zugunsten eines platinblonden Kurzhaarschnitts von ihren langen blonden Locken getrennt. -Ihren neuesten Look präsentierte die 46-jährige Schauspielerin am Mittwoch in Los Angeles und stellte auch gleich einen Schnappschuss auf ihre Twitter-Seite. -Es ist das erste Mal seit 20 Jahren, dass das blonde Sexsymbol kurze Haare hat, und wir lieben die sittsame Veränderung. -Was halten Sie von Pammys Haar? -Verraten Sie uns Ihre Meinung unten in den Kommentaren. -Salem: Johanna Rahner beim Ökumenischen Gesprächsforum -Beim nächsten Ökumenischen Gesprächsforum am Montag, 21. Oktober, im Neuen Museum des Schlosses geht es um das Thema "Zur Hölle mit der Hölle - Theologische Überlegungen zum heutigen Umgang mit den letzten Dingen". -Referentin ist Professorin Johanna Rahner. -Das Ökumenische Gesprächsforum ist eine Initiative der katholischen und evangelischen Kirchengemeinden Salem, des Schlosses Salem und des Kulturamtes Bodenseekreis. -Johanna Rahner will im Forum der Frage nachgehen, wie denn heute die Menschen die "letzten Dinge" nach dem Tod einstufen. -Hätten die Menschen in den vergangenen Jahrhunderten davon zu viel gewusst und dabei auf jene Bilder von Himmel und Hölle gesetzt, "die bis heute unsere Vorstellungen prägen", so zeichne sich die zeitgenössische prophetische Lehre von den Hoffnungen auf Vollendung eher durch eine gewisse Verlegenheit oder gar Sprachlosigkeit aus. -"Die größere Hoffnung ist Christinnen und Christen durch die Religionskritik gehörig ausgetrieben worden", sagt die Referentin. -Sie habe "uns mit so vielen Fragezeichen versorgt, dass uns die Ausrufezeichen abhanden gekommen zu sein scheinen". -Johanna Rahner sagt weiter: "Doch die entscheidende Herausforderung bleibt das Ärgernis des Todes". -Damit verbunden sei die Frage nach jener Sehnsucht, "die davon träumt, dass am Ende alles gut sein möge". -Johanna Rahner, 1962 geboren in Baden-Baden, studierte von 1982 bis 1989 Katholische Theologie und Biologie an der Albert-Ludwigs-Universität Freiburg. -Sie promovierte 1997 zum Dr. theol., ebenfalls in Freiburg. -Die Habilitation für Fundamentaltheologie und Ökumenische Theologie folgte 2003 an der Westfälischen Wilhelms-Universität. -Seit 2010 hat Johanna Rahner einen Lehrstuhl für Systematische Theologie am Institut für Katholische Theologie der Universität Kassel inne. -Geislingen: Mehr als 100 junge Katholiken vom Kleinen Heuberg bekräftigen bei der Firmung ihr Glaubensbekenntnis -In zwei Gottesdiensten sind 101 15- und 16-jährige Jugendliche der Seelsorgeeinheit Am Kleinen Heuberg gefirmt worden. -Den Gottesdienst begleitete am Vormittag die Gruppe Laudato-Si, am Nachmittag der Jugendchor The Spirit. -Mit dem Empfang des Firmsakraments sagten die Jugendlichen Ja zu ihrer Taufe und erbaten die Kraft des Heiligen Geistes. -Die Firmlinge hatten sich in den vergangenen Monaten, unter der Leitung von Diakon Reiner Dehner und der Mithilfe zahlreicher Gemeindemitglieder auf den großen Festtag vorbereitet. -In unterschiedlichen Projekten lernten sie dabei verschiedene Aufgaben des christlichen Gemeindelebens kennen und erlebten gemeinsam den Jugendtag in Untermachtal. -Illmensee: Zweites Mountainbike-Rennen begeistert Teilnehmer -Bei herrlichem Wetter kamen 214 Fahrradfahrer nach Illmensee, um den Rundkurs über die Hügelketten rund um den See zu bewältigen. -Das teilt Ulrich Knobel vom veranstaltenden Sportverein Illmensee mit. -Unter den Teilnehmern waren, insbesondere auf der Kurzdistanz über 15 Kilometer und 300 Höhenmeter, auch zahlreiche Jedermannsradler, die ihr Hobby einmal in Wettkampfatmosphäre ausüben wollten. -Zwei Juniorenfahrer der deutschen Spitzenklasse waren in diesem Wettkampf ebenfalls am Start: Felix Bader aus Bad Waldsee und Pascal Treubel aus Aach-Linz kamen mit der Empfehlung gute Platzierungen bei den Deutschen Meisterschaften. -Diese Beiden konnten setzten sich erwartungsgemäß vom Feld ab. -Wie im Vorjahr konnte Felix Bader einen kleinen Zeitvorsprung bis ins Ziel retten und verteidigte seinen Titel als Gesamtsieger der Kurzdistanz. -Auf Platz zwei konnte sich erneut Pascal Treubel platzieren. -Bei den Damen war es Theresa Duelli vom Team Albtraum, die es ganz oben aufs Siegertreppchen schaffte. -Auf Platz zwei und drei kamen Anne Adel aus Illmensee und Leonie Treiber aus Owingen. -Eine erfreuliche Entwicklung ist die zunehmende Anzahl von Fahrern, die in der Gemeinde wohnen oder arbeiten. -Nicht nur was das Leistungsvermögen betrifft, war die ganze Bandbreite an Sportlern vertreten. -Auch die Altersstruktur reichte von zehn Jahren bei den Jüngsten bis zu über 70 Jahren bei den ältesten Startern. -Im Hauptrennen waren in diesem Jahr noch mehr absolute Topathleten am Start. -Vier Fahrer konnten sich von den regionalen Spitzenfahrern absetzen. -Darunter Philipp Pangerl, ein Mountainbike-Halbprofi vom Black-Tusk-Racing-Team. -Pangerl war zusammen mit einem Teamkollegen bereits zwei Mal Weltmeister im Zwölf-Stunden-Fahren. -In diesem Jahr wurde er Europameister in dieser Spezialdisziplin. -Mit dabei auch Roland Ballerstedt, der bereits zwei deutschen Meistertitel im Duathlon sein Eigen nennt. -Nach 45 Kilometern und 900 Höhenmetern kam es zu einem Wimpernschlagfinale im Zieleinlauf vor der Drei-Seen-Halle. -Pangerl raste als Erster über die Ziellinie. -Nur eine Sekunde dahinter konnte sich der Routinier Ballerstedt den zweiten Platz in der Gesamtwertung sichern. -Zeitgleich kamen dann Hermann und Warthmann als Dritte der Gesamtwertung ins Ziel. -In der Damenwertung war Christiane Cohsmann die Zeitschnellste im Hauptrennen. -Den zweiten Platz sicherte sich Areane Blersch aus Binzwangen vor Natascha Werner aus Stuttgart. -Sportler und Helfer zeigten sich sehr zufrieden mit dem reibungslosen und gut organisierten Ablauf der Veranstaltung. -Unterwegs durch Schlamm, Flüsse und Dschungel für kostenlose medizinische Versorgung -Dr. Georges Bwelle bringt kostenlose medizinische Hilfe in entlegene Dörfer in Kamerun -Bwelle und sein Team verbringen beinahe jedes Wochenende damit, Hunderte von Patienten zu behandeln -In dem westafrikanischen Land gibt es nicht viele Ärzte, nur einen für 5.000 Menschen -Stimmen Sie hier oder mit Ihrem Mobilgerät ab -Dr. Georges Bwelle gehört zu den Top 10 der „CNN Heroes of 2013“. -Sie können für ihn oder einen der anderen Helden in den Top 10 abstimmen, um ihn zum „CNN Hero of the Year“ zu wählen. -Der Sieger erhält 250.000 Dollar zur Fortsetzung seiner außergewöhnlichen Arbeit. -21 Jahre lang sah Georges Bwelle mit an, wie sein kranker Vater immer wieder das Bewusstsein verlor und zu Krankenhäusern reiste, die nicht über die Mittel verfügten, um ihm zu helfen. -Jamef Bwelle wurde 1981 bei einem Autounfall in der Nähe von Yaoundé, der Hauptstadt Kameruns, verletzt. -Zuerst hatte er nur einen gebrochenen Arm, doch dann bildete sich eine Entzündung, die sich auf sein Gehirn ausweitete und dort ein Hämatom verursachte, das ihn für den Rest seines Lebens beeinträchtigen sollte. -„Es gab keine Neurochirurgen in Kamerun“, so Georges Bwelle. -Wir hätten ihn aus Kamerun herausgebracht, wenn wir das Geld gehabt hätten. -Stattdessen verbrachte Bwelle Jahre damit, seinen Vater in überfüllte Kliniken und Hospitäler zu begleiten, um dort die Behandlung zu bekommen, die sie zu bieten hatten. -„Es ist nicht einfach“, kommentiert Bwelle. -Man kann um fünf Uhr morgens das Haus verlassen und zum Krankenhaus rennen, um der Erste zu sein, und ist doch nicht Erster. -Es gibt sehr viele Patienten. -Einige Menschen sterben sogar, weil sie warten müssen. -Die Situation hat sich kaum verändert, seit Bwelles Vater 2002 starb. -In Kamerun gibt es nur einen Arzt für je 5.000 Menschen, so die Weltgesundheitsorganisation. -Demgegenüber liegt das Verhältnis in den Vereinigten Staaten bei einem Arzt für 413 Personen. -Doch selbst wenn es einen Arzt für sie gäbe, könnten sich die meisten Kameruner den Besuch nicht leisten. -Zwei von fünf Menschen im Land leben unterhalb der Armutsgrenze und nahezu Dreiviertel der Gesundheitsausgaben im Land sind privat. -„Das einzige Problem, das sie haben, ist die Armut“, sagt Bwelle. -Und die Armut macht es unmöglich, das Leben zu genießen. -Seinen Vater und so viele seiner Landsleute leiden zu sehen ließ in Bwelle den Entschluss reifen, etwas dagegen zu unternehmen. -Dr. Georges Bwelle und sein Freiwilligenteam haben im letzten Jahr 700 kostenlose Operationen durchgeführt. -Er wurde selbst Arzt und arbeitet als Gefäßchirurg im Central Hospital in Yaoundé. -Außerdem gründete er eine gemeinnützige Organisation namens ASCOVIME, die an Wochenenden in ländliche Gegenden reist und dort kostenlose medizinische Versorgung anbietet. -Seit 2008 haben er und seine Freiwilligengruppe knapp 32.000 Menschen geholfen. -Beinahe jeden Freitag zwängen er und bis zu 30 Leute sich in Kleinbusse und fahren mit der verschnürten medizinischen Ausrüstung auf dem Dach über unbefestigtes Terrain in die Dörfer, die Hilfe benötigen. -Nicht immer verläuft alles reibungslos. -Schon mehr als einmal mussten sie die Fahrzeuge durch Flüsse und Schlamm schieben. -Doch wenn sie ankommen, werden sie wie Helden empfangen: ein Festessen, Gesang und Tanz und die beste Unterbringung, die der Ort zu bieten hat. -In diesen Dörfern ist kostenlose medizinische Versorgung wirklich ein Grund zum Feiern und Bwelle – mit seinem breiten Lächeln und seiner grenzenlosen Energie – ist gerne bei der fröhlichen Feier dabei. -Am nächsten Morgen beginnt das Team dann mit der Untersuchung von Hunderten von Patienten. -„Pro Fahrt behandeln wir 500 Personen“, so Bwelle. -Sie kommen aus einem Umkreis von 60 Kilometern um das Dorf – und das zu Fuß. -Jede dieser Wochenendkliniken bietet medizinische Versorgung in einer Reihe von Bereichen an. -Viele Leute werden wegen Malaria, Tuberkulose, Unterernährung, Diabetes, Parasiten und sexuell übertragbaren Krankheiten behandelt. -Andere erhalten Krücken, gespendete Brillen oder kostenlose Geburtsurkunden – ein Dokument, das für die Schule erforderlich ist, sich aber viele arme Familien einfach nicht leisten können. -Abends führt das Team einfache Operationen mit örtlicher Betäubung aus. -Die Operationen finden normalerweise in einer Schule, dem Rathaus oder einer Wohnung statt; nach dem Eingriff stehen die Patienten auf und gehen in den Erholungsbereich, um Platz zu machen für den Nächsten. -Die Beleuchtung für den OP und die Desinfektionsausstattung wird vom Generator der Gruppe gespeist und so arbeiten Bwelle und seine Freiwilligen bis in die frühen Morgenstunden am Sonntag. -Es ist ein anstrengendes Pensum, aber die Dorfmusiker helfen normalerweise, das Team motiviert zu halten. -„Sie trommeln die ganze Nacht, damit wir wach bleiben und unsere Arbeit fortsetzen können“, sagt Bwelle. -Am Sonntag fährt das Team dann zurück in die Stadt, müde, aber stolz auf die geleistete Arbeit. -Die Gruppe – eine Mischung aus kamerunischen Ärzten und ausländischen Medizinstudenten – hat im letzten Jahr 700 kostenlose Operationen durchgeführt und weiß, dass sie mit ihrer Arbeit einen enormen Unterschied für die Menschen bewirkt, denen sie hilft. -Ein Mann erklärte, dass die kostenlose Operation eines Leistenbruchs, die er erhalten habe, ihm nun ermögliche, wieder zu arbeiten. -„Das verändert die Zukunft meiner Familie“, meinte der Mann. -Neben den Wochenendkliniken und seiner Arbeit als Chirurg im Krankenhaus arbeitet Bwelle auch nachts in privaten Kliniken in Yaoundé. -Mit diesem zweiten Job finanziere er 60 % seiner gemeinnützigen Tätigkeit, so Bwelle; der Rest stamme aus privaten Spenden. -„Ich weiß wirklich nicht, wann er schläft“, sagt Katie O'Malley, die im zweiten Jahr an der Drexel University in Philadelphia Medizin studiert und freiwillig in Bwelles Gruppe mitarbeitet. -Er ist immer entweder im Krankenhaus oder versucht, Geld für die Organisation aufzubringen, damit er auf diese Kampagnen gehen kann. -Für Medizin- und Krankenpflegestudenten wie O'Malley, die aus den USA und Europa kommen und Bwelle unterstützen, ist dies eine Chance, praktische Erfahrungen zu sammeln, die sie zu Hause nie bekommen würden. -„Wir können bei Operationen dabei sein, wo wir helfen, Blut abzutupfen oder die Instrumente für Dr. Bwelle halten“, erklärt O'Malley. -Das wäre in Amerika als Medizinstudent im zweiten Jahr niemals möglich. -Die Studenten bezahlen ihre Reise nach Kamerun normalerweise selbst und bringen häufig gespendetes Sanitätsmaterial mit. -Sind sie aber erst einmal in Yaoundé, übernimmt Bwelle die Unterbringung, den Transport und die Anleitung. -„Er ist ganz ohne Zweifel ein Held“, sagt O'Malley. -Er widmet sein Leben dieser Organisation und sein Wunsch, dem kamerunischen Volk zu helfen, kennt keine Grenzen. -Für Bwelle ist die beinahe ständige Arbeit keine Belastung. -Anderen zu einem glücklicheren Leben zu verhelfen und damit ein Versprechen zu erfüllen, das er seinem Vater gegeben hat, bereitet ihm große Freude. -„Ich bin so glücklich, wenn ich diese Arbeit mache“, meint Bwelle. -Und ich denke an meinen Vater. -Ich hoffe, er sieht, was ich tue. -Ich mache es, um Menschen wieder zum Lachen zu bringen und um die Schmerzen zu lindern. -Besuchen Sie die Website von ASCOVIME und erfahren Sie, wie Sie helfen können. -Exzellent den Kampf gemeistert -Die Rolle des Santiago, dem alten Mann in Hemingways Novelle "Der alte Mann und das Meer", ist Horst Janson auf den Leib geschnitten. -Janson ist selbst ein alter Hase in seinem Metier, der Schauspielkunst. -Dem alten Mann Santiago kann keiner beim Fischfang etwas vormachen. -So ergänzen sich die beiden einfach großartig. -Man meint fast, die Falten im Nacken des alten Mannes, von denen der Fischerjunge Manolo spricht, sehen zu können. -Wenn Santiago müde vom Leben gebeugt auf dem Stuhl sitzt, ist das bei Janson echt. -Daneben ist die Rolle des alten Fischers durch und durch eine Charakterrolle, die Janson exzellent meistert. -Hemingway erzählte die Geschichte eines Kämpfers, eines mutigen Mannes, der nicht aufgibt und letztendlich durch sein Wissen und auch seine Willensstärke den Kampf gegen den Fisch gewinnt. -Janson ist in weiten Strecken als Alleindarsteller auf der Bühne. -Er schafft es, die Aufmerksamkeit des Publikums auf sich zu fokussieren, so dass seine Monologe keinen Moment langweilig sind. -Seine Anstrengungen, den Fisch endlich an die Oberfläche des Wassers zu ziehen, sind so echt, dass die Zuschauer vergessen, dass das Meer doch nur aus blau beleuchteten Wasserflaschen besteht. -Regisseur Jens Hasselmann lässt den alten Mann im Boot vor dem Fischerdorf agieren. -Im Verlauf der Aufführung setzt er die jeweils aktive Szene ins Licht: Die Hütte von Santiago, die Bar der Sängerin Marie-Luise Gunst, die mit ihren Lieder und ihrer herrlichen Stimme das Lebensgefühl Kubas besingt. -Die vier Musiker spielen Kubas Rhythmus dazu und sind zugleich Gäste der Bar. -Tourismus: Abstieg zu den Römern -Auf rund 120 Metern Länge ist unterhalb von Köln ein Abwasserkanal aus der Römerzeit erhalten. -Wer Köln besucht, kommt am Dom nicht vorbei. -Rund 157 Meter ragen die beiden Türme in den Himmel und machen ihn zum zweithöchsten Kirchengebäude Europas. -Doch nur wenige Besucher, die die beiden filigranen Kirchtürme bestaunen, ahnen, dass es auch unter dem Dom eine Menge zu entdecken gibt. -Rainer Schulze ist Experte in Sachen Kölner Unterwelt. -Treffpunkt für seine Führungen ist das Service-Center von Köln-Tourismus direkt am Dom. -Der Rundgang beginnt ganz unspektakulär: "Wir fangen in der Tiefgarage an", sagt Schulze. -In den siebziger Jahren wollten die Stadtväter eine autogerechte Stadt, und deshalb blieb von den Resten der alten Stadtmauer nur das stehen, was nicht zu groß war. -Die meisten Benutzer des Parkhauses eilen deshalb heute auch an den römischen Überresten vorbei und stellen nur ihr Auto ab. -Doch wer genauer hinschaut, entdeckt im Parkdeck D2 eine durch Metallgitterstäbe abgetrennte archäologische Fundstelle. -Zu sehen sind etwa fünf Meter hohe Ruinen aus handtellergroßen Steinen. -"Es sind Reste der römischen Stadtmauer, die in der zweiten Hälfte des ersten Jahrhunderts nach Christus erbaut wurde", erzählt Schulze. -Bevor es wieder zurück ans Tageslicht geht, lässt Schulze die Teilnehmer auf der oberen Parkebene noch einen Blick in einen finsteren, 15 Meter tiefen Schacht werfen. -Das ist der Brunnen des alten Domes, der leider auch kaum wahrgenommen wird. -Nächste Station des Rundgangs ist das Prätorium, und hier steigen die Teilnehmer wieder hinunter in die Tiefe. -Wir erleben hier den Amtssitz des einst mächtigsten Mannes nördlich der Alpen, des Prätors der CCAA. -Was CCAA bedeutet, möchte eine Besucherin wissen. -"Die Abkürzung steht für "Colonia Claudia Ara Agrippinensium", was auf gut Deutsch so viel wie "Stadt römischen Rechts und Stadt der Agrippinenser, gegründet unter Kaiser Claudius am Ort des Altars für den Kaiserkult" bedeutet". -Und beim Prätorium handelt es sich um den ehemaligen Statthalterpalast, das Machtzentrum des römischen Imperiums am Rhein. -Vom Vorraum der Ausstellung führt ein Tunnelzugang zum römischen Abwasserkanal, der hier auf rund 120 Metern Länge erhalten ist. -"Sie können ruhig ein Stück entlang der sogenannten Cloaca Maxima entlanggehen, wenn Sie wollen", muntert Schulze die Gäste auf. -Eine Überraschung hat Schulze noch. -Nachdem er an der Kasse den Schlüssel geholt hat, führt er die Gruppe zu einem 16 Meter tiefen, begehbaren Schacht. -"Wir sind jetzt in der Mikwe, einem rituellen jüdischen Bad", erklärt er. -Die jüdische Gemeinde baute ihr Bad tief in den Boden, um an das Grundwasser zu kommen, das für die rituellen Waschungen unabdingbar war. -Bis heute sind im unteren Ende der Mikwe, deren Ursprünge bis ins achte Jahrhundert zurückgehen, unterschiedliche Wasserstände durch wechselnde Pegel des Rheins ablesbar. -Die Mikwe wird Teil einer archäologischen Zone auf dem Rathausplatz, für das die Bauarbeiten in Kürze beginnen sollen. -Bis zur Fertigstellung werden aber noch einige Jahre vergehen. -Bein fast abgehackt: Neuauflage von Rocker-Prozess -Eine blutige Attacke im Rockermilieu wird nach über vier Jahre neu aufgerollt. -Ein Prozess wegen versuchten Totschlags, gefährlicher Körperverletzung und Sachbeschädigung gegen zwei 30-Jährige hat vor dem Landgericht Frankfurt (Oder) begonnen. -Er wird von strengen Sicherheitsvorkehrungen begleitet. -Die Angeklagten schwiegen zum Auftakt. -Sie sollen in der Rockerszene aktiv gewesen sein und damals zu den Bandidos gehört haben. -Die Staatsanwaltschaft wirft ihnen vor, im Juni 2009 gemeinsam mit unbekannten Komplizen in Finowfurt auf drei Männer in einem Auto eingestochen und eingeschlagen zu haben. -Alle Opfer wurden schwer verletzt, ein Mann hätte fast sein Bein verloren. -Die Opfer sollen zur konkurrierenden Gruppierung Hells Angels gehört haben. -Einer soll ein hochrangiges Mitglied aus Berlin gewesen sein. -Vorausgegangen war anscheinend eine Verfolgungsjagd mit mehreren Autos. -Laut Anklage ging es um Revierkämpfe und eine Machtdemonstration. -Die beiden Angeklagten waren Anfang 2012 in einem ersten Verfahren freigesprochen worden. -Ein Gerichtssprecher sagte damals, es habe nicht bewiesen werden können, dass die Männer an der blutigen Fehde tatsächlich beteiligt gewesen seien. -Die Täter hatten Masken getragen und waren nicht erkannt worden. -Der Bundesgerichtshof hob das Urteil nach Revision der Staatsanwaltschaft auf und verwies es zur erneuten Verhandlung an das Landgericht zurück. -Einer der Angeklagten ist derzeit wegen eines anderen Deliktes im offenen Vollzug. -Zum Auftakt ging es vor allem um die Rekonstruktion der Abläufe in der Tatnacht. -Als Zeuge war der damals ermittelnde Kommissar geladen. -Im Zentrum standen Handygespräche der Angeklagten und weiterer Personen, die die Polizei abgehört hatte. -Schwierig ist, dass sich Rocker vor Gericht fast immer an ein für sie ehernes Gesetz halten - und nicht aussagen. -Das gilt auch für die Opfer. -Im ersten Prozess hatte nur eines der Opfer ausgepackt. -Staatsanwalt Stefan Golfier von der Staatsanwaltschaft Frankfurt (Oder) sagte auf Nachfrage, es seien seit dem letzten Verfahren neue Spuren aufgetaucht. -Für den Prozess sind elf weitere Verhandlungstage angesetzt. -Die Wegwerfgesellschaft denkt nicht -Die Nacht war lang, die Musik laut und die Stimmung gut, aber irgendwann geht es nach Hause. -Nur: In den Bäuchen der Leute auf dem Beifahrer- und Rücksitz macht sich der Hunger bemerkbar. -Klar, dafür mag der eine oder andere Cocktail oder Sekt verantwortlich sein! -Was liegt da näher, als gechillt zum nächsten Drive-In zu fahren, um sich eine Kleinigkeit zu gönnen? -Ich gebe zu, ich bin gerne mit dabei, wenn es spätnachts beziehungsweise frühmorgens ans Fastfood-Holen geht. -Ein paar Pommes, eine Cola, ein Burger - und dann nix wie ab nach Hause und ins Bett! -Doch auf dem Heimweg machen sich die offensichtlichen Unterschiede zwischen mir und anderen hungrigen Disco-Gängern bemerkbar. -Denn was muss einem fehlen, dass man mit steter Regelmäßigkeit seine Fastfood-Tüten aus dem Autofenster wirft?! -Vor allem an den Wochenenden liegen weggeworfene Papiertüten an Straßenrändern und auf Parkplätzen. -"Wieso?", frage ich mich. -Sind die Leute zu doof, um die Tüten mit nach Hause respektive bis zum nächsten Mülleimer zu nehmen? -Es ist ja nicht nur so, dass das Papierzeugs die Landschaft verschandelt. -Tüten haben auch schon Verkehrseinrichtungen wie Leitplanken verdeckt und dadurch Unfälle ausgelöst. -Über so was denkt besagte "Wegwerfgesellschaft" aber nicht nach, vermutlich weiß sie nicht einmal, was dieses Wort eigentlich bedeutet. -Wenn man abends öfter unterwegs ist, erst recht mit mehreren Leuten im Auto, weiß man, wie eine Rückbank nach durchfeierten Nächten aussehen kann: Klamotten, Flaschen und sonstiger Müll türmen sich da. -Da würden ein paar Tüten mehr oder weniger doch auch nix ausmachen - im Gegensatz zur Straße, wo keiner auf den Essensresten anderer rumtrampeln will. -Ich sehe ja schon den Tag kommen, an dem den Städten und Gemeinden das Wegräumen zu blöd wird - und die Fastfood-Ketten für ihre Tüten Pfand verlangen müssen. -Obamas Rückzieher in der Gesundheitspolitik -Nach heftiger Kritik machte Präsident Obama gestern einen Rückzieher bei seinem häufig wiederholten, eindeutigen Versprechen: „Wenn Ihnen Ihre Krankenversicherung gefällt, dann können Sie sie behalten.“ -Nachdem Hunderttausende von ihrem Versicherer ein Kündigungsschreiben erhalten haben, warfen die Republikaner dem Präsidenten in den letzten Tagen vor, er habe die amerikanische Öffentlichkeit getäuscht. -Gestern relativierte Obama sein ursprüngliches Versprechen. -„Die überwiegende Mehrheit der Bürger, die eine funktionierende Krankenversicherung haben, können sie behalten“, sagte er bei einer Rede in Boston. -Als Reaktion auf die zahlreichen Medienberichte zu den Kündigungen forderte Obama jene Amerikaner, die derartige Mitteilungen erhalten haben, auf, sich auf dem freien Markt nach einer neuen Versicherung umzusehen. -Die meisten Bürger könnten so eine bessere, umfassendere Krankenversicherung für denselben Preis oder günstiger als prognostiziert erhalten. -„Sie kommen ein besseres Angebot“, sagte er. -Die Regierung bezeichnete es als wenig überraschend, dass sich die fünf Prozent der Bevölkerung, die selbst eine Versicherung abgeschlossen haben, nach einem anderen Tarif umsehen müssten, da ihr Vertrag nicht den neuen Standards nach dem Affordable Care Act entspreche. -„Ich möchte diesen Amerikanern ganz direkt sagen: Sie haben Besseres verdient“, sagte Sebelius bei einer Anhörung vor dem Energie- und Handelsausschuss des Repräsentantenhauses in Washington. -Sebelius, die für die Umsetzung des Affordable Care Acts verantwortlich ist, erklärte, dass der Start der Online-Plattform seit Oktober „miserabel“ verlaufen sei. -„Ich bin ebenso frustriert und wütend wie alle anderen auch“, sagte sie. -Ich bin entschlossen, Ihr Vertrauen zurückzugewinnen. -Dieser Ausspruch einer aufgebrachten Sebelius zu einem hinter ihr sitzenden Berater bei der gestrigen Anhörung im Repräsentantenhaus wurde nach einem Streitgespräch mit dem republikanischen Abgeordneten Billy Long aus Missouri aufgenommen. Dabei ging es darum, ob sie zur Teilnahme an Obamacare verpflichtet sein sollte. -Nach mehr als drei Stunden Anhörung bedrängte Long Sebelius wiederholt mit der Frage, warum die „Architektin“ des Affordable Care Acts nicht freiwillig auf eine staatlich geförderte Versicherung verzichtet habe, um selber einen Tarif bei HealthCare.gov abzuschließen, den sie nun Millionen von Amerikanern verkaufen wolle. -Regierung in Jerusalem bestätigt Angriff auf syrischen Luftwaffenstützpunkt nicht -Ein amerikanischer Regierungsbeamter, der anonym bleiben wollte, hat am Donnerstag gegenüber dem amerikanischen Nachrichtensender CNN behauptet, israelische Kampfflugzeuge hätten einen Tag zuvor einen Luftwaffenstützpunkt im syrischen Latakia angegriffen. -Das Ziel seien "Raketen und zugehörige Ausrüstung" gewesen, von denen die israelische Regierung befürchtet habe, sie seien für die libanesische Hisbollah bestimmt gewesen, hieß es laut CNN. -Syrische und libanesische Medien sowie der arabische Sender Al-Arabiya hatten zuvor berichtet, dass der Stützpunkt in der Hafenstadt Latakia in der Nacht von Mittwoch auf Donnerstag beschossen worden sei, und die Israelis dafür verantwortlich gemacht. -Der israelische Fernsehsender Channel 2 zeigte Satellitenbilder des Stützpunktes in Latakia, auf denen russische Abwehrraketen vom Typ S-125 Newa sowie eine Batterie von SA-3 Raketen zu sehen waren, die Channel 2 zufolge eine Reichweite von 35 Kilometern haben und Sprengköpfe bis zu 70 Kilogramm transportieren können. -Unsicher Die Regierung in Jerusalem hat den Angriff offiziell nicht bestätigt. -Allerdings zitiert die Nachrichtenagentur Reuters einen ebenfalls anonymen israelischen Regierungsbeamten mit den Worten, er glaube, dass Israel den Angriff ausgeführt habe, sei aber nicht sicher. -Ein Sprecher des israelischen Verteidigungsministeriums sagte jedenfalls: "Wir kommentieren diese Berichte nicht". -Der Journalist Ron Ben-Yishai betonte in der Tageszeitung Yedioth Ahronoth, dass das syrische Regime bereits früher, teils erfolgreich, versucht habe, Boden-Luft-Raketen an die Hisbollah zu liefern. -Die israelische Regierung hatte wiederholt davor gewarnt, dass jeder Versuch Syriens, die Hisbollah mit chemischen oder anderen gefährlichen Waffen zu versorgen, das Überschreiten einer "roten Linie" bedeute, dem eine militärische Antwort folgen würde. -John Kerry erklärt in einem beispiellosen Eingeständnis, die US-Spionage sei „unangemessen zu weit gegangen“ -John Kerry deutete mit einem unerwarteten Eingeständnis ein Aufweichen der abwehrenden Haltung der USA in Bezug auf ihre Überwachungsprogramme an, indem er sagte, die Spionage sei in bestimmten Fällen „unangemessen zu weit gegangen“. -Der Außenminister gab außerdem zu, er trüge gemeinsam mit Barack Obama die Verantwortung dafür, dass man auf „Autopilot“ geschaltet habe, als die explosiven Enthüllungen des Whistleblowers Edward Snowden über die Spionageaktivitäten der NSA öffentlich wurden. -Durch die durchgesickerten Informationen ist die US-Regierung in einen diplomatischen Sturm der Entrüstung vonseiten ihrer Verbündeten geraten. -Bei einer per Videoverbindung übertragenen Rede auf der Open Government-Konferenz in London sagte Kerry: „Ganz ohne Frage haben der Präsident und ich sowie andere in der Regierung von einigen Dingen erfahren, die quasi per Autopilot abgelaufen sind, einfach weil die Fähigkeit dazu bestand, was noch auf den Zweiten Weltkrieg und die schwierigen Jahre des Kalten Krieges sowie natürlich auf den 11. September zurückgeht.“ -Anschließend gestand er als erstes hochrangiges Mitglied der US-amerikanischen Regierung ein, dass die US-Spionage eine Grenze überschritten habe, betonte aber auch, dass keine Rechte missbraucht worden seien. -Er sagte: „In einigen Fällen ist das unangemessen zu weit gegangen.“ -Der Präsident ist entschlossen, Klarheit zu schaffen, und führt deshalb eine gründliche Untersuchung durch, damit niemand das Gefühl haben muss, hier finde Missbrauch statt. -Ich versichere Ihnen, dass die Rechte unschuldiger Personen in diesem Prozess nicht beeinträchtigt werden. -Kerry bestand allerdings darauf, dass die NSA eine Kraft des Guten sei und durch ihre Überwachungsvorgänge viele Leben gerettet worden seien. -Er fügte hinzu: „Wir haben es mit einer neuen Welt zu tun, in der Menschen bereit sind, sich selbst in die Luft zu sprengen.“ -Es gibt einen radikalen Extremismus in der Welt, der versessen darauf ist, Menschen zu töten, in die Luft zu sprengen und Regierungen anzugreifen. -Was wäre also, wenn man in der Lage wäre, das abzufangen und zu stoppen, ehe es passiert? -Wir haben tatsächlich Flugzeugabstürze, Anschläge auf Gebäude und die Ermordung von Menschen verhindert, weil wir im Vorfeld von solchen Plänen erfahren haben. -Unterdessen werden US-amerikanische Gesetzgeber nach Europa reisen, um die Bedenken gegen die angebliche amerikanische Spionage zu zerstreuen und die Europäer von der Notwendigkeit zu überzeugen, bei den Anti-Terror-Bemühungen weiterhin mit den USA zusammenzuarbeiten, wie der Vorsitzende des Senatsunterausschusses für europäische Angelegenheiten am Donnerstag erklärte. -Senator Chris Murphy aus Connecticut sagte, er habe diese Woche mit Vertretern des Europäischen Parlaments und anderen Personen gesprochen und sei besorgt wegen ihrer Drohungen, sich aufgrund der Frustration über die Überwachung durch die National Security Agency nicht mehr an Anti-Terror-Organisationen zu beteiligen. -„Es ist sehr wichtig für die nationalen Sicherheitsinteressen der USA, dass die Europäer im Hinblick auf unsere gemeinsamen Anstrengungen gegen den Terrorismus mit an Bord bleiben“, erklärte Murphy, der als Demokrat erstmals im Senat sitzt und als Vorsitzender des Senatsunterausschusses für auswärtige Beziehungen und europäische Angelegenheiten tätig ist, in einem Interview in Washington. -Außerdem gehe ich nach Europa, um dort klarzumachen, dass wir in der Terrorbekämpfung weiterhin zusammenarbeiten müssen, ungeachtet ihres Ärgers über die NSA-Programme. -Medienberichte, wonach die NSA Millionen von Telefondaten in Europa erfasst hat, haben die Beziehungen zu einigen US-Verbündeten belastet, wenngleich der Leiter des Geheimdienstes diese Woche erklärte, die Berichte seien unzutreffend und basierten auf einem Missverständnis der Metadaten, die von Nato-Verbündeten gesammelt und mit den Vereinigten Staaten ausgetauscht worden seien. -Andere Enthüllungen zitierten aus von Snowden weitergegebenen Dokumenten, wonach die NSA das Handy von Bundeskanzlerin Angela Merkel und bis zu 34 weiteren Staatsoberhäuptern weltweit überwacht habe. -Der Leiter der nationalen Geheimdienste, James Clapper, verteidigte die Spionage bei Verbündeten als notwendig und erklärte, dies sei auf beiden Seiten üblich. -In all dem Tumult sagte Murphy, seine Behörde bereite die Reise des Kongresses vor, die dieses Jahr stattfinden soll, und hoffte, die Delegation werde Mitglieder aus beiden Parteien und Kammern umfassen. -Die Namen der anderen teilnehmenden Abgeordneten sollen in den kommenden Tagen veröffentlicht werden. -Er sagte, das genaue Reiseroute werde noch ausgearbeitet. -Während Murphy betonte, der Zweck der Reise sei die Verbesserung der Beziehungen, sprach er auch davon, dass es einige „ernste Worte“ geben würde. -Er sagte, die europäischen Regierungen müssten ihrer Bevölkerung gegenüber ehrlich über die Art von Spionageprogrammen sein, die sie selbst seit Jahren einsetzten. -„Zwar können wir unsere Überwachungsprogramme ändern und so die Rechte der Europäer besser schützen, diese müssen sich aber auch mit der Tatsache abfinden, dass wir nicht die einzigen sind, die spionieren“, so Murphy. -Währenddessen steht für John Kerry am Wochenende eine Reise in den Nahen Osten und nach Polen an, um sich dort mit dem Groll über die US-amerikanische Strategie in Syrien, Ägypten und Iran sowie die US-amerikanischen Überwachungsaktivitäten auseinanderzusetzen. -Eine hartnäckige Verletzung am Sprunggelenk droht den Einsatz von Nicolai Müller im Bundesliga-Auswärtsspiel des FSV Mainz 05 beim FC Augsburg zu verhindern. -Mainz bnagt um einen Einsatz von Nicolai Müller. -Morgen wird er versuchen, mit der Mannschaft zu trainieren. -Bisher konnte er das nur individuell. -"Wir werden kurzfristig über seinen Einsatz entscheiden", sagte Trainer Thomas Tuchel. -Der 40-Jährige hofft, seinen mit sechs Treffern besten Torschützen dabei zu haben. -Denn die Mainzer müssen bereits auf die verletzten Leistungsträger Niki Zimling, Julian Baumgartlinger und Niko Bungert verzichten. -Australierin legt Berufung gegen Gefängnisstrafe in Thailand ein -Eine 21-jährige Frau aus Sydney, die in Phuket zu 15 Tagen Gefängnis verurteilt wurde, weil sie fälschlicherweise behauptet hatte, von einem Taxifahrer vergewaltigt worden zu sein, hat gegen das Urteil Berufung eingelegt und wurde auf Kaution freigelassen. -Stevie Rochelle Bamford wurde zunächst von einem Provinzgericht in Phuket am 15. Juni für schuldig befunden, eine Falschaussage gemacht zu haben, nachdem sie der thailändischen Polizei gegenüber angegeben hatte, ein lokaler Taxifahrer habe sie in den Morgenstunden am Sonntag, dem 10. Juni, vergewaltigt, während zwei andere Männer sie festhielten. -Später allerdings zeigten die Aufnahmen einer Videokamera, dass sie sicher in ihr Hotel gelangt war, nachdem sie von ihrem australischen Freund getrennt worden war. -Die Polizei in Phuket verhörte Bamford zwei Tage lang, bis sie gestand, sich die Geschichte ausgedacht zu haben. -Bis zur Gerichtsverhandlung befand sie sich vor Ort in Polizeigewahrsam. -Bamford wurde zu einer 15-tägigen Gefängnisstrafe in einer Haftanstalt mit niedriger Sicherheitsstufe am Rande Phukets verurteilt, statt in einem Gefängnis für erwachsene Frauen. -Sie ist die Tochter von Peter Tunks, einem ehemaligen Spieler der australischen Rubgy-Liga, der sich an das Außenministerium in Canberra mit der Bitte um Hilfe für seine Tochter gewandt hat. -Tunks erklärte gegenüber dem Sunday Telegraph in Sydney, die ganze Familie sei „extrem besorgt“ über das Wohlergehen seiner Tochter und wollte sie zurück in Australien haben. -„Wir machen uns natürlich große Sorgen, aber wir hoffen, dass sie so bald wie möglich wieder zu Hause ist“, sagte Tunks. -Bamford hat gegen das Urteil Berufung eingelegt und ist gegen eine Kaution von 50.000 Baht auf freiem Fuß. -In australischen Berichten war zu lesen, dass sie in der Zwischenzeit im Ferienort Krabi in Südthailand Urlaub macht. -Vonseiten des Gerichts war zu hören, Bamford werde durch einen lokalen Anwalt in Phuket vertreten und sei gewarnt worden, dass die Berufung auch zur Verhängung einer höheren Strafe von bis zu zwei Jahren in einem Gefängnis für Erwachsene führen könne. -Allerdings ist es auch möglich, dass Thailand nach der kürzlichen Ermordung der Reisekauffrau Michelle Smith in Phuket bemüht sein könnte, sein angeschlagenes touristisches Image zu verbessern, und es deshalb zu einem Freispruch kommt. diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/test_data/newstest2014.en b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/test_data/newstest2014.en deleted file mode 100644 index c5df515b..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/test_data/newstest2014.en +++ /dev/null @@ -1,3003 +0,0 @@ -Gutach: Increased safety for pedestrians -They are not even 100 metres apart: On Tuesday, the new B 33 pedestrian lights in Dorfparkplatz in Gutach became operational - within view of the existing Town Hall traffic lights. -Two sets of lights so close to one another: intentional or just a silly error? -Yesterday, Gutacht's Mayor gave a clear answer to this question. -"At the time, the Town Hall traffic lights were installed because this was a school route," explained Eckert yesterday. -The Kluser lights protect cyclists, as well as those travelling by bus and the residents of Bergle. -The system, which officially became operational yesterday, is of importance to the Sulzbachweg/Kirchstrasse junction. -We have the museum, two churches, the spa gardens, the bus stop, a doctor's practice and a bank, not to mention the traffic from the 'Grub' residential area. -"At times of high road and pedestrian traffic, an additional set of lights were required to ensure safety," said Eckert. -This was also confirmed by Peter Arnold from the Offenburg District Office. -"According to current measurements, around 12,000 vehicles travel through the town of Gutach on the B33 on a daily basis, of which heavy goods traffic accounts for around ten per cent," emphasised Arnold. -Therefore the construction of an additional set of lights was more than necessary: "Here safety comes first, it's that simple," said Arnold. -A total of four road safety inspections were carried out and a roundabout was also considered, however, this idea was rejected on account of the narrowness of the Sulzbachweg/Kirchstrasse junctions. -According to Arnold, every possible test was carried out prior to the selection of the location for the traffic light posts: "Using a goods vehicle loaded with particularly long tree trunks, we also tested whether such vehicles could access the B 33 from the Sulzbachweg without knocking over the traffic light posts". -The traffic light system itself, which cost around EUR 15,000, is the "most modern system that is currently available on the market," explained Arnold. -The system is fitted with coloured LEDs, which are bright enough that drivers can easily see the lights, even when the sun is low in the sky. -And they are also energy-efficient: The older light systems consume around 100 Watts, with the new ones consuming just eight. -There are three sets of lights per direction of travel. -Arnold explained the technology used by the new system: It is fitted with two radar sensors. -If the pedestrian presses the button at the traffic lights, the top radar sensor checks the traffic status. -If the street is clear, the pedestrian obtains a green light immediately, if not, there is a delay of around 15 seconds. -An additional radar sensor checks whether the green phase for the pedestrian can be ended. -"If a group of people or if disabled persons are crossing the street, the green phase is extended, thus ensuring that everyone gets safely across the street," explained Arnold. -Of course, drivers must also play their part and keep their eyes on the road. -Yesterday this was not the case: The light had barely turned green for pedestrians when a luxury vehicle sped through on a red light. -For more than 30 years, Josef Winkler has been writing from the heart, telling of the hardships of his childhood and youth. -The catastrophes of his Catholic village upbringing - the speechlessness, his tendency towards brute force and dulled sexuality, the confinement and lack of joy - all of this has been described many times by the Kaernten-born poet. -The Büchner prizewinner is known primarily as a writer of prose, with theatre texts something of a rarity for him. -In a collage of prose texts For his performance piece, "Wetterleuchten auf der Zungenspitze" (Summer lightning on the tip of your tongue), which can now be seen in Garage X on Petersplatz, Gerhard Fresacher ,creates a collage of prose texts. -The theatre producer has thus combined elements from the autobiographically inspired novel "Der Leibeigene" (1987) [The Bondsman] featuring prose miniatures from "Leichnam, seine Familie belauernd" (2003) [Corpse, stalking his family]. -On the predominantly empty stage - with one important requirement: a crumpled sofa, on which cast members allude to copulating and masturbating - the eight-person ensemble work their way through the text material. -However, Director Fresacher seems to have little trust in the text. -The 70-minute performance glosses over the script with a host of director's additions, well-known from the repertoire of post-dramatic theatrical styles. -In particular, the actresses play a major role in the sometimes rather dubious staging. -They are manhandled, their heads held under water, tacked to the wall by their evening gowns. -Wrapped in cellophane or in girdles, they stumble on dangerously high heels across the set, either delivering monologues at the top of their voices or lying completely silent on the stage. -However, the source text makes barely any reference to this intense delivery. -The best moments of the evening is when the singing starts - tracks range from Deep Purple to traditional folk songs. -Only towards the end does the highly charged performance start to wind down, and we see flashes of Winkler's somewhat absurd sense of humour. -A black box in your car? -As America's road planners struggle to find the cash to mend a crumbling highway system, many are beginning to see a solution in a little black box that fits neatly by the dashboard of your car. -The devices, which track every mile a motorist drives and transmit that information to bureaucrats, are at the center of a controversial attempt in Washington and state planning offices to overhaul the outdated system for funding America's major roads. -The usually dull arena of highway planning has suddenly spawned intense debate and colorful alliances. -Libertarians have joined environmental groups in lobbying to allow government to use the little boxes to keep track of the miles you drive, and possibly where you drive them - then use the information to draw up a tax bill. -The tea party is aghast. -The American Civil Liberties Union is deeply concerned, too, raising a variety of privacy issues. -And while Congress can't agree on whether to proceed, several states are not waiting. -They are exploring how, over the next decade, they can move to a system in which drivers pay per mile of road they roll over. -Thousands of motorists have already taken the black boxes, some of which have GPS monitoring, for a test drive. -This really is a must for our nation. -"It is not a matter of something we might choose to do," said Hasan Ikhrata, executive director of the Southern California Assn. of Governments, which is planning for the state to start tracking miles driven by every California motorist by 2025. -There is going to be a change in how we pay these taxes. -The technology is there to do it. -The push comes as the country's Highway Trust Fund, financed with taxes Americans pay at the gas pump, is broke. -Americans don't buy as much gas as they used to. -Cars get many more miles to the gallon. -The federal tax itself, 18.4 cents per gallon, hasn't gone up in 20 years. -Politicians are loath to raise the tax even one penny when gas prices are high. -"The gas tax is just not sustainable," said Lee Munnich, a transportation policy expert at the University of Minnesota. -His state recently put tracking devices on 500 cars to test out a pay-by-mile system. -"This works out as the most logical alternative over the long term," he said. -Wonks call it a mileage-based user fee. -It is no surprise that the idea appeals to urban liberals, as the taxes could be rigged to change driving patterns in ways that could help reduce congestion and greenhouse gases, for example. -California planners are looking to the system as they devise strategies to meet the goals laid out in the state's ambitious global warming laws. -But Rep. Bill Shuster (R-Pa.), chairman of the House Transportation Committee, has said he, too, sees it as the most viable long-term alternative. -The free marketeers at the Reason Foundation are also fond of having drivers pay per mile. -"This is not just a tax going into a black hole," said Adrian Moore, vice president of policy at Reason. -People are paying more directly into what they are getting. -The movement is also bolstered by two former U.S. Transportation secretaries, who in a 2011 report urged Congress to move in the pay-per-mile direction. -The U.S. Senate approved a $90-million pilot project last year that would have involved about 10,000 cars. -But the House leadership killed the proposal, acting on concerns of rural lawmakers representing constituents whose daily lives often involve logging lots of miles to get to work or into town. -Several states and cities are nonetheless moving ahead on their own. -The most eager is Oregon, which is enlisting 5,000 drivers in the country's biggest experiment. -Those drivers will soon pay the mileage fees instead of gas taxes to the state. -Nevada has already completed a pilot. -New York City is looking into one. -Illinois is trying it on a limited basis with trucks. -And the I-95 Coalition, which includes 17 state transportation departments along the Eastern Seaboard (including Maryland, Pennsylvania, Virginia and Florida), is studying how they could go about implementing the change. -The concept is not a universal hit. -In Nevada, where about 50 volunteers' cars were equipped with the devices not long ago, drivers were uneasy about the government being able to monitor their every move. -"Concerns about Big Brother and those sorts of things were a major problem," said Alauddin Khan, who directs strategic and performance management at the Nevada Department of Transportation. -It was not something people wanted. -As the trial got underway, the ACLU of Nevada warned on its website: "It would be fairly easy to turn these devices into full-fledged tracking devices." -There is no need to build an enormous, unwieldy technological infrastructure that will inevitably be expanded to keep records of individuals' everyday comings and goings. -Nevada is among several states now scrambling to find affordable technology that would allow the state to keep track of how many miles a car is being driven, but not exactly where and at what time. -If you can do that, Khan said, the public gets more comfortable. -The hunt for that technology has led some state agencies to a small California startup called True Mileage. -The firm was not originally in the business of helping states tax drivers. -It was seeking to break into an emerging market in auto insurance, in which drivers would pay based on their mileage. -But the devices it is testing appeal to highway planners because they don't use GPS and deliver a limited amount of information, uploaded periodically by modem. -"People will be more willing to do this if you do not track their speed and you do not track their location," said Ryan Morrison, chief executive of True Mileage. -There have been some big mistakes in some of these state pilot programs. -There are a lot less expensive and less intrusive ways to do this. -In Oregon, planners are experimenting with giving drivers different choices. -They can choose a device with or without GPS. -Or they can choose not to have a device at all, opting instead to pay a flat fee based on the average number of miles driven by all state residents. -Other places are hoping to sell the concept to a wary public by having the devices do more, not less. -In New York City, transportation officials are seeking to develop a taxing device that would also be equipped to pay parking meter fees, provide "pay-as-you-drive" insurance, and create a pool of real-time speed data from other drivers that motorists could use to avoid traffic. -"Motorists would be attracted to participate because of the value of the benefits it offers to them," says a city planning document. -Some transportation planners, though, wonder if all the talk about paying by the mile is just a giant distraction. -At the Metropolitan Transportation Commission in the San Francisco Bay Area, officials say Congress could very simply deal with the bankrupt Highway Trust Fund by raising gas taxes. -An extra one-time or annual levy could be imposed on drivers of hybrids and others whose vehicles don't use much gas, so they pay their fair share. -"There is no need for radical surgery when all you need to do is take an aspirin," said Randy Rentschler, the commission's director of legislation and public affairs. -If we do this, hundreds of millions of drivers will be concerned about their privacy and a host of other things. -Königsfeld: Small team gives a spirited performance -The voluntary fire service bravely came through the main autumn test run in spite of a lack of personnel. -Only eleven men took part in the exercise. -For the training exercise, department commander, Hans Kammerer, chose the Feder premises on Burgberger Strasse. -A short-circuit was reported to have occurred there during sawing. -Two people were injured by the resulting fire and the spread of smoke, however, one was able to make an emergency call. -Two fire fighters equipped with respiratory gear prepared to enter the building following the initial inspection of the situation by Hans Kammerer. -After a short time they managed to find the first person and direct them out of the building. -The second person had to be carried. -This was not so simple as they also had to negotiate a narrow stairwell. -The building, a workshop with integrated stabling for two horses, was not easy to secure. -There were large quantities of wood and bales of straw stored inside. -In addition to this, there were also electrical machines for wood processing. -The first attempt to extinguish the fire was made using the tank on the fire engine. -Another line was taken from the surface hydrant around 100 metres away. -Only three attempts could be made to extinguish the blaze. -A hollow-stream nozzle in the building was also used. -For Hans Kammerer, the exercise was also a chance to show what can be achieved with just a few available staff. -In case of emergency, support is provided by the Königsfeld daytime task force. -The Commander expressed his satisfaction following the exercise. -Networking between universities and companies is important -On a visit to the district of Breisgau-Hochschwarzwald, it was explained to Parliamentary President Guido Wolf in Titisee-Neustadt where the workforce of tomorrow will come from. -In the Hochschwarzwald Support Centre, he addressed this important question alongside the executive principals of the vocational colleges and the district grammar school and the chairs of the parents' council. -"Education is an important factor for the location," highlighted Claudia Stehle, Director of the Hans Thoma School, which introduced the networked Hochschwarzwald Education Centre Schools Project. -During the presentation, Wolf seemed impressed by the educational pilot project. -Since 2011, there has been successful collaboration in Wälderstadt between the Support Centre, the District Grammar School and the Vocational School Complex. -He also praised the family-friendly approach within the district. -"It is important, not only to keep the family life stage in mind, but, on account of demographic change, the period spent providing long-term care and support for relatives as well," explained Wolf. -He also stated that an increasing number of employed persons are looking after the long-term care and support of relatives. -"Without support from their employer, the resulting burden placed on employees could result in the termination of the employment contract," said Wolf. -However, nobody can afford to lose qualified workers," he added. -Another, particularly important factor is that of networking between universities and companies. -"After all, people who have learned of the merits of the region during their studies often remain within the region, along with their acquired knowledge and skills, which contributes to the strengthening of the region," said the State Parliamentary President, expressing his thorough conviction. -For it is only if a sufficient number of educational places can be provided that the need for skilled workers can be covered. -In his opinion, the district's advantage as a location is its advantageous location in south-west Germany, in immediate proximity to France and Switzerland. -In addition to this, on his train journey from Titisee-Neustadt to Freiburg, he experienced first-hand the efforts already undertaken to develop public transport. -"You must continue to be tenacious and make sure that local public transport becomes even better," he explained in the address given to the local politicians in attendance. -Music makes for an entertaining afternoon -It was 15 years ago that the Kaul family first invited the Dietingen senior citizens for coffee and cake, followed by a snack. -Previously, the elderly citizens were hosted in the service station canteen. -The coffee afternoon is now held in the St. Josef nursing home. -The residents of the home were delighted with the delicious cakes and tarts. -Later on, a sausage salad was provided. -Silvia Kimmich-Bantle and her father Karl Kimmich entertained those in attendance with popular tunes. -The more than 100-year-old natural stone wall, serving as the foremost boundary between the old school yard - which is due to be renovated - and Kirchstrasse, will once again feature on the renovation cost plan for the town of Bräunlingen. -In the days before the Kilbig Autumn Folk Festival, when the first renovation work was being carried out at the front of the school yard, a small corner wall with balustrades was installed to stabilise and relieve the static load on the old natural stone wall, which is more than one hundred years old. -According to Alexander Misok of the Bräunlingen Municipal Planning and Building Control Authority, the ongoing procedure plans to involve the Regional Memorial Authority in the planning stage and to draw up a renovation concept for the old wall. -A cost calculation is to be performed by a team of professionals, which will then be submitted to the Town Council, which will make a decision and determine the ongoing procedure. -A decision is expected to be made in the coming year. -Following the renovation, plastering and planting of trees in the old internal school yard, within the two wings of the 1912 school, as a subsequent measure the boundary wall, which is in need of refurbishment, must be renovated from the ground up within the foreseeable future. -The historic, one hundred-year-old boundary wall between the old school-yard and Kirchstrasse is in need of renovation, primarily as a result of water and frost damage, on account of the lack of drainage, and is leaning forwards. -Deep cracks in a number of individual stones testify to the weather damage, however, at present the wall is not in danger of collapse. -A secure wall is essential for a school yard that is used by pupils, and this is the purpose of the current fortification work. -The school yard renovation was originally planned back in 2008/2009, however, high unplanned expenses meant that the work had to be pushed back. -The Regional Memorial Authority will have a major say in the historic school wall renovation, which has even been used for filming. -If this authority were to impose requirements, then the town of Bräunlingen would hope to receive subsidies from the monument preservationists. -The pupils of the Bräunlingen Primary School, who are now being taught in a well renovated old school building, with a new hall, will soon be able to use the old school yard as well. -Özdemir wants jazz training in Stuttgart -Cem Özdemir, Federal Chairman of the Green Party, has now engaged in the ongoing discussions regarding the future of the Baden-Wuerttemberg Music Academy. -"I do not think it is right to abandon comprehensive training in the Stuttgart Music College," said Özdemir, speaking to the "Stuttgarter Nachrichten". -Jazz and classical music belong together at the jazz location of Stuttgart. -As such, Özdemir, who is the favourite in Stuttgart for a direct mandate in the parliamentary elections on 22 September, is in disagreement with the Green-Red State Government. -On the basis of an expert study, this government is urging the reorganisation of the five music colleges in Baden-Wuerttemberg. -In accordance with these plans, the jazz and pop courses, among others, are to be relocated from the Stuttgart Music College to the Mannheim Music College. -US borders: "Super tunnel" for drug smuggling discovered -On the border between Mexico and the USA, investigators have discovered and shut down a "super tunnel" intended for drug smuggling. -As announced by the US Customs Authorities, the pipe between Tijuana and San Diego, measuring more than 500 metres in length, was equipped with electricity, a railway track and a ventilation system. -Three suspects were detained in relation to the discovery, with eight tonnes of marijuana and almost 150 kilograms of cocaine seized. -According to the details provided, the tunnel had not yet been put into use. -"These cartels are stupid if they think that they can dig through under the radar," said the US Attorney for the District of Southern California, Laura Diffy, at a press conference held in front of a warehouse in San Diego, where the end of the tunnel was discovered. -Referring to the drug gangs, Duffy assured: "If you continue to build and attempt to use these tunnels, we are determined to make this a big waste of your dirty money". -Both the US authorities and the Mexican security forces are engaged in an ongoing battle against the drug cartels. -Since 2006, more than 77,000 people have been killed in conjunction with drug crime in Mexico. -Built by experts -Due to the elaborate configuration of the tunnel, investigators are working on the assumption that it was build by architects and engineers and that the construction took around one year. -The railway system was equipped in such a way that electrically powered carts could be used on it. -On the Mexican side, the entrance is located in a building located 80 metres from the border. -According to the authorities, a ladder runs 20 metres underground to the actual entrance of the tunnel. -The tunnel has a cross-section measuring 1.20 metres high and 90 centimetres across. -It would thus be suitable to assist illegal immigration into the USA. -Two of the suspects were detained in conjunction with the cocaine find. -The third, a Mexican, was detained due to the seizure of the marijuana. -All three face a maximum penalty of life imprisonment, according to the authorities. -Since 2006, eight tunnels of this type have been discovered, the press conference in San Diego continued. -However, this was the first time that cocaine was found in such a tunnel construction. -Normally cocaine is smuggled in smaller quantities and not through tunnels. -This shows the "desperation" of the drug gangs, whose traditional routes have now been cut off, said Bill Sherman of the DEA Drug Squad in San Diego. -They will do anything to make it to the USA. -No specific details were given regarding those detained, but it is reported that at least one is Mexican -They can expect prison terms of up to ten years. -2014 is to be a year of celebration in Geisingen -The anniversary celebrations in the town of Geisingen, to mark the 1,250th -anniversary of the first documented mention of the town, are drawing closer. -When the year comes to an end in just eight weeks, the anniversary year will be upon us. -Geisingen and Kirchen-Hausen were first documented together in the year 794. -A deed was drafted in Kirchen, in which both towns are mentioned. -On 15 March, the anniversary year will be officially launched, and because Geisingen currently has no suitable venues, this launch event will take place in the location where the two towns were first documented 1,250 years ago, in Kirchen-Hausen. -As part of the anniversary celebrations, a number of events are planned both in Geisingen and Kirchen-Hausen. -Kirchen-Hausen will celebrate the 1,250th anniversary with a celebratory weekend from 18 to 20 -July. -On the Friday there will be a ceremony, and on the Saturday and Sunday a party will be held at the Kirchtalhalle. -The Kirchen-Hausen Kirchenfest festival will also be held on this celebratory weekend. -Town Musical Director, Rudolf Barth, has composed a commissioned piece for the town on the occasion of the anniversary, which will be performed on three dates. -On 21 June, in conjunction with the Schwarzwaldverein [Black Forest Association] Mid-Summer Festival, on 12 July in the Arena, accompanied by fireworks, and on 13 December as the first concert put on by the Stadtmusik (Town Musical Society) in the new Festival Hall. -This venue will be completed and inaugurated in the anniversary year, with the official date set for the last weekend in September, on 27 and 28 September. -On the occasion of the Geisingen town anniversary, a new chronicle will also be published. -"However, it will not merely be a chronicle of Geisingen itself, but rather a printed work on the Geisingen of today, which now consists of Geisingen, Kirchen-Hausen, Leipferdingen, Aulfingen and Gutmadingen," said Mayor Walter Hengstler. -And the Geisingen of today is also celebrating a birthday in 2014, as it was in 1974 that the incorporations to form the current region were completed. -The new chronicle is to be presented on 21 or 22 November in the new Festival Hall in Geisingen. -However, 2014 is also a year of many anniversaries in other respects. -The Narrenzunft Strohglonki (Strohglonki Fool's Guild celebration) begins on 8 February with an evening of traditional customs on the occasion of its 60-year anniversary/ The Harmonie Gutmadingen music association will turn 90 years old and on 29 March will hold a celebratory banquet and then from 1 to 4 May will be celebrating the Bezirksfest [District Music Festival]. -The Leipferdingen Nursing Home will turn 40 years old, the Geisingen School has now been at its new location for 50 years and will be celebrating this on 10 May, and the Polyhymnia Leipferdingen music association will turn 150 years old and will be celebrating this as part of the Brunnenfest [Fountain Festival] from 4 to 7 July. -The town of Geisingen was first officially documented in the year 764, in a deed of donation, which is now in the possession of the St. Gallen Monastery. -The deed was drawn up in Kirchen (Hausen), which at the time was the location of the courthouse. -Both communities are thus mentioned in the same deed. -One reason for choosing 2014 to celebrate the 1,250-year anniversary in this way, is that it also coincides with the 40th anniversary of the Community Reform (Gemeindereform). -Council sets its sights on rail system -Should the "Am Hirschen" railway crossing be reconstructed at great cost, in order to increase traffic safety? -The Town Councils have decided that the answer is yes. -Schiltach will have to contribute up to EUR 220,000 to the project. -The Deutsche Bahn hopes to improve the Kinzigtal railway line in the coming year. -In particular, safety at railway crossings is to be increased - collisions between trains and other vehicles frequently occur at these across the country. -If the municipalities agree to the reconstruction measures, they will be required to contribute one third of the costs, with the remainder divided between Deutsche Bahn and the Federal Government. -Schiltach's town master mason, Roland Grießhaber, explained the peculiarities of the "Am Hirschen" railway crossing to the Town Council. -At the crossing, the town is planning to increase the height of the mouth of the tunnel to the west of the railway line. -Larger vehicles, for example those that transport long logs, would be able to wait there in the event of oncoming traffic, without having to block the railway line. -Grießhaber therefore suggested that a required supporting wall be constructed in such a way that the town could in due course widen the adjoining street in order to guarantee the smooth flow of traffic. -This was the subject of controversial discussion in the Town Council, as some council members cast doubt over the need to widen the road: Thomas Kipp summarised the perspective of the critics with this statement: "Do we have to hand over so much money on account of so few vehicles?" -Mayor Thomas Haas retorted: The "Hirschen" railway crossing is used regularly for the transportation of long logs. -Even wood from the "Kuhbach" area is occasionally transported via this route, as the vehicles, which measure 20 metres in length, would not be able to use the route via the Häberles Bridge, as they would not be able to make the turn on the Hauptstrasse. -Furthermore, when the Hauptstrasse is blocked, the route would also serve as a bypass for those vehicles that are not permitted to use the tunnel, argued Haas. -Given that it is likely that the Kirchberg and Schlossberg tunnel will have to be reconstructed and renovated, as is currently the case in Wolfach, the "Am Hirschen" railway crossing may become of major traffic significance for a longer period of time. -The councillors agreed to have an investigation conducted as to what costs the planned widening of the road would incur. -Nevertheless, the reconstruction of the railway crossing was consented to by a majority, on the provision that the required supporting wall be built in such a way that the town could, if required, extend the connecting road in the "Bahnhofstraße" direction to around 5.5 metres to accommodate oncoming traffic. -The reconstruction of the railway crossing at the Trautwein tannery could, on the other hand, bring with it an entirely different problem with regard to access to the steep "Geroltzhäuser Weg". -The Administrative Department had Deutsche Bahn's reconstruction plans investigated by the Breinlinger Engineering Office, as they had doubts as to whether the planned connection of the Geroltzhäuser Weg will be straightforward. -The investigation came to the conclusion that it could not be ruled out that the inclination of the road could result in longer vehicles becoming stuck. -The town, as the authority responsible for road construction, would then bear responsibility for this. -The reconstruction could therefore result in liability disputes. -Because both the transportation of wood and turning are both possible as things are, the reconstruction of the railway crossing should not be consented to, argued Haas. -At this point, the risk of a train collision with a vehicle positioned on the crossing is considerably lower than at other railway crossings, on account of the fact that there is a midpoint from which there is a good line of sight, directly alongside the stopping point. -The trains themselves, coming from the central station, must also be sufficiently slowed down so that there is no threat of collision. -According to the councillors, the reconstruction of this railway line was rejected by a majority, as the town of Schiltach would "bear the brunt of the cost" for the problem of vehicles becoming stuck, since the Deutsche Bahn would be able to deny any responsibility following the completion of the construction work. -The committee considers the reconstruction of the "Vor Heubach" and 'Vor Kuhbach" railway crossings, for which the crossing agreements have already been reached with Deutsche Bahn, to be unproblematic. -ESA makes decision on major space missions: Missions costing billions -Distant planets, gravitational waves or black holes - experts from the European Space Agency must now agree on two major projects that are to be launched in the coming years. -There were 30 proposals to choose from, five of which are still in the running. -Distant worlds. -Astronomers have already found more than 1,000 planets near other stars. -Whether or not life exists on at least some of these, no-one knows. -The choice is a difficult one: Should we search for gravitational waves? -Or instead conduct research into the origin of the cosmos? -Or search for planets similar to the Earth and thus perhaps discover extraterrestrial life? -These are the questions currently being asked by those responsible at the European Space Agency, ESA. -For their "Cosmic Vision" programme, they are looking for ideas for two major space projects, which should greatly advance research. -The budget for "L-Missions" such as this lies at around one billion Euro. -The projects are planned to start in 2028 and 2034. -At the beginning of September, the elite in the field of European space research met in Paris to discuss proposals for such missions. -30 ideas were up for debate, of which five have now made it onto the short list. -Numerous panels are assessing the proposals, posing questions to the researchers. -Over the coming days, a final decision is to be made by the Scientific Programme Committee. -In total, four main questions were chosen by the ESA, to which the missions of the "Cosmic Vision" programme, initiated in 2007, are to find the answers. -What are the conditions required for the formation of planets and life? -The precise processes for the formation of stars from large gas clouds - and thus the formation of the planets that orbit these stars - lie in proverbial darkness. -A large infra-red telescope in space could penetrate this darkness. -And if a star has planets, under what circumstances can life exist there? -In order to pursue this question, the "Cheops" space telescope is to be launched in 2017, with which 500 known planetary systems in proximity to us will be investigated in greater detail. -The possibilities are, however, limited - Cheops is a small mission with a budget of 150 million Euro. -With larger instruments, astronomers could map planets similar to Earth and even analyse the composition of their atmospheres, and thus search for "biomarkers": gases that serve as an indicator for biological activity. -How does the solar system function? -This question should also provide information regarding the preconditions for the origins of life. -The magnetic activity of a star, the interaction of its magnetic field and the emitted particle radiation play an important role. -It is possible to investigate these processes in our solar system, to serve as an example to be applied elsewhere. -With the "Solar Orbiter", the ESA has already approved an M-class mission (for which the budget stands at around half a million Euro) to address this matter. -The probe is to be launched in 2017 and will closely observe the surface and activity of the sun. -Jupiter and its moons are also of great interest to the researchers. -They want to find our what role the giant planet has played in the development of the solar system. -The ESA is planning that the first of the total of three L-Missions of the "Cosmic Vision" programme will address this subject. -The "Juice" (Jupiter Icy Moons Explorer) probe is planned to begin its journey in 2022. -Eight years later it will reach Jupiter and will investigate the atmosphere of the planet as well as the icy moons Europa, Callisto and Ganymede. -What are the basic physical laws of the Universe? -Outer space offers many possibilities for studying substances under extreme conditions and thus examining whether the laws of nature, as known to us, are still valid there. -There may also be deviations that could show physicians the path to a new theory, under the umbrella of which all known natural laws could be united. -How did the universe come about and what does it consist of? -13.7 billion years ago, our cosmos was created by the Big Bang. -Alongside the materials familiar to us, from which stars, planets and life forms such as ourselves are composed, there exists dark matter, the gravitational force of which holds galaxies and galaxy clusters together, and dark energy, the effects of which accelerate the expansion of the universe. -Thus far, researchers know neither which physical laws applied in the initial moments after the Big Bang, nor what dark matter and dark energy consist of. -With "Euclid", the ESA has already approved an M-mission, planned to commence in 2020. -With a special telescope, the probe is to examine the distribution of material in the universe across the entire sky, thus enabling conclusions to be drawn regarding the properties of dark matter and dark energy. -Proposals that are in the running... -Of the 30 proposals, only two will remain at the end of the selection procedure. -The proposal with the best chance of approval as an L-mission, in the opinion of many experts, is "E-Lisa", a demanding concept that aims to prove the existence of gravitational waves. -Many physicians perceive this to entail changes in the structure of space-time, which Albert Einstein predicted almost 100 years ago. -To date they have never been proved to exist. -The scientists behind the project hope that Elisa could achieve this. -The mission involves placing a detector, consisting of two or three probes, in outer space. -Unlike systems on Earth, it could also prove gravitational waves created during the Big Bang, and thus provide fresh knowledge regarding the origin of the cosmos. -Four other proposals are still competing for the second L-mission place: a large X-ray telescope called "Athena", which among other things would research black holes, "Icy Planets", another mission to the outer planets of the solar system, "prism", a mission for the measurement of cosmic background radiation, and the "Exoplanet Finder", which would find and investigate planets similar to Earth. -Among others, proposals that have already been ruled out include proposals for bringing samples back from Mars, conducting research on the moon, Venus and asteroids, as well as proposals in the area of solar physics. -Snowden ready to "cooperate" with Germany over US surveillance -Edward Snowden, the US intelligence whistleblower, has declared that he is willing to travel to Berlin to give evidence to the German parliament if the US National Security Agency and its director Keith Alexander fail to provide answers about its activities. -German MP Hans-Christian Ströbele on Thursday met Mr Snowden in Russia, where he has been granted asylum, to discuss him testifying in Germany. -A letter from Mr Snowden, presented to the media in Berlin on Friday by the MP, said: "Though the outcome of my efforts has been demonstrably positive, my government continues to treat dissent as defection, and seeks to criminalise political speech with felony charges that provide no defence." -However, speaking the truth is not a crime. -In the letter, Mr Snowden said he believed the support of the international community could persuade the US government to abandon criminal charges against him. -The charges filed by the US justice department include espionage and theft of government property. -Hans-Peter Friedrich, German interior minister, told Zeit Online: "If Mr Snowden is ready to speak to German officials, we will find ways to make this possible." -Relations between the US and Germany have come under strain following claims that the NSA bugged Chancellor Angela's Merkel's phone. -Thomas Oppermann, the MP who heads the parliamentary panel that oversees intelligence, said that if there were an opportunity to hear Mr Snowden as a witness "without bringing him into danger and completely ruining relations with the US," it should be taken. -Mr Ströbele, an MP for Germany's Green party, published a picture of himself with Mr Snowden on his Twitter feed. -He was accompanied on his visit to Russia by two German journalists. -Mr Ströbele said that, according to the former NSA contractor's lawyer, Mr Snowden would not be able to return to Russia if he left. -If Mr Snowden testified in Germany he would need assurances that he would be "safe" there, the MP said. -Mr Snowden said in his letter that he had faced a "severe and sustained" campaign of persecution that forced him from his home. -However he said that he was heartened by the worldwide response to "my act of political expression." -Citizens around the world as well as high officials - including in the United States - have judged the revelation of an unaccountable system of pervasive surveillance to be a public service. -The letter extends an offer to cooperate with German authorities "when the difficulties of this humanitarian situation have been resolved." -Frontier Airlines to charge for carry-on baggage -Frontier Airlines plans to charge up to $100 for passengers to store carry-on luggage on board their flight. -Frontier Airlines plans to start charging up to $100 for a carry-on bag and $2 for coffee or soda, although its announcement on Wednesday did say that passengers will get to keep the whole can. -The new carry-on fee is for bags in the overhead bin, so small bags under the seat will still be free. -Frontier said it will charge $25 if the fee is paid in advance, $100 if travelers wait to pay until they're at the gate. -Frontier spokeswoman Kate O'Malley said the $100 fee is to get travelers to take care of the charge in advance. -"We don't want to charge that," she said. -Airlines began charging for the first and second checked bags in 2008. -Passengers trying to avoid those fees have been stuffing as much as they can into carry-on baggage stashed in overhead bins, meaning those bins often run out of space. -Fees are one way to get passengers to bring less on board. -O'Malley said the new charge is not really about raising money. -It's about Frontier's most loyal customers making it very clear that finding overhead bin space has become increasingly difficult. -Passengers who buy their tickets on the airline's website won't have to pay. -That means one passenger in line at a Frontier gate might get to bring a bag on for free, while the next person in line might owe $100 for a similar bag. -O'Malley said Frontier's website and check-in procedures are being changed to make sure passengers know about the fee before they get to the gate. -Frontier's new carry-on fee won't start until summer, though a date hasn't been set. -Passengers often grumble about baggage charges and other fees, but airlines love them. -They argue that luggage costs money to handle, and passengers who want the service should pay for it. -Many on Wall Street view the addition of baggage fees as a sign that airlines are charging enough money to cover the cost of air travel after years of losses. -Most haven't touched carry-on bag fees, though. -Spirit Airlines Inc. started the first carry-on fee three years ago, and fellow discounter Allegiant Air later followed. -The only other airline with such a fee is Hungary's Wizz Air, said airline consultant Jay Sorensen, who closely tracks add-on fees. -He estimated in a December 2011 report that Spirit's carry-on fee brings in $50 million a year. -Sorensen, a former executive with Midwest Airlines, flew Spirit recently and wondered what he'd find at the gate as passengers encountered Spirit's unusual carry-on bag fee. -"The boarding process was the smoothest I had seen in my airline career," he said. -I was expecting to see gnashing of teeth and a fight breaking out at the gate. -The plane was full, he said, "and it boarded lickety-split." -Frontier is also following Spirit's $2 charge for coffee, tea, soda, or juice. -Frontier said passengers who get soda or juice can keep the whole can, and it will give coffee refills for free. -It will still give away water. -US Airways briefly tried charging for beverages in 2008 but backed down seven months later after passengers complained and no other major airline followed. -Frontier's move to charge the carry-on fee if passengers don't buy direct from the airline is its latest effort to steer customers toward its own website. -Airlines pay online travel sellers such as Orbitz $10 to $25 for each ticket sold. -That has given all airlines an incentive to steer passengers to buy directly from them instead of going through an online travel agency. -Frontier has gone the furthest in this area, though. -In September it began giving half as many frequent flier miles to customers who bought through an online travel agency. -On Wednesday it slashed the mileage award to 25 percent of the miles of the trip. -So, a 1,000 mile Frontier trip purchased from an online travel agency would earn 250 miles. -It also allows passengers to choose their seat in advance only if they buy directly from the Frontier website. -Frontier has a loyal base of customers in its home city of Denver, but its business is shrinking and losing money. -Revenue dropped 9 percent and its flying capacity shrank almost 13 percent in the first quarter, according to financial results released Wednesday by corporate parent Republic Airways Holdings Inc. -Republic has been trying to fix Frontier's finances as part of selling the airline. -Sayings come from the Bible -At least 40 women attended the last women's breakfast of the year, in the Evangelical Parish of Bisingen. -The theme of the day in the parish hall was "Sayings from the Bible" and "Expressions from the Middle Ages". -Zita Köhler, the Chairperson of the Parish Council, agreed to give a talk on Biblical proverbs after breakfast. -"Proverbs contain pearls of wisdom, commandments or warnings," she explained. -She compared several German sayings with the corresponding Bible verses and explained the meaning. -She referred to sayings such as: "Holzauge sei wachsam" (keep your eyes peeled), "wie Schuppen von den Augen fallen" (like scales falling from one's eyes), "ein Auge auf jemand werfen" (to cast an eye on someone), "den Seinen gibt's der Herr im Schlaf" (good things come to some when they sleep) and "seine Hände in Unschuld waschen" (to wash one's hands of something). -On several occasions, the speaker asked those listening to guess what proverb they were based on. -Expressions from the Middle Ages were addressed by Parish Councillor Christel Dehner: "Aller guten Dinge sind drei" (All good things come in threes), "blau machen" (to skip school/work), "etwas verhauen" (to wallop something). -She explained the meaning and origin of the expressions, which she referred to as "bridges to the past". -To round off the morning there was a tombola. -The prizes were placed, covered, on a table and were humorously paraphrased prior to being presented to the winners. -Mayoral candidate Roman Waizenegger was also to be found among the visitors to the joint breakfast. -Learning rather than unemployment: Tourism pilot project for the untrained -A pilot project for the tourist industry is being launched today in the Mecklenburg lake district. -Under the management of the Federal Employment Agency, 49 men and women who are already working within the industry as untrained workers, will be trained as qualified hotel and restaurant staff over the course of three winters. -"What's innovative about it is that the six-month courses end with diplomas," said Carmen Wiechert of the Neubrandenburg Employment Agency. -There are many benefits: The participants will not be unemployed, the companies receive trained skilled workers, which are in continually increasing demand in hotels and restaurants, and the agency will not have to fund unemployment. -Also involved in the project are the Chamber of Industry and Commerce in Neubrandenburg and the German Hotel and Restaurant Association (Deutsche Hotel- und Gaststätte, Dehoga) of Mecklenburg-Western Pomerania. -NSA Blames "Internal Error," Not Hackers, For Website Crash -The shadowy National Security Agency said late Friday that it was a glitch that brought down its public website for a few hours, not hackers as some claimed online. -"NSA.gov was not accessible for several hours tonight because of an internal error that occurred during a scheduled update," the spy agency said in an emailed statement. -The issue will be resolved this evening. -Claims that the outage was caused by a distributed denial of service [DDoS] attack are not true. -Earlier this evening online server trackers noted the NSA's website had been down for at least six hours, and the site continues to be inaccessible for some users. -Earlier an NSA spokesperson told ABC News the agency's internal, sensitive network was "not at all" compromised. -No classified information is in danger, the spokesperson said. -At least one hacktivist group online claimed that they were responsible for bringing down the NSA site with a DDoS attack. -DDoS attacks are designed to flood a target website with traffic until the servers are overloaded and the site collapses. -The cyber tactic is a relatively unsophisticated one and the attacks are not meant to penetrate the internal network of the target system. -The formerly super secretive NSA, once nicknamed No Such Agency, has found itself in very public light, and amid vicious criticism, in past months following a stream of revelations about is vast foreign and domestic surveillance programs - collectively the product of secret NSA files stolen from the agency and leaked by disenchanted former NSA contractor Edward Snowden. -Such growing controversy surrounding the agency prompted early speculation that tonight's incident was the result of a targeted cyber operation. -In October, the number of jobless fell slightly by 22, to a total of 1,307. -The rate of 3.1 per cent is indeed better than the previous year and is also better than in September, "however, we had hoped for more," said Monika Felder-Bauer, acting branch manager of the Employment Agency in Sonthofen. -Several companies have thus far reacted cautiously when it comes to hiring. -Stating a reason for this, Felder-Bauer said: "We have barely any skilled workers in the fields of skilled crafts and trades, healthcare and geriatric care." -Seasonal job offers for staff in hotel and restaurant businesses have been coming in since September. -The Winter season within the industry begins in mid-December. -The complete background report on this can be found in the "Allgäuer Anzeigeblatt" newspaper dated 31/10/2013 (page 33). -Bombardier profit dips as plane deliveries, orders fall -Canadian plane and train maker Bombardier Inc reported a 15 percent fall in net profit on Thursday, pressured by fewer aircraft orders and deliveries in the third quarter and contract issues in its train unit. -Montreal-based Bombardier also did not release any flight test data for its brand-new CSeries aircraft or offer an update on whether the plane will meet its ambitious schedule of going into commercial service by next September. -After the test plane's inaugural flight about a month and a half ago, it has only flown three more times, raising questions over whether the testing phase is on track. -Results fell short of forecasts and sent shares sliding more than 8 percent on the Toronto Stock Exchange. -Cameron Doerksen, an analyst with National Bank Financial, lowered his rating to "sector perform" from "outperform" on Thursday with the view that the stock has limited upside over the next one or two quarters. -"While the weaker aircraft deliveries were mostly anticipated, we are clearly disappointed by the margin performance in transportation," Doerksen said in a client note. -We believe that Bombardier will receive new orders for the CSeries as the flight test program progresses. -However, if no new orders are announced in the coming months, we suspect that the market will become more skeptical of the program. -Bombardier hopes the CSeries aircraft family can catapult it into the low end of a market now dominated by Boeing and Airbus. -The first test plane was unveiled in March and took flight for the first time in September after months of delays. -But firm orders for the CSeries are moderate so far at 177 as potential buyers wait for flight test results to validate the company's claims about the new jetliner's fuel efficiency and cost savings potential. -There are currently 403 total orders and commitments with 15 customers and operators. -Chief Executive Officer Pierre Beaudoin was confident Bombardier would meet its 300 firm order target by the time the first jet is put into commercial use. -Executives also reassured analysts and media on Thursday the program was progressing according to schedule. -"The test plane didn't stay on the ground longer than anticipated," Beaudoin said in a conference call, adding that ground tests and software updates were scheduled during the plane's downtime. -Every manufacturer schedules it in a different way. -We had decided to do a first flight and to do an update period and that's what we have done. -That will happen all through the flight program. -The second of five test planes is expected to take flight in the coming weeks, with the remainder following shortly after, the company said. -Still, analysts are skeptical the first customer can begin operating a CSeries plane 12 months after its maiden flight. -Bombardier said it was evaluating the entry-into-service (EIS) schedule and will provide an update in the next few months. -"This slow pace of flight testing - although in line with Bombardier's internal schedule apparently - reinforces our view that entry-into-service will be pushed to Q1/15," said Doerksen. -For the third quarter ended September 30, Bombardier's net profit fell to $147 million, or 8 cents per share, from $172 million, or 9 cents per share a year earlier. -Adjusted earnings per share were unchanged at 9 cents. -Revenue dipped marginally to $4.1 billion from $4.2 billion. -Analysts had expected earnings of 10 cents per share and revenue of $4.56 billion, according to Thomson Reuters I/B/E/S. -The world's fourth-largest planemaker said it delivered 45 aircraft during the quarter, down from 57 a year earlier. -Net orders fell to 26 aircraft, from 83. -The backlog in the aerospace division was $32.9 billion as of September 30, unchanged from December 31. -"In aerospace, results were in line with our guidance, but the low order intake and overall market conditions were a disappointment," Beaudoin said. -Aerospace revenue fell 13 percent to $2 billion. -Bombardier, the world's largest trainmaker, said revenue in that division rose nearly 11 percent to $2.1 billion. -The order backlog in the transportation unit was $32.6 billion as of September 30, up marginally from December 31. -The transportation division's margins were affected by execution issues in a few large contracts. -Executives said new guidance would be provided in the fourth quarter. -Shares of Bombardier, which also announced that Google Inc Chief Financial Officer Patrick Pichette would join the board, were down 8.5 percent at C$4.83 in mid afternoon trading on Thursday. -Brazil's Embraer SA, the world's third-largest commercial planemaker and Bombardier's closest rival, reported a 10 percent fall in quarterly profit on Thursday. -USA: Shots fired at Los Angeles Airport -On Friday morning (local time) an unknown suspect fired shots at the LAX Airport. -While the airport management announced via short messaging service Twitter that the shooter is in custody, the "Los Angeles Times" reported that the man is dead. -A member of security staff was also killed. -The media also reported that several people injured. -US President Barack Obama is being kept informed of the situation, said White House spokesperson Jay Carney. -The incident took place in Terminal 3. Eye witnesses reported seeing a shooter with a gun in one of the departure lounges, as was reported by several media. -One traveller told the broadcaster CNN about how many people had sought protection in panic. -Travellers screamed and children cried. -Panic broke out among those waiting, with many hiding behind seats in fear. -An employee of the "Los Angeles Times" who was travelling at the time told of how he had heard two shots fired. -An employee of security company TSA suffered a gunshot wound to the foot. -The scene was "like something out of a movie". -The man reported that he was then himself taken to a safety area. -The shots were said to have been fired near a security inspection area. -Airport building evacuated -Television footage revealed how numerous ambulances and police cars arrived at a terminal. -One hundred people were brought out of the building to safety. -Users on Twitter published photos of a SWAT officer and a weapon lying on the ground. -According to a message shared by the airport management via twitter, the incident began at 9.30 am (local time). -The access roads were blocked off, which, according to CNN, caused long tailbacks. -Except for the landing of arrival aircraft, flight traffic was temporarily suspended, it was reported. -However, several planes could be seen taking off on CNN. -Terminal 3 serves mainly small US airlines. -The Los Angeles LAX airport is one of the largest airports in the USA. -Cocaine-addict lawyer who tipped off Mr Big about police investigation is jailed -Basharat Ditta, 42, would feed information to crime lord Neil Scarbrough -The solicitor feared his secret drug addiction would be exposed -Was given a three-year prison sentence at Liverpool Crown Court -A top defence lawyer who told a drugs Mr Big about a major police investigation, because he feared his secret drug addiction would be exposed, has been jailed for three years. -Basharat Ditta, 42, would feed sensitive intelligence to crime lord Neil Scarbrough about inquiries into his drug trafficking activities after he became compromised by his cocaine habit. -The solicitor, who was nicknamed "Bash" and hailed by criminals as a "top brief," was arrested at his home in 2011 following a police surveillance operation into Scarborough, who he had represented in a previous narcotics trial. -Officers spotted Sarborough, 32, dropping off three bags of cocaine at the lawyer's home in Blackburn, Lancashire, while he was out at a Law Society dinner with colleagues. -Inquiries revealed Ditta was a "regular user" of the Class A drug after tests found traces of cocaine in his hair, on his wallet and on his credit cards. -Over an eight month period between January and August 2011 he sought to illicitly obtain information on the arrests of two men on behalf of Scarborough as well as one of his associates. -All four suspects were being watched by police at the time as part of a major investigation into a heroin and cocaine racket operating across Lancashire, Cumbria, Merseyside, Berkshire and West Yorkshire. -They and 32 other men were later jailed after police seized heroin and cocaine worth £1.5million along with more than £200,000 in cash during a series of raids. -Ditta, 42, fed information to criminals because of fears his cocaine addiction would be exposed -Today at Liverpool Crown Court Ditta, who works at law firm Forbes Solicitors, based in Blackburn, was facing disgrace after being found guilty of two counts of perverting the course of justice following a three week trial at Liverpool Crown Court. -He admitted cocaine possession at an earlier hearing. -The lawyer's downfall came after police investigating Scarborough discovered he had been in regular phone contact with Ditta in February 2011. -Two detectives trailed the suspect and spotted him arriving at Ditta's house in and was seen to place the drugs which had a purity of 60 per cent under the lawyer's bins in a black golf glove. -Soon after the drop off, Scarborough was in regular phone contact with Ditta who had been out at the dinner at the Blackburn Rovers football stadium, Ewood Park. -The lawyer returned home to discover the drugs and there were nine communications between them. -The court heard Ditta was a "regular user" of cocaine after tests found traces of the Class A drug in his hair, wallet and on his credit cards -Ditta was arrested later but denied using cocaine and and said he had been speaking to the suspected dealer because he was his client and argued their discussions were subject to "legal privilege." -During his arrest Ditta picked up his wallet and tried to remove several credit cards but they were all seized and a hair sample was taken fom him. -In a police interview he said he ran an office at his home address as well as work place and clients would call at his house on legal business. -But the court heard he would call major players in the drugs supply chain, some of whom he had previously represented, after key arrests to tell them what detectives knew about them. -Prosecuting, Anne Whyte said: "If anyone should know not to the break the law, it is a criminal solicitor." -Mr Ditta is accused of abusing his position as a criminal solicitor, having become too involved with specific clients. -The relationship we are talking about is not simply a drug dealer, but a drug dealer providing his own lawyer with drugs. -Some of his communications will undoubtedly have been legitimate ones because he was their lawyer. -But this went way beyond the ordinary confines of a lawyer-client relationship. -He thwarted the police's investigation as much as possible to enable them to continue in their criminal activities. -Mr Ditta was not honouring his profession, but dishonouring it. -He got too close to certain clients, in particular Scarborough, and he allowed his independence to be compromised. -Ditta denied wrongdoing and claimed: "If I was a corrupt lawyer, which I am not, and I wanted to feed information to Mr Scarborough, I would not wait 15 hours, I would do it immediately." -But after the hearing Supt Lee Halstead from Lancashire Police said: "Mr Ditta turned from criminal solicitor to a criminal himself the moment he started obtaining drugs from organised criminals." -His addiction to cocaine left him hopelessly compromised and vulnerable to the motives of leading members of organised crime groups who tasked him to obtain valuable information regarding police investigations. -Solicitors should uphold the highest standards of integrity and should instil trust and confidence in the public. -Mr Ditta has betrayed this trust and attempted to hide behind the veneer of his profession. -Lancashire's Serious and Organised Crime Unit led the investigation into Mr Ditta which has also seen him convicted of three counts of possession of cocaine and now perverting the course of justice, demonstrating our commitment to bringing criminals to justice. -Let this case serve as a warning to criminals that no one is beyond the reach of the law. -We will find you and put you before the courts. -Scarborough himself was jailed for 14 years after pleading guilty to conspiracy to supply heroin, cocaine and cannabis. -Thirty five other men involved in the racket were jailed for a total of 153 years for drugs offences. -On his website Ditta gave a question and answer session about himself in which he says his fantasy job would be a lawyer representing clients on Death Row in America, his ultimate dinner guest as being Mohammed Ali and inequality as his motivation for work. -Revolutionary Sacred Music Factory -To close the celebrations on the occasion of its 160th anniversary, the Liederkranz Dunningen (Dunningen Amateur Choir, or Glee Club, if you like) will perform Franz Schubert's "Deutsche Messe" (German Mass) to accompany two church services. -Following the great success of the musical, "Rock my Life", which enthused audiences this past Spring, the Liederkranz will bless two church services with the performance of Franz Schubert's Deutsche Messe, to close its anniversary year celebrations. -On Saturday 26 October, this exceptional mass will be heard from 7.00 p.m. during the evening mass in the St. Marinuskirche Dunningen and on Sunday 27 October, from 10.15, at the service in the St. Johannes Baptistkirche in Lackendorf. -The so-called 'German Mass', with the original title "Gesänge zur Feier des heiliges Opfers der Messe" (Songs to celebrate the holy sacrifice of the mass), D 872, is a spiritual work by composer Franz Schubert, dating from 1826, at at the time it was written was considered almost revolutionary. -Patron Johan Philipp Neumann has in mind for the mass to be performed as part of the church services. -Under the directorship of Hermann Schneider, the choir will be accompanied by the wind ensemble of the Frohsinn Tennenbronn Music Society, and by Noemi Lokodi on the organ. -Haslach: Scepticism visibly gives way to confidence -The 2015 Parish Council elections will bring about fundamental change, for example in the Haslach Pastoral Care Unit (Seelsorgeeinheit Haslach, SE). -There will then only be one joint Parish Council for all six parishes. -In a joint meeting on Saturday, the committees from Haslach, Hofstetten, Mühlenbach, Fischerbach, Steinach and Welschensteinach prepared for the changes. -This is because there are no further changes to be made to the fundamental decisions of the diocese and it is up to the Parish Councils to implement these in as satisfactory a manner possible. -During the course of the day one thing became particularly clear: the scepticism towards the new system is reluctantly yet visibly giving way to confidence. -The extremely constructive discussions between the six committees and the full-time members of the pastoral care team addressed topics ranging from perceptions and expectations regarding the 2015 reforms, to concrete proposals regarding the future composition and size of the Parish Council. -Regional Dean Georg Schmitt explained the newly drawn-up guidelines for pastoral care units in the arch-diocese of Freiburg with the help of a presentation. -According to the guidelines, the SE Haslach, as a church community, will be a corporation under public law, which shall assume the rights and obligations of the six individual church communities. -There will then only be one joint Parish Council and a Foundation Council, in which the rector will be represented by virtue of his office. -From each parish, at least two elected members are to be represented, with so-called church community teams set up on-site in the six parishes. -In future their task will be to promote church life on a local basis. -Regarding the issue of the finances of the individual parishes, the need for extensive discussion became evident. -The reforms plan for the accumulated assets in the parishes to be retained for a specified purpose. -Accounts payable, on the other hand, will be passed over to the "church community" as a whole. -In particular, planned major investments, the debts from which thus affect all parishes, were cause for great concern. -"In the past, investments were only made in projects that could be managed by the individual parishes by themselves," said Haslach's Parish Council Chairman Bruno Prinzbach. -Barbar Ritter, Chair of the Committee in the Schutterwald-Neuried pastoral care unit, has practical experience of a joint committee for five parishes. -In 2006 she set herself the task, "Become one, remain five" and came to realise that the task is really not that simple. -The Committees of the Haslach pastoral care unit set out to answer questions regarding the future composition and size of the joint Parish Council, and the practical work of the church community teams. -The specific details will be confirmed in subsequent meetings and decided in an additional joint meeting of all six committees in the coming spring. -At the end, Parish Counsellor Michael Schöner from Steinach presented the new logo of the pastoral care unit, which was agreed on by a clear majority. -Electricity to become more expensive in Pfullendorf -The citizens of Pfullendorf will have to adjust to high electricity costs. -As Managing Director of the municipal energy company, Jörg-Arne Bias, confirmed to the SÜDKURIER, families of four in Pfullendorf will be on the receiving end of a price increase of EUR 70 to 90 per year. -One major reason for the upcoming price increase is the rise of the so-called EEC Levy from 5.277 to 6.3 Cents per kilowatt hour. -This levy will be reset by the four operators of the large power grids, in October of each year. -They estimate the additional costs they will incur as a result of the "energy turnaround", which they will be able to recover by means of a levy on the price of electricity. -The Pfullendorf municipal energy company forms the last link in this chain. -In its projections, the supervisory council of the energy company even assumed a levy higher than that reported by Jörg-Arne Bias. -The specific figures must now be calculated before the municipal energy cmpany can inform its customers of the precise price increase next week. -"We are working on the assumption of an increase of 1 cent plus x," said Bias, with the figure likely to be closer to 2 cents. -Traffic lights permanently red: observe waiting time -Sometimes a traffic light simply never turns green. -The contact loop on the road does not react, or the light itself is defective. -What should you do - drive through on red? -If an affected person has waited for a sufficiently long period of time at a red light, and provided the crossing is clear, they can drive on, explained Stuttgart-based lawyer Ralf Becker in "Motorrad" (Motorcycle) magazine. -The waiting time must, however, be "appropriate", which can be interpreted differently on a case-to-case basis. -Becker advises that you wait at least five minutes before a fault with the traffic lights can be assumed. -Anyone who drives on in spite of a red light must, however, be aware that he/she will be fully liable for any resulting accident. -Anyone who waits for less than five minutes has, under certain circumstances, not waited long enough, warned Becker, referring to a ruling of the Hamm Higher Regional Court. -In the case in question, a driver ignored a light that was permanently red after around three minutes and and was held responsible for negligently jumping a red light. -The traffic lights were actually not defective, but rather turned green just a few minutes later. -He was spared a driving ban on account of the special circumstances - but was not spared a fine. -Town Council delighted with solid budget -Every time discussion in Town Council meetings turns to the budget, Mayor Ralph Gerster has good reason to smile. -The municipality coffers in Herdwangen-Schönach are well stocked and the administration has been debt-free since 2005. -In this sense, this the 2012 budgetary year, can also be recorded as a complete success. -This became clear upon presentation of the annual accounts for 2012. -As Andrea Rothmund explained, the administrative budget came in at more than EUR one million higher than originally planned, at around 7.6 million Euro. -The administrative budget surplus, at around EUR 1.8 million, also stood at around EUR one million above projections. -The surplus is primarily due to higher trade tax income and the local authorities' contribution towards income tax. -The capital budget also stood well above the projected values. -Instead of the projected EUR 1.5 million Euro, the value for last year was EUR 2.5 million. -"This is mainly due to the higher contribution from the administrative budget," said Rothmund. -Because fewer investments were made in the 2012 budgetary year than planned, the reserves also came in higher. -In total, they managed to increase the general contingency reserves by around EUR 2.1 million, to a total of EUR 4.9 million. -Mayor Ralph Gerster thanked Rothmund and its team for the good work. -"A job like this is not something you achieve overnight," said Gerster in praise of the annual financial statement. -He and the Town Councillors are clearly delighted with the good figures. -It may sound like a paradox, but it is precisely these good figures that could soon work out expensive for the citizens. -In the past, a good financial situation meant that the local authorities could do away with high municipal rates. -However, according to Gerster, from time to time this has resulted in subsidies being reduced by the state. -There are prerequisites that must be fulfilled in order to receive these subsidies from various compensation elements. -Among other requirements, the towns and municipalities applying for the subsidies must levy certain municipal rates. -"We have to address the subject of municipal rates, as there are investments to be made in the coming years for which we could do with subsidies," commented Gerster regarding this matter. -Nagold: Hugo Wermter - a choir member for 60 years -Having been a singer for 60 years, Hubert Wermter's story with the Cäcilia Male Choral Society is almost twice as long as that of the Autumn Festival, during which he was ceremonially honoured. -As per usual, Hubert Wermter stood on the stage in the colours of his male voice choir. -Even after 60 years he still finds great pleasure from singing with the choir, which has achieved much more than popularity during this period. -Wermter has also been active within the association, serving as a member of the committee for 28 years, among other roles. -For his commitment and loyalty to the organisation, he has also received several awards: Jörg Kohr, Lay Pastor acting on behalf of the Cäcilian Association of Rottenburg-Stuttgart, presented him with an Association Certificate and a letter of commendation signed by Bishop Gebhard Fürst - the choir also serves as a church choir. -His choir colleagues presented him with a German Choral Association certificate and an engraved pewter plate. -Aside from honouring Hugo, the 31st Autumn Festival progressed as usual: Alongside the Betra Male Voice Choir, the Salzstetten Choral Club, the Baisingen Choral Division and the Local Music Society, the Vollmaringen singers delivered a colourful blend of different choral and song styles, which entertained the 400 or so visitors. -The Vollmaringen Male Voice Choir got things running with atmospheric songs such as "Im Weinparadies" and "Lustig, ihr Brüder". -They had also prepared a short serenade as a tribute to their honorary singer Hubert Wermter - Wolfgang Amadeus Mozart's "Bundeslied". -The Betra Male Voice Choir also sang classic choral pieces such as "Jägerwerben" by Julius Wengert, "Weit, weit weg" by Hubert Goisern and the folk song "Wann du durchgehst durchs Tal". -With its mixed choir and its ladies' ensemble - the "Impulschor" (Impulse Choir), the Salzstetten Choral Club ensured that the female quota was fulfilled at the festival. -The mixed choir carried listeners away to foreign lands with the Neapolitan folksongs "Santa Lucia" and "Eviva Espana" by Antos Gus, while the "Impulschor" also drew on foreign languages: "Liberatio" and "Hey Jude" were included on their programme. -The Choral Division of the Baisingen Sports Club returned to its home ground with "Was isch der Schwob?" by Hans Süssmuth and Robert Pappert's "Bierlied", while the Vollmaringen Music Society provided the crowning musical conclusion. -For children, there was a small auxiliary programme - "Oma Hanne" (Grandma Hanne), a.k.a Hannelore Stollsteimer, performed a 'Kasperl' theater (similar to Punch and Judy) and read some stories, which kept the children entertained throughout the afternoon. -Children's dreams come true -Over the next few days, the nursery schools and day care centres in Zollernalb district will be receiving mail. -The Schwarzwälder Bote (Black Forest Herald), in collaboration with Bauhaus and the Sparkasse Zollernalb Savings Bank, would like to support the institutions in granting their construction and renovation wishes. -Does the nursery school need a new sand box? -All nurseries, day care centres and créches from across the entire Zollernalb district can become involved in the campaign. -If they have a construction or renovation request for their institution, they can register this with the Schwarzwälder Bote. -Get creative with your children and surprise the judging panel of the Kindergarten-Baustelle (Nursery School-Construction Site) campaign. -A panel consisting of representatives from the Schwarzwälder Bote, Bauhaus and Sparkasse Zollernalb will select three wishes from those submitted. -What are the requirements for participation? -It must be possible to fulfil the wish using construction materials to a maximum value of EUR 2,000 and on one (campaign) day. -Furthermore, the winner must provide a number of hard-working building helpers. -What is the deal with the campaign day and the helpers? -With the support of the Sparkasse Zollernalb, Bauhaus will be providing the materials. -They will be delivered to the nursery schools on an agreed day and should there be assembled/made use of directly by the helpers. -Helpers can include parents, grandparents, friends, organisations and, of course, the teachers and children themselves. -Kenyan press outraged at controversial media law -"It is a frightening place, and it is valid to ask: what is there to prevent Parliament from simply sweeping away the independence of the judiciary tomorrow?" the paper said, challenging the bill as unconstitutional. -"This law is draconian and very punitive and we reject it," said Cyrus Kamau, managing director for Capital Group - home to CapitalFM, one of Kenya's most respected independent radio stations and news websites. -He said the new media tribunal "will always be biased because it's an extension of the government," and that restrictions on content and advertising would damage Kenya's place in the global economy. -"I hope the president will listen to us, and we appeal to him to reject this bill and return it to the MPs," he said. -According to The Star newspaper, the new bill will effectively hand the government "a stranglehold over the media," while The Standard said democracy and free speech in Kenya had been "dealt a major blow" and lambasted the bill as "draconian." -The passing of the bill comes amid a string of measures to reinforce national security in the wake of the September's attack by Islamist gunmen on the Westgate shopping mall. -Kenya media drew the ire of authorities by broadcasting security camera footage of troops who were dispatched to the scene of the attack purportedly robbing the upmarket mall. -Police chief David Kimaiyo reacted by summoning two journalists and a media executive for questioning, although the summons was retracted following a media outcry. -Under the new bill, media houses can be fined up to 20 million Kenyan shillings and individual journalists up to one million with the additional risk of being "de-listed," or barred from receiving official press accreditation. -The tribunal also has the power to seize the property of an offender if a fine is not paid. -According to the Daily Nation, "even one fine is enough to cripple most FM stations." -It also said the measures could have a devastating effect on what it described as Kenya's "lively blogosphere." -By silencing the media, politicians know they can do whatever they like with impunity. -"No one will ever know," wrote Nation journalist Mutuma Mathiu, describing the Kenyan media as a key source of checks and balances in public life. -"Left to themselves, politicians would bankrupt the country and take us back to hunting and gathering," he wrote. -Kenyan lawmakers have been the target of public anger in the past. -In May they voted to overturn cuts ordered by the national salaries commission and reinstate their hefty salaries of around 532,000 shillings a month tax-free - ranked among the highest in the world. -Bad-Dürrheim: A dream comes true for the FC -The official inaugural ceremony for the astro-turf sports field did not wane in significance for the town and the football club, in spite of the cold weather and rain. -Everyone had hoped for more pleasant conditions for the ceremony last Saturday, however the weather gods chose not to cooperate. -So anyone who came without an umbrella, or for whatever reason could not hold onto one, drew the short straw. -This was the case for the musicians who accompanied the ceremony, as well as for a number of speakers. -Thus, FC Chairman Albrecht Schlenker, who spoke of the fulfilment of a dream, also got fairly wet. -His thanks went first and foremost to Mayor Walter Klumpp, who was heavily involved in the realisation of the project, the Town Council, the companies involved and club members who played a part. -Particular mention went out to Lothar Held, Paul Weizenegger, Heiner Gail and Peter Graf. -The spry pensioners laid 400 square metres of flagging for the new paths. -This remarkable voluntary commitment was also honoured by Member of the State Parliament, Karl Rombach. -Further words of greeting were issued to the town Sports Commissioner, Hubert Baier, the District Chairperson of the Southern Baden Football Association, Kuno Kayan, and Friedrich Knorr from the planning office, who announced a donation to the club's youth division. -Mayor Klumpp recalled that weather-related influences in the Spring and Autumn had repeatedly afflicted the original sports field, to such an extent that it could not be played on. -It was way back 15 years ago that solutions to the problem were first considered, when the construction of a third pitch on the football club site or at the secondary school on Salinensee Lake were considered. -Both ideas were rejected. -Six years ago, thoughts turned to laying an astro-turf pitch, which was generally viewed as the perfect solution. -Specific plans were drawn up two years ago, when the FC inherited the Schabelstube in leasehold, promised a contribution of EUR 100,000 for the construction of the pitch and the state approved a subsidy of EUR 104,000. -In October 2012, the Town Council decided to cover the remaining EUR 356,000 of the total cost of EUR 560,000, summarised Klumpp. -Work began on the 68 x 108 metre astro-turf pitch on 4 June. -During the construction phase, which lasted over three months, 3,000 cubic metres of earth were removed, of which half was used for the laying of a dirt bike track. -Union and SPD have continued their coalition negotiations, addressing the topics of internal affairs and justice. -To this end, the competent working group met this morning. -Among other issues, topics discussed included direct democracy, the bribery of MPs and the Federal Police. -During the morning, the Migration and Integration working group also sought to continue its discussions. -Among the issues to be addressed was that of dual nationality, in which regard both sides have opposing ideas. -The SPD would like to put an end to the pressure placed on immigrant children to decide on one nationality - the Union rejects the notion of a dual passport. -Regarding the subject of traffic, which was not on the agenda for this Friday, the SPD substantiated its opposition to a car toll charge promoted by the CSU. -A toll road sticker would amount to a "flat rate fee for frequent drivers and would thus be environmentally counterproductive," explained the SPD negotiator for issues relating to traffic, Florian Pronold, in a letter to his parliamentary group. -There would be a threat of diverted traffic on country roads, which are already accident hotspots. -The introduction of a toll for cars would also only be a first step, in Pronold's eyes. -"There is the risk that the compensation for Germans would be abolished at a later point in time," he said. -Dieter Thomas Kuhn in Stuttgart: The "singing mountain wave" on the open-air stage -Stuttgart - "When will it be summer again?" was actually not the question on people's lips at the Killesberg Open-Air Theatre on Friday evening. -It genuinely was a tropical evening in Stuttgart. -This therefore provided the ideal conditions for pop star, Dieter Thomas Kuhn, and his band. -Click through the image gallery of the performance by the "singing mountain wave". -Car driver seriously injured in accident -A 37-year-old car driver from Aachen was seriously injured in an accident on Thursday evening. -According to information from the police, a 41-year-old from Müsch was travelling in his car on the Kempener bypass in the direction of Grefrath, at around 9.15 p.m.. -As he went to turn right onto St. Töniser Straße he failed to see the 37-year-old's oncoming vehicle. -The vehicles collided. -The Aachen resident suffered serious injuries and had to be taken to the hospital for treatment. -Konstanz: Road block following accident on the Schänzle roundabout -According to police, the accident occurred when a 51-year-old driver of a Swiss Seat Ibiza was travelling out of town in the left lane of the two-lane Reichenaustrasse. -Shortly before the Schänzle roundabout he noticed that was in the left-turn lane going in the direction of Stromeyersdorfstraße and moved across to the right lane. -In so doing, he collided with the BMW of a 23-year-old Konstanz resident, travelling in that lane. -The VW Passat of a 19-year-old driver travelling behind the BMW was also damaged. -In total, EUR 15,000 of material damage was caused, announced the police. -The road had to be blocked off while the accident was being investigated, the statement continued. -Vienna Airport abolishes passenger call-outs -Mr. Max Maier, please make your way to Gate 24. -In future, announcements such as this will no longer happen at Vienna International Airport. -The majority of loudspeaker announcements will cease - thus considerably reducing the noise level. -The goal of this adjustment is to create a more relaxing atmosphere. -As airport spokesperson Peter Kleeman announced to Radio Vienna that in adjusting the approach towards announcements, Vienna Airport is following an international trend. -Among others, the airports in Kopenhagen, Frankfurt and Dubai have already done away with the noisy call-outs. -By doing away with these announcements - on average around 200 personal call-outs come through the loudspeakers per day - the atmosphere in the airport should be calmer in future. -"Experience has also shown that these individual announcements are relatively seldom taken notice of, and at the same time, all other information regarding changes to departure gates or flight times are somewhat lost among them," said Kleeman while chatting to the radio broadcaster. -Highland Games in Kaltenhof -Alongside brake van pulling with tractors, there will also be a second contest at the agricultural autumn festival at Kaltenhof from 6 to 8 September. -On Saturday 7 September, the Highland Games will start at 1:30 p.m. -While they are of course a Scottish tradition, they have been given a touch of Swabian colour at the Kaltenhof event grounds. -Teams and individual competitors will be pitted against one another in the disciplines of tossing the caber, horseshoe tossing and bucket carrying. -The caber is four and a half metres long and weighs 25 to 30 kilometres. -"It isn't about the distance," explains organiser Peter Saile. -The log must be thrown vertically, somersault once and then finish lying straight. -This scores maximum points. -In tossing the horseshoe, it all comes down to accuracy. -Horseshoes are thrown onto a peg from a distance of eight metres. -Participants must then prove their skill and strength by carrying a bucket of water 50 metres. -Time is recorded and water quantity measured in a vat at the end. -Two members of each "clan" or team can then take part in the individual competition. -The winners of the team and individual contests receive prizes. -Teams can practice from 10:00 a.m. -Suitable clothing is desirable - for example competitors can wear kilts. -However, Swabian traditional clothing is also permitted. -The judging panel will also award a few bonus points for "outfits". -Registration is open until a few hours prior to the beginning of the competition. -With the Scottish-Swabian Highland Games and the tractor pulling contest, which will begin half an hour beforehand, there promises to be a great deal of action at the event grounds on Saturday. -In the evening, fans of brass band music will be in for a treat of their own. -At 19:00, the "biraböhmische" (a play on words alluding to the regions pear trees, Bohemia, and perhaps their love of beer as well!) wind music group from Schömberg, provide a great evening of entertainment. -The band plays lively polkas and marches. -However, their repertoire also includes emotive waltzes and a full big band sound. -Heinz Koch from Weilen unter den Rinnen will also be there, better known as the 'singing inn keeper and black sausage baron". -In the past, he has appeared at the Christmas market in Dornhan and the Narrenzunft (Fool's Guild event). -On Sunday, the Leinstetten Music Society will entertain audiences with a morning session, with the Böffingen Farmer's Band as guests from 2:00 p.m., and then the "Oldies" will bring the celebrations to a close. -The celebrations will get under way on Friday, with the "Stoppelacker" (Stubble Field) party featuring DJ Ralf. -Other items in the itinerary on Saturday and Sunday include the vintage car and tractor meet and exhibitions by companies on the topic of agriculture and forestry. -Further attractions on Sunday are the corn maze, the craft market and sheep shearing. -Specially for children, there is a petting zoo, "straw bouncy castle", tractor driving (under supervision) and tractor surfing. -The event is organised by the community hall association, the ski club and the "Zündkapsele" (blasting caps) tractor enthusiasts. -An overview of milk and egg alternatives -Vegan nutrition promotes vegetable alternatives to eggs, milk and dairy products. -Mashed tofu with plant cream, for example, serves as a substitute for quark cheese. -The Vegetarian Association will provide further examples on World Vegan Day on 1 November. -On the occasion of World vegan Day on 1 November, the Vegetarian Association of Germany will propose a host of vegan alternatives: -Pure plant margarine is a good alternative to butter, while yoghurt can be replaced by soya yoghurt. -Soya, oat, almond or rice milk can be used in place of milk. -Plant whipping cream can replace traditional whipping cream. -There are also plant alternatives to eggs. -Half a mashed, ripe banana, for example, can replace an egg as a binding agent in a cake. -50 grams of apple sauce or a tablespoon of ground linseed plus three tablespoons of water are also suitable. -A dough becomes fluffy by replacing egg with a mixture of one tablespoon of baking powder, one tablespoon of cornflour and three tablespoons of mineral water, for example. -50 grams of soya yoghurt or silken tofu can also fulfil this purpose. -According to information from the Vegetarian Association, around 800,000 Germans currently have a vegan diet, i.e. a 100% vegetable diet. -Sleepless in New York -On the way to their host families in Weymouth, Massachusetts, the pupils of the Schwarzwald Grammar School got to know the American city of New York. -After an eight-hour flight, they headed straight for Brooklyn Bridge, via which the heart of the city, Manhattan, can be reached on foot. -From here there is a fascinating view of the world-famous skyline, which is particularly impressive in the evening light. -Due to the time difference, the pupils had been on the go for almost 24 hours, however, true to the slogan, "the city never sleeps", the day was rounded off with a trip to Times Square. -In spite of a shutdown, they were able to visit the Statue of Liberty on the second day, and climb the statue on foot. -They then moved on to the Financial District, via Wall Street, to visit the 9/11 memorial. -On the third day, the pupils were able to take in an overview of the fascinating city from the viewing platform of the Empire State Building, enjoying the views from a height of 373 metres. -They then made their way through the hustle and bustle of the New York streets, via the Rockerfeller Center, to Central Park, which provided the perfect opportunity to rest a while in the Autumn sun. -Following a short flight from New York to Boston, the pupils have now arrived with their host families and are experiencing the school day at Weymouth High School. -Once again this year, on Saint Hubert's day, a Hubert mass will be celebrated in the Parish of St. Marien on Hülsbergstrasse. -St. Hubert was, according to legend, a truly ruthless hunter, who converted following a vision and was later appointed Bishop of Lüttich. -Hunting horn group "Jagdreiter Westfalen", under the directorship of Brigitte Kluth, played old, traditional French melodies on parforce horns (natural horns in the key of E flat). -The wind players wear riding clothes, as they provide musical accompaniment mainly for riders, horses and packs of hounds on drag hunts. -The solemn mass is to take place on Sunday 3 November at 11.30 a.m. -The newly designed Opschlag stands out with a new café. -One that invites you to linger a while and to come again. -One that wants to get its name out there. -According to dictionaries, déjà vu is defined as a psychological phenomenon, which expresses itself in the feeling of having already experienced or seen a new situation. -In the case of a visit to the café of the same name at the Opschlag in Kleve County, with a view of the Rhein-Waal University, it doesn't always have to remain a pure déjà vu feeling. -Ultimately, this is precisely the aim of the café with the wonderful name: to make an actual visit and something that can be repeated on a recurring basis, not just theoretically, but tangibly. -Because it is fun and simply beautiful. -A glance into the internal workings at Opschlag 8 gives the onlooker a positive feeling right from the outset. -With its ultra-modern yet cosy café ambience, it looks like a place designed to make you feel good. -The team here, with business founders, Mirjam van der Stelt and Daniel Büttner at the helm, spoils guests with a wide range of coffees and cocktails. -In addition to this, there are delicious cakes or freshly baked baguettes, filled to order. -The owners, both 33 years old, have already made a name for themselves as restaurateurs prior to opening the chic café on the Opschlag: They previously managed the "Art Lounge" in Kranenburg. -By moving to Kleve on the Opschlag, the friendly café lovers hope to reach a wider audience. -Not least through university customers. -"We don't have a specific target group," emphasises Daniel Büttner. -In actual fact, here the pair hope to appeal as much to young students as they do to senior citizens and all age brackets. -Speaking of café lovers... -The two friendly owners are motivated by their love of coffee. -They themselves enjoy the fragrant, hot brew in a variet of formats. -Espresso fan -Daniel Büttner is more of an espresso fan, while Mirjam van der Stelt is the cappuccino drinker. -"And a baguette with it - that's my thing," she revealed to the NRZ. -There have already been two events held in the brightly lit café. -Or perhaps more accurately, one. -This is because the Hafenfest took place more outside than in - Café Déjà Vu did not benefit from it. -However, there was also the Ringelnatz evening. -The two 33-year-olds found it "beautiful, successful and funny". -But it was not for everyone. -Perhaps this is why Daniel Büttner is not a vocal fan of evening events: "You just cannot please everyone". -And with an event, I am always only appealing to those who have expressed an interest in the special event. -For this reason, events will always be an exception to the rule for us. -"We want to cause as little disruption as possible to normal business". -That is to say: Déjà Vu also stands for reliability. -This is with regard to the quality of the products that are offered here, as well as the team and the fantastic setting on the Opschlag in Kleve. -The street that is fast becoming a 'restaurant mile'. -This is to the delight of many Kleve residents, guests from near and far and the students who are discovering the 'restaurant mile', and with it the Café Déjà Vu, for themselves. -Anke Gellert-Helpenstein -A day of thanks with the MGV (Male Choral Society) in Dinker -This year the "Friedrich Wilhelm Dinker" MGV issued an invitation to its 165th anniversary celebration, which 52 guests, active singers and their wives were happy to accept. -In the clubhouse at the "Witteborg" Inn in Dinker, all those present enjoyed the friendly get-together in the name of music. -Traditionally, the anniversary celebration is also always the fitting occasion to bestow honours. -This time around, secretary Rolf Wagener was delighted to receive the special "Singer of the Year" award. -"Not only have you kept countless records for us, but you have also done so much running around for us, and for this we offer our sincere thanks," said Choir Chairman Erich Schlotmann. -The awards for participation in the performances and choir rehearsals by means of the presentation of "activity posies", this year went to Choir Director Dieter Schulze, with 44 attendance marks, followed by Honorary Chairman Horst Pier-Ribbert, the Chairman himself, and last year's "Singer of the Year" Friedrich Winkler, each having attended 42 times. -Fifth place went to Artur Brückner, responsible for organising music copies. -The Board of Directors gave special thanks to the team managed by innkeeper, Ange Pier-Ribbert, which once again delighted those present with a top class menu, and to Gerda Pier-Ribbert for the table decorations. -The "Friedrich-Wilhelm" MGV meets for its regular choir rehearsals in the clubhouse every Tuesday at 19:45. -"New voices that love to sing are more than welcome any time," said Schlotmann. -UN hails new goals to tackle poverty -The United Nations is to start work immediately on a new set of goals to replace the millennium development goals, which were put place 12 years ago to tackle global poverty. -Australian diplomats played a key role in pushing for "sustainable development goals" to replace the MDGs, which expire in 2015, ahead of the UN sustainable development summit that began in Rio de Janeiro overnight. -They were included in the final draft of the document, which will be endorsed by world leaders including Ms Gillard during the summit. -UN Secretary-General Ban Ki-moon told the summit overnight that now is the time to "rise above national interests." -"I am pleased that member states have agreed to launch and take ownership of a process to establish universal sustainable development goals - SDGs," he said. -These SDGs will build on our advances under the millennium development goals, and they will be an integral part of the post-2015 development framework. -I will spare no effort to implement the mandate given to me by member states to realise our vision of sustainable development goals that build on the success of the MDGs. -Even though the SG Achim/Baden has gone four match days without victory, a positive mood is still prevailing within the top flight handball team. -Trainer Tomasz Malmon will not let there be any doubt of this fact. -Everyone is still fully motivated for the task at hand. -That said, I do hope that we finally start winning again. -"I no longer remember how a victory like that tastes," said Malmon, anticipating success for his team at VfL Fredenbeck II. -Even though the third-tier reserves from Fredenbeck are no't exactly in the best form at the moment, Malmon has issued a warning in advance -I trained many of the players myself in the A-Youth. -Consequently, they will be particularly motivated playing against their former coach. -"In addition, we will have to wait and see whether reinforcements from the first team will be there," explained the SG coach. -In any case, if they are to finally get back on the winning track, Malmon's team will have to improve on recent performances. -There have been major problems, primarily in terms of covering and counter attacking. -The team's eye for the goal must be considerably improved. -Therefore, I hope that my players have finally managed to clear their heads during the short break. -Mozambique security concerns mount as powerful personalities clash -With a statue of Samora Machel, Mozambique's founding president, staring down on them, thousands of people gathered in central Maputo to chant peace slogans in a rare public demonstration. -"We want peace back; we want stability," said Vanessa de Sousa, chief executive of an investment company. -Fearful about the future of her country, she swapped her corporate attire for a T-shirt emblazoned with "we demand security" in Portuguese as she joined the crowds in the capital's Independence Square on Thursday. -For two weeks, there have been almost daily reports of clashes between government forces and Renamo, some of the worst skirmishes since a peace deal more than 20 years ago. -Renamo was once a notorious rebel movement, initially backed by white-ruled Rhodesia and then South Africa's apartheid government as part of efforts to destabilise the country's independent government. -After a 1992 peace deal, it became an opposition party. -Analysts believe the country is unlikely to slide back into full-blown conflict, but recent events have unnerved foreign investors and locals. -The stakes are high for the fast-growing economy as the discovery of huge offshore gas reserves and coal deposits in the northwest could bring in more than $50bn of investment over the next few next years from companies including Rio Tinto, Vale of Brazil, Eni of Italy and Anadarko of the US. -The ruling Frelimo party, the dominant political force since 1975, and Renamo blame each other for the tension. -Renamo says the government initiated the latest clashes by launching an attack on its members in Sofala province, traditionally a Renamo stronghold, on October 17. -Assaults on the former rebels then escalated as government forces attacked Renamo bases and attempted to kill Afonso Dhlakama, the group's leader, Fernando Mazanga, Renamo's spokesman, told the Financial Times. -The government blames Renamo for triggering the clashes, accusing it of attacking soldiers. -President Armando Guebuza has sought to play down concerns about instability. -Mr Guebuza told AFP, the French news agency, on Wednesday that Mr Dhlakama saw himself as a "loser" who wanted to use "whatever remains of his forces to try to prove that he can impose on the government his own decisions." -Both Frelimo and Renamo insist they want to avoid war. -But concerns have grown after Mr Mazanga was quoted as saying Renamo was abandoning the 1992 peace accord. -He told the FT that he meant the agreement was no longer being respected by Frelimo. -"Our vision is to come back to negotiations, but with seriousness," Mr Mazanga said. -Previous talks between the parties have done little to ease tensions fuelled by a series of clashes this year. -"It's two big men (Guebuza and Dhlakama) butting heads together," said Joseph Hanlon, a lecturer at the Open University and Mozambique expert. -Neither of them are good negotiators and neither of them are prepared to make the kind of concessions that are necessary. -Renamo, which has been pushing for electoral reforms, had already said that it would boycott municipal elections due in November. -Presidential and parliamentary polls are scheduled for next year. -Some commentators have interpreted its use of force as the attempt of an ailing movement to win concessions and financial spoils from the government. -Renamo's share of the vote has been declining since 1992, while a newer party, the Democratic Movement of Mozambique (MDM) which was formed by a former Renamo member, is expected to improve its showing at the elections. -Mr Mazanga says Mr Guebuza - who is due to step down when his second term ends next year - wants to destroy the country's democracy. -"He does not want multi-party democracy, he does not want transparent elections he does not want peace because he does not want to leave the presidency," Mr Mazanga said. -It is unclear how much capacity Renamo has, but it has carried out disruptive hit-and-run attacks on police stations and vehicles on a key north-south highway. -Most of the skirmishes have taken place in Sofala province, which is several hundred kilometres north of Maputo, but hosts Beira, the port that miners, including Rio Tinto and Vale, rely on to export coal. -In June, Rio suspended its use of the railway for about a week after Renamo threatened to attack the line. -Mr Mazanga was coy when asked about whether Renamo would repeat this threat. -Renamo wanted to "warn the international community that things were not going well in Mozambique," Mr Mazanga said. -The instability has added to frustrations with the government, says Fernando Lima, head of Mediacoop, an independent media company, with many people also concerned about corruption, the slow pace of development and a recent spate of kidnappings. -"People think the ones responsible for the future of the country are the government and the president, and he should be the one to find solutions for the problems," he says. -Omar Sultuane, a demonstrator, said people just wanted stability. -"No one cares about Renamo and Frelimo, they just want peace again, they want free access to the roads," he said. -Children should be taught myths and legends as "models for a way of life", author says. -Tales of Thor could show "brute strength is no match for subtle trickery," while the Arthurian legends reveal the importance of having a dream. -Saying many of the myths would be "far too wild, far too scandalous and in some cases far too filthy to be taught in schools," Crossley-Holland advocated a "careful selection" of age-appropriate works. -"I find it wonderful that in America, myth and folklore already has a part in education," he said. -I have been advocating it as a plan for twenty years. -He added authors and teachers being "overtly didactic" is a "total switch-off" for children, with messages being "subliminated" in enjoyable stories. -Crossley-Holland, who has translated Beowulf from Anglo-Saxon as well as writing the Penguin Book of Norse Myths and British Folk Tales, said: "You may well have intentions but you do better to keep them well out of sight." -Perhaps the big difference between an adult author writing for an adult and an adult author writing for a child is the necessity for some sense of hope. -Not that everything has to be simplified or come to a happy ending, but that there is an innate sense of good and evil. -And that must be subliminated; revealed through a story rather than stated. -The old basis of showing not telling. -Hansjakob back in Freihof once again -100 years ago, Heinrich Hansjakob moved into his retirement home in Haslach, on Sunday his final years in the "Freihof" were brought to life. -The large audience was inspired by the extremely amusing role play. -From his arrival, to his being conferred with honorary citizenship and his death, Alois Kraftzcyk, the scriptwriter and actor portraying Hansjakob, has written an entertaining play, which received great acclaim under the directorship of Cornelia Volk. -Marcus Zagermann, serving as narrator, guided the audience through the ten different scenes, explained circumstances and provided links explaining leaps from his youth to the time when he was approaching retirement. -On 22 October 1913, Hansjakob returned home to his childhood paradise, to his Freihof. -It was there that the role-play began, with Alois Krafczyk shining once again in his famous role as the town's great son. -True to style, he travelled with a two-horse carriage drawn by "Schwarzwälder Füchsen" (Black Forest Chestnut Horses) owned by Erich Becherer from Mühlenbach. -Not only was he received with great applause from the audience, but was also welcomed by his sister Philippine. -Billy Sum-Hermann managed unbelievably well to place herself in the role of the sister and brought the character to life amazingly well, in terms of both her expressions and gestures. -Between the individual scenes, the Mühlenbacher Bauernkapelle played appropriately selected musical pieces, thus rounding off the performance. -The honour of presenting the title of honorary citizen went to Haslach's Mayor, Heinz Winkler, who, together with a section of the Town Council, made an appearance representing the Mayor of Hüttich from back in Hansjakob's day. -The "Dreikönig" singers then made their own appearance, leading Hansjakob to exclaim: "Oh how beautiful, this brings childhood memories of my own time in a Dreikönig group back to life". -With their song, "O Jesulein", they delighted the audience in the Freihof, as did the "Storchentagskinder" with their loud cries of "Heraus, Heraus" . -In his day, Hansjakob expressed his criticism of the institutional church, as well as his concerns regarding the excesses in the rural economy, the consequences of industrialisation and the effects of the war. -He found his place of rest on his native soil, behind his mausoleum on the Brand near Hofstetten. -The role-play ended with the words "A restless spirit finally found rest and returned home forever". -The performance received great applause and many words of praise from the audience. -Basketball: Hopes are growing for the Neckar Riesen (Neckar Giants). -The chances of the relegated Ludwigsburg Budesliga basketball players remaining in the league have improved. -This is because there is to be a wild card procedure, and the Neckar Riesen are now up against one applicant fewer. -The Hamburg Towers basketball project is declining to make an application for a possible stand-in place. -"We will not participate in the wild card process," said former national team player Pascal Roller, who has been putting together the concept for a professional club in the Hansa town since 2012. -However, there was no sigh of relief to be heard from Ludwigsburg. -"We are not concerning ourselves with the wild card process until it is certain that Düsseldorf is not receiving a licence," said boss of the Neckar Riesen Alexander Reil, speaking to our paper. -Newly promoted Düsseldorf Baskets have until 23 May to submit documents to the arbitration panel and prove their financial fitness for the Bundesliga. -However, the Rhineland team have already been refused the licence twice. -The Court of Arbitration is the authority of last resort. -Haigerloch: Focus on the Abendmahlskirche -As the town's contribution to the 150th anniversary of the Protestant Church in Haigerloch, the town's Office of Culture and Tourism is to dedicate the last of this year's public thematic tours on Sunday 27 October to the Abendsmahlskirche (Church of the Holy Communion). -Following a visit to the Nuclear Bunker Museum, the focus will turn to Haigerloch in the period after 1850, when Haigerloch came under Prussian rule. -The crowning glory will be a visit to the Abendmahlskirche. -There the participants of the tour will be told the story of the construction of the church and will gain insight into the development of the Protestant congregation, in what is a Catholic area through and through. -Last but not least, while in the church eyes will also turn to the last supper painting, painted by Friedrich Schüz with the support of Walter Kröll and Georg Halbritter. -It is a faithful reproduction of the famous work Leonardo da Vinci in Milan. -The meeting point for the one and a half hour tour is the Nuclear Bunder Museum, at 3:00 p.m. -Tickets are available from the ticket office at the Nuclear Bunker Museum. -The Haigerloch Town Tourist Office is available for questions and to provide you with information. -Snowden may make further statements in Russia -The former US intelligence expert, Edward Snowden, may make further statements regarding the US espionage scandal from his Russian asylum. -Representatives of the Federal Prosecutor could either submit questions in writing or meet the 30-year-old in person in Russia. -This was reported by the Interfax Agency with reference to groups aware of the situation, which were not named in greater detail. -Such matters are to be clarified at an intergovernmental level, it was stated. -A departure of Snowden from Russia is virtually impossible. -"If this were to happen, he would lose his status as a refugee," said the Agency, quoting their source. -Because Germany is an ally of the USA, Snowden would be at risk of deportation if he were to travel there, the source continued. -Endurance pays dividends -Six teams battled to become champion at the club tennis tournament. -In the end, it was the endurance of the Maier/Bronner team that decided the final match. -The tennis division of the Sportfreunde Rohrdorf (Rohrdorf Sports Fans) enjoyed perfect weather for the tournament for local clubs and enthusiasts. -The focus of the tournament was on the fun of playing tennis. -Five matches were scheduled for each team, with every team playing one match against all the others. -The matches were doubles matches with a long set up to nine points, or a maximum duration of 45 minutes. -Anja Schlichter managed the tournament -The organisation of the tournament was placed in the trusty hands of Anja Schlichter, who was supported by Carmen Müller and Inga Kronemeyer. -After the first three matches and a lunch break with pasta, the tournament moved into the crucial phase. -The favourites, Andre Maier/Matthias Bronner, were tied with Michael Klippel and Sadmin Osmicic (both teams from the SG Rohrweiler), meaning that the last two games had to produce a victor. -At the awards ceremony, Carmen Müller finally had the pleasure of presenting the Challenge Cup to Andre Maier and Matthias Bronner, who had shown the greatest staying power on the final leg. -Second place was taken by the Michael Klippel / Sadmin Osmicic partnership. -Bronze went to Rainer and Bernd Maier of the Asphaltriecher team. -In the late afternoon sun, accompanied by coffee and cake, the tournament drew to a successful close. -On the last weekend in August, the Vogtsbauernhof Black Forest Open Air Museum in Gutach near Hornberg will once again be firmly in the hands of children. -Traditionally, the museum welcomes visitors on a Saturday and Sunday, this year on on 24 and 25 August, to a colourful party for children and families as part of the summer holiday programme, with numerous participation activities and events. -"On both days, adults and children alike can learn various traditional handicrafts," said the press release. -Whether producing soap, turning candles, felting or making silk, there is a suitable activity whatever your age. -The children will receive help from regional artisans, present from 11.00 a.m. until 5:00 p.m. -Activities will also be taking place in the museum workshop. -The young guests will be able to demonstrate their manual skills building cuckoo pipes, water wheels or spinning tops. -The host of old games, such as wheelbarrow racing or walking on stilts, promises plenty of fun. -Here speed and coordination are the order of the day. -For any miniature detectives, the open air museum will be offering a mystery tour through the museum on Saturday and Sunday, at midday and 2.00 p.m. respectively. -On a tour of the premises, the children will have to work out which stories are true and which have been made up. -In addition, visitors will have the special opportunity to get to know the open air museum on a carriage journey drawn by Black Forest Chestnut horses. -It is not just horses that can be found and admired on the grounds, but many other animals such as sheep, goats, cows and chickens as well. -On the Sunday the children will also be able to enjoy the clown, Otsch, who will be doing pranks with the museum guests from 11.00 a.m. -Meanwhile, a cosy and magical alternative will be provided by Hermann Büttner. -The story-teller will be taking the younger guests to the wonderful world of fairytale at 11.00 a.m., 1:00 p.m. and 3:00 p.m. -The traditional children's dance group from Gutach will also be performing at 11:30 a.m. -Furthermore, on both event days, from 11.00 a.m. until 5.00 p.m., numerous artisans such as the broom maker, the sewer, the spinner, the weaver and the baker will be letting visitors look over their shoulders. -So, visitors to the open air museum can expect a varied holiday programme right through to the end of the summer holidays in Baden-Wuerttemberg. -In a small town in New Zealand, Burt Munro is considered a lovable oddball, all because he resolved to compete in a race in America with his 1920 motorbike. -In spite of his heart defect, Burt could not be deterred from pursuing his dream. -With his savings, a few donations and a mortgage on his old workshop, he set off to Los Angeles by ship. -The film will be followed by coffee, tea and cake. -Piercing beep disturbs residents -Rhenus Midgard has also invested highly in land -They stockpile the coal that arrives on ships. -At the same time, the company also has other irons in the fire at the location. -Besides coal imports, Rhenus Midgard is also active in the field of wind park logistics, among other ventures. -The coals shimmer black in the storage yard in front of the embankment. -Here two huge blue stacker and reclaimer machines distribute or load the coals as required. -With each movement of the handling machines, a shrill warning noise rings out over the site. -"The signals are for the purpose of security and are legally prescribed," said Matthias Schrell, Managing Director of Rhenus Midgard in Wilhelmshaven. -Unfortunately, in the event of unfavourable wind, they can be heard from far away - and as a result there have already been a number of complaints from people from the north of the town. -The 40-year-old said that he is taking the complaints very seriously and has therefore been in touch with those affected. -In close consultation with the authorities, we are now in a position to make the warning signals quieter by means of the implementation of technical measures. -In future he will also seek open dialogue with neighbours. -For Matthias Schrell has some further plans at the location. -With the construction of the Lower Saxony Bridge, Rhenus Midgard is continuing to focus on coal exports for power stations and is one of the major powers in this sector in Europe. -Thanks to the mooring basin to the front of the terminal, sunken to a depth of 18.5 metres, it has for some time now been possible to unload Capesize bulk carriers here. -"In terms of handling, this year we hope to break the 3 million tonne mark," said Schrell. -Alongside the local Eon power plant, the imported coals also go to power plants inland. -When the GDF-Suez plant is added to the network, around 5 million tonnes of coal imports per year will be realistic. -The three ship unloaders on the bridge and the second transport belt could see this rise to 10 million. -Bavaria's basketball players optimistic in spite of first defeat. -Even their first defeat in the Euroleague could not curtail the huge optimism of the FC Bayern München Basketballers. -"No-one can stop us believing that we can win, even against the best team in Europe over the last two years," assessed Bayern coach Svetislav Pesic following the unfortunate 83:88 (39:47) defeat to title defenders Olympiakos Piräus on Thursday evening. -We have delivered a message: Basketball also exists in Munich! -In their first three appearances in the European top flight, the Munich team put in a good performance for long periods, but in the crucial phases were not sufficiently focussed. -Initially the guests managed to turn a 15-point deficit into a lead, with 13 minutes to go, before Piräus was able to turn the game around once again. -"We fought back incredibly well against the two-time Euroleague champions, but then once again made careless mistakes. -We are proud of our performance, but we want to win every game. -Malcolm Delaney and Nihad Djedovic were the most successful strikers for the Munich team, who had celebrated clear wins against the Italian Series champions Montepaschi Siena, and the Polish title-holders, Zielona Góra, in their first two games. -Ben Greenman: The Tenth Anniversary of the New York Comedy Festival: The New Yorker -One could argue that New York City is the birthplace of standup comedy in America: nearly a hundred years ago, the vaudevillian Frank Fay, who served as the master of ceremonies at the Palace Theatre, on Broadway, started telling jokes directly to the crowd, in a conversational manner. -Fay's innovation has been extended through the years, most recently by the New York Comedy Festival. -Created and overseen by Caroline Hirsch, the founder of the standup institution Carolines, the festival celebrates its tenth anniversary this year, with more than sixty shows at small clubs and large theatres. -"Most of these headliners appeared at Carolines, and went on to greater success, to the point where they're too big to play a club," Hirsch said. -We built this festival as a way of continuing to work with them. -This year's event includes appearances by Wanda Sykes, Kathy Griffin, and Bill Maher, as well as "Stand Up for Heroes," an annual music-and-comedy benefit for military veterans, at Madison Square Garden, featuring, among others, Bruce Springsteen, Jon Stewart, Roger Waters, and Bill Cosby. -As the festival has expanded, so has the world of comedy. -Several of the comedians participating in this year's festival came up through nontraditional channels, such as shows on smaller networks, like Comedy Central, FX, and Spike. -Nick Kroll rose to prominence on a deep-cable sitcom (FXX's gleefully raunchy fantasy-football-themed "The League") and now has his own Comedy Central sketch show. -Jenny Slate has been a cast member on both "Saturday Night Live" and "Parks and Recreation," though she is best known for her viral video series "Marcel the Shell with Shoes On." -Both Kroll and Slate, as well as other young comedians with distinctive voices (the surreally pessimistic Anthony Jeselnik, the wry, racially focussed W. Kamau Bell), are products of the decentralized world of American comedy. -One of the festival's biggest draws will be an interview: David Steinberg talking to Larry David. -Steinberg started as a standup comedian but has become an accomplished television and film director, as well as an unofficial comedy historian. -From 2005 to 2007, he hosted a show on TV Land called "Sit Down Comedy with David Steinberg." -The meeting takes place at Town Hall, in the center of Manhattan. -"The city is definitely in the comedy DNA of all of Larry's work," Steinberg said. -He was telling me that, when he's here, sometimes he'll walk down an alley between two buildings and think to himself, Hey, if I lose all my money, maybe I'll live here. -Iran satisfied with the nuclear negotiations process -Iran has expressed satisfaction with the negotiation process, one week before the next nuclear meeting with the five UN veto powers and Germany. -"After many years, we have now reached an agreement with the International Nuclear Energy Authorities to clear up the differences from past years," wrote Foreign Minister Mohammed Dschawad Sarif on his Facebook page. -It will without doubt be a long path, but the chief nuclear negotiator is satisfied with the negotiation process and is also optimistic that both sides will come to a solution in the end. -Drama in Uruguay: Boys kill playmate with machetes -Two children have confessed to the murder of an 11-year-old in Uruguay. -The two boys, aged 14 and 12, had invited their victim to go bird hunting with them. -On Tuesday, armed with machetes and knives, they stabbed the 11-year-old to death and threw the body into a well, reported the "El País" newspaper on Thursday. -They then went and played football in front of their homes in a working class district. -The body was found the following night in the village of Cerro Pelado, 15 kilometres to the north of the exclusive beach resort of Punta del Este. -A five year-old half-sister of the younger offender, who they took with them, was to make a statement as an alibi, stating that she was sexually assaulted by the boy. -However, when the girl described the progression of events to the police without mentioning an attack against herself, the two boys confessed to the pre-meditated murder of the 11-year-old. -The older of the pair cited a rivalry on the football field as the motive. -He was provisionally taken to a psychiatric young offenders institute. -Pimp must go to prison -The District Court of Constance has condemned a 33-year-old man from St. Georgen to three and a half years in prison for exploitative human trafficking, pimping, issuing threats and grievous bodily harm. -The former German soldier who, following two tours in Afghanistan, had worked as a doorman, made a confession within the context of a plea-bargaining procedure. -According to the confession, on five occasions he got to know women via Internet or telephone contacts, who he then sent out onto the streets against their will after a few weeks. -He broke any resistance by means of violence and the issuing of threats. -He kept the income from women, some of whom worked for him for years. -He prevented the women from making contact with the outside world by confiscating their mobile phones and SIM cards. -Together with an accomplice, who procured the women, he brought them to various brothels in the south-west. -There he had them monitored to some extent, in order to control their income. -Appearing before the court, the accused initially claimed that he was suffering from severe trauma after colleagues serving alongside him in Afghanistan had lost their lives in an explosion. -Once a psychiatric expert had cast considerable doubt on him suffering from any psychiatric condition, and thus on his limited culpability, the 33-year-old made a full confession. -The court did not have to question any more of the female victims. -As an official from the Criminal Investigation Department reported, following the first conviction achieved using telephone surveillance measures, at least four further cases of exploitative human trafficking could be identified. -The intercepted conversations were characterised by a high level of aggression. -In subsequent hearings, the victims confirmed their ordeal. -A further witness, whereby one of the woman had suffered blue bruises all over her body, is yet to make a statement. -This is a case of a "completely broken personality", that views the violence and exploitation in this field as normal. -Yesterday the accused expressed regret for his behaviour. -"I have removed myself from these circles and hope to lead a completely normal life following my imprisonment," he stated. -Oil extends drop toward $96 a barrel -The price of oil continued to fall on Friday as concerns over high supplies offset a report showing China's power-hungry manufacturing sector is strengthening. -Benchmark U.S. crude for December delivery was down 14 cents at $96.24 a barrel by late morning in Europe in electronic trading on the New York Mercantile Exchange. -The contract fell 39 cents on Thursday, leaving it down 5.8 percent for the month of October. -Ample supplies of crude have weighed on the price in recent weeks. -The Energy Department said Wednesday that U.S. supplies increased 4.1 million barrels last week. -Over five weeks, supplies have risen by more than 25 million barrels. -But a suggestion of stronger demand came Friday from two reports on Chinese manufacturing that showed an uptick in activity. -That suggests China's economic recovery could continue to strengthen after growth rebounded to 7.8 percent in the third quarter from a two-decade low in the previous quarter. -Brent crude, a benchmark for international crude also used by U.S. refineries, fell 26 cents to $108.58 a barrel on the ICE exchange in London. -Court blocks ruling on NYPD stop-and-frisk policy -A federal appeals court on Thursday blocked a judge's order requiring changes to the New York Police Department's stop-and-frisk program and removed the judge from the case. -The 2nd U.S. Circuit Court of Appeals said the decisions of Judge Shira Scheindlin will be stayed pending the outcome of an appeal by the city. -The judge had ruled in August the city violated the Constitution in the way it carried out its program of stopping and questioning people. -The city appealed her findings and her remedial orders, including a decision to assign a monitor to help the police department changes its policy and training program associated with it. -The appeals court heard arguments Tuesday on the requested stay. -The appeals court said the judge needed to be removed from the case because she ran afoul of the code of conduct for U.S. judges by compromising the necessity for a judge to avoid the appearance of partiality in part because of a series of media interviews and public statements responding publicly to criticism of the court. -The judge had ruled that police officers violated the civil rights of tens of thousands of people by wrongly targeting black and Hispanic men with its stop-and-frisk program. -She appointed an outside monitor to oversee major changes, including reforms in policies, training and supervision, and she ordered a pilot program to test body-worn cameras in some precincts where most stops occur. -In August, New York City agreed to end the practice of storing the names and addresses of people whose cases are dismissed after a police stop. -An oral argument on the city's appeal is scheduled for sometime after March 14, 2014. -The stop-and-frisk tactic has been criticized by a number of civil rights advocates. -Stop-and-frisk has been around for decades in some form, but recorded stops increased dramatically under the administration of independent Mayor Michael Bloomberg to an all-time high in 2011 of 684,330, mostly of black and Hispanic men. -A lawsuit was filed in 2004 by four men, all minorities, and became a class action case. -Supporters of changes to the NYPD's stop-and-frisk program say the changes will end unfair practices, will mold a more trusted and effective police force and can affect how other police departments use the policy. -Opponents say the changes would lower police morale but not crime, waste money and not solve a broader problem of a police force under pressure after shrinking by thousands of officers during the last decade. -The judge noted she wasn't putting an end to the stop-and-frisk practice, which is constitutional, but was reforming the way the NYPD implemented its stops. -On the happiness of dreaming camels -Happiness is an oasis that only dreaming camels manage to reach -It was with this piece of Bedouin wisdom that the first ever chairman Wolfgang Henne described the history and fascination behind the "Helping Hands" society. -Saturday evening saw the celebration of the tenth birthday of the organisation, which can already look back on impressive results. -Founding Chairman Henne reported in depth on the work of the organisation. -In 2004, he visited the Cheijk-Zajed Hospital in Nouakchott in Mauritania for the first time. -As a result, an important collaboration was developed and the gynaecologist, Hanne, has performed countless operations on several journeys to the location. -During the speech, images from the trips were shown on a large screen, in order to give the guests at the anniversary event an insight into the initiatives mentioned. -The organisation's desert vehicle was also shown. -It is a former federal border protection vehicle. -Rainer Prewo, the former Lord Mayor of Nagold, made the suggestion of equipping this vehicle with a photovoltaic system and, according to Henne, he is now planning to assume an advisory role within the organisation. -In 2008, the Schleeh carpentry company from Baiersbronn built the hospital station in Socogim, a poor district on the edge of the Capital, in the record time of less than one week - free of charge. -Henne also mentioned a new mother and child hospital in Nouakchott -This is particularly interesting at a time when Nagold is set to do away with its maternity clinic. -You learn a great deal working as a doctor in Africa, said Henne, for example that you should slow down to avoid burn-out. -Due to the political unrest in the country, it has not been possible to make as many journeys as planned, but for this reason, many local doctors have come to Germany, to sit in on lectures on a variety of topics. -In addition, containers with materials are regularly sent to Africa, for example in March of this year when medical devices, medicines, bandaging material, hospital beds and spectacles were sent. -Co-initiator Hans-Joachim Fuchtel himself provided plenty of local colour, telling of Mercedes vehicles in Mauritania converted into mobile goat stalls and sharing the experiences of the Stammheim musicians, who traditionally support the organisation on their trips to Mauritania. -He identified his personal motivation as being his work in the area of auditing as a young member of parliament. -When it became clear to him how much money is wasted, he decided to help the Africans with controlling of their budget by the people. -Fuchtel also emphasised that a global economy requires global human kindness. -The fact that Africa lies closer than many think should by no means be forgotten. -Africa is not at all that far from Gran Canaria. -Coulson used phone hacking to verify tip -Former News of the World editor Andy Coulson allegedly used "phone hacking, surveillance and confrontation" in an attempt to confirm a bogus tip about an affair involving then-home secretary Charles Clarke. -Prosecutor Andrew Edis QC told the Old Bailey that the News of the World heard a false rumour in May 2005 that Clarke was seeing his "attractive special adviser," Hannah Pawlby. -The newspaper tasked private investigator Glenn Mulcaire with hacking Pawlby's voicemails and "door-stepped" her, but Coulson also called and left her voicemails, the court heard. -"The prosecution suggests that Mr Coulson, who is now the editor of the NotW, he is not the man who stands outside people's houses hoping to catch them out, he is the man who likes to put the story to people to see what they will say," Mr Edis said. -He said the NotW used three ways to investigate stories: phone hacking, surveillance, and confrontation. -The editor is personally involved in the third. -Obviously he knows about the second, surveillance, he must do. -What about the first? -Does he know about phone hacking? -He says he doesn't, we say "Oh yes, he did". -Rumours about an affair involving Clarke were first picked up by the NotW's features desk when a source who was sexually interested in Ms Pawlby was told: "Don't bother wasting your time, she's with Charles." -A tape of voicemails taken from her phone on at least three occasions was seized from Mulcaire's home in August 2006. -Investigators also found entries on the private investigator's computer which had Ms Pawlby and her sister as "Projects." -During the period she was being investigated, Ms Pawlby's grandparents received anonymous calls asking for information about her, Mr Edis said. -Meanwhile, former chief reporter Neville Thurlbeck and former reporter James Weatherup oversaw surveillance of Ms Pawlby's movements. -Leaving her a voicemail on June 18 2005, Coulson told her: "I've got a story that we're planning to run tomorrow that I really would like to speak to Charles about." -Mr Edis said Coulson's involvement in the story followed the same pattern as with other important men, such as former home secretary David Blunkett. -The jury heard on Thursday that Coulson confronted Mr Blunkett over an affair with a married woman while he was himself seeing co-defendant Rebekah Brooks, who was married at the time. -Coulson and Brooks deny conspiring with others to hack phones between October 3 2000 and August 9 2006. -Mulcaire, Thurlbeck and Weatherup have admitted phone hacking. -In protest against the planned tax on the rich, the French Football Association is set to actually go through with the first strike since 1972. -A mediation meeting comes to an end without resolution. -In the conflict regarding the tax on the wealthy, the government and the professional football association in France have manoeuvred into a cul-de-sac following a failed mediation meeting. -President François Hollande welcomed club and association representatives to Paris' Élysée Palace on Thursday, where he listened to their complaints. -However, he refused to spare the football association from the planned 75% income tax to be imposed on those earning in excess of one million €per year, which every company in the country will have to pay for two years, from 2014. -The opposition also remained adamant. -The strike announced for the end of November will now go ahead, informed the head of the Union of Professional Football Clubs (UCPF), Jean-Pierre Louvel. -The Ligue 1 and Ligue 2 match days scheduled between 29 November and 2 December will thus be cancelled. -Gechingen: Absolute protection not possible -"We want to involve the residents in the planning at as early a stage as possible," said Mayor Jens Häußler, opening the citizen's information event regarding the flood protection concept in Gechingen. -Around 80 participants came to the Gemeindehalle (community hall) to have the planned measures explained to them, to express their concerns and submit ideas. -Häußler made clear: "The final decisions will be made by the members of the Town Council". -Häußler commented that the flood that found Gechingen unprepared on 15 May 2009, was a decisive event in raising the question of protective measures for the future. -A river basin study was commissioned in collaboration with the neighbouring community of Aidlingen, which now forms the basis for the flood protection concept in Gechingen. -"The goal is the protection of as many developed plots of land as possible," said Häußler. -The basis for the plans is the need for protection against expected floods, which statistically occurs every 100 years, according to the German Weather Service. -Climate change is taken into account in the form of an allowance of 15 per cent. -In expert circles this is referred to as "HQ 100aKlima". -The masses of water that caused considerable damage in Gechingen in 2009 was equivalent to a 1,000-year flood. -"There is no such thing as absolute protection, but we can attempt to make a relative improvement," said Häußler. -In order to achieve HQ 100aKlima protection, around four million €must be invested in Gechingen, whereby the town can count on a State subsidy of around 70 per cent. -However, Häußler indicated that the funding would only be provided if an overall concept is developed. -In order to develop such a concept, the town is reliant on the cooperation of its citizens. -A complex planning and approval procedure must be completed. -In a best case scenario, implementation could begin in 2016. -Among other measures, the protection concept, which (we reported) has already been presented to the Town Council during the past week, plans local measures in the tributary to the River Irm, running from the Stammheim Valley. -In conjunction with the presentation of the flood protection concept, the participants made use of the opportunity to submit their concerns. -Among others, the question was raised as to why embankments and detention reservoirs are no longer planned to protect the location. -Gregor Kühn, technical planner at the commissioned engineering company, Hügelsheim-based 'Wald und Corbe', who presented the concept, emphasised that it required the interplay of all proposed measures, in order that their sum might achieve the desired goal. -Furthermore, the most economic solution must be found, said Joachim Wald of Wald und Corbe. -There was resentment among local residents of the old town, who have repeatedly to suffer as a result of the overflowing of the canal network, even during normal thunderstorms. -The problem is known to the administrative department and initial measures have already been taken. -However, Häußler indicated that the guidelines for canal systems are different to those for flood protection, and these must not be confused. -The town is obliged to design the canal network in such a way that it can withstand rainfall events every two to three years. -To make the sewerage channels wider would gobble up millions. -Persons seriously injured following collision -Early on Friday afternoon, two car drivers were seriously injured in a head-on collision on Landesstraße 44 (a rural road). -The police completely blocked off the road immediately, but initially could not make much comment on the cause of the accident. -However, they did mention that there were various witness statements that first had to be assessed. -What is certain is that the 19-year-old female driver of a VW Golf was driving in the direction of Revensdorf and the 38-year-old man from Gettorf came towards her in his Hyundai. -The two vehicles collided, whereby the woman was so severely stuck that it took the fire service almost half an hour to free her from the vehicle wreckage. -Initially the actual cause of the accident remained unclear. -Both unfortunate parties were taken to nearby hospitals. -The road remained fully blocked off for around two hours. -You could call him the Dostoevsky of the USA. -Philip K. Dick was a God seeker, a metaphysician, and to a certain extent his novels and stories also feature recurring themes. -The external likeness is astounding: the piercing eyes, the beard, the high forehead. -And so, as Fyodor M. Dostoevsky - with his massive oeuvre - is considered the revealer and apologist of the Russian soul, Philip K. Dick is considered an American prophet, who is certainly highly esteemed within his own native country. -Some titles have been released by the Heyne Verlag publishing house,the 'Haffman's cassette' featuring 118 stories was distributed by Zweitaudenseins. -Dick illustrates America - the kingdom of breathtaking technical progress, paired with paranoia, a fixation on security and the belief of having been chosen. -Dick had 'God experiences', and his later books read like a mixture of the Book of Revelation and a computer manual. -Perhaps he was crazy, or perhaps drugs had affected his brain. -He realised, early on, that computers are divinities. -He wrote a 'Theology of the Computer' and questioned what separates humans from the machine, the creator from the creation. -That is the "Blade Runner" problem. -Killing. -A master of science fiction. -A fantastic writer. -His stories have inspired film producers such as Ridley Scott, Paul Verhoeven and Steven Spielberg. -The latter story is now particularly significant. -"Minority Report" originates from the year 1956, and exudes the scent of the Cold War and the McCarthy Tribunal. -Here the principle of the "Pre-Crime" is developed, i.e. something that is now more or less official doctrine within the White House. -In Dick's world, which is alarmingly similar to our own, mutants look into the future - and the police have access to this insight. -"We arrest individuals who've broken no law," says the government official. -"We get to them first, before they can commit an act of violence". -And: "In our society we have no major crimes, but we do have a detention camp full of would-be criminals". They read time streams. -They assume that bad things are going to happen and prevent the criminal thought turning into action. -Dick's tale acts as a blueprint for the anti-terror laws and the phone tapping madness of the NSA. -Anticipation of attack: This is also how Obama's drone philosophy and practices work. -However, Dick's story is not yet finished. -The Police Chief argues that there is no more division of power, the military controls daily life - and what is left of the State. -Then the watcher is himself being watched and is convicted of a future crime. -The system slips up. -It is perfect, but it lies. -It devises its own conditions. -It not only accepts errors and victims, but builds itself on them. -The system creates the threat that it is fighting against. -To read Philip K. Dick means to get closer to the truth, time and time again. -Issuing statements regarding the Merkel spying scandal from Russia -The former US intelligence expert, Edward Snowden, may make further statements regarding the US espionage scandal involving Federal Chancellor, Angela Merkel, from his Russian asylum. -Representatives of the German Federal Prosecutor's Office could either submit questions in writing or meet the 30-year-old in person in Russia, reported the Interfax Agency. -Green Party parliament representative, Hans-Christian Ströbele, was yesterday the first German politician to meet with Snowden in Moscow, to discuss the scandal with him. -NSA revelations boost corporate paranoia about state surveillance -On a mild day in late August a German police helicopter buzzed low over the US consulate in Frankfurt, the financial capital of Germany. -On the instruction of the Office for the Protection of the Constitution (BfV), Germany's domestic intelligence agency, its mission was to photograph the rooftop of the US outpost, which is located less than 5km from the European Central Bank and Bundesbank. -German media say the BfV hoped to identify the presence of listening antennas and the action prompted an exchange between the US and the German foreign ministry in Berlin. -James Clapper, US Director of National Intelligence, insisted again in September that the US does not use foreign intelligence capabilities "to steal the trade secrets of foreign companies on behalf of US companies to enhance their international competitiveness or increase their bottom line." -But ever since Edward Snowden, the contractor turned whistleblower, began releasing his treasure trove of US surveillance secrets, European governments and business leaders are no longer sure whether to take the director at his word. -Reports that the US National Security Agency spied on Brazilian oil company Petrobras and gained access to data held by US cloud providers including Google and Yahoo have ratcheted corporate paranoia about state surveillance to new highs. -The final straw came when it was revealed that Chancellor Angela Merkel's phone had been bugged, possibly for about a decade. -If Europe's most powerful person can be targeted, then surely business leaders are also potential targets. -Snowden has made transparent the intensive collaboration between US intelligence services and companies. -I think it's conceivable that these data are used for mutual benefit. -"Germany must wake up," says Oliver Grün, president of BITMi, which represents small and medium sized German IT companies. -German companies believe the US now poses almost as big a risk as China when it comes to industrial espionage and data theft, according to a survey published in July by EY, the consultancy. -In all the documentation leaked by Mr Snowden, there has, however, been no evidence to date that the US has passed on foreign companies' trade secrets to its own companies. -Politicians have expressed concern that the EU lacks certain IT and internet capabilities and should strive to reduce its dependence on the US. -Business leaders are sceptical about this. -Someone in the German parliament says we should build a German Google. -I can only shut my eyes and slowly open them again... -"That's not the way," Hasso Plattner, chairman of German business software company SAP, says. -If one wanted a strong European IT industry, then one shouldn't have let it die out 20 years ago. -Everything is subsidised in Germany, from coal, to cars and farmers. -Everything but the IT industry. -Still, the reach and technical sophistication of US spy agencies exposed by the Snowden revelations have come as a shock to some companies who previously thought the biggest surveillance risk was posed by China. -A big shift is occurring in cloud computing where European executives have become more aware that data stored in the US is subject to that jurisdiction and therefore potentially vulnerable. -According to a survey carried out by the Cloud Security Alliance, a trade body, some 10 per cent of non-US members cancelled plans to use a US-based cloud provider after revelations about the US Prism data mining programme. -Jim Snabe, co-chief executive at SAP, says: "We see a new question from customers that didn't come up a year ago - which is where is my data stored and can you guarantee that it stays physically in that jurisdiction." -Many German executives argue that the latest reports are simply confirmation of what they already knew: that powerful states want to steal their most prized secrets and these data must therefore be guarded at all costs. -That economic spying takes place is not a surprise. -It has always taken place. -"This has been a topic for many years and hasn't fundamentally changed through the current discussion," says Kurt Bock, chief executive of chemical maker BASF. -The Americans spy on us on the commercial and industrial level as we spy on them too, because it is in the national interest to defend our businesses. -Corporate leaders are not generally keen to boast about the countermeasures they have taken, in case this hands an advantage to an attacker. -For large companies, the message has long since been drummed home that picking up a free USB stick at a trade fair, or leaving a laptop unguarded in a hotel room are unwise, to say the least. -Ulrich Hackenberg, board member at carmaker Audi, says it has been standard practice for years for mobile phones to be collected before board meetings so they cannot be used as remote listening devices. -Germany's BfV advises executives to consider using simple prepaid mobiles when on foreign trips because of the risk that smart phones are compromised. -The prepaid mobiles are then thrown away afterwards. -However, there is concern that small and medium-sized companies remain vulnerable to hacking and surveillance. -In Germany, many of these companies are global market leaders in their particular niche. -"Small and medium sized companies often lack the experience, personnel and financial resources to protect corporate secrets effectively against unauthorised access," the BfV warns in a report. -The US warns its own companies about economic espionage by other countries. -The US National Intelligence Estimate in February named France alongside Russia and Israel in a second tier of offenders who engage in hacking for economic intelligence, behind China, according to The Washington Post. -A board member at a German blue-chip company concurred that when it comes to economic espionage, "the French are the worst." -Bernard Squarcini, former head of the French internal intelligence agency DCRI, was quoted in an interview this month as saying: "The services know perfectly well that all countries, even as they co-operate in the antiterrorist fight, spy on their allies." -Hechingen: Trade fair provides the answers to wedding questions -"Marriage is and remains the most important journey of discovery a person can undertake," said the philosopher Sören Kierkegaard. -Tips, at least as to how the wedding celebration can be a success, have now been given at a special trade fair in the Domäne in Hechingen. -Important suppliers of all wedding-related products and services were represented. -A trend: The retro look is back once again. -This begins with the wedding clothing. -White is a must, best muted or in cream. -At the front, dresses with numerous ruffles and embroidery are in, often with a veil, while to the rear they run down the back and often end in a train. -However, opinions differ, as Stefanie Koch from the Modehaus Kleidermüller emphasised. -For men, suits with a waistcoat and neckerchief are most popular, although some people do choose cream or brown combinations. -Selecting the venue is important, it was explained. -Plain adjoining rooms are not ideal, the ambience should be special. -In this regard, the Domäne drew attention to their own facilities in the carriage house. -Uwe Link has an offer for anyone who wants to set off in a carriage. -"However, carriages are also popular for hen parties," he commented. -People were also on the lookout for romantic ideas for invitations, flower arrangements and wedding photographs. -The "After-Wedding Photo" is becoming increasingly fashionable - that is, series of pictures taken a few days after the celebration, at special locations, preferably in front of waterfalls or the like, but in full wedding clothing, explained Elisabeth Keidel. -Many other details for a successful wedding were presented in the Domäne on Sunday. -Flower arrangements, rings, wedding tables, cakes and also a magician, such as Marko Ripperger, who can entertain guests with style. -The correct cosmetics and nail care mustn't be forgotten. -Everything you need for an unforgettable celebration. -Dog-lovers victorious -Once again the athletes from the Bitz dog-lovers association were victorious at a Rally Obedience Tournament. -Achim Scherrenbacher started things off in Kandel with two dogs; with his bitch Sandy he achieved 94 points in the Beginners class and thus took fifth place, while with 15-month-old Marley he took twelfth place with 87 points. -With her dog Woody competing in the Class 1 competition, Susi Höpp was subject to the critical scrutiny of the judge. -Parents of Georgia teen who died in 'freak accident' believe son was murdered -The parents of a Georgia teenager, whose body was found inside a rolled-up wrestling mat in his high school gym, believe their son was murdered, the family's attorney said Thursday. -Kendrick Johnson, of Valdosta, Ga., was found Jan. 11 stuck in an upright mat propped behind the bleachers inside his high school gym. -Lowndes County sheriff's investigators concluded Johnson died in a freak accident, but the 17-year-old's family disputes that. -"They absolutely think their son was murdered," Benjamin Crump, an attorney representing Kenneth and Jacquelyn Johnson, told FoxNews.com. -They never believed he died the way the sheriff concluded. -"They believe that it defies logic, the laws of physics as well as common sense," Crump said. -They think this is a cover-up to protect the person or people responsible for their son's death. -"They sent their son to school with a book-bag and he was returned to them in a body bag," he said. -U.S. Attorney Michael Moore said Thursday he is conducting a formal investigation into Johnson's death, noting that several key questions remain unanswered. -What was the cause of death? -Was his death the result of a crime? -Moore said at a press conference Thursday afternoon. -I will follow the facts wherever they lead. -My objective is to discovery the truth. -"I am of the opinion that a sufficient basis exists" for a formal investigation, he said. -Moore told reporters that the initial autopsy indicated Johnson died as a result of "positional asphyxia." -A second autopsy, however, listed a different cause of death, according to Moore. -"There are several questions that must be answered or confirmed," he said. -Moore added that if he uncovers sufficient evidence to warrant a criminal or civil rights investigation into the death of Johnson he will ask the FBI to conduct it. -A representative from the Lowndes County Sheriff's Office was not immediately available for comment when contacted Thursday. -A southern Georgia judge on Wednesday ordered authorities to release all surveillance video that investigators reviewed. -The teenager's parents said they hope the video footage will contain clues to how he died. -CDC issues children's allergy guidelines for schools -On Wednesday, the Centers for Disease Control and Prevention released a set of guidelines to manage children's food allergies at school. -This is the first set of such guidelines the U.S. government has put out, as the number of school-age children suffering from food allergies climbs. -One in 20 children in the United States now have food allergies. -The CDC found the prevalence of food allergies among children increased 18 percent between 1997 and 2007. -The guide contains information for schools on how to make faculty and staff aware of children's food allergies, and how to handle them should an allergic reaction occur. -It also recommends schools have epinephrine stocked -- the EpiPen brand auto-injector being most commonly used -- to respond to potentially fatal anaphylaxis. -State legislatures have recently been updating rules to allow schools to stock epinephrine more easily. -The report also includes a list of typical symptoms communicated by children who are having an allergic reaction. -Kids may say, "It feels like something is poking my tongue," "My tongue feels like there is hair on it," or "My tongue is tingling." -Parents of Intersex Kids Can Pick 'Gender Undetermined' -Germany became the first European nation to recognize a third gender for babies born with ambiguous genitalia. -No longer will newborns be rigidly assigned to male or female. -The new law doesn't require parents to declare any gender for such children, allowing parents to declare gender "undetermined" or "unspecified" on their birth certificates. -The aim of the law was to take the pressure off parents who might make hasty decisions on sex-assignment surgery for newborns, and to fight discrimination against those who are intersex. -One intersex person, according to the BBC, said years later, "I am neither a man nor a woman." -I will remain the patchwork created by doctors, bruised and scarred. -An estimated one in 2,000 children born each year is neither boy nor girl. -They are intersex, part of a group of about 60 conditions that fall under the diagnosis of disorders of sexual development, an umbrella term for those with atypical chromosomes, gonads (ovaries or testes), or unusually developed genitalia. -Wallis Simpson may have been intersex. -Gender identification is still not well understood, but most experts in the United States say that when sex cannot be determined, it's better to use the best available information to assign it then to wait and monitor the child's psychological and physical development before undertaking surgery, if at all. -New York City psychiatrist Dr. Jack Drescher, who specializes in issues of gender identification, said the new German law "sounds like a good thing." -Intersex children pose ethical dilemma. -"Some people have life-endangering conditions that require surgery, but most kids do not," he said. -You can make a gender assignment without surgery, and then see how identity develops. -The science of knowing how a child will develop any gender identity is not very accurate. -Nobody can answer the questions about why this happens. -It's like the mystery of why people are gay. -A report filed to the European Commission in 2011 described intersex people as different from transsexual or transgender people, as their status is not gender related but instead relates to their biological makeup, which is neither exclusively male nor exclusively female, but is typical of both at once or not clearly defined as either. -These features can manifest themselves in secondary sexual characteristics, such as muscle mass, hair distribution, breasts and stature; primary sexual characteristics such as reproductive organs and genitalia; or in chromosomal structures and hormones. -The report also gives an overview of the discrimination faced by intersex and transgender people in the realm of employment, as well as levels of harassment, violence and bias crimes. -Gender nonconforming boys now have special camp. -Already, Australia and Nepal allow adults to mark male, female or a "third gender" on their official documents. -In June, a 52-year-old Australian, Norrie May-Welby, became the world's first recognized "genderless" person after winning a legal appeal to keep an "unspecified" gender status for life. -German passports will have a third designation other than M or F -- X, for intersex, according to the Interior Ministry. -In neighboring France, gender issues are still controversial, according to a news report on France 24. -In 2011, dozens of French lawmakers from that strongly Catholic country signed a petition for "gender theory" to be withdrawn from school textbooks. -The U.S. website Catholic Online has also opposed the German law, writing that "as the world is being dragged into a new state, where gender is a choice, but sexual activity is not, we reverse two more pillars of civilization." -One Maryland mother of a newborn also told the Baby Zone that she would rather see babies assigned gender at birth. -"Parenting is stressful enough without extra limitations, especially if you don't know the gender of your child," she told the parenting website. -Children need stability and certainty. -Historically, children born with both male and female genitalia were called hermaphrodites, named for the handsome Greek god who had dual sexuality. -And as little as a decade ago, the medical community thought of gender as a slate that could be erased and then redrawn. -But now, many are challenging the ethical basis of surgery, knowing that gender identity is complex, and doctors can sometimes get it wrong, not knowing how a child will feel about their gender assignment when they grow up. -"Back in the middle of the 20th century, it was called a 'psychiatric emergency,'" said Drescher. -When these kids were born, you didn't call the psychiatrist, you called a surgeon. -The prevailing theory on how to treat children with ambiguous genitalia was put forward by Dr. John Money at Johns Hopkins University, who held that gender was malleable. -He coined the term "gender identity" and argued that social and environmental cues -- how parents raised a child -- interacted with a child's genes and hormones to shape whether the person identified as male or female. -But in one 1966 case, known as "John/Joan," his theories became controversial. -He advised the parents of a boy whose penis had been severed in a botched circumcision to have the child fully castrated, removing his testicles, as well, and to raise him as a girl. -"Money presented the case as a successful case of transition, but it was not," said Drescher. -When the boy was around 15, he transitioned back to a boy and married a woman. -But at 38, he committed suicide. -Drescher said that now some doctors are still "practicing that model." -But in the 1990s, with the advent of the Internet, survivors of these gender surgeries have come forward "not happy with the outcome." -Such was the case with Jim Bruce, a 36-year-old writer from Montana, who was born with XY male chromosomes but ambiguous genitals. -Doctors couldn't be sure if he had a large clitoris or a small penis and were convinced he could never live a "satisfactory life" as a man. -So shortly after his birth in 1976, Bruce's external organ and testes were surgically removed and he was raised as a girl. -He was given female hormones at age 12. -"I knew that I wasn't a girl," he told ABCNews.com. -I was unhappy, but it was really difficult to ask questions. -At 18, he was set for a vaginoplasty. -But depressed and knowing something was wrong, he demanded medical records. -What he found out was horrifying. -I was sterilized at birth -- and no one ever told me. -Bruce was born with a DSD that prevented his body from producing enough testosterone to properly develop his genitals. -After learning the truth, he changed back to a man, taking testosterone shots and having his breasts removed. -Surgery rendered him infertile. -Today, he advocates for others in an organization called the Interface Project, trying to normalize perceptions of those who are intersex. -But Anne Tamar-Mattis, executive director for California-based legal group Advocates for Informed Choice, worries that the German law "invites labeling and stigma." -"A lot of activists are concerned that what the German rule will do is encourage parents to make quick decisions and give the child an 'undetermined,'" she said. -We are afraid it will encourage intervention. -We think a better process is assigning male or female sex, then waiting. -But we haven't seen how the law will play out, so all we can do is speculate. -Tamar-Mattis said that her organization supports the Australian law because "it allows adults to choose to be recognized in a third gender." -"Adults should be able to make their own decisions about legal gender," she said. -German law is about assigning it at birth. -That is not a battle young children should have to take up at this point. -When they are grown, they can make decisions about their own bodies. -But Dr. Arlene Baratz, a Pittsburgh breast radiologist who has a daughter with a disorder of sexual development and helps hundreds of others in a support group, said the German law will "empower" both parents and children. -Baratz's daughter Katie was born with male chromosomes, but has a DSD called complete androgen insensitivity syndrome. -Because her androgen receptors are faulty, Katie developed female characteristics. -She has a vagina, but no uterus or ovaries. -Now at 29, Katie is married and at the University of Pennsylvania, a resident in child psychiatry. -Though she is infertile, she hopes to become a parent through adoption or gestational surrogacy. -"The law gives parents some space not to have to rush into making decisions themselves," said Baratz. -It gives them the time to do some tests and figure it out and a period of time before they write 'male' or 'female.' -This way, you are OK -- raise the child, love the child. -You have a wonderful baby and enjoy the fun. -We don't have to rush into surgery that is irreversible. -"It brings the children into the decision and takes away the anxiety that motivates parents because they don't feel they are doing the right thing," she said. -Ultimately, the child will decide which sex he or she feels more comfortable with -- and that's a wonderful thing. -It empowers children to make the decision for themselves. -Unknown attackers injure girl with shots fired from car -Unknown attackers have injured a twelve-year-old girl in Saxony-Anhalt when firing shots from a car. -The assailants fired on a group of children on Thursday evening in Genthin, presumably with an air gun. -This was announced by the police in Burg. -The girl suffered minor injury to her left calf. -Rescue workers took the girl to the hospital. -The shots came from a small car, which drove past a group of six children and suddenly stopped. -Witnesses saw two people sitting in the car. -The assailants also fired shots at a bus stop. -A pane of glass was shattered. -They then drove away. -The Criminal Investigation Department is investigating the matter, it was stated on Friday. -Thus far, the Kempten Police Department can look back on a calm Halloween night. -The authorities were only called out twice to deal with children throwing eggs against houses. -Because the home owners realised this immediately, the egg remnants could be cleaned off at once, meaning that no material damage occurred. -Furthermore, in the town centre area, while a few fireworks were set off, no material damage was caused here either. -The residents of the Bischof-Freundorfer-Weg reported that a car was wrapped in toilet paper and its wheel trims stolen. -However, the man found all his wheel trims nearby prior to the police arriving, on account of which no further police involvement was required. -Living with a future-oriented perspective -If truth be told, Waltraud Ries, lives a dreamy life. -Her house is located in a peaceful neighbourhood in Stuttgart, with plenty of greenery, an old tree population, nice neighbours and not too much traffic. -She is only a few minutes from the city centre using public transport. -"Hopefully you aren't afraid of spiders?" asks Waltraud Ries, pointing out a large spider on the door frame. -I have been looking for a new home for my husband and myself for some time now. -But you know how difficult that is in Stuttgart. -However, the reason for the desire to move is not arachnophobia, but rather the fear of not being able to manage the large number of steps up to their house, and inside their home in their old age, she explained. -Furthermore: Since undergoing a meniscus operation, the lady, in her mid-fifties, has had to experience for herself what it means to move from floor to floor with a disability. -Only recently, she released a book on the subject of "Living happily in old age - Which type of property is the best for me?". -"Our home is the best example of a property that simply doesn't work in old age," she begins to recount. -When she moved to the maisonette with her husband 20 years ago, it was simply just a pretty apartment in green surroundings. -Now, in her mid-fifties, Waltraud Ries has different thoughts about it. -However, she is aware that she is one of the few people to actually deal with the topic of 'living in old age'. -The majority of people put it to the back of their minds for as long as possible. -Only other people get old, she says, smirking. -Only when the psychological strain becomes severe do people give it consideration. -"But at this stage it can be too late," said Ries. -In her book, the interior decorator presents 17 housing models for independent living in old age. -"However, which type of residence you choose is always a personal decision," she explains. -There is no solution to overcoming old age. -Even if you can afford to spend your retirement in Tuscany or Brittany, you should always take the fact that you may become ill into consideration. -And: 'Without a sound knowledge of the local language, you can even become lonely when living the dolce vita,' added the author, providing food for thought for those seeking to move abroad. -Her models include classic care models, alternative living arrangements and the often-mentioned pensioners' living community. -In old age everyone has their quirks and ticks. -'However, this type of residential community would not be for me,' she commented with a wink. -Definitely nothing like student accommodation with a shared bathroom and kitchen. -'In old age you simply need your own space to withdraw to,' said Ries. -However, the author does not wish to rule out the possibility that a pensioners' residential community can work. -However, it must operate differently to the classic student digs. -And sometimes you also just want your peace and quiet,' she believes. -With this opinion she is at least not alone in the State Capital. -The experience of Theresa Rütten, head of the citizen's Life in Old Age service in the State Capital, is also that 'the majority of old people in Stuttgart want to remain in their own homes for as long as possible'. -Her agency agency advises people on the topic of 'growing old'. -Therefore the experience of the Stiftung Warentest consumer study is also that elderly people in particular would accept inconveniences and limitations just to be able to remain in their usual environment. -Nowadays there are many possibilities for designing the house or apartment in such a way as to be barrier-free. -Ries recommends that all those working on the age-appropriate renovation of their house or apartment first seek out in-depth information and don't simply use the first tradesman that comes along. -Nowadays there are specialists in renovation to suit the needs of the elderly. -Even in the case of rental apartments some landlords can have this work carried out, even if it is just a matter of raising the level of the toilet seat, clarified the interior designer. -However, it is not always the case that your existing apartment can be made age-appropriate and therefore barrier-free, even if it is as beautiful as ever. -Waltraud Ries had to go through this experience herself. -You could of course install a stair lift inside the apartment, but the cobbled path up to the door of the apartment, featuring a number of steps would be a much greater hindrance. -She will keep looking until she has found the ideal property for herself and her husband. -She is more afraid of the move than of the spider on her front door. -The USA still want to prosecute Snowden -The USA have not changed their position regarding the former US Secret Service employee Edward Snowden. -He is still accused of passing on secret information without authorisation. -He must therefore face criminal proceedings in the USA, said the spokesperson for the US Foreign Office, Jennifer Psaki, in Washington. -Even the most recent statements from Snowden will not change this fact. -Following a recent meeting with Snowden in Moscow, German Green Party politician Hans-Christian Ströbele appealed to the USA not to continue to threaten him with prosecution. -Psaki emphasised that Ströbele's comments were the views of a parliamentarian, and not those of a German government member. -Secret Service whistleblower, Edward Snowden is, in principle, willing to hold a discussion with the German authorities. -However, he first wishes for his situation to be clarified. -This was made clear by the former NSA employee, currently sought by the USA, in an open letter that Green Party politician Hans-Christian Ströbele made public on Friday following a meeting with Snowden, and passed on to the Federal Government, the Federal Parliament and the Federal Public Prosecutor General. -The letter was purported to have been worded as follows: -To whom it may concern, -I have been invited to write to you regarding your investigation of mass surveillance. -I am Edward Joseph Snowden, formerly employed through contracts or direct hire as a technical expert for the United States National Security Agency, and Defense Intelligence Agency. -In the course of my service to these organizations, I believe I witnessed systemic violations of law by my government that created a moral duty to act. -As a result of reporting these concerns, I have faced a severe and sustained campaign of persecution that forced me from my family and home. -I am currently living in exile under a grant of temporary asylum in the Russian Federation in accordance with international law. -I am heartened by the response to my act of political expression, in both the United States and beyond. -Citizens around the world as well as high officials - including in the United States - have judged the revelation of an unaccountable system of pervasive surveillance to be a public service. -These spying revelations have resulted in the proposal of many new laws and policies to address formerly concealed abuses of the public trust. -The benefits to society of this growing knowledge are becoming increasingly clear at the same time that claimed risks are being shown to have been mitigated. -Though the outcome of my efforts has been demonstrably positive, my government continues to treat dissent as defection, and seeks to criminalize political speech with felony charges that provide no defence. -However, speaking the truth is not a crime. -I am confident that with the support of the international community, the government of the United States will abandon this harmful behaviour. -I hope that when the difficulties of this humanitarian situation have been resolved, I will be able to cooperate in the responsible finding of facts regarding reports in the media, particularly with regard to the truth and authenticity of documents, as appropriate and in accordance with the law. -I look forward to speaking with you in your country when the situation is resolved, and thank you for your efforts in upholding the international laws that protect us all. -Edward Snowden, as witnessed by Hans-Christian Ströbele -A kindergarten has found a private buyer -"The building is in good hands," said Winterlingen's Mayor Michael Maier. -The local authorities have sold the former Kindergarten on Gartenstraße to a private buyer. -The Town Council consented to the sale in its most recent meeting, during the part of the meeting that was not open to the public. -The building changed hands for an "appropriate" price. -"The local authorities are satisfied," emphasised Maier. -EUR 100,000 have been added to the local budget. -It is not yet clear how the new owner will use the former Kindergarten. -The Mayor considers it the right decision that Winterlingen has handed over responsibility for the building to someone else before the winter, and thus will no longer have to pay for the upkeep of the building, such as heating it, tending to the garden and clearing the pavement. -"This expense has now been offloaded," Maier was glad to say. -Open-air concert accompanied by high summer temperatures. -The Hammereisenbach Music Society gave an open-air concert on the terrace of the Hammer Guest House. -When conductor, Bianca Willmann, raised the baton at 7:30 p.m., the thermometer was still reading 25 degrees. -Around 50 listeners enjoyed the sounds of the Hammrich musicians. -Chairperson, Manuela Honeck, provided explanation to accompany the pieces. -In so doing, Honeck thanked all those who had helped successfully manage the first Musikerhock. -Report: Obama campaign considered dumping Biden for Hillary Clinton -President Barack Obama's closest advisers secretly considered replacing Vice President Joe Biden with Hillary Clinton on the 2012 ticket, according to the New York Times. -The revelation is the most notable bombshell from Mark Halperin and John Heilemann's heavily anticipated 2012 campaign tome, "Double Down: Game Change 2012." -The Times obtained a copy of the forthcoming book and reported Thursday evening that the President's top aides conducted "extensive group-sessions and polling in late 2011" to gauge whether the dumping Biden could help bolster Obama's waning re-election hopes. -According to the Times' national political correspondent Jonathan Martin, the book provides a thorough account of the effort by senior officials inside the campaign and the White House, namely former White House Chief of Staff Bill Daley, to measure what effect swapping former Secretary of State Clinton for the Vice President would have in the polls. -The potential switch was a closely guarded secret within the Chicago campaign infrastructure and inside the Oval Office. -Only half a dozen of the President's closest advisers -- including Daley, former Obama campaign chief Jim Messina, and former White House senior advisers David Axelrod and David Plouffe -- knew the change was under consideration. -"Double Down" claims Daley spearheaded the effort to replace Biden, despite their "close personal rapport," before ultimately deciding against the move when their data showed adding Clinton to the ticket wouldn't "materially improve Obama's odds." -In an interview with Martin, Daley confirmed that the administration did in fact consider replacing Biden with Clinton. -"I was vocal about looking into a whole bunch of things, and this was one of them," Daley told the paper. -You have to remember, at that point the President was in awful shape, so we were like, "Holy Christ, what do we do?" -While Daley characterized the research as "due diligence," Martin told CNN's Anderson Cooper that the re-election campaign made a significant investment in finding out whether the move would pay dividends at the polls. -"Campaigns don't spend the kind of money on polling and focus groups unless they're seriously considering something," Martin said on AC360. -It's unclear, however, whether Obama knew his team was exploring the swap. -Martin told CNN that he asked Daley whether his then-boss knew about the potential shuffle. -While Daley said he doesn't think the President "was aware" of the potential change, the former chief of staff admitted that it's "possible" Obama knew. -Martin added that "Double Down" does not definitively answer whether the political probing reached Obama's desk. -Cooper asked Martin whether he seriously thought Obama did not know about the research into dumping Biden from the ticket. -"Possibly," Martin replied. -What happens with my e-mail when in transit? -E-mail is short for electronic mail. -Are there technical measures to maintain privacy of correspondence, similar to that of the traditional postal service, implemented into the design of this Internet-based service? -When designing the early Internet services, the focus lay on making communication possible. -Thus, when developing the underlying technical protocols, no attention was given to creating a stable foundation for the interception-proof exchange of mail. -For this reason, a traditional email is more like an open postcard than a sealed letter. -When user "Anna" sends an email to "Benni", are the pair's computers then directly connected? -Anna's email program or browser first sends the mail to her service provider's mail server. -If she has a Gmail account, for example, this would be Google. -This mail server sends the content to the provider that Benni uses. -En route, the mail can pass through any number of other servers on the Internet. -Benni can then call up the content from his provider. -Are traditional e-mails read by third parties on their journey through the net? -The majority of e-mails are read several times by software robots en route to the recipient. -Checks are generally carried out by the sender's provider as to whether the mail contains malware as an attachment. -A virus check is also carried out on the recipient systems. -In addition, the providers check whether it is nuisance or unwanted spam mail, which are deleted immediately or at least sorted into a spam folder. -Do the robots have other tasks? -In the case of systems such as Gmail, the robots also identify the information that Google requires for displaying context-based advertising. -If Anna and Benni communicate about their upcoming holiday travels via Gmail, Google can display corresponding links to holiday offers. -Do unknown individuals also read the e-mails? -The probability of unauthorised persons reading a mail is extremely low. -However, theoretically, it is possible. -Within companies, administrators are often able to read electronic mail that is sent. -Law enforcement authorities and Secret Services also have legal authorisation to intercept or take note of e-mails. -This may include potential illegal spying activities. -Is it possible to protect e-mails against being read? -With an encryption using the OpenPGP standard, an e-mail can be effectively protected against being read. -With great technical effort, it is also possible to conceal the metadata of email communication so that it is not even possible to tell who is communicating with whom. -How important are Yahoo and Google within the e-mail market in Germany? -According to a study conducted by Convios Consulting in August 2013, among the main mail services in private use, Yahoo and Google play only a minor role. -While the number of Yahoo Mail accounts has fallen recently, Google's Gmail managed to grow considerably. -Mother buried in shallow forest grave and pension stolen -When his elderly mother died, a man from Wolfsburg devised a plan. -He buried the deceased in a forest - and continued to collect her pension and care allowance. -According to police investigations, a 67-year-old Wolfsburg resident buried his dead mother in the woods in order that he might continue to collect her pension and care allowance. -He first covered up the death of the 89-year-old and then pocketed the money for more than a year and a half. -The 67-year-old must now face charges of fraud, said a spokesperson on Friday. -According to the statement, the man hid the woman in a forest near Helmstedt. -The man was already under suspicion back in May: At that time the fact that medications for his care-reliant mother were no longer required drew suspicion. -The pensioner told investigators that his mother had travelled to Spain. -The officials found this strange and so continued to investigate. -It was discovered that the mother and son had lived in the same residence for more than 15 years and that he had cared for her in her final days. -The 67-year-old finally acknowledged the death of his mother. -According to police, an autopsy showed no evidence of any capital offence. -Fancy a glow-in-the-dark ice cream? -A British entrepreneur has created the world's first glow-in-the-dark ice cream - using jellyfish. -Charlie Francis has harnessed the fluorescent properties of the marine animal to develop the luminescent snack. -He came up with the idea after reading a research paper on jellyfish and convinced scientists in China to chemically recreate the glowing protein. -The ice cream reacts with the eater's tongue - raising the pH level in the protein and making it glow. -Chris says because the ice cream lights up when it reacts with the heat of the mouth it means the more you lick, the brighter it becomes. -Charlie, founder of the "Lick Me I'm Delicious" ice cream company, said: "It is incredible stuff but still at very early days in terms of production, so £200 gets you about 2g of the stuff." -The protein we are using in the ice cream reacts with your tongue at neutral pH. -So as your mouth warms up the protein it will raise the pH level and the ice cream will glow. -We have been testing it out over the past few months and it seemed perfect to share it over Halloween because it gives that wonderful glow effect. -It is probably the most expensive ice cream I have made because the jellyfish luminescence is four times more expensive than gold. -So each scoop costs me around £140. -It tastes pretty good though. -Charlie's experimental company, based in Bristol, is famed for its unusual flavours including beer, cheese, beef and gold leaf. -But his next creation is set to be even more ambitious. -He said: "I really want to develop an invisible ice cream." -It is inherently impossible because of the refraction caused by the ice crystals which make up the ice cream, but I reckon we will find a way of doing it. -The ice cream harnesses the fluorescent properties of a jellyfish, synthesized by Chinese scientists -After six match days, TSV Morsum are yet to score a single point in the Handball Association League. -And of all possible times, Ingo Ehler's team has a derby match against TSV Daverden tomorrow, which celebrated their first victory last week. -The season begins afresh for us with this match. -"I hope that we finally have our heads clear," commented Ehlers, remaining cautiously optimistic. -For this match he finally has his complete squad available, with the exception of Hendrick Blohme who is out with long-term injury. -Like us, Daverden place emphasis on high tempo play. -For this reason, we must keep our errors to a minimum. -However, the top priority is to improve our cover play, which has recently left much to be desired. -Daverden's coach Thomas Panitz comes across as much more relaxed following his team's first victory of the season against Nordhorn. -The match was of course a indication of what we are capable of. -However, it by no means makes us favourites, as anything can happen in the derby. -Nonetheless, Panitz is now looking to come up with the goods in Morsum and thus start a winning streak. -Jan-Malte Jodat will not be playing. -Thus, A-Youth player Joost Windßuß, who plays an active part for A-Youth League team HC Bremen, will be celebrating his season début. -Anyone who is not fired up for a game like this should stay at home. -Halloween 2013: By the Numbers -When I was little, Halloween was magical. -My sister and I were allowed to eat candy, stay up late and play dress-up for the neighborhood. -Nowadays, I've become more of a scrooge. -I haven't signed up for the past two years to give out candy in my apartment and probably won't this year. -But stats show that I'm a black sheep when it comes to Halloween. -The majority of Americans - 158 million of them in fact - will be celebrating Halloween this year, spending a total of $6.9 billion on candy, costumes and decorations, according to the National Retail Federation. -One thing I do look forward to every Halloween are the trends. -Costumes are expected to account for $1.2 billion dollars out of the $6.9 billion spent, according to the NRF. -This year, sexy inanimate objects are all the rage. -Women don't have to be sexy professionals anymore; they can also be sexy foods like pizza, hamburgers and carrots. -As for men, I expect we will be seeing a lot of zombies, thanks to The Walking Dead and I'll bet the Daft Punk space men will make it into our Instagram feeds this year. -According to Google, the highest searched costumes are zombies, Batman, pirates and witches. -I guess there's nothing wrong with going traditional. -We dressed our dogs up last year and to my amazement we were not alone. -In fact, Americans will spend $330 million on pet costumes this year, according to the NRF. -That's a lot of ironic hotdog dogs. -When it comes to candy, we don't screw around. -Americans will spend $1.9 billion on it this year, according to The Nielsen Company. -That's around 600 million pounds worth of Hershey bars, lollipops, Milk Duds, Twizzlers and Clark Bars. -That's great news for the 41 million trick-or-treaters set to take over our neighborhoods, according to the U.S. Commerce Department. -In fact, we will buy and, who are we kidding, consume 90 million pounds of chocolate during Halloween. -The one thing we don't want to consume, candy corn; and yet nearly 35 million pounds of it are sold around Halloween, according to the National Confectioners Association. -That's about 9 billion individual kernels of corn. -It's a mystery I have yet to solve. -Nothing is more quintessentially Halloween than haunted houses. -They have the best names, like "Terror Behind the Walls" (which, by the way is in an actual prison), "Howl-O-Scream" and "The House of Shock." -In fact, there are 1,200 officially sanctioned haunted houses in the United States generating about $500 million in revenue, according to America Haunts, and that includes those awesome photos of you mid-peeing your pants that your friend puts on Facebook and you can't take down and then that guy you like sees the photo and leaves a comment like "nice face." -Finally, let's talk pumpkins. -Charlie Brown introduced us to The Great Pumpkin when we were kids, and carving a jack-o-lantern is like decorating a Christmas tree - it's something we've done since we were little. -Lucky for us, the "baby in a pumpkin trend" started only last year thanks to Pinterest, so most of us grew up carving these gourds not sitting in them. -This year, Americans will spend around $106 million on pumpkins, according to the U.S. Census Bureau. -The jack-o-lantern slowly withering on your front porch probably came from Illinois, which grew 542 million pounds of pumpkin this year. -If you're looking for extra credit, call Tim and Susan Mathisdon in Napa, Calif., and try to carve up their 2,032 pound pumpkin. -House fire in Helmbrechts: Rescue services retrieve charred body -The fire service were called out to Helmbrechts late on Thursday evening. -A three-bedroom house in the district of Hof catches fire during the night. -Rescue workers retrieve a charred body from the house. -During a fire in Helmbrechts, rescue services found a charred body in a three-bedroom house. -Whether this person was living in the house is as yet unclear, said a police spokesperson in Bayreuth. -Late on Thursday evening, fire broke out on the ground floor of the house. -When the fire service arrived, the flames were already bursting out of a window. -Rescue workers entered the premises and found the body in a bedroom. -This and another bedroom were completely burnt out. -The entire ground floor suffered damage. -There were no other residents at home during the fire, continued the police spokesperson. -For the time being, the house is no longer habitable. -The precise cause of the fire was initially unclear. -Germany's largest national church has launched a campaign advertising the minister's position. -This comes at the same time as a possible shortage of ministers: The Protestant Lutheran Regional Church of Hanover calculates that, as things stand, the current number of around 1,800 pastors will be halved by 2030. -For the young people of today, the career is very attractive and offers good future prospects, said Pastor Mathis Burfien (43) in a conversation with the Protestant Press Service. -It is attractive to be able to determine your own daily working routine. -With Burfien, the regional church has for first time commissioned a minister to work full time, endeavouring to inspire young people to study theology. -At present, fewer and fewer young people are deciding to study theology after their Abitur examinations (equivalent to A levels). -Burfien puts this down to the process of secularisation: "God's voice is quiet, the world is loud". -The job is characterised by great freedom and variety. -I am the master of my own schedule and can therefore turn my focus to what is important to me. -As pastoral workers, ministers can be close to people. -They earn as much as teachers and can make a good living. -Of course wages are higher within the private sector, but on the other hand, theologians have a secure employer. -This would suit the young people of today, as it is not just about your career, but about having a meaningful job as well. -According to information from the Regional Church, which covers three quarters of Lower Saxony, around 60 pastors retire every year. -At the same time, around 40 theology graduates begin as vicars. -In future it may be difficult to fill positions in sparsely populated outlying areas such as Harz, Emsland or Wendland. -Among other initiatives, Burfien wants to organise study days for young people, inviting famous faces who have studied theology. -Theology courses offer broad training. -You can even become Federal President with a theology degree. -Land Rover rally series announced -The interior has racing seats and six-point harness belts, as well as an intercom system. -Options include upgraded brakes, a service package providing access to Bowler Works mechanics, logistic support, and vehicle storage between events. -Drew Bowler, the managing director of Bowler Motorsport, said: "Rally customers coming to Bowler have changed." -They're not all experienced racers, but people looking for excitement and adventure, and an achievable path towards world-class events. -We're delighted to be offering this path in partnership with Land Rover and the MSA, and believe the format offers a new way to experience different rally disciplines in the UK and overseas, and prepare entrants for the rigours and realities of Rally Raid. -We've really enjoyed developing the Defender Challenge car - it'll be a really fun championship. -Additionally, the Defender Challenge will provide a training and test day in February, as well as the option to compete in desert events in North Africa and the Middle East. -Fire crews called to rescue lost puppy after she got stuck 50ft above the ground on precarious ledge in a quarry -Cocker spaniel Ruby had run off after she was in a minor road crash -She was spotted three days later by a dog walker trapped in the quarry -Firefighters abseil down cliff face to pluck the dog from certain death -A puppy had a lucky escape after fire crews were called to lift her to safety when she somehow got herself stuck 50ft up on a precarious cliff ledge. -Nine month-old cocker spaniel Ruby had run off after being involved in a road crash on Sunday afternoon and survived three days alone before being rescued from a quarry on Wednesday. -Her owners Scott Alderson, 25, and his girlfriend Becky Hall, 20, were at Flappit Quarry in Denholme, West Yorkshire, to be reunited with Ruby and have thanked West Yorkshire Fire and Rescue Service. -They had searched frantically for their missing dog and posted appeals on social networking sites after she had ran into the quarry following the minor accident. -At around 2.15pm on Wednesday, an eagle-eyed dog walker spotted Ruby on the ledge in the quarry, stranded 50ft up. -A Technical Rescue Team from Cleckheaton Fire Station rushed to the scene and abseiled down to rescue Ruby and used a pet tube to transport her up the cliff. -Specialist Technical Rescue Officer Andy Clayton said: 'She was in a precarious situation. -She was right in the middle of the cliff face - 50ft from the top and bottom. -She did not move a muscle during the rescue - she was frozen solid. -But she is fine now. -She was eating biscuits afterwards. -This was a very unusual call-out. -The fact that the dog was spotted is unbelievable. -Specialist Technical Rescue Officer Peter Lau said: "Ruby had a very lucky escape." -The potential was there that she could have been very seriously injured or worse. -Ruby was taken to the vets for a check-up and was found to be fine other than exhaustion and dehydration. -Miss Hall, from Halifax, West Yorkshire, said: "Watching the rescue was terrifying." -I could not believe that she was up there in the first place. -It was amazing to get her back in our arms. -The vet said that if she became too exhausted and collapsed she would probably have fallen. -The firefighters were amazing. -It was really daring what they did. -We are just so grateful and every single one of them was absolutely tremendous. -Mr Alderson, from Keighley, added: "We were scared that she might fall but she stayed there." -The firefighters were brilliant. -I just can't believe where she was. -Mick Jagger says he never hit on Katy Perry when she was 18. -During an interview with an Australian radio show this week, the pop star said she sang backing vocals for Jagger's 2004 song "Old Habits Die Hard." -Perry said she had dinner with the veteran rocker and that "he hit on me when I was 18." -She added, "That was a long time ago, and he's been very kind." -In a statement Thursday, a representative for Jagger, 70, says he "categorically denies that he has ever made a pass at Katy Perry." -The rep adds: "Perhaps she is confusing him with someone else." -Perry was one of the singers to make a guest appearance on the Rolling Stones' tour this year. -Her new album, "Prism," debuted at No. 1 this week. -George Kerevan: Europe break-up gives Scots choice -Another day, another independence scare story. -This time we are warned that an independent Scotland would be required to join the Europe-wide free-travel zone as a condition of EU membership. -Cue stories about passport controls at Berwick and a barbed wire border along Hadrian's Wall. -True, the Strathclyde paper pointed out the possible economic benefits of freer movement with the rest of Europe, though - predictably - that did not figure in the headlines. -Nor did anyone point out that the EU member states spend much of their time bending their formal rules if it suits them. -Since Scotland isn't in the Schengen area now, continued non-compliance would be a cheap concession for Brussels to offer up in return for whatever it really wanted out of the Scots. -So, a non-story, then. -And one that is so long in the tooth it has become fossilised: I first heard the "independence means passport controls" canard at least 40 years ago. -Yet there is an interesting point lost in this retelling of a whiskery old tale. -Why should an independent Scotland be expected to do Europe's bidding, anyway? -Why trade London's yoke for that of Brussels, especially now? -Here is the real European news: the great, post-war plan to unite Europe has finally stalled. -With the euro crisis, Project Europe is officially dead. -Across the EU, parties which are dedicated to opposing the EU, or to scrapping the euro as a common currency, are gaining ground. -Even in Germany, the Eurosceptic Alternative for Germany Party - founded only this year - came from nowhere to grab nearly five million votes in September's federal elections, thus effectively knocking the Free Democrats (equivalent to our own Lib Dems) out of the Bundestag. -There has always been domestic opposition to the plan to create a federal Europe. -However, the current economic crisis has proved a watershed. -The austerity imposed by Berlin and the European Central Bank, coupled with the straitjacket imposed on national economies through adherence to the common currency, has led many people to think Project Europe has gone too far. -The crisis of the euro has little to do with national governments running excessive budget deficits - that was true only of Greece. -Rather, the euro system locked in its members at exchange rates favourable to German exporters - something German politicians want to keep. -Without the possibility of domestic currency devaluation, southern Europe finds itself with a built-in productivity disadvantage vis-à-vis Germany. -The only recourse is to slash wages and public spending - spurred on by Berlin. -Beyond the current budget and currency problems lies a deeper European productivity malaise. -As a result of "green" energy policies imposed by Brussels - code for subsidising French and German energy firms at the consumer's expense - European industry pays twice as much for electricity, and four times as much for gas, as in the United States. -That is a crippling cost disadvantage, as we've already seen at Grangemouth. -All the wage freezes in the world won't stop the European petrochemicals industry being hammered by cheap US shale gas. -As a result, revolt is brewing, especially in France, once the EU's main cheerleader. -After the war, the French political elite saw the EU as a vehicle to keep Germany in check, and to give Paris equal billing in the world with Washington. -But Berlin no longer needs Paris as a passport to political legitimacy and has imposed its own economic policy on Europe, leaving the battered French economy struggling. -Result: Marine Le Pen's right-wing, anti-EU National Front has just won a crucial by-election, knocking the ruling Socialists into third place. -The Front is now the most popular party in France with 24 per cent of the vote - a timely warning to British Labour that they can't assume a split on the right will automatically favour the left. -What is Le Pen doing with her newfound popularity among the French white, working class? -She wants to use next year's EU elections to create an anti-EU, anti-common currency bloc across the European Parliament. -If, as is very possible, anti-EU parties do well in these elections, such a bloc could dominate the European Parliament for the first time. -Here's my point: sometime soon growing anti-EU and anti-common currency feeling in Europe will coalesce to kill the euro. -The EU won't disappear, but it will revert to something more like the loose "Europe of the (Sovereign) Nations" favoured by General de Gaulle. -Germany and a few of its satellite economies might keep the euro but France and southern Europe will revive their own currencies. -I expect the UK will distance itself from this project, hoping to cosy up to the US. -However, Washington's growing interest in the Pacific suggests Britain will be left out in the Atlantic cold. -Where does this leave Scotland? -We can choose to be a region of (essentially) Little England. -Or we can defend our own economic interests - which includes telling Berlin and Brussels where to get off. -I suspect that Scotland could do well inside a looser European arrangement provided we kept our own currency. -Co-operation with other like-minded countries will be easier in a non-federal Europe of the Nations. -Otherwise we should consider emulating Norway and retaining our economic independence. -The SNP government in Scotland is - remarkably-- the most successful anti-austerity political movement in Europe, having won a spectacular majority in 2011 on the basis of opposing the cuts proposed (and implemented) by Labour's chancellor Alistair Darling and the subsequent Tory-Lib Dem coalition. -It would be ridiculous now for Scotland to vote for independence only to accept austerity imposed by Berlin and Brussels. -Early puberty: Growing older sooner -African-American and Hispanic girls tend to reach puberty earlier than their white counterparts, research shows. -Physical changes don't mean puberty is imminent -There's no evidence that hormones or other chemicals are to blame -Experts think the obesity epidemic might be one trigger of early puberty -The trend toward early puberty is not as pronounced with boys -Former CNN correspondent Pat Etheridge is a journalist specializing in children's health and family issues. -Should a mother be alarmed if her daughter begins to sprout breast buds and pubic hair at 7 or 8? -At the annual conference of the American Academy of Pediatrics this week in Orlando, Florida, pediatric endocrinologist Dr. Paul Kaplowitz explained that these early physical changes are quite common among American girls and represent a new norm. -"I spend a lot of time reassuring parents -- usually, this does not signal a rapid progression into full puberty," said Kaplowitz. -Obvious signs of development, such as budding breasts, pubic and underarm hair and body odor are appearing sooner in girls. -But there has been only a slight shift in the age of menarche (the first period) over the past four decades. -In the United States, the average age is 12.5 years, down from 12.75 in 1970. -"Once breasts begin to develop, it takes at least two to three years before menarche," said Kaplowitz, also author of "Early Puberty in Girls: The Essential Guide to Coping with This Common Problem." -Time is the most accurate test of how puberty is going to progress. -There is debate about what constitutes the actual onset of puberty, but it is considered "precocious" when breast enlargement is accompanied by a growth spurt before age 8. -In most cases, the process will slow down or stall -- something a pediatrician can monitor closely. -A more rapid progression may warrant tests by an endocrinologist to rule out serious problems such as tumors or cysts. -There are treatments to delay early menses and ward off another consequence: premature aging of the bones that ultimately can lead to stunted growth and being short as an adult. -Recommendations for drug or hormone therapy are based on the child's age, rate of development, growth rate and emotional maturity. -Psychosocial aspects are important, too. -Kaplowitz is cautious with medication but acknowledges, "suppressing puberty may alleviate behavioral issues and girls' feelings of being different from peers." -The other big issue is understandable: Parents simply don't want their very young daughters having periods. -"They worry about the risk of pregnancy or even how they will handle hygiene," said Kaplowitz. -"It was a shock," recalls one woman whose daughter started her period at 10. -Even though there were signs and we had talked about menstruation, she was not emotionally prepared. -She came home from school scared and upset to be the first among her friends. -There are lots of well-publicized theories about the causes of precocious puberty. -Yet, there's no consistent body of evidence that hormones in milk or other foods, chemicals in the environment or sexual messages in the media are to blame. -Boys - like girls - are hitting puberty earlier. -Kaplowitz contends the premise that holds the most weight is the epidemic of obesity. -He helped conduct a 2001 study of 6- to 9-year-old girls that links body fat to the timing of puberty. -Other findings support this conclusion, but there are many other contributing factors. -In this country, African-American and Hispanic girls tend to reach puberty earlier than their white counterparts. -There are varying explanations. -Globally, patterns of early puberty appear to be influenced by everything from economic conditions to climate to genes. -Another conundrum: Although boys are getting facial and pubic hair at younger ages, the trend toward full-blown early puberty is not as pronounced as it is with girls. -Other doctors attending the AAP conference reinforced the complexities of the topic. -The appearance of acne and pubic hair is common even in infants and toddlers. -"We need to be careful about how we identify the true onset of puberty," said Dr. Lawrence Silverman, a pediatric endocrinologist at Goryeb Children's Hospital in Morristown, New Jersey. -Parents should not hesitate to get guidance from their pediatrician about how to talk with their child. -"It may mean having a sooner-than-expected conversation," Kaplowitz advised. -If you remain calm, your child usually will respond well. -Girls who blossom early need reassurance that, even when it happens ahead of schedule, the process is a normal part of life. -Jet makers feud over seat width with big orders at stake -A row has flared up between leading plane makers over the width of tourist-class seats on long-distance flights, setting the tone for a bitter confrontation at this month's Dubai Airshow. -The dispute focuses on the width of seats provided on long-haul flights for economy passengers - not always the ones most courted by airlines, but whose allocated space holds the key to efficiency claims for the latest jets offered by Airbus SAS and Boeing Co. -Airbus this week called for an industry standard that would provide for a seat at least 18 inches (46 cm) wide in economy cabins, but its U.S. arch-rival Boeing says it should be for airlines to decide. -The dispute comes as plane makers vie to sell ever-larger versions of their twin-engined long-distance aircraft, with potentially record orders expected at the November 17-21 event. -How the back of the plane is laid out - particularly whether seating is 9 or 10 abreast - is central to the economic performance claims being made for new "mini-jumbo" jet designs. -Boeing says its revamped "777X" will hold 406 people based on economy seats more than 17 inches wide and set out 10 in each row. -Airbus says the competing version of its A350 will carry 350 people in 18-inch-wide economy seat laid out 9 abreast. -Plane giants often trade blows on technical matters through advertising in the trade press. -Now, Airbus is appealing directly to the public ahead of the Dubai Airshow, where the 777X is expected to dominate with more than 100 orders. -It recently previewed what may be the start of a new ad war by showing financiers a slide illustrating three people squashed together at a restaurant, titled "Would You Accept This?" -"Boeing is proposing long-distance flying in seats narrower than regional turbo-props," said Airbus sales chief John Leahy. -As diets change, people get bigger but plane seating has not radically changed. -Between the early 1970s, when the Boeing 747 jumbo defined modern long-haul travel, and the turn of the century, the weight of the average American 40- to 49-year-old male increased by 10 per cent, according to U.S. Health Department Data. -The waist of the average 21st-century American male is 39.7 inches, according to U.S. health statistics. -Airbus says its rival is sticking to a seat concept from the 1950s, when the average girth of the newly christened "jet set" was narrower. -Airbus says it has commissioned research suggesting an extra inch in seat width improves sleep quality by 53 per cent. -Boeing disputes Airbus's figures on seat measurements and says it is not up to manufacturers to step into decisions on how airlines balance fares and facilities. -It also says research shows cabin experience depends on more than the width of a seat. -"It really comes down to providing flexibility to airlines and allowing them to do the things that they believe they need to do to be successful," said Boeing cabins expert Kent Craver. -They don't want us to dictate to them what makes them profitable. -They know their business better than anyone else. -For flyers it is about more elbow room, but for suppliers it is increasingly an issue that could affect earnings. -Behind the dispute is a race for plane orders with at least $700-billion of estimated business at list prices in coming decades, enough to tip the scales of U.S. and European exports. -As Reuters first reported in July, seat layout is exactly what drives the battle between the latest jets. -Both Airbus and Boeing claim 20 per cent better efficiency per seat in their latest twin-engined long-haul designs than the market leader in that segment, the 365-seat Boeing 777-300ER. -Boeing's performance claims depend in part on comparing the 10-abreast 777X with an original 9-abreast 777 design. -The gain in unit costs is blunted compared with 10-abreast now in use. -"The reason Boeing are doing this is to cram more seats in to make their plane more competitive with our products," said Kevin Keniston, head of passenger comfort at Europe's Airbus. -On the other hand, analysts say full 10-seat-per-row cabins for existing 777s suggest many passengers are voting for the denser layout, which may go hand in hand with cheaper fares. -"Eighteen inches in seat width would be great for passengers, but the reality is that from a business point of the Airbus proposal is driven by the threat of the 777," said cabin interiors expert Mary Kirby, founder and editor of the Runway Girl Network. -Airbus and Boeing do not supply seats but offer a catalogue of suppliers for airlines to choose from. -Globe-trotting jet sellers even carry tape measures to check on competing layouts. -While boasting comfort, all builders also offer jets with high-density layouts for low-cost airlines and regional travel. -Airbus offers a 10-abreast A350 but says it has not yet sold it. -Until recently, Airbus was stressing the need for more cabin customization by offering wider aisle seats on some of its jets. -Without the support of the only other maker of large modern jets, experts say its call for a new industry standard is unlikely to fly, but could distract from a wave of 777X sales. -New anti-nicotine vaccine could take the pleasure out of smoking -Scientists have developed an anti-nicotine vaccine that could take the pleasure out of smoking a cigarette. -A single dose of the vaccine was able to protect mice against nicotine addiction for life. -Further tests are needed before starting human trials, which would take several years, but Professor Ronald Crystal of Weill Cornell Medical College in New York said the early signs are good. -"We are very hopeful that this kind of vaccine strategy can finally help the millions of smokers who have tried to stop, exhausting all the methods on the market today, but find their nicotine addiction to be strong enough to overcome these current approaches," Prof Cornell said. -The new vaccine contains a harmless virus that has been engineered to carry the genetic information to make anti-nicotine antibodies. -The virus selectively infects liver cells, which then start to make a steady stream of the antibodies. -The antibodies hunt down any nicotine molecules in the bloodstream, neutralising them before they reached the brain, preventing a smoker from getting a nicotine hit. -In tests, vaccinated mice who were subsequently given nicotine continued with their normal activity. -But mice who had not been given the vaccine "chilled out," say the researchers, a sign that the nicotine had reached their brains. -The experiments are described in the journal Science Translational Medicine. -Previous tobacco vaccines failed because they contained antibodies. -The jabs had to be given so frequently to keep antibody levels topped up that they proved expensive and impractical. -But the cost of the new vaccine is likely to be far lower, because it turns liver cells into antibody factories. -Prof Crystal said that if a future human vaccine was completely safe it could be given to children before they were tempted to try a cigarette, preventing nicotine addiction. -But more likely it would be used by smokers to quit. -"They will know if they start smoking again, they will receive no pleasure from it due to the nicotine vaccine, and that can help them kick the habit," he said. -British scientists said the results were interesting but warned far more research was needed. -Tripodi denies being influenced by Obeid -Former NSW Labor minister Joe Tripodi will be investigated by the state's corruption watchdog. -Former NSW minister Joe Tripodi has denied changing maritime leases policy at the request of his political mentor Eddie Obeid, who had hidden interests in three properties on government-controlled land. -The Independent Commission Against Corruption (ICAC) on Friday widened its inquiry into whether Mr Obeid lobbied several state ministers to have leases at Circular Quay, where the Obeids owned two restaurants and a cafe, renewed without going to tender after their expiration in August 2005. -It's now investigating allegations Mr Tripodi knew of Mr Obeid's secret interest in the properties, after evidence given by Mr Tripodi's former deputy chief of staff, Lynne Ashpole, on Thursday. -During years of discussions starting in 2005 the government had been pushing for the leases to go to public tender. -The lessees were against this and also wanted longer terms. -In 2009 leases for the Circular Quay enterprises, which earned the Obeids about $2.5 million annually, were renewed without going to public tender. -Mr Tripodi, who was ports minister from February 2006 until November 2009, was initially in favour of public tenders. -But he denied the changes were made at the request of Mr Obeid, who Mr Tripodi acknowledged was urging a shift in government lease policy. -A phone transcript tabled in ICAC showed calls in August and September 2007 between Mr Obeid, Mr Tripodi and Steve Dunn, a senior bureaucrat who had come into the ports ministry after working under Mr Obeid in the fisheries department. -"Was the matter being discussed in the course of these telephone conversations the development of the commercial lease policy," Assistant Commissioner Anthony Whealy asked Mr Tripodi. -"No," Mr Tripodi replied. -I can't remember what was discussed but it definitely wasn't that. -Definitely not between myself and Mr Obeid. -Israeli warplanes attack target inside Syria, official says -Israeli warplanes struck a target inside the Syrian port city of Latakia Thursday night, a senior administration official confirms to Fox News. -The official did not specify what the target was, but said there was at least one. -The Associated Press reports the target was Russian-made SA-125 missiles. -At least twice earlier this year Israel launched airstrikes on shipments of missiles inside Syria. -Foreign workers on 457 visas could undergo "genuineness" test -A "genuineness" test for foreign workers on 457 visas is being considered by the government as it contemplates expanding a crackdown. -The test, if adopted, would be applied through a criteria aimed at preventing 457s being used to fill unskilled positions or as a back door way to move family and friends to Australia. -A government discussion paper was released today as former Labor MP Maxine McKew slammed the government's rhetoric about foreign workers, saying it could offend Australia's neighbours. -"Loud declarations about 'foreigners getting to the back of the queue' and 'Aussie jobs first' are a very unpleasant throwback to a time when unions demanded a protected labor market," she told the Australia India Institute today. -Historically, that meant it was white labour that had to be protected - and if some in the region saw echoes of that historic artifact, I wouldn't be surprised. -The discussion paper outlines 12 measures that were previously considered by former Immigration Minister Chris Bowen. -Immigration Minister Brendan O'Connor, who was yesterday in Sri Lanka where he is meeting officials about people smuggling, has implemented five of the recommended changes with the remainder under consideration. -If the "genuineness" criteria was adopted a visa applicant could be scrutinised about "whether the nomination is genuine in circumstances where the nominee is a relation or personal associate of an owner or relevant person of the sponsoring business." -Businesses could also be required to account for the number of 457 visa holders after previously businesses who had intended to sponsor a small number of workers then employed hundreds. -Meanwhile, a 35-year-old Sri Lankan asylum seeker died of a suspected heart attack after arriving on an asylum boat at Christmas Island this week. -The man's distraught nine-year-old son travelled to Australia with him and has been comforted since the death of his father on Wednesday by an adult cousin who was also on the vessel. -Australian authorities rushed the man to Christmas Island Hospital, where he died. -A study aiming to increase the benefits to Scotland of the HS2 rail project has been announced by the UK government. -The work by HS2 Ltd suggests high-speed services to Scotland and the north of England will start as soon as Phase One opens in 2026. -Transport minister Baroness Kramer said the project would "bring the UK together." -Scottish transport minister Keith Brown said he was "excited" to work with the UK government on the plan. -Phase One will consist of a new high speed rail line between London and the West Midlands. -When Phase Two is completed, lines will run to Manchester and Leeds. -In June the government revised the estimated cost of building the high-speed link between London and the North of England from £32.7bn to £42.6bn. -The UK government, which has been holding talks with Transport Scotland, has instructed HS2 Ltd to look at further rail capacity and journey time improvements for northern England and Scotland. -This is to include the possibility of eventual journey times from Glasgow and Edinburgh to London of three hours or less. -Baroness Kramer said: "Our goal for HS2 is for a truly national network that will bring the UK and its cities closer together." -We are driving forward HS2 because the benefits it will bring are huge. -Without it we face a crisis in capacity on our rail network. -But it is also about connectivity, across the UK 18 cities including Glasgow and Edinburgh will be better connected because of HS2. -Scottish Secretary Alistair Carmichael added: "Today's announcement is good news for Scotland." -For the Scottish government, Keith Brown called on Mr Carmichael to "unequivocally" back Scotland's inclusion in the HS2 network. -Mr Brown said: "High speed rail has the potential to bring huge economic benefits to Scotland, but also adds Scotland's economic weight to the overall case for high speed rail across Britain." -So we are excited to work in partnership with the UK Government to examine options for bringing high speed rail to Scotland, creating benefit for all and complementing the Glasgow-Edinburgh line which the Scottish Government is already planning. -I look forward to reviewing the report of the investigation with UK ministers next year and together decide on the next steps. -Aircraft electronic device rules to stay in force in Australia for now -Australian airline passengers will need to continue turning off their tablets and smart phones during take-off and landing despite moves in the US to loosen regulations covering the devices. -The US Federal Aviation Administration has left the way open for American carriers to change their procedures so that passengers will be able to read e-books, watch videos or play games on their devices during critical phases of flight provided they remain in "airplane" mode. -Passengers can already do this during the bulk of a flight but many people find it annoying to be unable to access their e-books during take-offs and landings. -Australian carriers are looking at the decision, which requires US carriers to undertake a massive amount of work to meet the requirements, but have indicated they have no immediate plans to change their procedures. -The Civil Aviation Safety Authority also said it was looking at the announcement but emphasised that restrictions on the use of electronic devices in critical phases of flight were still in place in Australia. -"CASA currently has no specific regulations governing the use of electronic devices in aircraft," it said. -The issue is covered by regulations which require aircraft operators to ensure safety is maintained at all times and passengers to comply with the safety instructions given by crew members. -Virgin, which has already been talking to CASA about extending the use its in-flight wi-fi entertainment system, was amenable to a change but said it would take its lead from the regulator. -"We would welcome a review by CASA into allowing the use of electronic devices because we really do think it will improve the customer experience now that we have (wireless in-flight entertainment) on our planes," a spokesman said. -Qantas said it would stick with the current rules for now. -"Our current policy is that electronic devices cannot be used during take-off and landing and we have no immediate plans to change that," it said. -The FAA ruling applies to American airlines. -However, we are always interested in regulatory developments that could benefit passengers and we will certainly be taking a close look at the FAA's decision and the reasons behind it. -For US carriers, the impact of the ruling will vary from airline to airline and will depend on the age of their fleet. -Carriers will need to prove their planes can tolerate radio interference from mobile devices as well as revise manuals, training materials, carry-on baggage programs and passenger briefings. -"Once an airline verifies the tolerance of its fleet, it can allow passengers to use handheld, lightweight electronic devices such as tablets, e-readers, and smartphones-at all altitudes," the FAA said. -In rare instances of low visibility, the crew will instruct passengers to turn off their devices during landing. -The group also recommended that heavier devices should be safely stowed under seats or in overhead bins during take-off and landing. -Travelling BVB fans created terrible scenes with flares in the Veltins Arena before the district derby against Schalke -Borussia Dortmund announced that there would be serious consequences. -Before Friday's Bundesliga match against VfB Stuttgart, the 'Ultras' responded with silence - initially. -It was an unusual, almost ghostly atmosphere that the fans created until shortly before the kick-off of the Bundesliga match between the German runners-up Borussia Dortmend and VfB Stuttgart, in the Signal Iduna Park. -It was mainly the chants of away fans that could be heard. -The south stand, on the other hand, where the most loyal - and the loudest - of BVB fans stand, was initially unusually quiet: No singing and no battle cries. -The 'Capo's' podium, the individual who usually coordinates the unison chants, remained empty. -And right at the front, on the stand, a single large flag with the text "Stadium Ban Section" blew in the wind. -Only when goalkeeper, Roman Weidenfeller, was the first BVB player to step onto the field, did cheers briefly erupt, as is usually the case. -When his team mates followed, there was loud singing and scarves were waving - but by no means everywhere. -In the central blocks of the south stand, blocks 12 and 13, nothing moved. -Even stadium announcer, Norbert Dickel, speaking once again of the successful results in recent days - the derby victory, the win against Arsenal, the contract extension of coach Jörgen klopp - did not raise spirits. -Just five minutes before kick-off, the Capo climbed onto his podium - and the tension that had been almost tangible up until that point, lifted with a large shout, as Dickel asked for a shout from each section of fans as usual. -As always, the south stand was left until the end, and finally the stand resumed its usual role as the loudest section of the stadium. -And as soon as the vociferous request for "derby winners stand up" came from the south stand upon kick-off, everything was back to normal. -The previous silence was indeed a reaction to the events of previous days. -Immediately before the local district derby against FC Schalke 04, some of the travelling BVB fans fired flares into the crowds of spectators and onto the pitch - and in so doing almost hit goalkeeper Roman Weidenfeller. -The kick-off was delayed on account of the chaotic scenes, -As a result, BVB Chairman Hans Joachim Watzke had invited the heads of the Dortmund Ultras fan group to his office - and if Watzke's words are to be believed, this was a rather uncomfortable meeting for the fan representatives. -"There will be massive impact for the entire Ultra movement," announced the BVB Chairman on Thursday evening at a panel discussion in Frankfurt. -As an immediate measure, Watzke prohibited the Ultras from organising choreography for the home match against VfB Stuttgart. -"I have banned it," he explained. -We cannot ignore the zero tolerance policy regarding pyrotechnics in Saxony. -In addition, the Ultras were given the option of several different punishments - they now have until Tuesday to decide which one to accept. -Otherwise BVB will impose a sanction by the end of next week, said Watzke. -"It will be severe," assured Watzke. -It will be interesting to see the reactions in the south stand then. -Pawnbrokers shine in Singapore as middle class feel the pinch -At a pawnshop in Bendemeer shopping centre in Singapore, Janani Amirthalinga is swapping a gold bangle, ring and pair of earrings to pay her daughters" school fees. -"My husband and I have just bought a house so all my money's stuck there," Mrs Amirthalinga says. -Even though she earns S$3,000 ($2,400) a month as an administrator and her husband works as well, the monthly family income is insufficient, she says. -Indeed, such is demand across parts of southeast Asia - where household debt is rising - that ValueMax, where she is carrying out her transaction, this week became the third pawnshop to list on the Singapore stock exchange. -Pawning jewellery is not merely a fast way to land cash - S$1,300 in Ms Amirthalinga's case - but almost as cheap as unsecured bank loans. -Typically pawnbrokers in Singapore charge an effective annual percentage rate of 17 per cent, just above the 15.4 per cent offered at United Overseas Bank, a local lender with a branch in the same shopping centre. -However, pawnbrokers have the advantage of not requiring credit checks or proof of salary, and can arrange loans faster than banks. -Hence millions of people across the region are turning to pawnshops as families feel the squeeze from rising living costs and ballooning household and consumer debt. -After five years of robust growth since the global financial crisis, and cheap credit fuelled by loose monetary policy in advanced economies, lower- and middle-income families are turning to pawn shops to make up the difference as their economies slow. -This week Standard & Poor's, the rating agency, cited increasing household leverage, mainly from rising mortgages, as a risk factor for Asian banks" creditworthiness. -It said that Malaysia, Thailand and Singapore had the highest household debt to gross domestic product ratios in Asia. -Malaysia topped the list at 80 per cent of GDP, up from 60 per cent in 2008. -Economists are also worried about high levels of consumer debt in Thailand, which this week narrowly emerged from technical recession. -On Thursday, data showed continued export weakness, and a softening in consumer demand. -"Bottom line is that with costs rising, people in the middle to lower end [of the income scale] will be looking to supplement their income wherever they can," says Song Seng Wun, economist at CIMB, a Malaysian bank. -Historically high prices for gold in the past two years have added to the rush to pawn personal belongings, as people take the opportunity to cash in the value of their family jewellery. -In Singapore, about 70 per cent of items pawned at the city-state's 200 pawn outlets are gold. -People are saying "the gold price looks good, let's pawn grandma's gold chain and get it back next month. -In Thailand the largest pawnshop operator, EasyMoney, has seen an up to 20 per cent rise in the number of customers using its outlets in recent months. -Such is the growth in the pawn business that ValueMax, operator of the outlet at Bendemeer and of 15 others like it in Singapore, plans to expand not only in neighbouring Malaysia - where it has four shops - but outside Asia too, says Yeah Lee Ching, ValueMax's executive director. -The company will fund that by using 60 per cent of S$66m it raised this week in a listing on the Singapore stock exchange. -While some discount lenders have come under fire for high interest rates, Ms Yeah says that not only does pawning offer cheaper rates than other lenders, it also does not add directly to debt. -"Customers are mortgaging items that they already own, and monetising personal assets does not increase household debt," she says. -There's an increased social acceptance of pawnbroking as a means to secure short term, secured financing. -Nor are the types of people who use pawnbrokers only the financially stretched. -Wealthy people in Singapore also use ValueMax outlets, pawning gold bars or Rolex watches, which can command up to 60 per cent of their purchase price in cash. -We see customers from all walks of life. -"They include wealthy individuals who need to borrow short term for business ventures or investments, or small businesses with a need to tide over their cash flow needs," says Ms Yeah. -Sometimes they just need the money very quickly. -NSA siphons data from Google and Yahoo - Snowden wants to help -Secret Service whistleblower, Edward Snowden, has a fundamental interest in helping Germany clarify the increasingly explosive NSA espionage scandal. -According to Green Party MP. Hans-Christian Ströbele, his surprising meeting with Snowden in Russia addressed the conditions under which the former Secret Service employee would make a statement to a German District Attorney or before an investigation committee. -Snowden referred to his complex legal situation, Ströbele said on ARD's "Panorama" programme. -$325m rescue package for Tassie health -The Federal Government insists a $325 million rescue package for Tasmania's ailing health system has tough conditions attached that will ensure the State Government can't waste the funds. -Federal Health Minister Tanya Plibersek has announced the Commonwealth is taking "urgent action" to head off a crisis caused by the island state's aging population, higher rates of chronic disease and system constraints. -The funding, over four years, was decided after government consultations with Tasmanian independent MP Andrew Wilkie. -"The Government has come up with an emergency rescue package we believe will address the unique challenges faced by the state," Ms Plibersek said today. -The $325 million package includes a $31 million elective surgery blitz. -An additional 2600 operations including orthopedic and cataract surgery will help clear a backlog. -There's also money for walk-in clinics in Hobart and Launceston, better after-hospital care, medical specialist training, mental health services and the rollout of personal electronic health record systems in local hospitals. -"These investments respond to the ideas that front-line clinicians have told me will be the best ways to tend to Tasmania's health system," Ms Plibersek said. -The minister insisted the Tasmanian Government would face a strict reporting and accountability regime. -The state would have to maintain current funding levels in order to receive Commonwealth cash and report monthly on where it was spending the extra funds. -A three-person commission will be set up to ensure the state is delivering services as effectively and efficiently as possible. -Mr Wilkie today said the $325 million would count for little "unless it's followed by genuine reform to put Tasmania's public health system on a more sustainable footing." -He nevertheless praised the Government for responding to his request for urgent assistance which he first raised with the Prime Minister at the beginning of May. -"I'm hopeful the federal assistance package will go a long way towards taking the state's public health system off the critical list," Mr Wilkie said. -According to the State Government these additional elective procedures will largely reverse the recent cuts. -But federal Opposition health spokesman Peter Dutton believes today's announcement is a "band-aid solution." -"The reason we are here is that the Labor State Government ripped $430 million out of its health system," he told ABC TV. -You can't have a state government ripping out almost half-a-billion dollars and the Commonwealth put in $300 million and pretend it's a good news day. -Mr Dutton called on Ms Plibersek to guarantee that not one dollar out of the rescue package would be spent on additional bureaucracy. -Guillaume Nicloux's adaptation of Denis Diderot's novel boasts exceptional production design and period detail but is also heavier going than it should be. -Unfolding in 1760s France, it tells the grim story of Suzanne, a young aristocrat sent to a convent by her family. -When she rebels, she experiences extreme cruelty at the hands of a wantonly sadistic Mother Superior and becomes an object of erotic fascination for another. -The film never slips into prurience or sensationalism - and that's the problem. -The earnest solemnity of the storytelling risks making it a hair shirt-like ordeal for audiences, too. -Syria has destroyed its chemical weapons making ability, watchdog group says -Syria has destroyed critical equipment for producing chemical weapons and poison gas munitions, the global chemical weapons watchdog said Thursday as fierce clashes raged in the country's north, close to one of the sites where toxic agents are believed to be stored. -Also Thursday, a Syrian activist group said more than 120,000 people have been killed since the start of the country's civil war nearly three years ago. -The announcement by the Organization for the Prohibition of Chemical Weapons came one day ahead of the Nov. -1 deadline set by The Hague-based organization for Damascus to destroy or "render inoperable" all chemical weapon production facilities and machinery for mixing chemicals into poison gas and filling munitions. -The completion of what is essentially the initial stage of destruction is a significant milestone in an ambitious timeline that aims to destroy all of Damascus' chemical weapons by mid-2014. -Destruction of the equipment means that Syria can no longer produce new chemical weapons. -However, Damascus still has to start destroying existing weapons and stockpiles. -The country is believed to have around 1,000 metric tons of chemicals and weapons including mustard gas and the nerve agent sarin. -The announcement came as fighting raged Thursday in the town of Safira, which experts say is home to a chemical weapons production facility as well as storage sites, reported the Britain-based Syrian Observatory for Human Rights. -The activist group, which has been tracking the death toll through a network of activists in Syria, said Thursday that 120,296 people have died. -Of those, it said 61,067 are civilians, including 6,365 children. -On the government side, it said 29,954 are members of President Bashar Assad's armed forces, 18,678 are pro-government fighters and 187 are Lebanese Hezbollah militants. -Also among the dead it said were 2,202 army defectors and some 5,375 opposition fighters, many of them foreigners. -On July 25, the U.N. estimated 100,000 have died in the conflict since March 2011. -It has not updated that figure since. -The conflict has forced some 2 million people to flee the country. -Assad's troops have been battling rebels, many of them linked to al-Qaida groups, in Safira for weeks. -The Observatory said there were casualties on both sides Thursday but had no specifics. -The fighting underscored the dangers the chemical weapons' inspectors face as they race against tight deadlines in their mission to rid Syria of the toxic arsenal in the midst of an ongoing civil war. -A statement from the OPCW, which works closely with the United Nations, said its team was "now satisfied that it has verified - and seen destroyed - all of Syria's declared critical production and mixing/filling equipment." -It added that, "no further inspection activities are currently planned." -Earlier this week, the inspectors said they had completed their first round of verification work, visiting 21 of 23 sites declared by Damascus. -They were unable to visit two sites because of security concerns, the inspectors said. -On Thursday, OPCW said the two locations were, according to Syria, "abandoned and ... the chemical weapons program items they contained were moved to other declared sites, which were inspected." -It was not immediately clear if the facility in Safira was one of the two sites that OPCW inspectors were not able to visit. -Syria has submitted a plan for the total destruction of its chemical weapons that has to be approved next month by the OPCW's executive committee. -"I salute the fortitude and courage you've all demonstrated in fulfilling the most challenging mission ever undertaken by this organization," the watchdog's director-general, Ahmet Uzumcu, said in comments released by the OPCW. -Now in its third year, the civil war pits the primarily Sunni Muslim rebels against Assad's government and its security forces, which are stacked with members of his Alawite sect, an offshoot of Shiite Islam. -In other developments, the Observatory's chief Rami Abdurrahman said there had been a strong explosion Wednesday inside an air defense facility in Syria's coastal province of Latakia. -The cause of the blast was not known, he said. -Anger over Bali bomb plotter's sentence -Survivors and relatives of the 202 people killed in the 2002 Bali bombing have reacted with anger over the sentence given to the last of the plotters to face justice, saying Umar Patek should face a firing squad. -Patek, who spent almost 10 years on the run as one of South-East Asia's most wanted, was yesterday sentenced to 20 years in jail for his role in building the explosive devices used in the bombing. -He could be released within 15 years if granted parole. -The 45-year-old was found guilty of mass murder for the attack on two nightclubs in the popular tourist area of Kuta which left 202 people dead, including 88 Australians, and injured scores more. -He was also found guilty of a number of other terrorism-related charges, including a wave of bombings of churches across Indonesia on Christmas Eve in 2000. -Prosecutors had demanded a life sentence, although they could have pushed that the man dubbed the "Demolition Man" for his reputation as a master bomb-maker be sentenced to death. -The decision has reignited painful memories for Perth mother June Corteen, who lost her 39-year-old twin daughters Jane and Jenny in the destruction unleashed by Patek and his co-conspirators almost a decade ago. -Fighting back tears, she said Patek should have been sentenced to death. -I really feel that he should follow in the footsteps of the other guys. -"He should be put in front of the firing squad," Ms Corteen told AAP. -I have to live every day without seeing more grandchildren, and my daughters. -The Sari Club was levelled when a massive bomb loaded into a van parked outside was detonated just after 11pm on October 12, 2002. -Peter Hughes was in Paddy's Bar where a suicide bomber detonated a backpack loaded with explosives just 20 seconds earlier. -He lapsed into a month-long coma in the wake of the bombing, and "died" three times while on life support. -Mr Hughes said Patek should have shared the same fate as three other members of the Jemaah Islamiah terror cell responsible for the carnage - Amrozi, Mukhlas and Imam Samudra - who were executed four years ago. -Really, this guy should get the death penalty before anybody. -To keep him alive, well, there's no reason to keep him alive. -To get 20 years, after killing 202 people and injuring many hundreds, it's not much. -Patek is the last of the Bali bombers to face justice. -He had avoided capture for almost a decade but was eventually apprehended in January 2011 in the Pakistani town of Abbottabad, where US forces killed former al-Qaeda chief Osama bin Laden less than four months later. -During the trial, an FBI agent testified that intelligence reports had revealed Patek was in Pakistan to meet with bin Laden in an effort to re-establish links between South-East Asian terrorist groups and al-Qaeda. -"He didn't give himself up," Ms Corteen said. -Until just recently, he really didn't feel sorry for how much grief he caused other people. -The verdict comes ahead of the 10th anniversary of the attack later this year, which will be marked by ceremonies in Bali and Australia. -"There will be a lot of tears this year," Ms Corteen said. -Patek may yet appeal his sentence. -FAA: Air passengers can now use gadgets on planes (but not make cell phone calls) -Airline passengers will be able to use their electronic devices gate-to-gate to read, work, play games, watch movies and listen to music - but not talk on their cellphones - under much-anticipated new guidelines issued Thursday by the Federal Aviation Administration. -But passengers shouldn't expect changes to happen immediately. -How fast the change is implemented will vary by the airline, FAA Administrator Michael Huerta said at a news conference. -Airlines will have to show the FAA how their airplanes meet the new guidelines and that they've updating their flight crew training manuals and rules for stowing devices to reflect the new guidelines. -The FAA said it has already received plans from some airlines to expand the use of portable electronic devices on planes. -Delta and JetBlue were among the airliners who have already submitted plans. -"Depending on the condition of the plan, we could approve expanded use of electronic devices very soon," the FAA said in a statement. -Currently, passengers are required to turn off their smartphones, tablets and other devices once a plane's door closes. -They're not supposed to restart them until the planes reach 10,000 feet and the captain gives the go-ahead. -Passengers are supposed to turn their devices off again as the plane descends to land and not restart them until the plane is on the ground. -Under the new guidelines, airlines whose planes are properly protected from electronic interference may allow passengers to use the devices during takeoffs, landings and taxiing, the FAA said. -Most new airliners and other planes that have been modified so that passengers can use Wifi at higher altitudes are expected to meet the criteria. -Laura Glading, president of the Association of Professional Flight Attendants, welcomed the changes. -"Once the new policy is safely implemented - and we're going to work closely with the carrier to do that - it will be a win-win," Glading said in a statement. -We're frankly tired of feeling like 'hall monitors' when it comes to this issue. -But connecting to the Internet to surf, exchange emails, text or download data will still be prohibited below 10,000 feet, the agency said. -Passengers will be told to switch their smartphones, tablets and other devices to airplane mode. -So, still no Words With Friends, the online Scrabble-type game that actor Alec Baldwin was playing on his smartphone in 2011 when he was famously booted off an American Airlines jet for refusing to turn off the device while the plane was parked at the gate. -And heavier devices such as laptops will continue to have to be stowed because of concern they might injure someone if they go flying around the cabin. -In-flight cellphone calls also will continue to be prohibited. -Regulatory authority over phone calls belongs to the Federal Communications Commission, not the FAA. -FAA may lift ban on some electronic devices during takeoff and landing -Last month, National Transportation Safety Board Mark Rosenker, a CBS News national transportation safety expert, said that cell phones are still considered a risk. -"Cell phones, that really is an issue, not just because potentially it could create interference with navigational devices, but we do know, according to the FCC, that it could interfere with cell phone towers when they're in the air," Rosenker said. -An industry advisory committee created by the FAA to examine the issue recommended last month that the government permit greater use of personal electronic devices. -Pressure has been building on the FAA in recent years to ease restrictions on their use. -Critics such as Sen. Claire McCaskill, D-Mo., contend there is no valid safety reason for the prohibitions. -The restrictions have also become increasingly difficult to enforce as use of the devices has become ubiquitous. -Some studies indicate as many as a third of passengers forget or ignore directions to turn off their devices. -The FAA began restricting passengers' use of electronic devices in 1966 in response to reports of interference with navigation and communications equipment when passengers began carrying FM radios, the high-tech gadgets of their day. -New airliners are far more reliant on electrical systems than previous generations of aircraft, but they are also designed and approved by the FAA to be resistant to electronic interference. -Airlines have been offering Wi-Fi use at cruising altitudes to passengers for several years. -Planes modified for Wi-Fi systems are also more resistant to interference. -The vast majority of airliners should qualify for greater electronic device use under the new guidelines, Huerta said. -Today's electronic devices generally emit much lower power radio transmissions than previous generations of devices. -E-readers, for example, emit only minimal transmissions when turning a page. -But transmissions are stronger when devices are downloading or sending data. -Among those pressing for a relaxation of restrictions on passengers' use of the devices has been Amazon.com. -In 2011, company officials loaded an airliner full of their Kindle e-readers and flew it around to test for problems but found none. -FAA advisory committee members expressed mixed feelings about whether use of the devices presents any risk. -Douglas Kidd of the National Association of Airline Passengers said he believes interference from the devices is genuine even if the risk is minimal. -Other committee members said there are only anecdotal reports from pilots to support that the devices can interfere with aircraft systems, and most of those reports are very old. -However, the committee recommended the FAA allow pilots to order passengers to shut off devices during instrument landings in low visibility. -A travel industry group welcomed the changes, calling them common-sense accommodations for a traveling public now bristling with technology. -"We're pleased the FAA recognizes that an enjoyable passenger experience is not incompatible with safety and security," said Roger Dow, CEO of the U.S. Travel Association. -Bird airlifted to safety from North Sea rig released back into wild -A bird airlifted ashore after being found exhausted on a North Sea oil rig has been released back into the wild. -The water rail was put on a helicopter to Aberdeen last month before being nursed back to health by the Scottish SPCA at its rescue centre in Alloa. -Centre manager Colin Seddon said: "This water rail was likely a winter migrant from Northern Europe who got caught up in strong winds over the North Sea." -It seems the bird became exhausted and managed to find refuge on the oil rig. -He added: "It was unable to fly off again so we were contacted for help." -The water rail was fit and well by the time it was released. -Is Europe's elite ready to do business with Britain? -Business for Britain launched in April with a pledge to bring business together and define what the UK's wealth and job creators want to see changed in our relationship with the EU. -To that end, we commissioned the largest and most comprehensive poll of British business leaders asking them for their thoughts on Britain, business and the EU. -YouGov polled over 1,000 business leaders, broadly representative of Britain's business sizes, sectors and regions. -The conclusions of the poll will come as a surprise to many. -We found that the vast majority of businesses are now looking to export outside of Europe, focusing on countries that are modernising and growing while the EU states stagnate. -They want to see the Government prioritise new trading links with the likes of China, India and Brazil, rather than getting bogged down in the long and arduous process of reforming the EU's arcane institutions. -When asked their views on specific policy areas - ranging from monopoly regulation to product laws - the majority of business leaders thought that control of these key competences should be returned to Westminster. -There was general discontent with the Single Market, with businesses saying that the costs of Brussels regulation now outweighed the benefits of being part of Europe's trading area - even 40 per cent of large businesses, traditionally the most pro-European of companies, agreed. -Finally, and most tellingly of all, our poll of business leaders found a clear majority wanted to see Britain pursue a course of treaty change and a relationship with the EU that is based on trade, not politics. -This finding, which was reflected across the sizes and major business groups, shows that business is pushing for a "meaningful change" that brings powers back to the UK. -The stakes are high - achieving treaty change and a better deal for Britain sees a 16 per cent swing towards voting to stay in the EU in a referendum. -The Prime Minister should be in no doubt: this poll shows that British business backs his plan for renegotiating the terms of Britain's membership of the EU. -It also shows that business expects that renegotiation to make a significant shift in the current balance of power back towards the UK. -A better deal for British business is possible, and increasingly necessary as the eurozone embarks on the road to closer economic and fiscal union. -The priority must be jobs and growth in Britain and, as the findings of our poll show, for business this means a renewed focus on trade and a fundamental change in Brussels" regulatory approach. -Gazprom's Alexei Miller says pipeline in Bulgaria starts new gas era -The start of construction of the South Stream gas pipeline in Bulgaria marks the launch of one of Europe's largest energy projects, Gazprom's chief said. -"A landmark event has taken place today: Construction started on the Bulgarian section of the South Stream gas pipeline, the most large-scale and important project in Europe," Gazprom Chairman Alexei Miller said in a statement Thursday. -This project is a key element of energy security of the whole European continent. -South Stream is meant to add diversity to Russia's export routes through Europe. -A contractual dispute between Gazprom and its counterparts in Ukraine, which hosts most of Russia's gas for Europe, adds a layer of risk to conventional routes, officials say. -Miller said the direct connection to Bulgaria, a member of the European Union, means geopolitical risks associated with transit countries are eliminated "forever." -Bulgarian consumers will receive gas from South Stream at a discounted rate once the entire project starts operating in 2015. -Gazprom said construction should begin in other downstream countries by year's end. -The pipeline is designed for an annual capacity of 2.2 trillion cubic feet of natural gas. -Nothing beats Germany as a property location for national and international investors. -The economic situation is right, and the financing options are fantastic. -This was the message of the 9th 'Immobilientag' (Property Day) organised by the Börsen Zeitung (stock exchange newspaper). -The German property markets are benefiting from the country's economic strength and general conditions which, compared with the rest of Europe, are very competitive. -This was pointed out by Christian Ulbrich, Jones Lang LaSalle CEO for Europe, the Near East and Africa, at the 9th 'Immobilientag' (Property Day) organised by the Börsen Zeitung (stock exchange newspaper). -The extent to which German commercial properties are sought after is evident in the transaction volumes, said Ulbrich. -In the first three quarters of this year, there was an increase of 31% compared to the same period the year before. -In Great Britain, growth stands at just 6%, and in France at 19%. -"Property investments offer an attractive rate of return," said Ulbrich. -The yield gap between properties and federal government bonds is at a historically high level. -The FAA is easing restrictions on the use of electronic gadgets on airplanes - though chatting on cellphones will still be prohibited. -Warplanes attack a store of Russian missiles in the port city of Latakia, an official says. -It's an apparent continuation of Israel's campaign to keep arms from proliferating in the Mideast. -A federal appeals court blocks a judge's ruling that the NYPD's controversial tactic discriminates against minorities. -Nearly 100 African migrants hoping to travel to Algeria die of thirst after their two trucks break down in the middle of the Sahara. -Experts say violence that left 14 adults and seven children dead is nothing more than random chance, not a sign of growing violence in America. -Rather than being rattled by the U.S. government shutdown, investors kept their focus on what probably matters more: the Federal Reserve. -The California woman plans to challenge what may be a first-of-its-kind citation, saying the Internet-connected eyewear makes navigation easier. -Police say they have a video that appears to show Mayor Rob Ford smoking a crack pipe. -Even close allies keep things from one another - and work every angle to find out what's being held back. -The Vatican wants to know how Catholic parishes around the globe handle sensitive issues like contraception, divorce and gay couples. -Two YMCA employees charged with sex offences before allegations against Jonathan Lord, Royal Commission hears -Two YMCA NSW employees had been charged with child sex offences before allegations were raised against Caringbah child care worker Jonathan Lord in 2011, the child sexual abuse Royal Commission has heard. -But in its opening statement to the Commission it said it had "never dealt with an incident of child sexual assault within its organisation," the Commission was told. -Chief executive officer Phillip Hare was asked about one case where a YMCA employee was charged child pornography offences, and another when a gym instructor at the YMCA Caringbah Hall was convicted of child sexual offences against children in his care in 1991. -Mr Hare told Gail Furness, counsel assisting the Commission, he knew about the first case but did not know about the second one. -He conceded the YMCA's opening statement to the commission was also inaccurate in claiming "there have been external audits of the YMCA that have recognised the YMCA as being at the forefront of child safety." -Evidence before the commission is that YMCA was notified that it received the second lowest of four possible ratings in a Department of Education and Communities quality audit in August this year. -Mr Hare, who started with the YMCA when he was 21, conceded management "from myself down" failed by recruiting Lord and failed to make sure staff were clear about their obligations to report child safe policy breaches. -Earlier this year Lord was convicted for sexual offences against 12 boys during the two years he worked at the YMCA. -He was jailed for a minimum of six years. -But Mr Hare rejected the suggestion the YMCA had a cultural problem which prevented staff from reporting Lord's breaches of child safety. -Staff gave evidence they observed breaches including Lord being alone with children, babysitting them privately, having them sit on his lap, saying he loved one and letting them play with his mobile phone. -Danielle Ockwell, who was supervised by Lord and asked for child protection training because she was concerned about his behaviour, testified she found the YMCA Caringbah children's services manager Jacqui Barnat who supervised Lord "very intimidating and hard to approach a lot of the time." -The CEO said he did not accept staff's evidence that they were uncomfortable with reporting upwards to their managers. -Rather, he said, their friendships with Lord clouded their judgements about reporting him. -Mr Hare said he had provided his view to the YMCA NSW board that the lesson for the organisation from the "Jonathan Lord incident" was "not about reporting" by staff, and the board agreed with him. -Mr Hare said the decision to get staff to sign confidentiality agreements soon after the allegations emerged was made by YMCA general manager of children's services Liam Whitley. -He said it was intended to avoid contamination of evidence but was "overzealous" and poorly executed. -YMCA NSW was not a child safe organisation at the time Jonathan Lord was employed between 2009 and 2011, child sex abuse expert Professor Stephen Smallbone of Griffith University told the commission. -He said there were "serious problems" in recruitment, screening, induction, training and supervision of staff. -The hearing adjourned until December 20. -Tony Blair said he'd seize the chance to return as Britain's prime minister - but acknowledges a comeback is unlikely. -In an interview overnight to mark the fifth anniversary of his departure from office, the 59-year-old aired his views on various domestic policies. -Since he stood down in June 2007 after a decade as leader, Mr Blair has largely avoided discussing British politics, confining most of his comments to foreign affairs and his role as envoy to the Quartet of Middle East peacemakers. -Asked if he would return to the post of prime minister, Mr Blair was quoted by London's Evening Standard as saying: "Yes, sure, but it's not likely to happen is it, so..." -As crowds of horse-showing experts gathered in Cardiff to battle it out for Horse of the Year, they knew the competition would be tough. -But nobody was quite ready for three-year-old Fenton Kirkland. -Not yet in school and just months on from taking his first steps, the toddler and his pet Shetland pony Toffee trotted through the three rounds with ease to take the top prize - leaving their 30 adult opponents trailing behind. -The inseparable pair - who are the same height - were commended for appearance, behaviour and style at the annual contest run by Sunnybank Equestrian Centre, in Rudry near Cardiff. -Taking to the stage against men and women in smart bowler hats, he tipped his flat cap at a jaunty angle and paraded two-year-old Toffee around the ring. -Fenton was lauded by judges for natural handling skills well beyond his years. -And Toffee received top marks for his appearance and personality. -Fenton was given Toffee as a third birthday present last March and has practised with the Shetland pony every day since. -His mother Donna, 30, said: "Fenton and Toffee are a great double act." -They were up against all comers but the two of them walked off with the gold cup and rosette. -It was only the second time he had competed with Toffee and we were all ecstatic when he won. -Complete strangers in the arena all thought he was so phenomenal they wanted photos taken with him. -The youngster, from the village of Nantyglo, near Ebbw Vale, South Wales, is following in the footsteps of his aunt Sharon Howells, who has been showing horses for more than 10 years. -Mrs Howells said: "The whole place was electric and everybody was cheering and clapping." -He was running on sand down the full length of the arena and even though he looked so tiny he did a marvellous job. -Fenton is animal mad - he loves horses, tractors and farms and has got two chickens which he looks after. -The way he has started he'll be at the Horse of the Year show before long - and I'm sure he'll do well. -A spokesman for the annual horse show said: "Fenton is only three but he knows how to handle his pony." -They are a great team together. -The judges marked Fenton and Toffee on how well they were turned out and the way they presented in the show ring. -They look for good teamwork between the pony and the handler - Fenton and Toffee were the best in the ring. -I'm sure Fenton was helped by his cute clothes, he really looked the part. -China plea paper 'to be overhauled' -A Chinese newspaper that made a front-page appeal for the release of a reporter accused of defamation is to be overhauled, a press regulator says. -The Guangzhou-based New Express made a rare public plea for the release of journalist Chen Yongzhou. -But Mr Chen subsequently admitted on television that he had taken bribes to fabricate stories about a part state-owned company. -Now the New Express is to undergo "full rectification," the regulator said. -The "rectification" order came from the Guangdong Administration of Press and Publication, Radio, Film and Television. -A preliminary investigation showed that Yangcheng Evening News Group's New Express had published several untrue reports about listed company Zoomlion in the period of September 2012 to August 2013. -"New Express's editorial management was disordered," the regulator said in a statement. -It said it had decided to "impose an administrative penalty on Chen Yongzhou by revoking his reporter's license." -It had also "instructed Yangcheng Evening News Group to undertake a complete rectification of New Express, and recommended they investigate the relevant responsible persons at New Express and immediately revise New Express's leadership team." -Mr Chen wrote several articles for the New Express alleging financial irregularities at a construction-equipment company called Zoomlion. -After he was detained, his newspaper published two front-page appeals for his release, saying it backed his journalism. -But Mr Chen then appeared on state television admitting he had published false stories for money. -"In this case I've caused damages to Zoomlion and also the whole news media industry and its ability to earn the public's trust," he told state broadcaster CCTV. -I did this mainly because I hankered after money and fame. -I've realised my wrongdoing. -Following Mr Chen's apology, New Express issued a front-page apology, saying it had failed to properly check his reports. -Several high-profile suspects have made televised confessions recently. -Experts say confessions are still routinely coerced, despite a change in the law earlier this year banning the authorities from forcing anyone to incriminate themselves. -US-Mexico drug tunnel with its own railway found -One of the most sophisticated drug smuggling tunnels between the USA and Mexico has been found, complete with its own lighting, ventilation and electric rail systems. -US authorities described the four foot by three foot tunnel as one of the most sophisticated secret passages they have ever discovered. -The tunnel, which zigzags the length of nearly six football pitches, links warehouses near Tijuana, Mexico and San Diego, USA. -The area is filled with nondescript warehouses, making it easier to conceal trucks being loaded with drugs. -The tunnel was shut down before any drugs made it through undetected, authorities said. -Authorities seized eight-and-a-half tons of marijuana and 327 pounds of cocaine in connection with the tunnel's discovery, according to court records. -Three men who authorities say worked as drivers were charged with possession of marijuana and cocaine with intent to distribute. -They face prison sentences between 10 years and life imprisonment if convicted. -In Nogales, Arizona, smugglers tap into vast underground drainage canals. -The tunnel is the eighth major passage discovered in San Diego since 2006. -Some of the largest tunnels have been discovered after central Mexico's marijuana harvest in October, which presents drug cartels with a challenge of how to quickly get their product to consumers. -In 2010, authorities found a roughly 700-yard passage equipped with rail tracks that extended from the kitchen of a Tijuana home to two San Diego warehouses. -Orlando Bloom and Miranda Kerr still love each other -Actors Orlando Bloom and Model Miranda Kerr want to go their separate ways. -However, in an interview, Bloom has said that he and Kerr still love each other. -Miranda Kerr and Orlando Bloom are parents to two-year-old Flynn. -Actor Orlando Bloom announced his separation from his wife, supermodel Miranda Kerr. -In an interview with US journalist Katie Couric, which is to be broadcast on Friday (local time), Bloom said, "sometimes life doesn't go exactly as we plan or hope for". -He and Kerr still love each other, emphasised the 36-year-old. -"We're going to support one another and love each other as parents to Flynn". -Kerr and Bloom have been married since 2010. Their son Flynn was born in 2011. -According to dictionaries, veganism is a sub-category of vegetarianism, yet the two do differ considerably. -For while vegetarians allow themselves to consume cream tarts, ice cream or a strong Edam, these indulgences are off the menu for vegans. -Milk, cheese, egg - alongside avoiding meat and fish, vegans refrain from eating all products of animal origin - even honey is not permitted. -You might ask what is left, however, upon closer inspection the world of vegan nutrition is characterised by great resourcefulness. -"Spreads are very popular," explained Ute Henkelmann the owner of the local health food store, which provides a host of special products for vegans. -Reuben Proctor also likes to visit the store on "Domgasse". -The New Zealander has been vegan for 13 years, and in Lampertheim he can find (almost) everything that he needs to get by. "In 1997 I adjusted my diet and became a vegetarian". -At the turn of the millennium I took the next step and became vegan. -"However, it wasn't just the diet that convinced me, but also my choice of shoes," said Proctor, who prefers artificial leather over normal leather. -He realised that you can also overcome all other daily concerns without animal products. -First and foremost, being vegan is a matter of ethics. -"I don't want animals to have to die for me," said Proctor, expressing the maxim of vegans, which applies to all their consumer behaviour. -Rather than missing burgers, scrambled eggs or gummi bears, the vegan has quickly discovered entirely new products that really inspire him. -"It is a double bonus," says Proctor now, "in actual fact being vegan is not about doing without, it's not about asceticism, but rather it is an enhancement". Regarding the assumption that when you convert your diet you are reducing your choice of meal options, Proctor puts this down to force of habit. -And it is precisely this conditioning that results in many a well-intentioned attempt failing. -"The temptation factor is a decisive factor in causing many to quickly give up, similar to with smoking," says Proctor, providing a comparison. -And he argues: "Certain flavours are not linked to animal produce". -Often it is the herbs or the type of preparation that provide the flavour. -"You can, however, create the same effect in other ways," explains the vegan. -For example, when attending a barbecue party, Proctor brings aubergines, mushrooms and also a few vegan sausages. -He ensures a balanced, varied diet, whereby everything should also have a good hearty flavour. -Of course you want to have something with a bit of bite, that is fair enough. -"It should be tasty, but this doesn't necessarily mean animal-based food," said Proctor. -He has not suffered any damage to his health or eating disorders - another wide-spread assumption. -Converting has had a positive effect for me. -I do not consume any foreign cholesterol and have increased my vitamin intake. -There are top athletes who also have an exclusively vegan diet. -"My blood values are excellent - I have myself checked on a regular basis," revealed Proctor. -Many critics of veganism warn in particular of the lack of vitamin B12. -"Because animals are fed on fodder instead of grazing, it is dubious whether the vitamin B12 content in meat sold in freezers is actually high," commented Proctor. -For vegans there is also the option of taking the co-enzyme in the form of tablets. -"My B12 values are fine," he explained. -Proctor does not have to travel far to buy food. -There are basic foodstuffs available everywhere, and every supermarket now has soya milk and other products. -I can find everything I need locally. -I only purchase special items now and again. -"Many people overlook the fact that even though ready-made vegan products are expensive, it is well-known that this is also the case for "normal" ready-made products. -In any case, the preparation of vegan dishes is much cheaper and less time consuming than commonly assumed. -"In home cooking, there is much to be discovered - with a few minor tweaks you can achieve good, if not sometimes better results," said Proctor. -Margarine in place of butter, a little more baking powder and bicarbonate of soda in place of egg - Proctor has even managed to astonish his in-laws with his vegan cheese cakes. -The vegan adds new spice to family parties. -When you cook together there is a certain dynamic - cooking takes on new value. -"The situations that may arise are as varied as the people themselves," said Proctor. -Of course he has already met with scepticism. -Do you not like my food any more? -"Should I feel bad now because I drink milk?" - as a vegan you have to be tactful in such matters. -"I do not disapprove of people, I am part of this society," said Proctor, explaining that he was not choosing a life of isolation. -And on closer observation, the everyday world does not appear quite so 'un-vegan' - and vegans are not quite as limited in their everyday lives as is commonly assumed. -Fast food is also permitted. -Chips are available everywhere! -"Many doner kebab houses offer falafel, or even vegetarian Yufka," says Proctor. -In a pizzeria he avoids pasta dishes or just orders a pizza without cheese, all just a question of habit. -Restaurants are slowly adapting to things. -"When I began eating a vegan diet 13 years ago, things were even more difficult," says the New Zealander, and he points out: "You have to talk with people, make them aware". -"If enough requests are made, they will perhaps be inclined to expand their menus," he hopes. -There are vegan restaurants opening up, such as the Kombüse in Mannheim or Café Vogelfrei. -Proctor hopes that other restaurants will also have at least one vegan dish on the menu. -Essentially, vegan dishes are for everyone. -Anyone can eat them, they are the exact opposite of exclusive. -"Meat dishes, on the other hand, are exclusive, in the truest sense of the word, as they rule others out," assessed Proctor. -"If you want to avoid food scandals, veganism is a safe way to rule out the majority of scandals from the outset," says Proctor. -However, purchasing vegan products can be difficult to start with. -OInce you come to understand that peanuts, in particular, may also contain animal additives as a colourant, people seem to quickly lose courage when following a code of ethics with so many hurdles and pitfalls. -Therefore, for this reason and others, Reuben Proctor and Lars Thomsen have written a book. "Veganissimo - Tierische Inhaltsstoffe und ihre Alternativen" (Veganissimo - Animal Ingredients and their Alternatives) explains the many ingredients hidden in both foodstuffs and cleaning agents. -"You gradually develop an internal scanner and you know which symbols and logos you have to look out for". Proctor has learned that "it is sometimes better too take small steps rather than to stand still or stumble". -Veganism is an important moral compass, which has an effect on your consciousness. -Perhaps he won't manage to help you break old habits altogether, but at least he manages to teach consumers to critically question their habits. -Stock exchange operator Nasdaq OMX keeps its customers on tenterhooks. -Trading at the Nasdaq Options Market was interrupted on Friday afternoon, German time. -In an announcement, the operator cited technical problems as the reason. -On the other eleven options markets, including two also operated by Nasdaq OMX, business continued as normal. -The most recent incident is the latest in a series of both small and large breakdowns suffered by the stock exchange operator. -As recently as last Tuesday, the Nasdaq indices were not calculated for one hour due to data transfer errors. -In August of this year there were two breakdowns in a single week. -First of all, US investment bank Goldman Sachs sent large quantities of incorrect purchase orders to the options markets due to technical problems. -As a result, the stock exchange operators required almost an entire day to look through and delete the orders. -Two days later, half of all stock dealing in the USA came to a standstill for several hours, due to a computer failure in the Nasdaq exchange. -Furthermore, last year the listing of Facebook on the stock exchange caused quite a furore. -The Nasdaq systems could not cope with the flood of purchase and sales orders, the Exchange Supervisory Authority established at a later date, imposing a record penalty of 10 million dollars on the company. -Konstanz: Cyclist knocks over 63-year-old man -According to the police, the accident occurred on Thursday evening at around 10:00 p.m., when a 26-year-old man was cycling illegally, on the left footpath of the Bahnhofplatz in the direction of Marktstätte, on a ladies bicycle. -According to police, when a 63-year-old man suddenly stepped out of a pub onto the footpath, the cyclist was unable to brake in time. -During the subsequent collision, the pedestrian was thrown against the wall of the building, and then fell onto the ground, suffering a laceration to the head that measured around 15 centimetres in length. -An ambulance team brought the injured man to the clinic for medical treatment. -British police serve Assange with extradition notice -Btitish police served an extradition notice today on WikiLeaks founder Julian Assange, who has taken refuge in Ecuador's embassy in London and requested asylum. -Scotland Yard said they had served a "surrender notice" on the 40-year-old Australian requiring him to appear at a police station, adding that failure to do so would make him further liable to arrest. -Assange faces extradition to Sweden over sex crime allegations, having exhausted his options under British law when the Supreme Court overturned his appeal against extradition earlier this month. -Fearing Stockholm would pass him on to the US, he sought refuge at Ecuador's embassy in London on June 19, asking the South American country for political asylum. -Scotland Yard has "served a surrender notice upon a 40-year-old man that requires him to attend a police station at date and time of our choosing," a spokesman said. -He remains in breach of his bail conditions. -The embassy declined to comment on the serving of the police notice. -Assange fears he will be extradited from Sweden to the United States to face possible espionage charges, after releasing more than 250,000 US diplomatic cables on the WikiLeaks anti-secrecy website. -Driver speeding at 130mph with hot drink between legs fined £1,000 -A motorist has been fined £1,000 for driving at up to 130mph (210km/h) with a hot drink balanced between his legs. -Andrew Howie, 35, of Tiptree, Essex, was spotted driving his Mercedes Benz on the A120 at Braintree on 27 May. -When police stopped him they discovered the takeaway drink between his legs. -At Colchester Magistrates' Court Howie admitted a charge of driving without due care and attention. -Seven points added to his licence resulted in him receiving a six-month driving ban. -Howie was also ordered to pay costs of £90 and a victim surcharge of £100. -Tax on foreign property owners to burst London's bubble -The Treasury have provisionally costed out the CGT measure but are awaiting a final decision from Mr Osborne, who, in the 2012 Budget, introduced a 7% rate of stamp duty for homes costing more than £2m and annual charges for buyers who choose to hold homes in a company rather than as individuals. -Already the stamp duty take for residential property in the boroughs of Westminster and Kensington & Chelsea, which stood at £708 million in the 2012/13 tax year, exceeds the combined total for Northern Ireland, Wales, Scotland, the North East, North West and Yorkshire and the Humber put together. -Mr Cook said: "Following increases in stamp duty of high value homes and the introduction of associated anti-avoidance legislation, it is very difficult to argue that high value property is under-taxed irrespective of the effect of the out-dated council tax system." -"But this move could make some foreign investors reticent to buy property in London or current owners reluctant to sell," he added. -Prime property - the top 5% to 10% of the housing market by price - in the affluent south-west London belt, which stretches from Fulham to Wimbledon, has increased by a record 11.8% over the past year. -Prices in central London continued to show steady year-on-year growth of 5.6% but were overshadowed by a burgeoning "domestic market" with the city's south west, north (7.4%) and east (6.5%) all experiencing an uptick, according to research from Savills. -Scientists have shed more light on how the movements of a dog's tail are linked to its mood. -Earlier research had revealed that happy dogs wag their tails more to the right (from the dog's point of view), while nervous dogs have a left-dominated swish. -But now scientists say that fellow canines can spot and respond to these subtle tail differences. -Prof Georgio Vallortigara, a neuroscientist from the University of Trento, said: "It is very well known in humans that the left and right side of the brain are differently involved in stimuli that invokes positive or negative emotions." -Here we attempted to look at it in other species. -He added that just as in humans, for dogs the right side of the brain was responsible for left-handed movement and vice versa, and the two hemispheres played different roles in emotions. -To find out more about how dogs react to the lop-sided tail wags of other dogs, the researchers monitored the animals as they watched films of other dogs. -They measured the pets' heart rates and analysed their behaviour. -It will probably not be long before we understand why their tails sometimes go one way, sometimes the other -Prof Vallortigara said: "We presented dogs with movies of dogs - either a naturalistic version or a silhouette to get rid of any other confounding issues, and we could doctor the movement of the tail and present the tail more to the left or right." -When the animals saw an otherwise expressionless dog move its tail to the right (from the tail-wagging dog's point of view), they stayed perfectly relaxed. -But when they spotted a tail veer predominantly to the left (again from the tail-swishing dog's point of view), their heart rates picked up and they looked anxious. -Prof Vallortigara said he didn't think that the dogs were intentionally communicating with each other through these movements. -Instead, he believes that they dogs have learned from experience what moves they should and shouldn't feel worried about. -He said: "If you have several meetings with other dogs, and frequently their tail wagging one way is associated with a more friendly behaviour, and the right side is producing a less friendly behaviour, you respond on the basis of that experience." -The researchers say the findings could give owners, vets and trainers a better insight into their animal's emotions. -Dog behaviour expert John Bradshaw, a visiting fellow at the University of Bristol's school of veterinary science, said this was not the first study to examine whether left and right were important to canines. -Last year a team from the University of Lincoln found that dogs turn their heads to the left when looking at an aggressive dog and to the right when looking at a happy dog. -And in another research paper from the University of Victoria in Canada, he said: "Dogs were more likely to approach a robot dog when its 'tail' was made to wag left rather than right, rather than becoming anxious - the opposite way around to the Italian study." -He said the differences could be because the dogs in the different studies were not fully interpreting the animals in the films or robo-dogs as canines. -A study of how dogs responded to real dogs could help, he explained. -"While there is considerable evidence from many different mammals that the two sides of the brain are used for different purposes, much of the detail still has to be hammered out - and dogs are no exception," he said. -However, given the ease with which their behaviour can be recorded, it will probably not be long before we understand why their tails sometimes go one way, sometimes the other. -Arctic Monkeys postpone Glasgow gig due to Alex Turner's illness -Rock band the Arctic Monkeys have postponed a gig in Glasgow after their lead singer was diagnosed with laryngitis. -The Sheffield group were scheduled to perform at the Hydro venue in the city on Friday. -However, lead singer Alex Turner's illness has forced them to reschedule the show. -The band's announcement came after they were forced to similarly postpone a gig at the LG Arena in Birmingham on Thursday. -In a statement on their official website, the Arctic Monkeys said: "Following the decision to postpone the show at the Birmingham LG Arena tonight and after seeking medical advice, Arctic Monkeys must also postpone the show at the Glasgow Hydro on Friday, November 1." -"Alex Turner has been diagnosed with laryngitis and is regrettably not able to perform." -The show at the LG Arena in Birmingham will now take place on November 20 and the show at the Glasgow Hydro will now take place on November 21. -All tickets remain valid for these shows. -We wish to apologise to all ticket holders for any inconvenience this has caused. -Please contact the customer services at the box office you purchased your tickets from for any further assistance. -Pope Francis to name first cardinals in February -Pope Francis will create new cardinals of the Catholic Church for his first time on February 22, the Vatican announced Thursday. -Cardinals are the highest-ranking clergy in the Catholic Church below the pope, and they're the ones who elect popes, so Francis will be appointing his first group of men who will ultimately help choose his successor. -There are now 201 cardinals. -However, once a cardinal reaches 80 he is no longer permitted to participate in the election of a pope -- this falls to a group of 120 "cardinal electors." -In a statement announcing the news, Father Federico Lombardi, a Vatican spokesman, said a meeting of all the existing cardinals would be held before the ceremony to elevate the new cardinals, known as a consistory. -"Pope Francis has decided to communicate his decision to convoke February's consistory in advance in order to facilitate the planning of other meetings involving the participation of cardinals from different parts of the world," Lombardi said. -Jack Valero of Catholic Voices said that by February, the number of cardinal electors was likely to have dropped. -He said usually a pope would name as many cardinals as was needed to raise the number of cardinal electors back to 120 and as many cardinals aged over 80 as he wanted. -Next year's consistory would be significant because it would be the first since Francis was elected in March this year, Valero said. -At the moment there is a sort of bias towards Europe and especially towards Italy. -"It will be interesting to see whether the new Pope will nominate cardinals from the rest of the world to restore the balance," he said. -Forty percent of Roman Catholics are in South America, but they have a tiny number of cardinals. -The cardinals will also be the first to be chosen since Francis formed the Council of Cardinals, a group of eight cardinals from around the world tasked with looking into ways to reform the church. -In the past the Pope decided everything on his own. -"Now Francis has selected these eight cardinals to help him," Valero said. -He said it was "quite possible" that Francis would ask the cardinals for advice. -But we've not been in that situation before -- it's all completely new. -Valero said popes typically elevated bishops from large places to the position of cardinal but that Francis was "full of surprises -- so we don't know who he'll name." -GM recalls some new pickup trucks in U.S. to fix seatbacks -General Motors Co is recalling nearly 19,000 of its all-new 2014 Chevrolet Silverado and GMC Sierra pickup trucks to repair a problem with the manual reclining seatback, according to a notice from U.S. auto safety regulators on Friday. -On some of the trucks, the front seats may have a defect in the reclining mechanism. -As a result, the seatbacks fail to comply with federal auto safety standards on head restraints. -"If the vehicle is struck from behind, the head restraint may not properly protect occupants, increasing the risk of injury," according to the notice posted on the National Highway Traffic Safety Administration website. -The recalled models were built between August 1 and September 10. -GM's truck roll-out began in June and represents the most important vehicle launch for the No. 1 U.S. automaker since its 2009 bankruptcy restructuring. -GM told truck owners about the defect in the first half of October. -NHTSA could not review the owner notification letter due to the 16-day government shutdown, which tempered auto sales growth in October. -Sales of the Silverado and Sierra trucks, which were redesigned for the 2014 model year, were up about 20 percent during the first 10 months of the year, GM said on Friday. -In October, GM sold 42,660 Silverado and 16,503 Sierra pickup trucks. -GM shares were up 1.4 percent at $37.47 on the New York Stock Exchange on Friday afternoon. -An Obama voter's cry of despair -I voted for President Obama twice, sharing hope in possibility of change -He says Obama has had worthy efforts thwarted by GOP obstructionism -Obstructionism can't excuse Obamacare website woes, drone attacks -Obama's 2008 campaign memoir is a sad reminder of what might have been -Nathaniel P. Morris is a second-year student at Harvard Medical School. -I'm reading a terribly sad book these days. -It's a book that I thought would uplift me during the doldrums of second-year medical school, and renew in me a sense of hope. -It's called "The Audacity to Win," and it's a memoir of Barack Obama's 2008 presidential campaign. -When I'm finished with my patient write-ups at night and get into bed, the book returns me to a time when politics inspired millions and speeches could take your breath away. -The election turned out to be a landslide, and news anchors paused to reflect on the historic nature of the hour. -My classmates cried with joy, and my parents saved every newspaper they could find. -A young team of visionaries was headed for the White House, and the nation was ready for change. -During Obama's transition to office in 2008, he had an 82% approval rating. -And then I close the book. -Cutting to the present is a rude awakening, like snapping out of a dream. -It's hard to remember those days of optimism -- they seem a distant memory, a sad reminder of opportunities gone by. -Change indeed happened, in the years since I cast my first ballot. -It was simply nothing I could have imagined. -I credit Obama with great and varied accomplishments, from the passage of the Affordable Care Act to our military exit from Iraq, the end of "don't ask don't tell," to the killing of Osama bin Laden. -Moreover, I believe that partisan obstructionism has upended too many efforts to push our nation forward: immigration reform, a public option for health care, and closing the base at Guantanamo Bay, among others. -But, after the countless times in which I have found myself defending the Obama administration to colleagues and peers, I've reached a limit to the explanations that I can provide. -I've reached a point of political despair. -Republican obstructionism cannot explain allowing the bugging of foreign leaders, nor having drones strike innocent children overseas. -It cannot explain having the National Security Agency collect data on the private lives of Americans, nor prosecuting whistle-blowers who reveal government wrongdoing. -It cannot account for assassinating Anwar al-Awlaki, an American citizen, without a trial, nor shirking public funding and spending limits during presidential campaigns. -It cannot justify the findings of a report that says the White House's efforts to silence the media are the "most aggressive ... since the Nixon Administration." -And, most recently, it cannot excuse the failure to design a simple website more than three years since the Affordable Care Act was signed into law. -I don't know if this is what I should have expected. -If, at 18 years old, I was supposed to figure out that governance may contradict the political campaigns that precede it. -Obviously, elective office isn't a predictable course, as the opposing political party and random events, such as the Newtown massacre, will shape our public conversation. -Yet, of all of the examples that I have listed above, they largely seem to be of the administration's own choosing. -That is what troubles me most of all. -I voted for Obama again in 2012, but not because I was excited by his candidacy. -Mitt Romney presented a confusing and unrefined alternative who could not seem to lock down his policies or his positions. -I felt that a second term for Obama, free from the pressures of future elections, would fulfill the hope that we had heard of for so long. -Still, as Obama's approval rating sank below 45% this week, returning to 2008 through that book has become that much harder. -It makes me yearn for the many promises that disappeared. -This week I was reading the portion of the book describing how Obama suffered a huge loss to Clinton in the Pennsylvania primary. -At a post-mortem campaign meeting, he told his staff that they needed to get back on track and stay true to the purpose of their cause. -"I want us to get our mojo back," he said. -We've got to remember who we are.' -It's five years later, Mr. President, and I couldn't agree with you more. -The opinions expressed in this commentary are solely those of Nathaniel Morris. -Clive Palmer claims PM Tony Abbott has conflict of interest over parental leave scheme -Billionaire MP Clive Palmer says Prime Minister Tony Abbott has a conflict of interest over his parental leave scheme because his daughters might get pregnant and benefit from it. -The mining magnate, who is in a dispute about paying a $6 million carbon tax bill, made the claim as he tried to brush off questions about whether he had a conflict. -The Palmer United Party could control up to four votes in the Senate that may be crucial in deciding if the carbon and mining taxes are axed. -But Mr Palmer claimed it was only ministers who could have a conflict of interest and said Mr Abbott's daughters stood to personally benefit from policies. -"He's got a major conflict of interest when it comes to paid parental leave because if any of those daughters get pregnant, he'll have a direct interest whether they get leave or not," Mr Palmer said. -Two months after the election, the electoral commission officially declared Mr Palmer the winner of the Sunshine Coast seat of Fairfax by 53 votes, after a recount. -Mr Palmer called for overhaul of election counting to speed up the process. -Tony Abbott's daughters Frances and Bridget. -Should this election be decided two months after we stopped voting? -"We need to have a better system," he said. -Why is it that we shouldn't have a system where you can walk in, punch your details into a computer, vote immediately and have a result at 6.30 that night? -Mr Palmer also criticised the use of pencils to mark ballots. -Is it because they can rub out the result if someone doesn't like it? -In this day and age having a pencil seems extraordinary. -The Electoral Commission has been studying options for electronic voting and recently released a joint discussion paper with New Zealand. -Mr Palmer, 59, said his policies included an international airport for the Sunshine Coast and he would take "very seriously" his new job. -Public office is about public service. -"We seek no reward, except the reward of history that we can at a critical time serve this community," he said. -RBS suspends two forex traders -Royal Bank of Scotland has suspended two traders in its foreign exchange division according to two people familiar with the situation, in another sign that the global probe by regulators into the suspected manipulation of the currency market is rapidly gaining traction. -Some of the world's largest banks, including UBS, Barclays, Deutsche Bank and RBS, have confirmed they are co-operating with regulators in investigations into the world's largest financial market, where $5.3tn changes hands each day. -The two traders would be the first RBS employees to be suspended in the widening probe that echoes the Libor interbank lending manipulation scandal. -The bank, which declined to comment on the suspensions, confirmed this month that it has received requests for information from regulators. -"Our ongoing inquiry into this matter continues and we are co-operating fully with the FCA and our other regulators," the bank said two weeks ago. -Last month, people close to the situation said that RBS had turned over records of emails and instant messages to the UK regulator, the Financial Conduct Authority, sent to and from a former trader. -This trader, Richard Usher, left RBS in 2010 and is understand to have be given leave from his current position as European head of forex spot trading at JPMorgan. -Rohan Ramchandani, head of European spot trading at Citi, went on leave this week, while Matt Gardiner, a former senior currencies trader at Barclays and UBS, was suspended by Standard Chartered this week. -None of these traders have been accused of any wrongdoing. -Mr Usher's instant message group included bankers at Barclays and Citigroup, people close to the situation said. -UBS said this week it had taken action against some of its employees after the Swiss regulator, Finma, said it was investigating suspected manipulation of the foreign exchange market at a number of Swiss banks. -At least six authorities globally - the European Commission, Finma, Switzerland's competition authority Weko, the FCA, the Department of Justice in the US and the Hong Kong Monetary Authority - are looking at allegations that bankers colluded to move the currencies market. -HSBC, Citigroup, JPMorgan and Credit Suisse have also launched internal probes or received requests for information from regulators, said people familiar with the situation. -Banks are scouring through years" worth of instant messages and emails to search for instances of wrongdoing. -News about the probes has rattled traders in an area that has been one of the bigger profit drivers of investment banks' trading units in past years but which has been challenged this year as low volatility in currencies cuts opportunities for speculators. -Some bankers have tried to play down the affair by saying the vast and highly liquid foreign exchange market is almost impossible to manipulate, but senior traders are saying this is not necessarily true. -A senior trader said that despite the huge volume of daily foreign exchange trading, the fragmentation of liquidity between different trading platforms and banks" increasing use of their own internal platforms meant that "you can start to get an impact on the market at quite small ticket prices." -The news came on the same day as Credit Suisse announced it had dismissed a trader at its London exchange traded funds desk this week after he had caused a nearly $6m loss late last year. -The bank promptly notified the relevant authorities and has been co-operating with its regulators. -"We are confident the trader acted alone and that the matter has been contained," Credit Suisse said. -Hoarding on the Bechtle plot painted -A group belonging to the "Schweizer Wiese" citizen's initiative has given the hoarding on the Bechtle plot on the spa promenade in Bad Herrenalb a face-lift. -In addition, they have cleaned and widened the footpath, a press release announced. -The planned covering of the dilapidated house to the rear of the plot was not permitted for safety reasons, as the premises were at risk of collapsing. -Alfred Abel, who currently manages the site, arranged the 'face-lift' in cooperation with his colleague, Reinhard Domke, from the citizen's initiative. -Before the citizen's initiative project, a banner with the message "We are in favour" was put up. -However, this did little to distract the 20 or so members of the citizen's initiative from their project. -Their motto, on the large billboard stated "We are against unfinished buildings". -In so doing they wanted to point out the danger of even bigger ruins on the Schweizer Wiese - should the huge project one day fail. -Chatting with many people passing by the "construction site", it became clear that many citizens believe the execution of the project would make things much better for the town, although many have the same questions as the citizen's initiative regarding the scale of the planned project. -However, they are beginning to resign themselves as the town continues to stall in providing answers, said a statement from the citizen's initiative. -We have now been told that the public statements made by the Mayor are also having an impact. -In May, in the meeting of the Town Council, he said that he feared for the outcome of the spa should the population vote against the plans for a spa and wellness complex on the Schweizer Wiese in the local referendum. -Some appear utterly frightened by it, others perceive it as a "blatant attempt to blackmail" the population, continued the press release from the citizen's initiative. -The citizen's initiative thanked those who donated paint and all those who lent a hand. -Rangnick scolding for refs: Mane is no diver -As satisfied as Ralf Rangnick is with the present winning form of the Austrian Bundesliga table-toppers, Red Bull Salzburg, the Sporting Director of the "Bulls" expressed equal disappointment with the referees. -"There is currently a dangerous trend, with Mane and Alan being dubbed as 'divers'," he said at a press conference in Salburg on Thursday, in defence of the two tricky offensive players. -The most recent cause for Rangnick's criticism was the yellow card for Mane at 3:0 against Grödig last Sunday, whereby the Senegalese striker was penalised for supposed play acting - admittedly this was not justified, as shown by TV footage. -"A blatant wrong decision," commented Rangnick, who referred to Harkam as being "completely out of his depth". -Harkam also played "his part" in the escalation between Salzburg Coach Roger Schmidt and Grödig Trainer Adi Hütter after the game. -He firmly defended his German compatriot: "In each individual situation I would have behaved in the same way as Roger Schmidt". -Schmidt himself did not wish to have any contact with his colleagues after the skirmish with Hütter. -I do not know why. -"After all, I was the one who was insulted," emphasised the 46-year-old. -However, with regard to the winning run, Rangnick sang his team's praises. -"There are only two words for it: Really good," he stated. -There is not a lot you can criticise. -Most of all, the manner in which the team is playing is impressive. -We must stay composed and keep it up. -There will hardly be any squad changes in the winter transfer window. -There is no major reason to change anything. -"Unless a player comes forward and expresses the desire to leave the club," explained Rangnick. -Supreme Court upholds Obama health care law -In a major victory for the Obama administration, the US Supreme Court today ruled Barack Obama's signature health care law is constitutional. -By a 5-4 vote, the justices ruled the Patient Protection and Affordable Care Act's individual mandate - which requires citizens to buy health insurance by 2014 or else pay a penalty - was constitutional under the taxing power of the government. -Chief Justice John Roberts sided with the four more liberal members of the court while Justices Scalia, Thomas, Alito and Kennedy dissented. -The court also upheld the remaining sections of the 2700 page law, and further held that the health care law's requirement that states increase Medicaid eligibility or else lose all federal Medicaid funding is not unconstitutionally coercive. -The suit to block the law was brought by 26 states and the National Federation of Independent Business. -The law was vigorously opposed by each of the major Republican presidential candidates during the 2012 primary season, including presumptive nominee Mitt Romney. -Norway: Norwegian village lights itself up with huge mirrors -Using huge mirrors, the residents of a Norwegian village have brought light to their dreary valley. -Due to the low level of sunshine, from autumn until spring the village of Rjukan in the Vestfjord valley normally languishes in the shadows of the surrounding mountains. -With three gigantic mirrors, each 45 metres tall, a centuries-old dream came true on Wednesday. -"Finally!', raved Mayor Steinar Bergsland at the inaugural ceremony, televised by broadcaster TV2. -Some valley residents lay back on sun loungers, while others safely put on their sunglasses. -Up until now, those hungry for sunshine in the winter had to travel to a nearby peak by cable car. -Ten years ago, local artist Martin Andersen presented the proposal of directing the sun into the valley using mirrors. -However, this basic idea had been talked of in the location since 1913. -After several years of debate, the Town Council finally approved the project, which cost 5 million krone (around EUR 615,000). -A similar construction has provided sunshine to the Italian alpine resort of Viganella for a number of years. -The post is hardly ever emptied on Sundays -"Many citizens will not yet have noticed that red dots indicating a Sunday post collection are still to be found on many post boxes in the town centre and its districts. However, if you read the small print on the yellow boxes operated by Post AG, you will quickly find out that the boxes are no longer emptied on Sundays and holidays," announced the FDP. -An inquiry by the FDP confirmed this. -Only the post boxes in front of the post office building at Ernst-Ludwig-Straße 36 and at Jakob-Müller-Straße 1 in Hüttenfeld are still emptied. -Whether there are still Sunday collections in Hofheim and Rosengarten is not addressed in the inquiry by the FDP. -The FDP spokesperson for social and youth-related affairs, Fritz Röhrenbeck wants to dig deeper into this. -The Sunday collection is an important postal service, especially in the case of scheduled post. -"The company is radically and unacceptably cutting back on this, despite the price increase for letters in January 2013. -Thomas Bittner, Liberal Party Parliamentary Group and Municipal Association Chairman, is supporting his fellow group members: "What annoys me, is the fact that the collection times have been changed or cancelled on the quiet here". -In so doing, the postal service is miles off the mark when it comes to public accessibility. -"At least the post boxes in the districts of Neuschloß and Rosengarten must still be emptied on Sundays," said Bittner and Röhrenbeck in unison. -It is also the case for the town centre that those citizens in particular who are not mobile must also be able to access a post box with a Sunday collection on foot. -In the Town Parliament, Röhrenbeck asked whether the Town Council was planning discussions with the postal service regarding this matter. -Mayor Erich Maier responded that the postal service had not communicated with Lampertheim. -To the best of his knowledge, however, there is a Sunday collection in Hofheim. -Their party has spoken out in favour of privatisation. -"Decisions will then be made elsewhere," said Maier. -Frankfurt parking fees to increase dramatically -Parking in Frankfurt could soon be noticeably more expensive. -The City Magistrate today discussed a submission by the head of the department for transport, Stefan Majer (Green Party). -According to the proposal, parking fees are to be increased by 50 per cent. -This increase will not, however, be the result of raised prices, but rather the fact that time interval permitted by parking meters and ticketing machines will be reduced from 30 to 20 minutes. -The City Council must reach a decision on this in December. -The Retail Committee of the Frankfurt Chamber of Industry and Commerce believes that this is "not a good idea". -The option of parking right outside their businesses is "an advantage to smaller retailers that must not be underestimated.". -The Verkehrsclub Deutschland (German Traffic Club) is of the opinion that an increase after two decades is "completely appropriate and actually long overdue". -Local public transport will also become more expensive. -Freudenstadt: Quick moves take hosts by surprise -With a slender squad, the TSV Freudenstadt Herren I team (Men's First Team) stepped into the Schiller School Gymnasium in Reutlingen to face TG Gönningen of the Swabian Alb region. -The Alb men stood on the other side of the net with what was undoubtedly a more experienced team. -However, it soon became evident that, due in part to their age, they could not really keep up with the fast pace of the Freudenstadt team. -Gönningen found it extremely difficult to cope with the hard and well-positioned offensive moves. -They repeatedly missed the passes and attacks coming cleanly over the net from Simon Schenk and Eduard Schulz and their blocking game was not up to scratch. -Clear victories in the first two sets clearly demonstrated the superiority of the Freudenstadt team in all areas. -Faced with this overwhelming and match-determining performance, the third set was won following a somewhat tame and spiritless performance, for a final score of 3:0 (25:13, 25:14, 25:22). -On 2 November, when they play TV Baiersbronn, who managed a clear victory in Nagold, they will step onto the court to face a much stronger opponent. -TSV Freudenstadt I: Dominik Bäuerle, Yannik Büchle, Sebastian Dölker, Felix Pälchen, Stefan Räller, Simon Schenk and Eduard Schulz. -In the B Class, two teams from Baiersbronn and Freudenstadt faced off in the Murgtalhalle in Baiersbronn. -They produced a hard-fought two-hour match. -Nerves on both sides, particularly when receiving the ball, resulted in errors and lost points; there was even a lack of passing at times and at some critical moments overeagerness saw potential points wasted. -However, in this evenly matched game, the great physical effort of Marcus Blasutto's TSV team proved decisive in spite of tremendous commitment by both teams. -Maintenance day at the small 'Engländerhütte' -For what was then the fifth maintenance day in the follow-up to the Natura 2000 project in Upper Hotzenwald, a group of dedicated participants met at the small Engländerhütte (Englishman's Cottage) in Ibach -Following suggestions from the population, the of the valley in the area of the cottage was to be cleared, said Mayor Helmut Kaiser. -According to Kaiser, many hikers refer to this area as the showpiece of the Schluchtensteig, where the landscape provides the ideal framework for fantastic views. -He thanked those present for their willingness to cooperate and emphasised that the maintenance day is a suitable instrument through which to strengthen the connection between people and nature, -He is offering every citizen the opportunity to focus on nature itself. -At the same time as the tourist aspect, environmental aspects should also be taken into consideration. -This environmental aspect was explained by Friederike Tribukait of the Department of Nature Conservation of the Freiburg Regional Council, following the greeting by Regional Councillor Tilman Bollacher, who had taken on the role of patron and wished the campaign every success. -According to Tribukait, the tourist objective of the vision coincided with the goal of preserving the now extremely overgrown old Allmend grazing pasture, with its specific protected habitats, through its opening and connection to the still existing grazing land. -Certain species, such as rare butterflies or grasshoppers, cannot get past barriers in the form of forest areas. -They require these clear pathways in order to preserve their species. -Tribukait said that she did not want to fail to use this opportunity to say a word in defence of the planned biosphere area. -The current measures do not integrate seamlessly into these plans. -A biosphere area offers the chance for sustainable economic and ecological development on the basis of the internationally recognised Unesco status, and thus offers an extension to what has been achieved thus far with the Life Project and the previous care days. -After the various statements, forest ranger Christoph Wehle and Life Project manager Cornelia Bischoff divided the helpers into groups. -The larger trees had been cut down in advance, although forest tractors were on-call if required. -One group of helpers were to take care of the clearance of juniper bushes and the maintenance of a dry stone wall. -The objective was to create a clear view at the viewpoint. -Löffingen: Köpfler still the only candidate -Information online: Although Löffingen, the small town of Baar, with its 7,514 residents, can be described as having an intact infrastructure, forward-looking school, youth and senior citizens policies and its own municipal energy companies, thus far no-one has registered as a second mayoral candidate. -So far, the only candidate to succeed Mayor Norbert Brugger, who is no longer running on account of his age, is 46-year-old banker and business economist Dieter Köpfler from Löffingen. -The deadline for applications is Monday 11 November, at 6:00 p.m. -Google, Samsung, Huawei sued over Nortel patents -The group that owns thousands of former Nortel patents filed a barrage of patent lawsuits on Thursday against cell phone manufacturers including Google, the company it outbid in the Nortel bankruptcy auction. -Rockstar, the consortium that bought the Nortel patents for $4.5 billion, sued Samsung Electronics Co Ltd, HTC Corp, Huawei and four other companies for patent infringement in U.S. District Court in Texas. -Rockstar is jointly owned by Apple, Microsoft, Blackberry, Ericsson and Sony. -Google is accused of infringing seven patents. -The patents cover technology that helps match Internet search terms with relevant advertising, the lawsuit said, which is the core of Google's search business. -Representatives for Samsung, Huawei, HTC and Rockstar could not immediately be reached. -Samsung, Huawei and HTC all manufacture phones that operate on Google's Android operating system, which competes fiercely with Apple and Microsoft mobile products. -In 2011 Google placed an initial $900 million bid for Nortel's patents. -Google increased its bid several times, ultimately offering as much as $4.4 billion. -After losing out to Rockstar on the Nortel patents, Google went on to acquire Motorola Mobility for $12.5 billion, a deal driven partly by Motorola's library of patents. -"Despite losing in its attempt to acquire the patents-in-suit at auction, Google has infringed and continues to infringe," the lawsuit said. -Rockstar is seeking increased damages against Google, as it claims Google's patent infringement is willful, according to the complaint. -Egypt swears in first freely elected president -Mohamed Morsi takes the oath of office but his day of triumph is unlikely to mark end of political strife in Egypt. -ISLAMIST Mohamed Morsi promised a "new Egypt" as he took the oath of office to become the country's first freely elected president, succeeding Hosni Mubarak who was ousted 16 months ago. -At his inauguration before the Supreme Constitutional Court, Morsi also became the Arab world's first freely elected Islamist president and Egypt's fifth head of state since the overthrow of the monarchy some 60 years ago. -He took the oath before the court's 18 black-robed judges in its Nile-side seat built to resemble an ancient Egyptian temple. -"We aspire to a better tomorrow, a new Egypt and a second republic," Morsi said during a solemn ceremony shown live on state television. -"Today, the Egyptian people laid the foundation of a new life - absolute freedom, a genuine democracy and stability," said Morsi, a 60-year-old US-trained engineer from the Muslim Brotherhood, a fundamentalist group that has spent most of the 84 years since its inception as an outlawed organisation harshly targeted by successive governments. -Hundreds of soldiers and policemen guarded the building as Morsi arrived shortly after 11am local time in a small motorcade. -Only several hundred supporters gathered outside the court to cheer the new president and, in a departure from the presidential pomp of the Mubarak years, traffic was only briefly halted to allow his motorcade through on the usually busy road linking the city centre with its southern suburbs. -Derided as the Brotherhood's uncharismatic "spare tyre," his personal prestige has surged since his victory and his delivery of a Friday speech that tried to present him as a candidate not just of Islamists but of all those who want to complete the work of the 2011 uprising against the authoritarian Mubarak. -"Egypt today is a civil, national, constitutional and modern state," Morsi, wearing a blue business suit and a red tie, told the judges in the wood-panelled chamber where he took the oath of office. -Morsi later travelled to Cairo University where he was to make his inauguration address. -He was given an official welcome by an army band that played the national anthem as he stood to attention. -Military ruler Field Marshal Hussein Tantawi was in attendance. -His arrival was greeted with chants of, "The army and the people are one hand," from the hundreds gathered in the university's main lecture room. -Established in 1908 as a bastion of secular education, Cairo University later became a stronghold of Islamist student groups in the 1970s. -Morsi took a symbolic oath on Friday in Tahrir Square, birthplace of the uprising that ended Mubarak's authoritarian rule last year, and vowed to reclaim presidential powers stripped from his office by the military council that took over from the ousted leader. -But by agreeing to take the official oath before the court, rather than before parliament as is customary, he is bowing to the military's will in an indication that the contest for power will continue. -Morsi's speech in Tahrir Square was filled with dramatic populist gestures. -Falkenberg club makes for two wonderful evenings. -The new season in the Falkenberg "Blue Velvet" club has begun. -This Friday evening, the "Pump this party" event will be taking place, while one day later, on Saturday evening, festivities will continue with an over-25s party. -Fat beats and cool sounds are of course promised by the organisers on both nights. -Worker falls from ladder: seriously injured -On Thursday, a 51-year-old worker in Eggelsberg fell from a ladder while replacing a broken external sensor and was seriously injured. -The man from Lamprechtshausen wanted to replace a broken heating sensor on the external facade of a guest house. -As he was climbing the ladder, which was resting on the roof guttering, it slipped away and the man fell onto the concrete below. -He suffered serious injuries and was taken by the "Christophorus 6" rescue helicopter to the Salzburg Regional Hospital. -Rapist in South Africa: two life sentences -Nine months after the brutal rape and mutilation of a 17-year-old girl in South Africa, the perpetrator was condemned to two life sentences on Friday. -This was reported by broadcaster SABC. -The victim later died as a result of the serious injuries. -Johannes Kana had already been found guilty of the crime at the start of the week. -The crime had caused nationwide horror. -The then 21-year-old had raped Anene Booysen in February in an industrial estate in Bredasdorp, near Capetown. -For 14 hours, doctors battled to save the life of the victim, ultimately in vain. -The sight of the young girl was so horrific that the carers and nursing staff required psychological support. -During the trial, Kana admitted to having been in a bar with Booysen on the night of the attack. -He also admitted to beating and raping her, however he denied being responsible for the death of the girl. -The police had initially assumed that there were multiple attackers, however, they released two suspects from custody as the evidence against them was insufficient. -During the proceedings the public prosecution also emphasised that they were convinced that Kana was alone at the time of the attack. -Rapes are almost part of everyday life in South Africa: There are around 64,000 reports of sexual assault every year. -This is almost ten times as many as in Germany, although with a population of 82 million people, Germany has a much higher number of residents than South Africa (50 million). -Police and women's associations in South Africa, however, estimate the number of unreported cases of sexual assault to be 10-25 times as high. -Jumbo visits the home of Black Forest ham -Jumbo on the search for the "world's best ham": For the TV programme "Galileo", the Pro7 star visited Bonndorf. -The presenter established one thing right from the outset: The most important thing when it comes to ham are the legs of pork - turbo fattening does not provide a good basis for this. -Can ham bought in a supermarket be as good as a quality product? -Uli, boss of the large ham factory in Schwarzwald, explained to Jumbo how the Bonndorf company, "Adler", operates. -"Germany's most-loved ham," said Jumbo, presenting the Black Forest ham. -He could only enter the factory when wearing hair protection and a white coat. -Jumbo also had to first disinfect his hands before he could reach for the microphone. -Jumbo seen getting something to keep him going at a fast food restaurant in Waldshut -"Here they work with huge quantities," commented the television producers, astonished by the mass production of Black Forest ham. -16,000 joints of meat per week speak for themselves. -Weight, fat content and colour: Uli explained what was required for the best quality. -The majority of the pigs are slaughtered at six months old. -Television with standards? -Even specific details such as the pH value of the meat play a role. -The darker the meat, the higher the pH value. -"I would be curious what would happen if you were to measure Roberto Blanco," said Jumbo cynically. -Here the standard of private television production is presented in its purest form. -Nonetheless, it is worth watching. -The viewer sees that joints are still carved by hand. -After all: Black Forest ham traditionally arrives on the shelf boneless. -The secret of the ham is the mix of herbs combined with salt. -The appearance of the smoking tower and furnace remind Jumbo of a submarine. -The trick: Here fir branches in the smoker provide the special flavour of Black Forest ham. -After 20 days in the smoke, the ham is finally almost black. -"And now the slice will disappear inside Jumbo," he said, announcing his sampling of the product. -"That is really delicious," said the TV star in conclusion. -All those involved will be happy with that evaluation. -Thought travel agents were a thing of the past thanks to the internet? -Flight Centre seem to be bucking the trend. -The company has upgraded its full year profit forecast and is looking to record earnings from holidaymakers in Australia and the United Kingdom. -The travel company now expects its full year underlying profit before tax to be between $325 million and $340 million, compared with the $305 million to $315 million it previously forecast. -If the current guidance is achieved it will represent a 12 to 17 per cent growth on the record $290.4 million profit it achieved in 2011/12. -Managing director Graham Turner said Flight Centre had made 8 per cent profit in the first half and had started the second half strongly especially in Australian and UK non-business travel. -"Year-to-date, our 10 countries are profitable and several are on track for record full-year earnings before interest and tax contributions," he said. -This includes Australia and the United Kingdom, which are typically our largest profit generators. -In Australia the leisure business rebounded during the second half which offset a slightly weaker domestic corporate travel market. -Similarly in the UK, Flight Centre's leisure business performed well while corporate clients were spending less. -Its US business had recovered its losses during its seasonally weaker first half and was expected to deliver its third consecutive full year profit. -Flight Centre shares were up 3c at $38.20 yesterday. -Schools urged to focus more on maths, spelling and grammar -English literature courses will require pupils to study at least one Shakespeare play, a 19th century novel, Romantic poetry and contemporary British fiction from 1914 onwards. -The exam will also feature "unseen texts" to encourage wider reading; -A combined English literature and language course will be scrapped. -From 2015, pupils will be required to take a standalone GCSE in language, with strong incentives to choose English literature as a separate qualification. -The Department for Education is due to release the new syllabuses in English and maths tomorrow - the first subjects to undergo a radical overhaul. -It will make changes in other core subjects next year. -In a separate move, Ofqual, the exams regulator, will unveil a shake-up of the structure of GCSEs, with a new grading system and less coursework. -Speaking in the summer, Michael Gove, the Education Secretary, said there was a "widespread consensus that we need to reform our examination system to restore public confidence," insisting GCSEs would be "more challenging, more ambitious and more rigorous." -Studies show that English schools devote less time to maths - 116 hours a year or three hours a week during term time - than in most countries. -By comparison, Australian schools provide an average of 143 hours a year and pupils do around 138 hours in Singapore. -While there will be no formal requirement to devote more of the timetable to maths, Coalition sources said the extensive maths GCSE - combined with more weighting for the subject in league tables - was likely to encourage schools to provide extra teaching. -The syllabus will place a greater focus on "real world problems," including financial mathematics. -Beautiful animals and delicious tarts entice -The most beautiful rabbits of various breeds and colourings will be presented at the District Exhibition this weekend in the community centre. -The evaluations were already made on Thursday. -Once again, the W514 local association are organising this large exhibition. -Here breeders from the clubs in the District of Soest will be exhibiting their best animals, while young private individuals from Wickeder are also showing their rabbits. -On Thursday, six adjudicators assessed the numerous rabbits and were able to assign awards to the exceptional animals. -The visitors to the show will be kept well informed, with the evaluations of all animals posted on their cages. -Mayor Hermann Arndt will be officially opening the exhibition at 14:30. -The rabbit show will, however, be open to all visitors from 10:00 a.m. on Saturday and from 10:00 a.m. until 3:30 p.m. on Sunday, which will be followed by the prize ceremony. -Guests are also invited to take part in a tombola with impressive prizes and to sample some delicious food from the cafeteria. -Pamela Anderson chops off those iconic blonde locks, debuts dramatic new pixie cut. -Pam's blonde locks were made famous by her role in sexy TV show Baywatch. -Pamela Anderson is the latest celebrity to shock fans with a dramatic new hairdo. -The ex-Baywatch babe has ditched her long blonde locks in favour of a platinum pixie crop. -The 46-year-old actress revealed her latest look while out and about in LA on Wednesday and shared a snap on her Twitter page. -It's the first time in 20 years that the blonde beauty has had short hair, and we're loving the demure change. -What do you think about Pammy's hair? -Share with us your thoughts in the comments below. -Salem: Johanna Rahner at the Ecumenical Discussion Forum -At the next Ecumenical Discussion Forum on Monday 21 October, in the Neues Museum des Schlosses (New Castle Museum), the focus will be on the topic of "To hell with Hell - Theological reflections on how we now approach the notion of end times". -The speaker will be Johanna Rahner. -The Ecumenical Discussion Forum is an initiative of the Catholic and Protestant churches in Salem, the Salem Castle and the Lake Constance District Cultural Office. -At the forum, Johanna Rahner wants to address the question of how people today view the "end times" after death. -While people in past centuries were overly conscious of it, focusing on the pictures of heaven and hell "that up until now have defined our ideas", contemporary prophetic teaching on the hope of consummation is characterised more by a certain bashfulness, or even speechlessness. -"The greater hope has been well and truly cast out of Christians by religious critics," said the speaker. -They have "provided us with so many question marks, that we seem to have lost our exclamation marks". -Johanna Rahner continued: "However, the crucial challenge to this remains fear of death". -Associated with this is the matter of that longing, "that dreams that everything will be good at the end". -Johanna Rahner, born in 1962 in Baden-Baden, studied Catholic theology and biology from 1982 to 1989 at the Albert Ludwig's University in Freiburg. -In 1997 she graduated as a doctor of theology, also in Freiburg. -A post-doctoral lecturing qualification in fundamental theology and ecumenical theology followed in 2003 at the Westfalian Wilhelm's University. -Since 2010, Johanna Rahner has occupied a Chair for Systematic Theology in the Institute for Catholic Theology at the University of Kassel. -Geislingen: More than 100 young Catholics from Kleiner Heuberg stand together in the confirmation of their profession of faith -In two services, 101 15 and 16-year-olds from the Am Kleinen Heuberg pastoral care unit were confirmed. -In the morning, the service was accompanied by the Laudato-Si Group, while the afternoon service featured 'The Spirit' youth choir. -Upon receiving the confirmation sacrament, the young people accepted their baptism and asked for the power of the Holy Spirit. -Over recent months those being confirmed, under the direction of Deacon Reiner Dehner and the assistance of several church members, have been preparing for the special day. -In various projects, they learned of the different tasks involved in Christian church life and attended the Youth Day in Untermachtal together. -Illmensee: Second mountain bike race enthuses participants -In fantastic weather, 214 cyclists came to Illmensee to take on the circuit, over the hills and around the lake. -This was announced by Ulrich Knobel of the organising Illmensee Sports Club. -Among the participants there were also numerous amateur cyclists who wanted the chance to practice their hobby in a competitive environment, in particular over the short distance of 15 kilometres with a 300 metre climb. -Two top-class junior cyclists were also in the starting line-up for the competition: Felix Bader from Bad Waldsee and Pascal Treubel from Aach-Linz came with the recommendation of having achieved good positions at the German Championships. -The pair managed to break away from the field in keeping with expectations. -As was the case the previous year, Felix Bader managed to hold on to a small lead right to the end and defended his title as the overall winner of the short distance race -Once again, Pascal Treubel took second place. -In the ladies' race it was Theresa Duelli of Team Albtraum who managed to take top place on the podium. -In second and third places were Anne Adel from Illmensee and Leonie Treiber from Owingen. -The increasing number of riders that live or work in the area is a fantastic development. -And it was not only about top performance - the entire range of athletic ability was represented. -Participants' ages were also wide-ranging, with the youngest starters at ten years old up to the oldest at over 70. -This year, there were even more absolute top athletes at the starting line for the main race. -Four cyclists managed to break away from the top regional racers. -These included Philipp Pangerl, a semi-professional mountain biker from the Black Tusk Racing Team. -Together with a team mate, Pangerl has already twice been crowned World Champion in the 12-hour race. -This year he became European Champion in this special discipline. -Also competing was Roland Ballerstedt, who has already claimed two German duathlon championships. -After 45 kilometres and a climb of 900 metres, the leading riders were neck and neck on the final straight in front of the Drei-Seen-Hall. -Pangerl sped over the finish line in first place. -Only one second later, the experienced Ballerstedt managed to take second place in the overall standings. -Hermann and Warthmann then reached the finish line simultaneously to take joint third in the overall standings. -In the ladies' race, Christiane Cohsmann achieved the fastest time in the main race. -Areane Blersch from Binzwangen took second place, ahead of Natascha Werner from Stuttgart. -Athletes and assistants seemed very satisfied with the smooth and well-organised running of the event. -Trekking through mud, rivers and jungle to provide free medical care -Dr. Georges Bwelle is bringing free health care to rural villages in Cameroon -Bwelle and his team spend almost every weekend seeing hundreds of patients -There aren't many doctors in the west African country; just one for every 5,000 people -Cast your vote here or through your mobile device -Dr. Georges Bwelle is one of the top 10 CNN Heroes of 2013. -You can vote for him, or any of the other top 10 Heroes, to be CNN Hero of the Year. -That person will receive $250,000 to continue their extraordinary work. -For 21 years, Georges Bwelle watched his ill father slip in and out of consciousness, traveling to hospitals that weren't equipped to help him. -Jamef Bwelle was injured in a 1981 car accident near Yaounde, Cameroon's capital. -He suffered only a broken arm at first, but an infection developed and spread to his brain, creating a hematoma that would affect him for the rest of his life. -"There were no neurosurgeons in Cameroon," Georges Bwelle said. -We would have taken him out of Cameroon if we had the money. -Instead, Bwelle spent years escorting his father to overcrowded clinics and hospitals, getting whatever treatment they could get. -"It's not easy," Bwelle said. -You can leave home at 5 a.m., running to the hospital to be the first, and you are not the first. -There are a lot of patients. -Some people can die because they are waiting. -The situation hasn't changed much since Bwelle's father passed away in 2002. -In Cameroon, there is only one doctor for every 5,000 people, according to the World Health Organization. -For comparison's sake, the ratio in the United States is one doctor for every 413 people. -And even if they could see a physician, many Cameroonians couldn't afford it. -Two out of five people in the country live below the poverty line, and nearly three-quarters of the country's health-care spending is private. -"The only problem they have is poverty," Bwelle said. -And with poverty, they cannot enjoy their life. -Seeing his father and so many of his countrymen suffer, Bwelle was determined to do something about it. -Dr. Georges Bwelle and his team of volunteers have performed 700 free surgeries in the past year. -He became a doctor himself, working as a vascular surgeon in Yaounde's Central Hospital. -And he started a nonprofit, ASCOVIME, that travels into rural areas on weekends to provide free medical care. -Since 2008, he and his group of volunteers have helped nearly 32,000 people. -Almost every Friday, he and up to 30 people jam into vans, tie medical supplies to the roofs and travel across rough terrain to visit villages in need. -Their luck doesn't always hold out. -They've had to push vehicles through rivers and mud more than once. -But when they arrive, they receive a true heroes' welcome: a feast, singing and dancing, and the best accommodations the community can offer. -In these villages, free medical care is truly a cause for celebration, and Bwelle -- with his big smile and boundless energy -- is more than happy to join in the fun. -The next morning, the team begins meeting with hundreds of patients. -"We are receiving 500 people in each trip," Bwelle said. -They are coming from 60 kilometers around the village, and they're coming on foot. -Each of these weekend clinics provides a variety of medical care. -Many people are treated for malaria, tuberculosis, malnutrition, diabetes, parasites and sexually transmitted diseases. -Others might receive crutches, a pair of donated eyeglasses or free birth certificates -- documentation that's required for school but that many impoverished families simply can't afford. -In the evenings, the team will do simple surgeries with local anesthesia. -Operations are usually done in a schoolhouse, town hall or home; after the procedure, patients get up and walk to the recovery area to make way for the next person. -With the group's generator lighting the operating room and sanitizing equipment, Bwelle and his volunteers work into the early hours of Sunday morning. -It's a backbreaking pace, but village musicians usually help keep the team motivated. -"They are beating drums all night to keep us awake and continue our work," Bwelle said. -On Sunday, the team heads back to the city, tired but proud of their work. -The group -- a mix of Cameroonian doctors and foreign medical students -- has performed 700 free surgeries in the past year, and they know that their help can make a world of difference to those they help. -One man explained that the free hernia surgery he'd received will allow him to work again. -"This will change my future with my family," the man said. -In addition to holding these weekend clinics and working as a hospital surgeon, Bwelle also works nights at private medical clinics around Yaounde. -It's this second job, he said, that funds about 60% of his nonprofit; the rest is covered by private donations. -"I'm not sure when he sleeps," said Katie O'Malley, a second-year medical student from Drexel University in Philadelphia and volunteer with Bwelle's group. -He is always either at the hospital or trying to make money for the organization so he can go on these campaigns. -For medical and nursing students such as O'Malley, who come from the United States and Europe to join Bwelle on his missions, it's a hands-on opportunity they'd never get at home. -"We've been able to scrub in on surgeries where we help blot blood away or hold tools for Dr. Bwelle," O'Malley said. -That's not something you'd ever get to do in America as a second-year medical student. -The student volunteers usually pay their own way to Cameroon, often arriving with donated medical supplies. -But once they arrive in Yaounde, their board, transportation and instruction are covered by Bwelle. -"He's a hero, without a doubt," O'Malley said. -He gives his life to this organization, and his desire to help the Cameroon people is everlasting. -For Bwelle, the near-constant workload isn't a hardship. -Helping others live happier lives, fulfilling a promise he made to his father, is something that brings him great joy. -"I am so happy when I am doing this work," Bwelle said. -And I think about my father. -I hope he sees what I am doing. -To make people laugh, to reduce the pain, that's why I'm doing this. -Check out the ASCOVIME website and see how to help. -Mastering the struggle to perfection. -The role of Santiago, the old man in Hemingway's novel "The Old Man and the Sea", is perfectly suited to Horst Janson. -Janson is an old hand himself when it comes to his profession, the art of acting. -The old man, Santiago, is the best there is when it comes to fishing. -As such, the pair complement one another superbly. -You almost think that you can see the folds in the old man's neck, of which young fisherman Manolo speaks. -When Santiago sits arched over on the chair, tired of life, this is a genuine emotion for Janson. -Furthermore, the role of the old fisherman is a character role, through and through, which Janson masters excellently. -Hemingway told the story of a fighter, a courageous man who doesn't give up and who ultimately wins the battle against the fish as a result of his knowledge and his strong will. -For long periods, Janson is the only performer on the stage. -He manages to focus the audience's attention on himself, with the result that his monologues never become boring, not even for just a moment. -His efforts to finally pull the fish to the surface of the water are so genuine that members of the audience forget that the sea in question consists merely of water bottles, illuminated in blue. -Director Jens Hasselmann has the old man in the boat perform in front of Fischerdorf. -During the course of the performance he has the respective active scene illuminated. Santiago's cabin and the bar of singer Marie-Luise Gunst, who sings of the Cuban lifestyle, with your songs and beautiful voice. -The four musicians accompany with Cuban rhythms and guests at the bar at the same time. -Tourism: Descent to the Romans -Beneath Cologne, a sewage channel from Roman times, measuring around 120 metres in length, has been preserved. -Anyone visiting Cologne cannot miss the cathedral. -The two towers, measuring around 157 metres, extend into the heavens, making it Europe's second tallest church building. -However, only a few visitors who marvel at the two ornate church towers realise that there is much to discover beneath the cathedral. -Rainer Schulze is an expert when it comes to the Cologne underworld. -The meeting point for his tours is the Cologne Tourism Service Centre, right at the cathedral. -The tour begins in a somewhat unspectacular fashion: "We begin in the underground car park," says Schulze. -In the 1970s, the city fathers wanted a car-friendly city, and therefore the only sections of the old city wall that remain are those that were not too tall. -The majority of the users of the parking garage therefore now hurry past the Roman relics and simply drop off their car. -However, anyone who takes a closer look on parking level D2 will discover an archaeological site, cordoned off by metal railings. -Ruins measuring five metres tall, made from stones around the size of the palm of your hand, are waiting to be discovered. -"These are remnants of the Roman city wall, which was built in the second half of the first century A.D," explains Schulze. -Before returning to daylight, Schulze has participants take a glance into a dark, 15-metre shaft on the upper parking level. -This is the well of the old cathedral, which is barely noticed at all these days. -The next stop on the tour is the praetorium, where participants once again descend to the depths. -Here we find the residence of what was once the most powerful man to the north of the Alps, the Praetor of the CCAA. -One visitor wants to know what CCAA means. -"The abbreviation stands for "Colonia Claudia Ara Agrippinensium", which is basically amounts to "City under Roman Law and City of the Agrippinians, founded under Emperor Claudius at the Site of the Altars for the Imperial Cult". -And the Praetorium refers to the former Governor's Palace and the centre of power of the Roman Empire on the Rhine. -From the exhibition vestibule, a tunnel leads to the preserved Roman sewer, measuring around 120 metres in length. -"If you want you can take a peaceful stroll along a section of the so-called Cloaca Maxima," says Schulze, encouraging the guests. -Schulze has one more surprise. -Once he has collected the key from the information desk, he leads the group to a 16-metre, accessible shaft. -"We are now in the Mikwe, a ritual Jewish bath," he explains. -The Jewish congregation built their bath deep into the ground in order to reach the groundwater essential for ritual washing. -To this day the differing water levels caused by the varying levels of the Rhine can be read at the lower end of the Mikwe, the origins of which trace back to the eighth century. -The Mikwe is to form part of an archaeological area at the Town Hall Square, on which construction work is set to begin shortly. -However, it will be a few years before it is complete. -Leg almost hacked off: Rocker retrial -A bloody attack within the 'Rocker' scene is to be re-investigated after more than four years. -Proceedings for attempted murder, grievous bodily harm and material damage, brought against two 30-year-olds have begun before the Frankfurt District Court. -They are being accompanied by strict security precautions. -The accused initially remained silent. -They are said to have been active within the Rocker scene and to have at the time belonged to the Bandidos. -The Public Prosecutor accused them of having stabbed and beaten three men in a car in Finowfurt, together with unknown accomplices. -All the victims were seriously injured, with one man almost losing a leg. -The victims were said to have belonged to the competing gang, the Hell's Angels. -One is said to have been a high-ranking member from Berlin. -It seems that the attack was preceded by a car chase involving several cars. -According to accusations, this was part of a turf war and represented a demonstration of power. -The two accused were acquitted in an initial trial at the beginning of 2012. -At the time, a speaker for the court said that it could not be proven that the men had actually been involved in the bloody feud. -The assailants had worn masks and had not been recognised. -The Federal Supreme Court repealed the verdict following an appeal by the Public Prosecutor's Office and referred the case back to the District Court for a re-trial. -One of the accused is currently serving time in an open prison on account of another offence. -Proceedings initially mainly involved the reconstruction of the series of events on the night of the crime. -The police inspector responsible at the time was invited as a witness. -The central point of focus was on mobile phone conversations between the accused and third parties, which the police had intercepted. -What makes things difficult is that Rockers almost always observe what they consider to be complete silence appearing before the court - they say absolutely nothing. -This also applies in the case of the victims. -In the first trial, only one of the victims spoke out. -When asked, Public Prosecutor Stefan Golfier of the Frankfurt (Oder) Public Prosecutor's Office said that new evidence had come to light since the previous trial. -A further eleven days of proceedings have been scheduled for the trial. -The throwaway society does not think -The night was long, the music loud and the atmosphere good, but at some point everyone has to go home. -Except: In the stomachs of those in the passenger and back seats, hunger strikes. -Of course, this may be down to the odd cocktail or glass of bubbly! -So what could be more natural than to casually pull into the next drive-through to pick yourself up a little treat? -I admit, I am right with you with it comes to picking up some fast food late at night, or early in the morning as the case may be. -A few chips, a coke, a burger - and then straight home and into bed! -However, on the way home the clear differences between me and other hungry clubbers become evident. -What is wrong with people who continually throw their fast food bags out of the car window?! -At the weekend in particular, discarded paper bags can be found at the side of the road and in car parks. -"Why is this?" I ask myself. -Are the people too stupid to take their bags home or throw them into the nearest rubbish bin? -After all, waste paper does more than spoil the landscape. -Bags have also been seen covering transport facilities such as crash barriers, thus causing accidents. -However, the so-called "throwaway society" does not think about such things. They probably do not even know what the term even means. -If you are often out and about in the evening, more often than not with several people in the car, you will know what a back seat can look like after having partied through the night: clothes, bottles and other rubbish begin to pile up. -A few bags won't make much of a difference there - in contrast to on the street where no-one wants to trample through other peoples' leftover food. -I can see the day coming when towns and municipalities will get fed up clearing up - and the fast food chains will have to operate a deposit scheme for their bags. -Obama's Health Care Walk Back -Amid a firestorm of criticism, President Obama yesterday walked back his oft-repeated, unambiguous promise that "if you like your health plan, you can keep it." -With hundreds of thousands receiving cancellation notices from their providers, Republicans have slammed the president in recent days for misleading the American public. -Yesterday, Obama tweaked his original pledge. -"For the vast majority of people who have health insurance that works, you can keep it," he said in a speech in Boston. -Addressing what he called the "flurry in the news" about the cancellations, Obama urged Americans receiving these notices to shop for new coverage in the marketplace. -Most people are going to be able to get better, comprehensive health care plans for the same price or even cheaper than projected. -"You're going to get a better deal," he said. -The administration has said it should come as no surprise that the 5 percent of the population who purchase insurance on their own may be forced to switch plans because their coverage doesn't meet the new standards required under the Affordable Care Act. -"Let me say directly to these Americans: you deserve better," Sebelius said in testimony before the House Energy and Commerce Committee in Washington. -Sebelius, who is overseeing implementation of the Affordable Care Act, said the launch of the online marketplace has gone "miserably" since October. -"I am as frustrated and angry as anyone," she said. -I am eager to earn your confidence back. -An exasperated Sebelius uttered that phrase, caught by a hot mic, to an aide seated behind her at yesterday's House hearing following a contentious exchange with Rep. Billy Long, R-Mo., over whether she should be required to enroll in Obamacare. -More than three hours into the hearing, Long repeatedly pressed Sebelius on why the "architect" of the Affordable Care Act has not voluntarily forgone government-sponsored insurance to purchase a plan through HealthCare.gov, which she is now pitching to millions of Americans. -The government in Jerusalem fails to confirm an attack on the Syrian airforce base -On Thursday, an American government official, who wished to remain anonymous, made a statement to American news broadcaster CNN, stating that Israeli fighter planes had attacked an airforce base in the Syrian town of Latakia the day before. -The target was "missiles and related equipment", which the Israeli government feared were intended for the Lebanese Hezbollah, according to CNN. -The Syrian and Lebanese media, as well as Arabic broadcaster Al-Arabiya, had previously reported that the base in the port town of Latakia was fired upon during the night from Wednesday to Thursday, and the Israelis were identified as the responsible party. -Israeli television broadcaster Channel 2 showed satellite images of the base in Latakia, in which Russian S-125 Newa-type defence missiles and a battery of SA-3 missiles could be seen, which according to Channel 2 have a range of 35 kilometres and can transport warheads weighing up to 70 kilograms. -Unsure, the government in Jerusalem has not officially confirmed the attack. -However, press agency Reuters has also quoted an anonymous Israeli government official as saying that he believed that Israel had carried out the attack, but was not sure. -In any case, a spokesperson for the Israeli Ministry of Defence said: "We are not commenting on these reports". -Journalist Ron Ben-Yishai, writing for daily newspaper Yedioth Ahronoth, emphasised that the Syrian regime had already previously attempted, in some cases successfully, to deliver ground to air missiles to Hezbollah. -The Israeli government had previously repeatedly warned that any attempt by Syria to supply Hezbollah with chemical or other dangerous weapons would be crossing a "red line", which would result in a military response. -John Kerry says US spying has "reached too far inappropriately" in unprecedented admission -John Kerry has indicated a softening of the U.S's defensive stance on its surveillance programmes with an unprecedented admission that on occasions its spying has "reached too far inappropriately." -The Secretary of State also admitted that he'd been guilty, along with Barack Obama, of being on "automatic pilot" as incendiary revelations from whistleblower Edward Snowden about the NSA's spying activities emerged. -The leaks have put the US government at the centre of a diplomatic storm with its allies. -Speaking to an open government conference in London via video link, Mr Kerry said: "There is no question that the President and I and others in government have actually learned of some things that had been happening on an automatic pilot because the ability has been there, going back to World War Two and to the very difficult years of the Cold War, and then, of course, 9/11." -He then became the first high-ranking member of the U.S government to admit that US spying had crossed the line, but emphasised that no one's rights had been abused. -He said: "In some cases, it has reached too far inappropriately." -And the President is determined to try to clarify and make clear for people and is now doing a thorough review in order that nobody will have the sense of abuse. -I assure you innocent people are not being abused in this process. -Mr Kerry insisted, however, that the NSA was a force for good and that its surveillance operations had saved many lives. -He added: "We're dealing in a new world where people are willing to blow themselves up." -There is radical extremism in the world that is hell-bent and determined to try to kill people and blow people up and attack governments. -So what if you were able to intercept that and stop it before it happens? -We have actually prevented airplanes from going down, buildings from being blown up, and people from being assassinated because we've been able to learn ahead of time of the plans. -Meanwhile, U.S. lawmakers will head to Europe to help address concerns abroad about alleged U.S. spying and convince the Europeans of the need to continue joint anti-terrorism efforts with the U.S., the chairman of a Senate subcommittee on European affairs said on Thursday. -Senator Chris Murphy of Connecticut said he spoke with European Parliament members and others this week and is concerned about their threats to stop participating in anti-terrorist organizations because of frustration over surveillance by the National Security Agency. -"It's really important for U.S. national security interests for Europeans to stay on board with us with respect to our mutual anti-terrorism endeavors," Murphy, a first-term Democrat and chairman of the Senate Foreign Relations Subcommittee on European Affairs, said in an interview from Washington. -And I'm going to Europe to make it clear to them that we need to continue to work together in combatting terrorism, notwithstanding their anger over these NSA programs. -News reports that the NSA swept up millions of phone records in Europe have frayed relations with some U.S. allies, though the agency's chief said this week that they were inaccurate and reflected a misunderstanding of metadata that Nato allies collected and shared with the United States. -Other revelations cited documents leaked by Snowden that the NSA monitored German Chancellor Angela Merkel's cellphone and those of up to 34 other world leaders. -The national intelligence director, James Clapper, defended spying on allies as necessary and said it's commonplace on both sides. -Amid the uproar, Murphy said his office is arranging the congressional trip, expected to take place this year, and hopes the delegation will include members of both parties and both chambers. -Names of other participating lawmakers were to be released in coming days. -He said the itinerary is still being worked out. -While Murphy said the purpose of the trip is to help improve relationships, he said some "tough love" will also be dispensed. -He said European leaders need to be honest with their own people about the kind of espionage programs they've used for years themselves. -"While we can amend our surveillance programs to better protect the rights of Europeans, they also need to come to terms with the fact that we're not the only ones that are out there spying," Murphy said. -Meanwhile, Mr Kerry is scheduled to head this weekend to the Middle East and Poland to address rancor over U.S. strategies in the Syria, Egypt and Iran as well as U.S. surveillance activities. -A stubborn ankle injury threatens to prevent the deployment of Nicolai Müller in the FSV Mainz's Bundesliga away match at FC Augsburg. -Mainz are concerned regarding the availability of Nicolai Müller. -Tomorrow he will attempt to train with the team. -Up until now he has only managed individual training. -"We will shortly make a decision regarding whether he will play," said manager Thomas Tuchel. -The 40 year-old hopes to have his best striker (with six goals) in the team. -Mainz are already without the injured key players Niki Zimling, Julian Baumgartlinger and Niko Bungert. -Australian woman appeals Thai jail time -A 21-year-old Sydney woman sentenced to 15 days jail in Phuket for falsely claiming she was assaulted by a taxi driver is appealing the verdict and has been granted bail. -Stevie Rochelle Bamford was initially found guilty by a Phuket provincial court on June 15 of making false claims after telling Thai police a local taxi driver, with two other men restraining her, carried out the assault in the early hours of Sunday June 10. -However, CCTV footage later revealed she had returned to her hotel safely after becoming separated from her Australian boyfriend. -Phuket police interviewed Bamford for two days before she confessed to fabricating the story. -She was held in local police cells before the court hearing. -Bamford was sentenced to serve the 15-day prison term at a low security detention centre on the outskirts of Phuket rather than in an adult women's jail. -She is the daughter of former Australian league player Peter Tunks, who has appealed to the Department of Foreign Affairs in Canberra to assist his daughter. -Tunks told Sydney's Sunday Telegraph the whole family was "extremely concerned" about his daughter's welfare and wanted her back in Australia. -"It's obviously been a worrying time but we're hopeful to have her back home safely as soon as possible," Tunks said. -Bamford is appealing the sentence and has been granted bail of 50,000 baht. -Reports in Australia said that in the meantime, she was holidaying at the resort area of Krabi in Southern Thailand. -Thai-based legal sources said Bamford was being represented by a local lawyer in Phuket but warned that the appeal may lead to the court increasing her sentence by up to two years and forcing her to serve it in an adult prison. -However, following the recent murder of Australian travel agent Michelle Smith in Phuket, Thailand may also be looking to repair its battered tourist image, leading to an acquittal. diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/transformer_main.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/transformer_main.py deleted file mode 100644 index 3560308e..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/transformer_main.py +++ /dev/null @@ -1,636 +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. -# ============================================================================== -"""Train and evaluate the Transformer model. - -See README for description of setting the training schedule and evaluating the -BLEU score. -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os -import tempfile - -# pylint: disable=g-bad-import-order -from six.moves import xrange # pylint: disable=redefined-builtin -from absl import app as absl_app -from absl import flags -import tensorflow as tf -# pylint: enable=g-bad-import-order - -from official.transformer import compute_bleu -from official.transformer import translate -from official.transformer.model import model_params -from official.transformer.model import transformer -from official.transformer.utils import dataset -from official.transformer.utils import metrics -from official.transformer.utils import schedule -from official.transformer.utils import tokenizer -from official.utils.accelerator import tpu as tpu_util -from official.utils.export import export -from official.utils.flags import core as flags_core -from official.utils.logs import hooks_helper -from official.utils.logs import logger -from official.utils.misc import distribution_utils -from official.utils.misc import model_helpers - -PARAMS_MAP = { - "tiny": model_params.TINY_PARAMS, - "base": model_params.BASE_PARAMS, - "big": model_params.BIG_PARAMS, -} - - -DEFAULT_TRAIN_EPOCHS = 10 -INF = int(1e9) -BLEU_DIR = "bleu" - -# Dictionary containing tensors that are logged by the logging hooks. Each item -# maps a string to the tensor name. -TENSORS_TO_LOG = { - "learning_rate": "model/get_train_op/learning_rate/learning_rate", - "cross_entropy_loss": "model/cross_entropy"} - - -def model_fn(features, labels, mode, params): - """Defines how to train, evaluate and predict from the transformer model.""" - with tf.variable_scope("model"): - inputs, targets = features, labels - - # Create model and get output logits. - model = transformer.Transformer(params, mode == tf.estimator.ModeKeys.TRAIN) - - logits = model(inputs, targets) - - # When in prediction mode, the labels/targets is None. The model output - # is the prediction - if mode == tf.estimator.ModeKeys.PREDICT: - if params["use_tpu"]: - raise NotImplementedError("Prediction is not yet supported on TPUs.") - return tf.estimator.EstimatorSpec( - tf.estimator.ModeKeys.PREDICT, - predictions=logits, - export_outputs={ - "translate": tf.estimator.export.PredictOutput(logits) - }) - - # Explicitly set the shape of the logits for XLA (TPU). This is needed - # because the logits are passed back to the host VM CPU for metric - # evaluation, and the shape of [?, ?, vocab_size] is too vague. However - # it is known from Transformer that the first two dimensions of logits - # are the dimensions of targets. Note that the ambiguous shape of logits is - # not a problem when computing xentropy, because padded_cross_entropy_loss - # resolves the shape on the TPU. - logits.set_shape(targets.shape.as_list() + logits.shape.as_list()[2:]) - - # Calculate model loss. - # xentropy contains the cross entropy loss of every nonpadding token in the - # targets. - xentropy, weights = metrics.padded_cross_entropy_loss( - logits, targets, params["label_smoothing"], params["vocab_size"]) - loss = tf.reduce_sum(xentropy) / tf.reduce_sum(weights) - - # Save loss as named tensor that will be logged with the logging hook. - tf.identity(loss, "cross_entropy") - - if mode == tf.estimator.ModeKeys.EVAL: - if params["use_tpu"]: - # host call functions should only have tensors as arguments. - # This lambda pre-populates params so that metric_fn is - # TPUEstimator compliant. - metric_fn = lambda logits, labels: ( - metrics.get_eval_metrics(logits, labels, params=params)) - eval_metrics = (metric_fn, [logits, labels]) - return tf.contrib.tpu.TPUEstimatorSpec( - mode=mode, loss=loss, predictions={"predictions": logits}, - eval_metrics=eval_metrics) - return tf.estimator.EstimatorSpec( - mode=mode, loss=loss, predictions={"predictions": logits}, - eval_metric_ops=metrics.get_eval_metrics(logits, labels, params)) - else: - train_op, metric_dict = get_train_op_and_metrics(loss, params) - - # Epochs can be quite long. This gives some intermediate information - # in TensorBoard. - metric_dict["minibatch_loss"] = loss - if params["use_tpu"]: - return tf.contrib.tpu.TPUEstimatorSpec( - mode=mode, loss=loss, train_op=train_op, - host_call=tpu_util.construct_scalar_host_call( - metric_dict=metric_dict, model_dir=params["model_dir"], - prefix="training/") - ) - record_scalars(metric_dict) - return tf.estimator.EstimatorSpec(mode=mode, loss=loss, train_op=train_op) - - -def record_scalars(metric_dict): - for key, value in metric_dict.items(): - tf.contrib.summary.scalar(name=key, tensor=value) - - -def get_learning_rate(learning_rate, hidden_size, learning_rate_warmup_steps): - """Calculate learning rate with linear warmup and rsqrt decay.""" - with tf.name_scope("learning_rate"): - warmup_steps = tf.to_float(learning_rate_warmup_steps) - step = tf.to_float(tf.train.get_or_create_global_step()) - - learning_rate *= (hidden_size ** -0.5) - # Apply linear warmup - learning_rate *= tf.minimum(1.0, step / warmup_steps) - # Apply rsqrt decay - learning_rate *= tf.rsqrt(tf.maximum(step, warmup_steps)) - - # Create a named tensor that will be logged using the logging hook. - # The full name includes variable and names scope. In this case, the name - # is model/get_train_op/learning_rate/learning_rate - tf.identity(learning_rate, "learning_rate") - - return learning_rate - - -def get_train_op_and_metrics(loss, params): - """Generate training op and metrics to save in TensorBoard.""" - with tf.variable_scope("get_train_op"): - learning_rate = get_learning_rate( - learning_rate=params["learning_rate"], - hidden_size=params["hidden_size"], - learning_rate_warmup_steps=params["learning_rate_warmup_steps"]) - - # Create optimizer. Use LazyAdamOptimizer from TF contrib, which is faster - # than the TF core Adam optimizer. - optimizer = tf.contrib.opt.LazyAdamOptimizer( - learning_rate, - beta1=params["optimizer_adam_beta1"], - beta2=params["optimizer_adam_beta2"], - epsilon=params["optimizer_adam_epsilon"]) - - if params["use_tpu"] and params["tpu"] != tpu_util.LOCAL: - optimizer = tf.contrib.tpu.CrossShardOptimizer(optimizer) - - # Calculate and apply gradients using LazyAdamOptimizer. - global_step = tf.train.get_global_step() - tvars = tf.trainable_variables() - gradients = optimizer.compute_gradients( - loss, tvars, colocate_gradients_with_ops=True) - minimize_op = optimizer.apply_gradients( - gradients, global_step=global_step, name="train") - update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) - train_op = tf.group(minimize_op, update_ops) - - train_metrics = {"learning_rate": learning_rate} - - if not params["use_tpu"]: - # gradient norm is not included as a summary when running on TPU, as - # it can cause instability between the TPU and the host controller. - gradient_norm = tf.global_norm(list(zip(*gradients))[0]) - train_metrics["global_norm/gradient_norm"] = gradient_norm - - return train_op, train_metrics - - -def translate_and_compute_bleu(estimator, subtokenizer, bleu_source, bleu_ref): - """Translate file and report the cased and uncased bleu scores.""" - # Create temporary file to store translation. - tmp = tempfile.NamedTemporaryFile(delete=False) - tmp_filename = tmp.name - - translate.translate_file( - estimator, subtokenizer, bleu_source, output_file=tmp_filename, - print_all_translations=False) - - # Compute uncased and cased bleu scores. - uncased_score = compute_bleu.bleu_wrapper(bleu_ref, tmp_filename, False) - cased_score = compute_bleu.bleu_wrapper(bleu_ref, tmp_filename, True) - os.remove(tmp_filename) - return uncased_score, cased_score - - -def get_global_step(estimator): - """Return estimator's last checkpoint.""" - return int(estimator.latest_checkpoint().split("-")[-1]) - - -def evaluate_and_log_bleu(estimator, bleu_source, bleu_ref, vocab_file): - """Calculate and record the BLEU score.""" - subtokenizer = tokenizer.Subtokenizer(vocab_file) - - uncased_score, cased_score = translate_and_compute_bleu( - estimator, subtokenizer, bleu_source, bleu_ref) - - tf.logging.info("Bleu score (uncased): %d", uncased_score) - tf.logging.info("Bleu score (cased): %d", cased_score) - return uncased_score, cased_score - - -def _validate_file(filepath): - """Make sure that file exists.""" - if not tf.gfile.Exists(filepath): - raise tf.errors.NotFoundError(None, None, "File %s not found." % filepath) - - -def run_loop( - estimator, schedule_manager, train_hooks=None, benchmark_logger=None, - bleu_source=None, bleu_ref=None, bleu_threshold=None, vocab_file=None): - """Train and evaluate model, and optionally compute model's BLEU score. - - **Step vs. Epoch vs. Iteration** - - Steps and epochs are canonical terms used in TensorFlow and general machine - learning. They are used to describe running a single process (train/eval): - - Step refers to running the process through a single or batch of examples. - - Epoch refers to running the process through an entire dataset. - - E.g. training a dataset with 100 examples. The dataset is - divided into 20 batches with 5 examples per batch. A single training step - trains the model on one batch. After 20 training steps, the model will have - trained on every batch in the dataset, or, in other words, one epoch. - - Meanwhile, iteration is used in this implementation to describe running - multiple processes (training and eval). - - A single iteration: - 1. trains the model for a specific number of steps or epochs. - 2. evaluates the model. - 3. (if source and ref files are provided) compute BLEU score. - - This function runs through multiple train+eval+bleu iterations. - - Args: - estimator: tf.Estimator containing model to train. - schedule_manager: A schedule.Manager object to guide the run loop. - train_hooks: List of hooks to pass to the estimator during training. - benchmark_logger: a BenchmarkLogger object that logs evaluation data - bleu_source: File containing text to be translated for BLEU calculation. - bleu_ref: File containing reference translations for BLEU calculation. - bleu_threshold: minimum BLEU score before training is stopped. - vocab_file: Path to vocab file that will be used to subtokenize bleu_source. - - Raises: - ValueError: if both or none of single_iteration_train_steps and - single_iteration_train_epochs were defined. - NotFoundError: if the vocab file or bleu files don't exist. - """ - if bleu_source: - _validate_file(bleu_source) - if bleu_ref: - _validate_file(bleu_ref) - if vocab_file: - _validate_file(vocab_file) - - evaluate_bleu = bleu_source is not None and bleu_ref is not None - if evaluate_bleu and schedule_manager.use_tpu: - raise ValueError("BLEU score can not be computed when training with a TPU, " - "as it requires estimator.predict which is not yet " - "supported.") - - # Print details of training schedule. - tf.logging.info("Training schedule:") - tf.logging.info( - "\t1. Train for {}".format(schedule_manager.train_increment_str)) - tf.logging.info("\t2. Evaluate model.") - if evaluate_bleu: - tf.logging.info("\t3. Compute BLEU score.") - if bleu_threshold is not None: - tf.logging.info("Repeat above steps until the BLEU score reaches %f" % - bleu_threshold) - if not evaluate_bleu or bleu_threshold is None: - tf.logging.info("Repeat above steps %d times." % - schedule_manager.train_eval_iterations) - - if evaluate_bleu: - # Create summary writer to log bleu score (values can be displayed in - # Tensorboard). - bleu_writer = tf.summary.FileWriter( - os.path.join(estimator.model_dir, BLEU_DIR)) - if bleu_threshold is not None: - # Change loop stopping condition if bleu_threshold is defined. - schedule_manager.train_eval_iterations = INF - - # Loop training/evaluation/bleu cycles - for i in xrange(schedule_manager.train_eval_iterations): - tf.logging.info("Starting iteration %d" % (i + 1)) - - # Train the model for single_iteration_train_steps or until the input fn - # runs out of examples (if single_iteration_train_steps is None). - estimator.train( - dataset.train_input_fn, - steps=schedule_manager.single_iteration_train_steps, - hooks=train_hooks) - - eval_results = estimator.evaluate( - input_fn=dataset.eval_input_fn, - steps=schedule_manager.single_iteration_eval_steps) - - tf.logging.info("Evaluation results (iter %d/%d):" % - (i + 1, schedule_manager.train_eval_iterations)) - tf.logging.info(eval_results) - benchmark_logger.log_evaluation_result(eval_results) - - # The results from estimator.evaluate() are measured on an approximate - # translation, which utilize the target golden values provided. The actual - # bleu score must be computed using the estimator.predict() path, which - # outputs translations that are not based on golden values. The translations - # are compared to reference file to get the actual bleu score. - if evaluate_bleu: - uncased_score, cased_score = evaluate_and_log_bleu( - estimator, bleu_source, bleu_ref, vocab_file) - - # Write actual bleu scores using summary writer and benchmark logger - global_step = get_global_step(estimator) - summary = tf.Summary(value=[ - tf.Summary.Value(tag="bleu/uncased", simple_value=uncased_score), - tf.Summary.Value(tag="bleu/cased", simple_value=cased_score), - ]) - bleu_writer.add_summary(summary, global_step) - bleu_writer.flush() - benchmark_logger.log_metric( - "bleu_uncased", uncased_score, global_step=global_step) - benchmark_logger.log_metric( - "bleu_cased", cased_score, global_step=global_step) - - # Stop training if bleu stopping threshold is met. - if model_helpers.past_stop_threshold(bleu_threshold, uncased_score): - bleu_writer.close() - break - - -def define_transformer_flags(): - """Add flags and flag validators for running transformer_main.""" - # Add common flags (data_dir, model_dir, train_epochs, etc.). - flags_core.define_base() - flags_core.define_performance( - num_parallel_calls=True, - inter_op=False, - intra_op=False, - synthetic_data=True, - max_train_steps=False, - dtype=False, - all_reduce_alg=True - ) - flags_core.define_benchmark() - flags_core.define_device(tpu=True) - - # Set flags from the flags_core module as "key flags" so they're listed when - # the '-h' flag is used. Without this line, the flags defined above are - # only shown in the full `--helpful` help text. - flags.adopt_module_key_flags(flags_core) - - # Add transformer-specific flags - flags.DEFINE_enum( - name="param_set", short_name="mp", default="big", - enum_values=PARAMS_MAP.keys(), - help=flags_core.help_wrap( - "Parameter set to use when creating and training the model. The " - "parameters define the input shape (batch size and max length), " - "model configuration (size of embedding, # of hidden layers, etc.), " - "and various other settings. The big parameter set increases the " - "default batch size, embedding/hidden size, and filter size. For a " - "complete list of parameters, please see model/model_params.py.")) - - flags.DEFINE_bool( - name="static_batch", default=False, - help=flags_core.help_wrap( - "Whether the batches in the dataset should have static shapes. In " - "general, this setting should be False. Dynamic shapes allow the " - "inputs to be grouped so that the number of padding tokens is " - "minimized, and helps model training. In cases where the input shape " - "must be static (e.g. running on TPU), this setting will be ignored " - "and static batching will always be used.")) - - # Flags for training with steps (may be used for debugging) - flags.DEFINE_integer( - name="train_steps", short_name="ts", default=None, - help=flags_core.help_wrap("The number of steps used to train.")) - flags.DEFINE_integer( - name="steps_between_evals", short_name="sbe", default=1000, - help=flags_core.help_wrap( - "The Number of training steps to run between evaluations. This is " - "used if --train_steps is defined.")) - - # BLEU score computation - flags.DEFINE_string( - name="bleu_source", short_name="bls", default=None, - help=flags_core.help_wrap( - "Path to source file containing text translate when calculating the " - "official BLEU score. Both --bleu_source and --bleu_ref must be set. " - "Use the flag --stop_threshold to stop the script based on the " - "uncased BLEU score.")) - flags.DEFINE_string( - name="bleu_ref", short_name="blr", default=None, - help=flags_core.help_wrap( - "Path to source file containing text translate when calculating the " - "official BLEU score. Both --bleu_source and --bleu_ref must be set. " - "Use the flag --stop_threshold to stop the script based on the " - "uncased BLEU score.")) - flags.DEFINE_string( - name="vocab_file", short_name="vf", default=None, - help=flags_core.help_wrap( - "Path to subtoken vocabulary file. If data_download.py was used to " - "download and encode the training data, look in the data_dir to find " - "the vocab file.")) - - flags_core.set_defaults(data_dir="/tmp/translate_ende", - model_dir="/tmp/transformer_model", - batch_size=None, - train_epochs=None) - - @flags.multi_flags_validator( - ["train_epochs", "train_steps"], - message="Both --train_steps and --train_epochs were set. Only one may be " - "defined.") - def _check_train_limits(flag_dict): - return flag_dict["train_epochs"] is None or flag_dict["train_steps"] is None - - @flags.multi_flags_validator( - ["bleu_source", "bleu_ref"], - message="Both or neither --bleu_source and --bleu_ref must be defined.") - def _check_bleu_files(flags_dict): - return (flags_dict["bleu_source"] is None) == ( - flags_dict["bleu_ref"] is None) - - @flags.multi_flags_validator( - ["bleu_source", "bleu_ref", "vocab_file"], - message="--vocab_file must be defined if --bleu_source and --bleu_ref " - "are defined.") - def _check_bleu_vocab_file(flags_dict): - if flags_dict["bleu_source"] and flags_dict["bleu_ref"]: - return flags_dict["vocab_file"] is not None - return True - - @flags.multi_flags_validator( - ["export_dir", "vocab_file"], - message="--vocab_file must be defined if --export_dir is set.") - def _check_export_vocab_file(flags_dict): - if flags_dict["export_dir"]: - return flags_dict["vocab_file"] is not None - return True - - flags_core.require_cloud_storage(["data_dir", "model_dir", "export_dir"]) - - -def construct_estimator(flags_obj, params, schedule_manager): - """Construct an estimator from either Estimator or TPUEstimator. - - Args: - flags_obj: The FLAGS object parsed from command line. - params: A dict of run specific parameters. - schedule_manager: A schedule.Manager object containing the run schedule. - - Returns: - An estimator object to be used for training and eval. - """ - if not params["use_tpu"]: - distribution_strategy = distribution_utils.get_distribution_strategy( - flags_core.get_num_gpus(flags_obj), flags_obj.all_reduce_alg) - return tf.estimator.Estimator( - model_fn=model_fn, model_dir=flags_obj.model_dir, params=params, - config=tf.estimator.RunConfig(train_distribute=distribution_strategy)) - - tpu_cluster_resolver = tf.contrib.cluster_resolver.TPUClusterResolver( - tpu=flags_obj.tpu, - zone=flags_obj.tpu_zone, - project=flags_obj.tpu_gcp_project - ) - - tpu_config = tf.contrib.tpu.TPUConfig( - iterations_per_loop=schedule_manager.single_iteration_train_steps, - num_shards=flags_obj.num_tpu_shards) - - run_config = tf.contrib.tpu.RunConfig( - cluster=tpu_cluster_resolver, - model_dir=flags_obj.model_dir, - session_config=tf.ConfigProto( - allow_soft_placement=True, log_device_placement=True), - tpu_config=tpu_config) - - return tf.contrib.tpu.TPUEstimator( - model_fn=model_fn, - use_tpu=params["use_tpu"] and flags_obj.tpu != tpu_util.LOCAL, - train_batch_size=schedule_manager.batch_size, - eval_batch_size=schedule_manager.batch_size, - params={ - # TPUEstimator needs to populate batch_size itself due to sharding. - key: value for key, value in params.items() if key != "batch_size"}, - config=run_config) - - -def run_transformer(flags_obj): - """Create tf.Estimator to train and evaluate transformer model. - - Args: - flags_obj: Object containing parsed flag values. - """ - num_gpus = flags_core.get_num_gpus(flags_obj) - - # Add flag-defined parameters to params object - params = PARAMS_MAP[flags_obj.param_set] - if num_gpus > 1: - if flags_obj.param_set == "big": - params = model_params.BIG_MULTI_GPU_PARAMS - elif flags_obj.param_set == "base": - params = model_params.BASE_MULTI_GPU_PARAMS - - params["data_dir"] = flags_obj.data_dir - params["model_dir"] = flags_obj.model_dir - params["num_parallel_calls"] = flags_obj.num_parallel_calls - - params["tpu"] = flags_obj.tpu - params["use_tpu"] = bool(flags_obj.tpu) # was a tpu specified. - params["static_batch"] = flags_obj.static_batch or params["use_tpu"] - params["allow_ffn_pad"] = not params["use_tpu"] - - params["use_synthetic_data"] = flags_obj.use_synthetic_data - - # Set batch size parameter, which depends on the availability of - # TPU and GPU, and distribution settings. - params["batch_size"] = (flags_obj.batch_size or ( - params["default_batch_size_tpu"] if params["use_tpu"] - else params["default_batch_size"])) - - if not params["use_tpu"]: - params["batch_size"] = distribution_utils.per_device_batch_size( - params["batch_size"], num_gpus) - - schedule_manager = schedule.Manager( - train_steps=flags_obj.train_steps, - steps_between_evals=flags_obj.steps_between_evals, - train_epochs=flags_obj.train_epochs, - epochs_between_evals=flags_obj.epochs_between_evals, - default_train_epochs=DEFAULT_TRAIN_EPOCHS, - batch_size=params["batch_size"], - max_length=params["max_length"], - use_tpu=params["use_tpu"], - num_tpu_shards=flags_obj.num_tpu_shards - ) - - params["repeat_dataset"] = schedule_manager.repeat_dataset - - model_helpers.apply_clean(flags.FLAGS) - - # Create hooks that log information about the training and metric values - train_hooks = hooks_helper.get_train_hooks( - flags_obj.hooks, - model_dir=flags_obj.model_dir, - tensors_to_log=TENSORS_TO_LOG, # used for logging hooks - batch_size=schedule_manager.batch_size, # for ExamplesPerSecondHook - use_tpu=params["use_tpu"] # Not all hooks can run with TPUs - ) - benchmark_logger = logger.get_benchmark_logger() - benchmark_logger.log_run_info( - model_name="transformer", - dataset_name="wmt_translate_ende", - run_params=params, - test_id=flags_obj.benchmark_test_id) - - # Train and evaluate transformer model - estimator = construct_estimator(flags_obj, params, schedule_manager) - run_loop( - estimator=estimator, - # Training arguments - schedule_manager=schedule_manager, - train_hooks=train_hooks, - benchmark_logger=benchmark_logger, - # BLEU calculation arguments - bleu_source=flags_obj.bleu_source, - bleu_ref=flags_obj.bleu_ref, - bleu_threshold=flags_obj.stop_threshold, - vocab_file=flags_obj.vocab_file) - - if flags_obj.export_dir and not params["use_tpu"]: - serving_input_fn = export.build_tensor_serving_input_receiver_fn( - shape=[None], dtype=tf.int64, batch_size=None) - # Export saved model, and save the vocab file as an extra asset. The vocab - # file is saved to allow consistent input encoding and output decoding. - # (See the "Export trained model" section in the README for an example of - # how to use the vocab file.) - # Since the model itself does not use the vocab file, this file is saved as - # an extra asset rather than a core asset. - estimator.export_savedmodel( - flags_obj.export_dir, serving_input_fn, - assets_extra={"vocab.txt": flags_obj.vocab_file}, - strip_default_attrs=True) - - -def main(_): - with logger.benchmark_context(flags.FLAGS): - run_transformer(flags.FLAGS) - - -if __name__ == "__main__": - tf.logging.set_verbosity(tf.logging.INFO) - define_transformer_flags() - absl_app.run(main) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/translate.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/translate.py deleted file mode 100644 index 0ea7fea5..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/translate.py +++ /dev/null @@ -1,236 +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. -# ============================================================================== -"""Translate text or files using trained transformer model.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os - -# pylint: disable=g-bad-import-order -from absl import app as absl_app -from absl import flags -import tensorflow as tf -# pylint: enable=g-bad-import-order - -from official.transformer.utils import tokenizer -from official.utils.flags import core as flags_core - -_DECODE_BATCH_SIZE = 32 -_EXTRA_DECODE_LENGTH = 100 -_BEAM_SIZE = 4 -_ALPHA = 0.6 - - -def _get_sorted_inputs(filename): - """Read and sort lines from the file sorted by decreasing length. - - Args: - filename: String name of file to read inputs from. - Returns: - Sorted list of inputs, and dictionary mapping original index->sorted index - of each element. - """ - with tf.gfile.Open(filename) as f: - records = f.read().split("\n") - inputs = [record.strip() for record in records] - if not inputs[-1]: - inputs.pop() - - input_lens = [(i, len(line.split())) for i, line in enumerate(inputs)] - sorted_input_lens = sorted(input_lens, key=lambda x: x[1], reverse=True) - - sorted_inputs = [None] * len(sorted_input_lens) - sorted_keys = [0] * len(sorted_input_lens) - for i, (index, _) in enumerate(sorted_input_lens): - sorted_inputs[i] = inputs[index] - sorted_keys[index] = i - return sorted_inputs, sorted_keys - - -def _encode_and_add_eos(line, subtokenizer): - """Encode line with subtokenizer, and add EOS id to the end.""" - return subtokenizer.encode(line) + [tokenizer.EOS_ID] - - -def _trim_and_decode(ids, subtokenizer): - """Trim EOS and PAD tokens from ids, and decode to return a string.""" - try: - index = list(ids).index(tokenizer.EOS_ID) - return subtokenizer.decode(ids[:index]) - except ValueError: # No EOS found in sequence - return subtokenizer.decode(ids) - - -def translate_file( - estimator, subtokenizer, input_file, output_file=None, - print_all_translations=True): - """Translate lines in file, and save to output file if specified. - - Args: - estimator: tf.Estimator used to generate the translations. - subtokenizer: Subtokenizer object for encoding and decoding source and - translated lines. - input_file: file containing lines to translate - output_file: file that stores the generated translations. - print_all_translations: If true, all translations are printed to stdout. - - Raises: - ValueError: if output file is invalid. - """ - batch_size = _DECODE_BATCH_SIZE - - # Read and sort inputs by length. Keep dictionary (original index-->new index - # in sorted list) to write translations in the original order. - sorted_inputs, sorted_keys = _get_sorted_inputs(input_file) - num_decode_batches = (len(sorted_inputs) - 1) // batch_size + 1 - - def input_generator(): - """Yield encoded strings from sorted_inputs.""" - for i, line in enumerate(sorted_inputs): - if i % batch_size == 0: - batch_num = (i // batch_size) + 1 - - tf.logging.info("Decoding batch %d out of %d." % - (batch_num, num_decode_batches)) - yield _encode_and_add_eos(line, subtokenizer) - - def input_fn(): - """Created batched dataset of encoded inputs.""" - ds = tf.data.Dataset.from_generator( - input_generator, tf.int64, tf.TensorShape([None])) - ds = ds.padded_batch(batch_size, [None]) - return ds - - translations = [] - for i, prediction in enumerate(estimator.predict(input_fn)): - translation = _trim_and_decode(prediction["outputs"], subtokenizer) - translations.append(translation) - - if print_all_translations: - tf.logging.info("Translating:\n\tInput: %s\n\tOutput: %s" % - (sorted_inputs[i], translation)) - - # Write translations in the order they appeared in the original file. - if output_file is not None: - if tf.gfile.IsDirectory(output_file): - raise ValueError("File output is a directory, will not save outputs to " - "file.") - tf.logging.info("Writing to file %s" % output_file) - with tf.gfile.Open(output_file, "w") as f: - for i in sorted_keys: - f.write("%s\n" % translations[i]) - - -def translate_text(estimator, subtokenizer, txt): - """Translate a single string.""" - encoded_txt = _encode_and_add_eos(txt, subtokenizer) - - def input_fn(): - ds = tf.data.Dataset.from_tensors(encoded_txt) - ds = ds.batch(_DECODE_BATCH_SIZE) - return ds - - predictions = estimator.predict(input_fn) - translation = next(predictions)["outputs"] - translation = _trim_and_decode(translation, subtokenizer) - tf.logging.info("Translation of \"%s\": \"%s\"" % (txt, translation)) - - -def main(unused_argv): - from official.transformer import transformer_main - - tf.logging.set_verbosity(tf.logging.INFO) - - if FLAGS.text is None and FLAGS.file is None: - tf.logging.warn("Nothing to translate. Make sure to call this script using " - "flags --text or --file.") - return - - subtokenizer = tokenizer.Subtokenizer(FLAGS.vocab_file) - - # Set up estimator and params - params = transformer_main.PARAMS_MAP[FLAGS.param_set] - params["beam_size"] = _BEAM_SIZE - params["alpha"] = _ALPHA - params["extra_decode_length"] = _EXTRA_DECODE_LENGTH - params["batch_size"] = _DECODE_BATCH_SIZE - estimator = tf.estimator.Estimator( - model_fn=transformer_main.model_fn, model_dir=FLAGS.model_dir, - params=params) - - if FLAGS.text is not None: - tf.logging.info("Translating text: %s" % FLAGS.text) - translate_text(estimator, subtokenizer, FLAGS.text) - - if FLAGS.file is not None: - input_file = os.path.abspath(FLAGS.file) - tf.logging.info("Translating file: %s" % input_file) - if not tf.gfile.Exists(FLAGS.file): - raise ValueError("File does not exist: %s" % input_file) - - output_file = None - if FLAGS.file_out is not None: - output_file = os.path.abspath(FLAGS.file_out) - tf.logging.info("File output specified: %s" % output_file) - - translate_file(estimator, subtokenizer, input_file, output_file) - - -def define_translate_flags(): - """Define flags used for translation script.""" - # Model flags - flags.DEFINE_string( - name="model_dir", short_name="md", default="/tmp/transformer_model", - help=flags_core.help_wrap( - "Directory containing Transformer model checkpoints.")) - flags.DEFINE_enum( - name="param_set", short_name="mp", default="big", - enum_values=["base", "big"], - help=flags_core.help_wrap( - "Parameter set to use when creating and training the model. The " - "parameters define the input shape (batch size and max length), " - "model configuration (size of embedding, # of hidden layers, etc.), " - "and various other settings. The big parameter set increases the " - "default batch size, embedding/hidden size, and filter size. For a " - "complete list of parameters, please see model/model_params.py.")) - flags.DEFINE_string( - name="vocab_file", short_name="vf", default=None, - help=flags_core.help_wrap( - "Path to subtoken vocabulary file. If data_download.py was used to " - "download and encode the training data, look in the data_dir to find " - "the vocab file.")) - flags.mark_flag_as_required("vocab_file") - - flags.DEFINE_string( - name="text", default=None, - help=flags_core.help_wrap( - "Text to translate. Output will be printed to console.")) - flags.DEFINE_string( - name="file", default=None, - help=flags_core.help_wrap( - "File containing text to translate. Translation will be printed to " - "console and, if --file_out is provided, saved to an output file.")) - flags.DEFINE_string( - name="file_out", default=None, - help=flags_core.help_wrap( - "If --file flag is specified, save translation to this file.")) - - -if __name__ == "__main__": - define_translate_flags() - FLAGS = flags.FLAGS - absl_app.run(main) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/__init__.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/dataset.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/dataset.py deleted file mode 100644 index fb37d4ca..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/dataset.py +++ /dev/null @@ -1,283 +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. -# ============================================================================== -"""Input pipeline for the transformer model to read, filter, and batch examples. - -Two things to note in the pipeline: - -1. Batching scheme - - The examples encoded in the TFRecord files contain data in the format: - {"inputs": [variable length array of integers], - "targets": [variable length array of integers]} - Where integers in the arrays refer to tokens in the English and German vocab - file (named `vocab.ende.32768`). - - Prior to batching, elements in the dataset are grouped by length (max between - "inputs" and "targets" length). Each group is then batched such that: - group_batch_size * length <= batch_size. - - Another way to view batch_size is the maximum number of tokens in each batch. - - Once batched, each element in the dataset will have the shape: - {"inputs": [group_batch_size, padded_input_length], - "targets": [group_batch_size, padded_target_length]} - Lengths are padded to the longest "inputs" or "targets" sequence in the batch - (padded_input_length and padded_target_length can be different). - - This batching scheme decreases the fraction of padding tokens per training - batch, thus improving the training speed significantly. - -2. Shuffling - - While training, the dataset is shuffled in two places in the code. The first - is the list of training files. Second, while reading records using - `parallel_interleave`, the `sloppy` argument is used to generate randomness - in the order of the examples. -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import math -import os - -import tensorflow as tf - -from official.utils.misc import model_helpers - -# Buffer size for reading records from a TFRecord file. Each training file is -# 7.2 MB, so 8 MB allows an entire file to be kept in memory. -_READ_RECORD_BUFFER = 8 * 1000 * 1000 - -# Example grouping constants. Defines length boundaries for each group. -# These values are the defaults used in Tensor2Tensor. -_MIN_BOUNDARY = 8 -_BOUNDARY_SCALE = 1.1 - - -def _load_records(filename): - """Read file and return a dataset of tf.Examples.""" - return tf.data.TFRecordDataset(filename, buffer_size=_READ_RECORD_BUFFER) - - -def _parse_example(serialized_example): - """Return inputs and targets Tensors from a serialized tf.Example.""" - data_fields = { - "inputs": tf.VarLenFeature(tf.int64), - "targets": tf.VarLenFeature(tf.int64) - } - parsed = tf.parse_single_example(serialized_example, data_fields) - inputs = tf.sparse_tensor_to_dense(parsed["inputs"]) - targets = tf.sparse_tensor_to_dense(parsed["targets"]) - return inputs, targets - - -def _filter_max_length(example, max_length=256): - """Indicates whether the example's length is lower than the maximum length.""" - return tf.logical_and(tf.size(example[0]) <= max_length, - tf.size(example[1]) <= max_length) - - -def _get_example_length(example): - """Returns the maximum length between the example inputs and targets.""" - length = tf.maximum(tf.shape(example[0])[0], tf.shape(example[1])[0]) - return length - - -def _create_min_max_boundaries( - max_length, min_boundary=_MIN_BOUNDARY, boundary_scale=_BOUNDARY_SCALE): - """Create min and max boundary lists up to max_length. - - For example, when max_length=24, min_boundary=4 and boundary_scale=2, the - returned values will be: - buckets_min = [0, 4, 8, 16, 24] - buckets_max = [4, 8, 16, 24, 25] - - Args: - max_length: The maximum length of example in dataset. - min_boundary: Minimum length in boundary. - boundary_scale: Amount to scale consecutive boundaries in the list. - - Returns: - min and max boundary lists - - """ - # Create bucket boundaries list by scaling the previous boundary or adding 1 - # (to ensure increasing boundary sizes). - bucket_boundaries = [] - x = min_boundary - while x < max_length: - bucket_boundaries.append(x) - x = max(x + 1, int(x * boundary_scale)) - - # Create min and max boundary lists from the initial list. - buckets_min = [0] + bucket_boundaries - buckets_max = bucket_boundaries + [max_length + 1] - return buckets_min, buckets_max - - -def _batch_examples(dataset, batch_size, max_length): - """Group examples by similar lengths, and return batched dataset. - - Each batch of similar-length examples are padded to the same length, and may - have different number of elements in each batch, such that: - group_batch_size * padded_length <= batch_size. - - This decreases the number of padding tokens per batch, which improves the - training speed. - - Args: - dataset: Dataset of unbatched examples. - batch_size: Max number of tokens per batch of examples. - max_length: Max number of tokens in an example input or target sequence. - - Returns: - Dataset of batched examples with similar lengths. - """ - # Get min and max boundary lists for each example. These are used to calculate - # the `bucket_id`, which is the index at which: - # buckets_min[bucket_id] <= len(example) < buckets_max[bucket_id] - # Note that using both min and max lists improves the performance. - buckets_min, buckets_max = _create_min_max_boundaries(max_length) - - # Create list of batch sizes for each bucket_id, so that - # bucket_batch_size[bucket_id] * buckets_max[bucket_id] <= batch_size - bucket_batch_sizes = [batch_size // x for x in buckets_max] - # bucket_id will be a tensor, so convert this list to a tensor as well. - bucket_batch_sizes = tf.constant(bucket_batch_sizes, dtype=tf.int64) - - def example_to_bucket_id(example_input, example_target): - """Return int64 bucket id for this example, calculated based on length.""" - seq_length = _get_example_length((example_input, example_target)) - - # TODO: investigate whether removing code branching improves performance. - conditions_c = tf.logical_and( - tf.less_equal(buckets_min, seq_length), - tf.less(seq_length, buckets_max)) - bucket_id = tf.reduce_min(tf.where(conditions_c)) - return bucket_id - - def window_size_fn(bucket_id): - """Return number of examples to be grouped when given a bucket id.""" - return bucket_batch_sizes[bucket_id] - - def batching_fn(bucket_id, grouped_dataset): - """Batch and add padding to a dataset of elements with similar lengths.""" - bucket_batch_size = window_size_fn(bucket_id) - - # Batch the dataset and add padding so that all input sequences in the - # examples have the same length, and all target sequences have the same - # lengths as well. Resulting lengths of inputs and targets can differ. - return grouped_dataset.padded_batch(bucket_batch_size, ([None], [None])) - - return dataset.apply(tf.contrib.data.group_by_window( - key_func=example_to_bucket_id, - reduce_func=batching_fn, - window_size=None, - window_size_func=window_size_fn)) - - -def _read_and_batch_from_files( - file_pattern, batch_size, max_length, num_parallel_calls, shuffle, repeat, - static_batch=False): - """Create dataset where each item is a dict of "inputs" and "targets". - - Args: - file_pattern: String used to match the input TFRecord files. - batch_size: Maximum number of tokens per batch of examples - max_length: Maximum number of tokens per example - num_parallel_calls: Number of cpu cores for parallel input processing. - shuffle: If true, randomizes order of elements. - repeat: Number of times to repeat the dataset. If None, the dataset is - repeated forever. - static_batch: Whether the batches in the dataset should have static shapes. - If True, the input is batched so that every batch has the - shape [batch_size // max_length, max_length]. If False, the input is - grouped by length, and batched so that batches may have different - shapes [N, M], where: - N * M <= batch_size - M <= max_length - In general, this setting should be False. Dynamic shapes allow the inputs - to be grouped so that the number of padding tokens is minimized, and helps - model training. In cases where the input shape must be static - (e.g. running on TPU), this setting should be set to True. - - Returns: - tf.data.Dataset object containing examples loaded from the files. - """ - dataset = tf.data.Dataset.list_files(file_pattern, shuffle=shuffle) - - # Read files and interleave results. When training, the order of the examples - # will be non-deterministic. - dataset = dataset.apply( - tf.contrib.data.parallel_interleave( - _load_records, sloppy=shuffle, cycle_length=num_parallel_calls)) - - # Parse each tf.Example into a dictionary - # TODO: Look into prefetch_input_elements for performance optimization. - dataset = dataset.map(_parse_example, - num_parallel_calls=num_parallel_calls) - - # Remove examples where the input or target length exceeds the maximum length, - dataset = dataset.filter(lambda x, y: _filter_max_length((x, y), max_length)) - - if static_batch: - dataset = dataset.apply(tf.contrib.data.padded_batch_and_drop_remainder( - batch_size // max_length, ([max_length], [max_length]))) - else: - # Group and batch such that each batch has examples of similar length. - dataset = _batch_examples(dataset, batch_size, max_length) - - dataset = dataset.repeat(repeat) - - # Prefetch the next element to improve speed of input pipeline. - dataset = dataset.prefetch(buffer_size=tf.contrib.data.AUTOTUNE) - return dataset - - -def _generate_synthetic_data(params): - """Create synthetic data based on the parameter batch size.""" - batch = length = int(math.sqrt(params["batch_size"])) - return model_helpers.generate_synthetic_data( - input_shape=tf.TensorShape([batch, length]), - input_value=1, - input_dtype=tf.int32, - label_shape=tf.TensorShape([batch, length]), - label_value=1, - label_dtype=tf.int32, - ) - - -def train_input_fn(params): - """Load and return dataset of batched examples for use during training.""" - file_pattern = os.path.join(params["data_dir"] or "", "*train*") - if params["use_synthetic_data"]: - return _generate_synthetic_data(params) - return _read_and_batch_from_files( - file_pattern, params["batch_size"], params["max_length"], - params["num_parallel_calls"], shuffle=True, - repeat=params["repeat_dataset"], static_batch=params["static_batch"]) - - -def eval_input_fn(params): - """Load and return dataset of batched examples for use during evaluation.""" - file_pattern = os.path.join(params["data_dir"] or "", "*dev*") - if params["use_synthetic_data"]: - return _generate_synthetic_data(params) - return _read_and_batch_from_files( - file_pattern, params["batch_size"], params["max_length"], - params["num_parallel_calls"], shuffle=False, repeat=1, - static_batch=params["static_batch"]) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/metrics.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/metrics.py deleted file mode 100644 index 3e41f985..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/metrics.py +++ /dev/null @@ -1,490 +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. -# ============================================================================== -"""Functions for calculating loss, accuracy, and other model metrics. - -Metrics: - - Padded loss, accuracy, and negative log perplexity. Source: - https://github.com/tensorflow/tensor2tensor/blob/master/tensor2tensor/utils/metrics.py - - BLEU approximation. Source: - https://github.com/tensorflow/tensor2tensor/blob/master/tensor2tensor/utils/bleu_hook.py - - ROUGE score. Source: - https://github.com/tensorflow/tensor2tensor/blob/master/tensor2tensor/utils/rouge.py -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import collections -import math - -import numpy as np -import six -from six.moves import xrange # pylint: disable=redefined-builtin -import tensorflow as tf - - -def _pad_tensors_to_same_length(x, y): - """Pad x and y so that the results have the same length (second dimension).""" - with tf.name_scope("pad_to_same_length"): - x_length = tf.shape(x)[1] - y_length = tf.shape(y)[1] - - max_length = tf.maximum(x_length, y_length) - - x = tf.pad(x, [[0, 0], [0, max_length - x_length], [0, 0]]) - y = tf.pad(y, [[0, 0], [0, max_length - y_length]]) - return x, y - - -def padded_cross_entropy_loss(logits, labels, smoothing, vocab_size): - """Calculate cross entropy loss while ignoring padding. - - Args: - logits: Tensor of size [batch_size, length_logits, vocab_size] - labels: Tensor of size [batch_size, length_labels] - smoothing: Label smoothing constant, used to determine the on and off values - vocab_size: int size of the vocabulary - Returns: - Returns the cross entropy loss and weight tensors: float32 tensors with - shape [batch_size, max(length_logits, length_labels)] - """ - with tf.name_scope("loss", values=[logits, labels]): - logits, labels = _pad_tensors_to_same_length(logits, labels) - - # Calculate smoothing cross entropy - with tf.name_scope("smoothing_cross_entropy", values=[logits, labels]): - confidence = 1.0 - smoothing - low_confidence = (1.0 - confidence) / tf.to_float(vocab_size - 1) - soft_targets = tf.one_hot( - tf.cast(labels, tf.int32), - depth=vocab_size, - on_value=confidence, - off_value=low_confidence) - xentropy = tf.nn.softmax_cross_entropy_with_logits_v2( - logits=logits, labels=soft_targets) - - # Calculate the best (lowest) possible value of cross entropy, and - # subtract from the cross entropy loss. - normalizing_constant = -( - confidence * tf.log(confidence) + tf.to_float(vocab_size - 1) * - low_confidence * tf.log(low_confidence + 1e-20)) - xentropy -= normalizing_constant - - weights = tf.to_float(tf.not_equal(labels, 0)) - return xentropy * weights, weights - - -def _convert_to_eval_metric(metric_fn): - """Wrap a metric fn that returns scores and weights as an eval metric fn. - - The input metric_fn returns values for the current batch. The wrapper - aggregates the return values collected over all of the batches evaluated. - - Args: - metric_fn: function that returns scores and weights for the current batch's - logits and predicted labels. - - Returns: - function that aggregates the scores and weights from metric_fn. - """ - def problem_metric_fn(*args): - """Returns an aggregation of the metric_fn's returned values.""" - (scores, weights) = metric_fn(*args) - - # The tf.metrics.mean function assures correct aggregation. - return tf.metrics.mean(scores, weights) - return problem_metric_fn - - -def get_eval_metrics(logits, labels, params): - """Return dictionary of model evaluation metrics.""" - metrics = { - "accuracy": _convert_to_eval_metric(padded_accuracy)(logits, labels), - "accuracy_top5": _convert_to_eval_metric(padded_accuracy_top5)( - logits, labels), - "accuracy_per_sequence": _convert_to_eval_metric( - padded_sequence_accuracy)(logits, labels), - "neg_log_perplexity": _convert_to_eval_metric(padded_neg_log_perplexity)( - logits, labels, params["vocab_size"]), - } - - if not params["use_tpu"]: - # TPU does not support tf.py_func - metrics.update({ - "approx_bleu_score": _convert_to_eval_metric( - bleu_score)(logits, labels), - "rouge_2_fscore": _convert_to_eval_metric( - rouge_2_fscore)(logits, labels), - "rouge_L_fscore": _convert_to_eval_metric( - rouge_l_fscore)(logits, labels), - }) - - # Prefix each of the metric names with "metrics/". This allows the metric - # graphs to display under the "metrics" category in TensorBoard. - metrics = {"metrics/%s" % k: v for k, v in six.iteritems(metrics)} - return metrics - - -def padded_accuracy(logits, labels): - """Percentage of times that predictions matches labels on non-0s.""" - with tf.variable_scope("padded_accuracy", values=[logits, labels]): - logits, labels = _pad_tensors_to_same_length(logits, labels) - weights = tf.to_float(tf.not_equal(labels, 0)) - outputs = tf.to_int32(tf.argmax(logits, axis=-1)) - padded_labels = tf.to_int32(labels) - return tf.to_float(tf.equal(outputs, padded_labels)), weights - - -def padded_accuracy_topk(logits, labels, k): - """Percentage of times that top-k predictions matches labels on non-0s.""" - with tf.variable_scope("padded_accuracy_topk", values=[logits, labels]): - logits, labels = _pad_tensors_to_same_length(logits, labels) - weights = tf.to_float(tf.not_equal(labels, 0)) - effective_k = tf.minimum(k, tf.shape(logits)[-1]) - _, outputs = tf.nn.top_k(logits, k=effective_k) - outputs = tf.to_int32(outputs) - padded_labels = tf.to_int32(labels) - padded_labels = tf.expand_dims(padded_labels, axis=-1) - padded_labels += tf.zeros_like(outputs) # Pad to same shape. - same = tf.to_float(tf.equal(outputs, padded_labels)) - same_topk = tf.reduce_sum(same, axis=-1) - return same_topk, weights - - -def padded_accuracy_top5(logits, labels): - return padded_accuracy_topk(logits, labels, 5) - - -def padded_sequence_accuracy(logits, labels): - """Percentage of times that predictions matches labels everywhere (non-0).""" - with tf.variable_scope("padded_sequence_accuracy", values=[logits, labels]): - logits, labels = _pad_tensors_to_same_length(logits, labels) - weights = tf.to_float(tf.not_equal(labels, 0)) - outputs = tf.to_int32(tf.argmax(logits, axis=-1)) - padded_labels = tf.to_int32(labels) - not_correct = tf.to_float(tf.not_equal(outputs, padded_labels)) * weights - axis = list(range(1, len(outputs.get_shape()))) - correct_seq = 1.0 - tf.minimum(1.0, tf.reduce_sum(not_correct, axis=axis)) - return correct_seq, tf.constant(1.0) - - -def padded_neg_log_perplexity(logits, labels, vocab_size): - """Average log-perplexity excluding padding 0s. No smoothing.""" - num, den = padded_cross_entropy_loss(logits, labels, 0, vocab_size) - return -num, den - - -def bleu_score(logits, labels): - """Approximate 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: - logits: Tensor of size [batch_size, length_logits, vocab_size] - labels: Tensor of size [batch-size, length_labels] - - Returns: - bleu: int, approx bleu score - """ - predictions = tf.to_int32(tf.argmax(logits, axis=-1)) - # TODO: Look into removing use of py_func - bleu = tf.py_func(compute_bleu, (labels, predictions), tf.float32) - return bleu, tf.constant(1.0) - - -def _get_ngrams_with_counter(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 upto max_order in segment - with a count of how many times each n-gram occurred. - """ - ngram_counts = collections.Counter() - for order in xrange(1, max_order + 1): - for i in xrange(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_with_counter(references, max_order) - translation_ngram_counts = _get_ngrams_with_counter(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 xrange(0, max_order): - if possible_matches_by_order[i] > 0: - precisions[i] = float(matches_by_order[i]) / possible_matches_by_order[i] - if matches_by_order[i] > 0: - precisions[i] = float(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: - ratio = translation_length / reference_length - bp = math.exp(1 - 1. / ratio) if ratio < 1.0 else 1.0 - bleu = geo_mean * bp - return np.float32(bleu) - - -def rouge_2_fscore(logits, labels): - """ROUGE-2 F1 score computation between labels and predictions. - - This is an approximate ROUGE scoring method since we do not glue word pieces - or decode the ids and tokenize the output. - - Args: - logits: tensor, model predictions - labels: tensor, gold output. - - Returns: - rouge2_fscore: approx rouge-2 f1 score. - """ - predictions = tf.to_int32(tf.argmax(logits, axis=-1)) - # TODO: Look into removing use of py_func - rouge_2_f_score = tf.py_func(rouge_n, (predictions, labels), tf.float32) - return rouge_2_f_score, tf.constant(1.0) - - -def _get_ngrams(n, text): - """Calculates n-grams. - - Args: - n: which n-grams to calculate - text: An array of tokens - - Returns: - A set of n-grams - """ - ngram_set = set() - text_length = len(text) - max_index_ngram_start = text_length - n - for i in range(max_index_ngram_start + 1): - ngram_set.add(tuple(text[i:i + n])) - return ngram_set - - -def rouge_n(eval_sentences, ref_sentences, n=2): - """Computes ROUGE-N f1 score of two text collections of sentences. - - Source: https://www.microsoft.com/en-us/research/publication/ - rouge-a-package-for-automatic-evaluation-of-summaries/ - - Args: - eval_sentences: Predicted sentences. - ref_sentences: Sentences from the reference set - n: Size of ngram. Defaults to 2. - - Returns: - f1 score for ROUGE-N - """ - f1_scores = [] - for eval_sentence, ref_sentence in zip(eval_sentences, ref_sentences): - eval_ngrams = _get_ngrams(n, eval_sentence) - ref_ngrams = _get_ngrams(n, ref_sentence) - ref_count = len(ref_ngrams) - eval_count = len(eval_ngrams) - - # Count the overlapping ngrams between evaluated and reference - overlapping_ngrams = eval_ngrams.intersection(ref_ngrams) - overlapping_count = len(overlapping_ngrams) - - # Handle edge case. This isn't mathematically correct, but it's good enough - if eval_count == 0: - precision = 0.0 - else: - precision = float(overlapping_count) / eval_count - if ref_count == 0: - recall = 0.0 - else: - recall = float(overlapping_count) / ref_count - f1_scores.append(2.0 * ((precision * recall) / (precision + recall + 1e-8))) - - # return overlapping_count / reference_count - return np.mean(f1_scores, dtype=np.float32) - - -def rouge_l_fscore(predictions, labels): - """ROUGE scores computation between labels and predictions. - - This is an approximate ROUGE scoring method since we do not glue word pieces - or decode the ids and tokenize the output. - - Args: - predictions: tensor, model predictions - labels: tensor, gold output. - - Returns: - rouge_l_fscore: approx rouge-l f1 score. - """ - outputs = tf.to_int32(tf.argmax(predictions, axis=-1)) - rouge_l_f_score = tf.py_func(rouge_l_sentence_level, (outputs, labels), - tf.float32) - return rouge_l_f_score, tf.constant(1.0) - - -def rouge_l_sentence_level(eval_sentences, ref_sentences): - """Computes ROUGE-L (sentence level) of two collections of sentences. - - Source: https://www.microsoft.com/en-us/research/publication/ - rouge-a-package-for-automatic-evaluation-of-summaries/ - - Calculated according to: - R_lcs = LCS(X,Y)/m - P_lcs = LCS(X,Y)/n - F_lcs = ((1 + beta^2)*R_lcs*P_lcs) / (R_lcs + (beta^2) * P_lcs) - - where: - X = reference summary - Y = Candidate summary - m = length of reference summary - n = length of candidate summary - - Args: - eval_sentences: The sentences that have been picked by the summarizer - ref_sentences: The sentences from the reference set - - Returns: - A float: F_lcs - """ - - f1_scores = [] - for eval_sentence, ref_sentence in zip(eval_sentences, ref_sentences): - m = float(len(ref_sentence)) - n = float(len(eval_sentence)) - lcs = _len_lcs(eval_sentence, ref_sentence) - f1_scores.append(_f_lcs(lcs, m, n)) - return np.mean(f1_scores, dtype=np.float32) - - -def _len_lcs(x, y): - """Returns the length of the Longest Common Subsequence between two seqs. - - Source: http://www.algorithmist.com/index.php/Longest_Common_Subsequence - - Args: - x: sequence of words - y: sequence of words - - Returns - integer: Length of LCS between x and y - """ - table = _lcs(x, y) - n, m = len(x), len(y) - return table[n, m] - - -def _lcs(x, y): - """Computes the length of the LCS between two seqs. - - The implementation below uses a DP programming algorithm and runs - in O(nm) time where n = len(x) and m = len(y). - Source: http://www.algorithmist.com/index.php/Longest_Common_Subsequence - - Args: - x: collection of words - y: collection of words - - Returns: - Table of dictionary of coord and len lcs - """ - n, m = len(x), len(y) - table = dict() - for i in range(n + 1): - for j in range(m + 1): - if i == 0 or j == 0: - table[i, j] = 0 - elif x[i - 1] == y[j - 1]: - table[i, j] = table[i - 1, j - 1] + 1 - else: - table[i, j] = max(table[i - 1, j], table[i, j - 1]) - return table - - -def _f_lcs(llcs, m, n): - """Computes the LCS-based F-measure score. - - Source: http://research.microsoft.com/en-us/um/people/cyl/download/papers/ - rouge-working-note-v1.3.1.pdf - - Args: - llcs: Length of LCS - m: number of words in reference summary - n: number of words in candidate summary - - Returns: - Float. LCS-based F-measure score - """ - r_lcs = llcs / m - p_lcs = llcs / n - beta = p_lcs / (r_lcs + 1e-12) - num = (1 + (beta ** 2)) * r_lcs * p_lcs - denom = r_lcs + ((beta ** 2) * p_lcs) - f_lcs = num / (denom + 1e-12) - return f_lcs diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/schedule.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/schedule.py deleted file mode 100644 index 11294177..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/schedule.py +++ /dev/null @@ -1,130 +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. -# ============================================================================== -"""Abstract training on a step or epoch basis.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import math - -import tensorflow as tf - - -_TRAIN, _EVAL = tf.estimator.ModeKeys.TRAIN, tf.estimator.ModeKeys.EVAL - - -NUM_EXAMPLES = { - tf.estimator.ModeKeys.TRAIN: 4572160, - # # Examples that are too long are filtered out, thus the total is less - # # than the total number of lines. - # 2399123 + # news-commentary-v12.de-en - # 1920209 + # commoncrawl.de-en - # 270769, # europarl-v7.de-en - tf.estimator.ModeKeys.EVAL: 3000, # newstest2013 -} - - -class Manager(object): - """Container for convenience functions to abstract step or epoch basis. - Transformer allows users to specify an epoch basis (generally recommended for - full training) or a number of steps basis (convenient since epochs are rather - large). TPUs furthermore require a step basis; however epochs are the norm in - the machine learning community and it is desirable to allow users to specify - epochs even when running with TPUS which requires behind the scenes - conversions. - This container simply groups what are largely mundane checks and conversions - rather than interspersing them throughout the run loop code. - """ - - def __init__(self, train_steps, steps_between_evals, train_epochs, - epochs_between_evals, default_train_epochs, batch_size, - max_length, use_tpu=False, num_tpu_shards=8): - if train_steps and train_epochs: - raise ValueError("Both train_steps or train_epochs were be defined.") - - # Determine training schedule based on flags. - if train_steps: - self.train_eval_iterations = train_steps // steps_between_evals - self._single_iteration_train_steps = steps_between_evals - self._single_iteration_train_epochs = None - else: - train_epochs = train_epochs or default_train_epochs - self.train_eval_iterations = train_epochs // epochs_between_evals - self._single_iteration_train_steps = None - self._single_iteration_train_epochs = epochs_between_evals - - self.max_length = max_length - self.batch_size = batch_size - self.use_tpu = use_tpu - self.num_tpu_shards = num_tpu_shards - - if self.use_tpu: - assert (self.batch_size // self.max_length) % self.num_tpu_shards == 0 - - @property - def single_iteration_train_steps(self): - if self._single_iteration_train_steps or not self.use_tpu: - return self._single_iteration_train_steps - - return self.epochs_to_steps( - num_epochs=self._single_iteration_train_epochs, mode=_TRAIN) - - @property - def single_iteration_eval_steps(self): - if not self.use_tpu: - return None - - return self.epochs_to_steps(num_epochs=1, mode=_EVAL) - - @property - def train_increment_str(self): - if self._single_iteration_train_steps: - return "{} steps.".format(self._single_iteration_train_steps) - - if not self.use_tpu: - return "{} epochs.".format(self._single_iteration_train_epochs) - - return "~{} epochs. ({} steps)".format( - self._single_iteration_train_epochs, - self.single_iteration_train_steps) - - @property - def repeat_dataset(self): - if (self._single_iteration_train_epochs is None and - self._single_iteration_train_steps > NUM_EXAMPLES[_TRAIN]): - return math.ceil(self._single_iteration_train_steps / - NUM_EXAMPLES[_TRAIN]) - return self._single_iteration_train_epochs - - def epochs_to_steps(self, num_epochs, mode): - """Converts a number of epochs to a number of training steps. - - TPU only: This function assumes that static_batch is True. - - TPU can not tolerate an OutOfRange error from a dataset. As a result the - number of examples to be processed must be known ahead of time. TPUs also - do not allow partial batches, so this function rounds down. - - Args: - num_epochs: An integer of the number of epochs to convert to steps. - mode: The estimator ModeKey of the computation - - Returns: - An integer of the number of equivalent steps rounded down. - """ - assert self.use_tpu, "epochs_to_steps should only be reached when using TPU" - total_num_tokens = NUM_EXAMPLES[mode] * self.max_length * num_epochs - return total_num_tokens // self.batch_size diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/schedule_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/schedule_test.py deleted file mode 100644 index bb6c8572..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/schedule_test.py +++ /dev/null @@ -1,84 +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. -# ============================================================================== -"""Test Transformer's schedule manager.""" - -import tensorflow as tf - -from official.transformer.utils import schedule - - -class ScheduleBaseTester(tf.test.TestCase): - def test_mutual_exclusivity(self): - with self.assertRaises(ValueError): - schedule.Manager( - train_steps=100, steps_between_evals=100, train_epochs=2, - epochs_between_evals=1, default_train_epochs=None, batch_size=2048, - max_length=256) - - def test_step_basis(self): - manager = schedule.Manager( - train_steps=1000, steps_between_evals=100, train_epochs=None, - epochs_between_evals=None, default_train_epochs=None, batch_size=2048, - max_length=256) - - self.assertEqual(manager.single_iteration_train_steps, 100) - - # Evaluation uses the full set - self.assertIsNone(manager.single_iteration_eval_steps) - - self.assertIsNone(manager.repeat_dataset) - - def test_epoch_basis(self): - manager = schedule.Manager( - train_steps=None, steps_between_evals=None, train_epochs=10, - epochs_between_evals=2, default_train_epochs=None, batch_size=2048, - max_length=256) - - # For non-TPU, estimator relies on dataset exhausion - self.assertIsNone(manager.single_iteration_train_steps) - self.assertIsNone(manager.single_iteration_eval_steps) - - self.assertEqual(manager.repeat_dataset, 2) - - def test_step_basis_tpu(self): - manager = schedule.Manager( - train_steps=1000, steps_between_evals=100, train_epochs=None, - epochs_between_evals=None, default_train_epochs=None, batch_size=2048, - max_length=256, use_tpu=True) - - self.assertEqual(manager.single_iteration_train_steps, 100) - # num_eval_examples / (batch_size / max_length) == 3000 / (2048 / 256) - self.assertEqual(manager.single_iteration_eval_steps, 375) - self.assertIsNone(manager.repeat_dataset) - - def test_epoch_basis_tpu(self): - manager = schedule.Manager( - train_steps=None, steps_between_evals=None, train_epochs=10, - epochs_between_evals=2, default_train_epochs=None, batch_size=2048, - max_length=256, use_tpu=True) - - self.assertEqual( - manager.single_iteration_train_steps, - schedule.NUM_EXAMPLES[tf.estimator.ModeKeys.TRAIN] * 2 // (2048 / 256) - ) - - # num_eval_examples / (batch_size / max_length) == 3000 / (2048 / 256) - self.assertEqual(manager.single_iteration_eval_steps, 375) - - self.assertEqual(manager.repeat_dataset, 2) - - -if __name__ == "__main__": - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/tokenizer.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/tokenizer.py deleted file mode 100644 index cab3b0d4..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/tokenizer.py +++ /dev/null @@ -1,611 +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. -# ============================================================================== -"""Defines Subtokenizer class to encode and decode strings.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import collections -import re -import sys -import unicodedata - -import numpy as np -import six -from six.moves import xrange # pylint: disable=redefined-builtin -import tensorflow as tf - -PAD = "" -PAD_ID = 0 -EOS = "" -EOS_ID = 1 -RESERVED_TOKENS = [PAD, EOS] - -# Set of characters that will be used in the function _escape_token() (see func -# docstring for more details). -# This set is added to the alphabet list to ensure that all escaped tokens can -# be encoded. -_ESCAPE_CHARS = set(u"\\_u;0123456789") -# Regex for the function _unescape_token(), the inverse of _escape_token(). -# This is used to find "\u", "\\", and "\###;" substrings in the token. -_UNESCAPE_REGEX = re.compile(r"\\u|\\\\|\\([0-9]+);") - -_UNDEFINED_UNICODE = u"\u3013" - -# Set contains all letter and number characters. -_ALPHANUMERIC_CHAR_SET = set( - six.unichr(i) for i in xrange(sys.maxunicode) - if (unicodedata.category(six.unichr(i)).startswith("L") or - unicodedata.category(six.unichr(i)).startswith("N"))) - -# min_count is the minimum number of times a subtoken must appear in the data -# before before it is added to the vocabulary. The value is found using binary -# search to obtain the target vocabulary size. -_MIN_MIN_COUNT = 1 # min value to use when binary searching for min_count -_MAX_MIN_COUNT = 1000 # max value to use when binary searching for min_count - - -class Subtokenizer(object): - """Encodes and decodes strings to/from integer IDs.""" - - def __init__(self, vocab_file, reserved_tokens=None): - """Initializes class, creating a vocab file if data_files is provided.""" - tf.logging.info("Initializing Subtokenizer from file %s." % vocab_file) - - if reserved_tokens is None: - reserved_tokens = RESERVED_TOKENS - - self.subtoken_list = _load_vocab_file(vocab_file, reserved_tokens) - self.alphabet = _generate_alphabet_dict(self.subtoken_list) - self.subtoken_to_id_dict = _list_to_index_dict(self.subtoken_list) - - self.max_subtoken_length = 0 - for subtoken in self.subtoken_list: - self.max_subtoken_length = max(self.max_subtoken_length, len(subtoken)) - - # Create cache to speed up subtokenization - self._cache_size = 2 ** 20 - self._cache = [(None, None)] * self._cache_size - - @staticmethod - def init_from_files( - vocab_file, files, target_vocab_size, threshold, min_count=None, - file_byte_limit=1e6, reserved_tokens=None): - """Create subtoken vocabulary based on files, and save vocab to file. - - Args: - vocab_file: String name of vocab file to store subtoken vocabulary. - files: List of file paths that will be used to generate vocabulary. - target_vocab_size: target vocabulary size to generate. - threshold: int threshold of vocabulary size to accept. - min_count: int minimum count to use for generating the vocabulary. The min - count is the minimum number of times a subtoken should appear in the - files before it is added to the vocabulary. If set to none, this value - is found using binary search. - file_byte_limit: (Default 1e6) Maximum number of bytes of sample text that - will be drawn from the files. - reserved_tokens: List of string tokens that are guaranteed to be at the - beginning of the subtoken vocabulary list. - - Returns: - Subtokenizer object - """ - if reserved_tokens is None: - reserved_tokens = RESERVED_TOKENS - - if tf.gfile.Exists(vocab_file): - tf.logging.info("Vocab file already exists (%s)" % vocab_file) - else: - tf.logging.info("Begin steps to create subtoken vocabulary...") - token_counts = _count_tokens(files, file_byte_limit) - alphabet = _generate_alphabet_dict(token_counts) - subtoken_list = _generate_subtokens_with_target_vocab_size( - token_counts, alphabet, target_vocab_size, threshold, min_count, - reserved_tokens) - tf.logging.info("Generated vocabulary with %d subtokens." % - len(subtoken_list)) - _save_vocab_file(vocab_file, subtoken_list) - return Subtokenizer(vocab_file) - - def encode(self, raw_string, add_eos=False): - """Encodes a string into a list of int subtoken ids.""" - ret = [] - tokens = _split_string_to_tokens(_native_to_unicode(raw_string)) - for token in tokens: - ret.extend(self._token_to_subtoken_ids(token)) - if add_eos: - ret.append(EOS_ID) - return ret - - def _token_to_subtoken_ids(self, token): - """Encode a single token into a list of subtoken ids.""" - cache_location = hash(token) % self._cache_size - cache_key, cache_value = self._cache[cache_location] - if cache_key == token: - return cache_value - - ret = _split_token_to_subtokens( - _escape_token(token, self.alphabet), self.subtoken_to_id_dict, - self.max_subtoken_length) - ret = [self.subtoken_to_id_dict[subtoken_id] for subtoken_id in ret] - - self._cache[cache_location] = (token, ret) - return ret - - def decode(self, subtokens): - """Converts list of int subtokens ids into a string.""" - if isinstance(subtokens, np.ndarray): - # Note that list(subtokens) converts subtokens to a python list, but the - # items remain as np.int32. This converts both the array and its items. - subtokens = subtokens.tolist() - - if not subtokens: - return "" - - assert isinstance(subtokens, list) and isinstance(subtokens[0], int), ( - "Subtokens argument passed into decode() must be a list of integers.") - - return _unicode_to_native( - _join_tokens_to_string(self._subtoken_ids_to_tokens(subtokens))) - - def _subtoken_ids_to_tokens(self, subtokens): - """Convert list of int subtoken ids to a list of string tokens.""" - escaped_tokens = "".join([ - self.subtoken_list[s] for s in subtokens - if s < len(self.subtoken_list)]) - escaped_tokens = escaped_tokens.split("_") - - # All tokens in the vocabulary list have been escaped (see _escape_token()) - # so each token must be unescaped when decoding. - ret = [] - for token in escaped_tokens: - if token: - ret.append(_unescape_token(token)) - return ret - - -def _save_vocab_file(vocab_file, subtoken_list): - """Save subtokens to file.""" - with tf.gfile.Open(vocab_file, mode="w") as f: - for subtoken in subtoken_list: - f.write("'%s'\n" % _unicode_to_native(subtoken)) - - -def _load_vocab_file(vocab_file, reserved_tokens=None): - """Load vocabulary while ensuring reserved tokens are at the top.""" - if reserved_tokens is None: - reserved_tokens = RESERVED_TOKENS - - subtoken_list = [] - with tf.gfile.Open(vocab_file, mode="r") as f: - for line in f: - subtoken = _native_to_unicode(line.strip()) - subtoken = subtoken[1:-1] # Remove surrounding single-quotes - if subtoken in reserved_tokens: - continue - subtoken_list.append(_native_to_unicode(subtoken)) - return reserved_tokens + subtoken_list - - -def _native_to_unicode(s): - """Convert string to unicode (required in Python 2).""" - try: # Python 2 - return s if isinstance(s, unicode) else s.decode("utf-8") - except NameError: # Python 3 - return s - - -def _unicode_to_native(s): - """Convert string from unicode to native format (required in Python 2).""" - try: # Python 2 - return s.encode("utf-8") if isinstance(s, unicode) else s - except NameError: # Python 3 - return s - - -def _split_string_to_tokens(text): - """Splits text to a list of string tokens.""" - if not text: - return [] - ret = [] - token_start = 0 - # Classify each character in the input string - is_alnum = [c in _ALPHANUMERIC_CHAR_SET for c in text] - for pos in xrange(1, len(text)): - if is_alnum[pos] != is_alnum[pos - 1]: - token = text[token_start:pos] - if token != u" " or token_start == 0: - ret.append(token) - token_start = pos - final_token = text[token_start:] - ret.append(final_token) - return ret - - -def _join_tokens_to_string(tokens): - """Join a list of string tokens into a single string.""" - token_is_alnum = [t[0] in _ALPHANUMERIC_CHAR_SET for t in tokens] - ret = [] - for i, token in enumerate(tokens): - if i > 0 and token_is_alnum[i - 1] and token_is_alnum[i]: - ret.append(u" ") - ret.append(token) - return "".join(ret) - - -def _escape_token(token, alphabet): - r"""Replace characters that aren't in the alphabet and append "_" to token. - - Apply three transformations to the token: - 1. Replace underline character "_" with "\u", and backslash "\" with "\\". - 2. Replace characters outside of the alphabet with "\###;", where ### is the - character's Unicode code point. - 3. Appends "_" to mark the end of a token. - - Args: - token: unicode string to be escaped - alphabet: list of all known characters - - Returns: - escaped string - """ - token = token.replace(u"\\", u"\\\\").replace(u"_", u"\\u") - ret = [c if c in alphabet and c != u"\n" else r"\%d;" % ord(c) for c in token] - return u"".join(ret) + "_" - - -def _unescape_token(token): - r"""Replaces escaped characters in the token with their unescaped versions. - - Applies inverse transformations as _escape_token(): - 1. Replace "\u" with "_", and "\\" with "\". - 2. Replace "\###;" with the unicode character the ### refers to. - - Args: - token: escaped string - - Returns: - unescaped string - """ - - def match(m): - r"""Returns replacement string for matched object. - - Matched objects contain one of the strings that matches the regex pattern: - r"\\u|\\\\|\\([0-9]+);" - The strings can be '\u', '\\', or '\###;' (### is any digit number). - - m.group(0) refers to the entire matched string ('\u', '\\', or '\###;'). - m.group(1) refers to the first parenthesized subgroup ('###'). - - m.group(0) exists for all match objects, while m.group(1) exists only for - the string '\###;'. - - This function looks to see if m.group(1) exists. If it doesn't, then the - matched string must be '\u' or '\\' . In this case, the corresponding - replacement ('_' and '\') are returned. Note that in python, a single - backslash is written as '\\', and double backslash as '\\\\'. - - If m.goup(1) exists, then use the integer in m.group(1) to return a - unicode character. - - Args: - m: match object - - Returns: - String to replace matched object with. - """ - # Check if the matched strings are '\u' or '\\'. - if m.group(1) is None: - return u"_" if m.group(0) == u"\\u" else u"\\" - - # If m.group(1) exists, try and return unicode character. - try: - return six.unichr(int(m.group(1))) - except (ValueError, OverflowError) as _: - return _UNDEFINED_UNICODE - - # Use match function to replace escaped substrings in the token. - return _UNESCAPE_REGEX.sub(match, token) - - -def _count_tokens(files, file_byte_limit=1e6): - """Return token counts of words in the files. - - Samples file_byte_limit bytes from each file, and counts the words that appear - in the samples. The samples are semi-evenly distributed across the file. - - Args: - files: List of filepaths - file_byte_limit: Max number of bytes that will be read from each file. - - Returns: - Dictionary mapping tokens to the number of times they appear in the sampled - lines from the files. - """ - token_counts = collections.defaultdict(int) - - for filepath in files: - with tf.gfile.Open(filepath, mode="r") as reader: - file_byte_budget = file_byte_limit - counter = 0 - lines_to_skip = int(reader.size() / (file_byte_budget * 2)) - for line in reader: - if counter < lines_to_skip: - counter += 1 - else: - if file_byte_budget < 0: - break - line = line.strip() - file_byte_budget -= len(line) - counter = 0 - - # Add words to token counts - for token in _split_string_to_tokens(_native_to_unicode(line)): - token_counts[token] += 1 - return token_counts - - -def _list_to_index_dict(lst): - """Create dictionary mapping list items to their indices in the list.""" - return {item: n for n, item in enumerate(lst)} - - -def _split_token_to_subtokens(token, subtoken_dict, max_subtoken_length): - """Splits a token into subtokens defined in the subtoken dict.""" - ret = [] - start = 0 - token_len = len(token) - while start < token_len: - # Find the longest subtoken, so iterate backwards. - for end in xrange(min(token_len, start + max_subtoken_length), start, -1): - subtoken = token[start:end] - if subtoken in subtoken_dict: - ret.append(subtoken) - start = end - break - else: # Did not break - # If there is no possible encoding of the escaped token then one of the - # characters in the token is not in the alphabet. This should be - # impossible and would be indicative of a bug. - raise ValueError("Was unable to split token \"%s\" into subtokens." % - token) - return ret - - -def _generate_subtokens_with_target_vocab_size( - token_counts, alphabet, target_size, threshold, min_count=None, - reserved_tokens=None): - """Generate subtoken vocabulary close to the target size.""" - if reserved_tokens is None: - reserved_tokens = RESERVED_TOKENS - - if min_count is not None: - tf.logging.info("Using min_count=%d to generate vocab with target size %d" % - (min_count, target_size)) - return _generate_subtokens( - token_counts, alphabet, min_count, reserved_tokens=reserved_tokens) - - def bisect(min_val, max_val): - """Recursive function to binary search for subtoken vocabulary.""" - cur_count = (min_val + max_val) // 2 - tf.logging.info("Binary search: trying min_count=%d (%d %d)" % - (cur_count, min_val, max_val)) - subtoken_list = _generate_subtokens( - token_counts, alphabet, cur_count, reserved_tokens=reserved_tokens) - - val = len(subtoken_list) - tf.logging.info("Binary search: min_count=%d resulted in %d tokens" % - (cur_count, val)) - - within_threshold = abs(val - target_size) < threshold - if within_threshold or min_val >= max_val or cur_count < 2: - return subtoken_list - if val > target_size: - other_subtoken_list = bisect(cur_count + 1, max_val) - else: - other_subtoken_list = bisect(min_val, cur_count - 1) - - # Return vocabulary dictionary with the closest number of tokens. - other_val = len(other_subtoken_list) - if abs(other_val - target_size) < abs(val - target_size): - return other_subtoken_list - return subtoken_list - - tf.logging.info("Finding best min_count to get target size of %d" % - target_size) - return bisect(_MIN_MIN_COUNT, _MAX_MIN_COUNT) - - -def _generate_alphabet_dict(iterable, reserved_tokens=None): - """Create set of characters that appear in any element in the iterable.""" - if reserved_tokens is None: - reserved_tokens = RESERVED_TOKENS - alphabet = {c for token in iterable for c in token} - alphabet |= {c for token in reserved_tokens for c in token} - alphabet |= _ESCAPE_CHARS # Add escape characters to alphabet set. - return alphabet - - -def _count_and_gen_subtokens( - token_counts, alphabet, subtoken_dict, max_subtoken_length): - """Count number of times subtokens appear, and generate new subtokens. - - Args: - token_counts: dict mapping tokens to the number of times they appear in the - original files. - alphabet: list of allowed characters. Used to escape the tokens, which - guarantees that all tokens can be split into subtokens. - subtoken_dict: dict mapping subtokens to ids. - max_subtoken_length: maximum length of subtoken in subtoken_dict. - - Returns: - A defaultdict mapping subtokens to the number of times they appear in the - tokens. The dict may contain new subtokens. - """ - subtoken_counts = collections.defaultdict(int) - for token, count in six.iteritems(token_counts): - token = _escape_token(token, alphabet) - subtokens = _split_token_to_subtokens( - token, subtoken_dict, max_subtoken_length) - - # Generate new subtokens by taking substrings from token. - start = 0 - for subtoken in subtokens: - for end in xrange(start + 1, len(token) + 1): - new_subtoken = token[start:end] - subtoken_counts[new_subtoken] += count - start += len(subtoken) - - return subtoken_counts - - -def _filter_and_bucket_subtokens(subtoken_counts, min_count): - """Return a bucketed list of subtokens that are filtered by count. - - Args: - subtoken_counts: defaultdict mapping subtokens to their counts - min_count: int count used to filter subtokens - - Returns: - List of subtoken sets, where subtokens in set i have the same length=i. - """ - # Create list of buckets, where subtokens in bucket i have length i. - subtoken_buckets = [] - for subtoken, count in six.iteritems(subtoken_counts): - if count < min_count: # Filter out subtokens that don't appear enough - continue - while len(subtoken_buckets) <= len(subtoken): - subtoken_buckets.append(set()) - subtoken_buckets[len(subtoken)].add(subtoken) - return subtoken_buckets - - -def _gen_new_subtoken_list( - subtoken_counts, min_count, alphabet, reserved_tokens=None): - """Generate candidate subtokens ordered by count, and new max subtoken length. - - Add subtokens to the candiate list in order of length (longest subtokens - first). When a subtoken is added, the counts of each of its prefixes are - decreased. Prefixes that don't appear much outside the subtoken are not added - to the candidate list. - - For example: - subtoken being added to candidate list: 'translate' - subtoken_counts: {'translate':10, 't':40, 'tr':16, 'tra':12, ...} - min_count: 5 - - When 'translate' is added, subtoken_counts is updated to: - {'translate':0, 't':30, 'tr':6, 'tra': 2, ...} - - The subtoken 'tra' will not be added to the candidate list, because it appears - twice (less than min_count) outside of 'translate'. - - Args: - subtoken_counts: defaultdict mapping str subtokens to int counts - min_count: int minumum count requirement for subtokens - alphabet: set of characters. Each character is added to the subtoken list to - guarantee that all tokens can be encoded. - reserved_tokens: list of tokens that will be added to the beginning of the - returned subtoken list. - - Returns: - List of candidate subtokens in decreasing count order, and maximum subtoken - length - """ - if reserved_tokens is None: - reserved_tokens = RESERVED_TOKENS - - # Create a list of (count, subtoken) for each candidate subtoken. - subtoken_candidates = [] - - # Use bucketted list to iterate through subtokens in order of length. - # subtoken_buckets[i] = set(subtokens), where each subtoken has length i. - subtoken_buckets = _filter_and_bucket_subtokens(subtoken_counts, min_count) - max_subtoken_length = len(subtoken_buckets) - 1 - - # Go through the list in reverse order to consider longer subtokens first. - for subtoken_len in xrange(max_subtoken_length, 0, -1): - for subtoken in subtoken_buckets[subtoken_len]: - count = subtoken_counts[subtoken] - - # Possible if this subtoken is a prefix of another token. - if count < min_count: - continue - - # Ignore alphabet/reserved tokens, which will be added manually later. - if subtoken not in alphabet and subtoken not in reserved_tokens: - subtoken_candidates.append((count, subtoken)) - - # Decrement count of the subtoken's prefixes (if a longer subtoken is - # added, its prefixes lose priority to be added). - for end in xrange(1, subtoken_len): - subtoken_counts[subtoken[:end]] -= count - - # Add alphabet subtokens (guarantees that all strings are encodable). - subtoken_candidates.extend((subtoken_counts.get(a, 0), a) for a in alphabet) - - # Order subtoken candidates by decreasing count. - subtoken_list = [t for _, t in sorted(subtoken_candidates, reverse=True)] - - # Add reserved tokens to beginning of the list. - subtoken_list = reserved_tokens + subtoken_list - return subtoken_list, max_subtoken_length - - -def _generate_subtokens( - token_counts, alphabet, min_count, num_iterations=4, - reserved_tokens=None): - """Create a list of subtokens in decreasing order of frequency. - - Args: - token_counts: dict mapping str tokens -> int count - alphabet: set of characters - min_count: int minimum number of times a subtoken must appear before it is - added to the vocabulary. - num_iterations: int number of iterations to generate new tokens. - reserved_tokens: list of tokens that will be added to the beginning to the - returned subtoken list. - - Returns: - Sorted list of subtokens (most frequent first) - """ - if reserved_tokens is None: - reserved_tokens = RESERVED_TOKENS - - # Use alphabet set to create initial list of subtokens - subtoken_list = reserved_tokens + list(alphabet) - max_subtoken_length = 1 - - # On each iteration, segment all words using the subtokens defined in - # subtoken_dict, count how often the resulting subtokens appear, and update - # the dictionary with subtokens w/ high enough counts. - for i in xrange(num_iterations): - tf.logging.info("\tGenerating subtokens: iteration %d" % i) - # Generate new subtoken->id dictionary using the new subtoken list. - subtoken_dict = _list_to_index_dict(subtoken_list) - - # Create dict mapping subtoken->count, with additional subtokens created - # from substrings taken from the tokens. - subtoken_counts = _count_and_gen_subtokens( - token_counts, alphabet, subtoken_dict, max_subtoken_length) - - # Generate new list of subtokens sorted by subtoken count. - subtoken_list, max_subtoken_length = _gen_new_subtoken_list( - subtoken_counts, min_count, alphabet, reserved_tokens) - - tf.logging.info("\tVocab size: %d" % len(subtoken_list)) - return subtoken_list diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/tokenizer_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/tokenizer_test.py deleted file mode 100644 index 46ab16c6..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/transformer/utils/tokenizer_test.py +++ /dev/null @@ -1,182 +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. -# ============================================================================== -"""Test Subtokenizer and string helper methods.""" - -import collections -import tempfile - -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.transformer.utils import tokenizer - - -class SubtokenizerTest(tf.test.TestCase): - - def _init_subtokenizer(self, vocab_list): - temp_file = tempfile.NamedTemporaryFile(delete=False) - with tf.gfile.Open(temp_file.name, 'w') as w: - for subtoken in vocab_list: - w.write("'%s'" % subtoken) - w.write("\n") - return tokenizer.Subtokenizer(temp_file.name, reserved_tokens=[]) - - def test_encode(self): - vocab_list = ["123_", "test", "ing_"] - subtokenizer = self._init_subtokenizer(vocab_list) - s = "testing 123" - encoded_list = subtokenizer.encode(s) - self.assertEqual([1, 2, 0], encoded_list) - - def test_decode(self): - vocab_list = ["123_", "test", "ing_"] - subtokenizer = self._init_subtokenizer(vocab_list) - encoded_list = [1, 2, 0] # testing 123 - decoded_str = subtokenizer.decode(encoded_list) - self.assertEqual("testing 123", decoded_str) - - def test_subtoken_ids_to_tokens(self): - vocab_list = ["123_", "test", "ing_"] - subtokenizer = self._init_subtokenizer(vocab_list) - encoded_list = [1, 2, 0] # testing 123 - token_list = subtokenizer._subtoken_ids_to_tokens(encoded_list) - self.assertEqual([u"testing", u"123"], token_list) - - -class StringHelperTest(tf.test.TestCase): - - def test_split_string_to_tokens(self): - text = "test? testing 123." - - tokens = tokenizer._split_string_to_tokens(text) - self.assertEqual(["test", "? ", "testing", "123", "."], tokens) - - def test_join_tokens_to_string(self): - tokens = ["test", "? ", "testing", "123", "."] - - s = tokenizer._join_tokens_to_string(tokens) - self.assertEqual("test? testing 123.", s) - - def test_escape_token(self): - token = u"abc_\\4" - alphabet = set("abc_\\u;") - - escaped_token = tokenizer._escape_token(token, alphabet) - self.assertEqual("abc\\u\\\\\\52;_", escaped_token) - - def test_unescape_token(self): - escaped_token = u"Underline: \\u, Backslash: \\\\, Unicode: \\52;" - - unescaped_token = tokenizer._unescape_token(escaped_token) - self.assertEqual( - "Underline: _, Backslash: \\, Unicode: 4", unescaped_token) - - def test_list_to_index_dict(self): - lst = ["test", "strings"] - - d = tokenizer._list_to_index_dict(lst) - self.assertDictEqual({"test": 0, "strings": 1}, d) - - def test_split_token_to_subtokens(self): - token = "abc" - subtoken_dict = {"a": 0, "b": 1, "c": 2, "ab": 3} - max_subtoken_length = 2 - - subtokens = tokenizer._split_token_to_subtokens( - token, subtoken_dict, max_subtoken_length) - self.assertEqual(["ab", "c"], subtokens) - - def test_generate_alphabet_dict(self): - s = ["testing", "123"] - reserved_tokens = ["???"] - - alphabet = tokenizer._generate_alphabet_dict(s, reserved_tokens) - self.assertIn("?", alphabet) - self.assertIn("t", alphabet) - self.assertIn("e", alphabet) - self.assertIn("s", alphabet) - self.assertIn("i", alphabet) - self.assertIn("n", alphabet) - self.assertIn("g", alphabet) - self.assertIn("1", alphabet) - self.assertIn("2", alphabet) - self.assertIn("3", alphabet) - - def test_count_and_gen_subtokens(self): - token_counts = {"abc": 5} - alphabet = set("abc_") - subtoken_dict = {"a": 0, "b": 1, "c": 2, "_": 3} - max_subtoken_length = 2 - - subtoken_counts = tokenizer._count_and_gen_subtokens( - token_counts, alphabet, subtoken_dict, max_subtoken_length) - - self.assertIsInstance(subtoken_counts, collections.defaultdict) - self.assertDictEqual( - {"a": 5, "b": 5, "c": 5, "_": 5, "ab": 5, "bc": 5, "c_": 5, - "abc": 5, "bc_": 5, "abc_": 5}, subtoken_counts) - - def test_filter_and_bucket_subtokens(self): - subtoken_counts = collections.defaultdict( - int, {"a": 2, "b": 4, "c": 1, "ab": 6, "ac": 3, "abbc": 5}) - min_count = 3 - - subtoken_buckets = tokenizer._filter_and_bucket_subtokens( - subtoken_counts, min_count) - - self.assertEqual(len(subtoken_buckets[0]), 0) - self.assertEqual(set("b"), subtoken_buckets[1]) - self.assertEqual(set(["ab", "ac"]), subtoken_buckets[2]) - self.assertEqual(len(subtoken_buckets[3]), 0) - self.assertEqual(set(["abbc"]), subtoken_buckets[4]) - - def test_gen_new_subtoken_list(self): - subtoken_counts = collections.defaultdict( - int, {"translate": 10, "t": 40, "tr": 16, "tra": 12}) - min_count = 5 - alphabet = set("translate") - reserved_tokens = ["reserved", "tokens"] - - subtoken_list, max_token_length = tokenizer._gen_new_subtoken_list( - subtoken_counts, min_count, alphabet, reserved_tokens) - - # Check that "tra" isn"t in the list (its count should be decremented to 2, - # so it should not be added to the canddiate list). - self.assertNotIn("tra", subtoken_list) - - self.assertIn("tr", subtoken_list) - self.assertIn("t", subtoken_list) - - self.assertEqual(len("translate"), max_token_length) - - def test_generate_subtokens(self): - token_counts = {"ab": 1, "bc": 3, "abc": 5} - alphabet = set("abc_") - min_count = 100 - num_iterations = 1 - reserved_tokens = ["reserved", "tokens"] - - vocab_list = tokenizer._generate_subtokens( - token_counts, alphabet, min_count, num_iterations, reserved_tokens) - - # Check that reserved tokens are at the front of the list - self.assertEqual(vocab_list[:2], reserved_tokens) - - # Check that each character in alphabet is in the vocab list - for c in alphabet: - self.assertIn(c, vocab_list) - - -if __name__ == "__main__": - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/__init__.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/accelerator/__init__.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/accelerator/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/accelerator/tpu.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/accelerator/tpu.py deleted file mode 100644 index 117accea..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/accelerator/tpu.py +++ /dev/null @@ -1,115 +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. -# ============================================================================== -"""Functions specific to running TensorFlow on TPUs.""" - -import tensorflow as tf - - -# "local" is a magic word in the TPU cluster resolver; it informs the resolver -# to use the local CPU as the compute device. This is useful for testing and -# debugging; the code flow is ostensibly identical, but without the need to -# actually have a TPU on the other end. -LOCAL = "local" - - -def construct_scalar_host_call(metric_dict, model_dir, prefix=""): - """Construct a host call to log scalars when training on TPU. - - Args: - metric_dict: A dict of the tensors to be logged. - model_dir: The location to write the summary. - prefix: The prefix (if any) to prepend to the metric names. - - Returns: - A tuple of (function, args_to_be_passed_to_said_function) - """ - # type: (dict, str) -> (function, list) - metric_names = list(metric_dict.keys()) - - def host_call_fn(global_step, *args): - """Training host call. Creates scalar summaries for training metrics. - - This function is executed on the CPU and should not directly reference - any Tensors in the rest of the `model_fn`. To pass Tensors from the - model to the `metric_fn`, provide as part of the `host_call`. See - https://www.tensorflow.org/api_docs/python/tf/contrib/tpu/TPUEstimatorSpec - for more information. - - Arguments should match the list of `Tensor` objects passed as the second - element in the tuple passed to `host_call`. - - Args: - global_step: `Tensor with shape `[batch]` for the global_step - *args: Remaining tensors to log. - - Returns: - List of summary ops to run on the CPU host. - """ - step = global_step[0] - with tf.contrib.summary.create_file_writer( - logdir=model_dir, filename_suffix=".host_call").as_default(): - with tf.contrib.summary.always_record_summaries(): - for i, name in enumerate(metric_names): - tf.contrib.summary.scalar(prefix + name, args[i][0], step=step) - - return tf.contrib.summary.all_summary_ops() - - # To log the current learning rate, and gradient norm for Tensorboard, the - # summary op needs to be run on the host CPU via host_call. host_call - # expects [batch_size, ...] Tensors, thus reshape to introduce a batch - # dimension. These Tensors are implicitly concatenated to - # [params['batch_size']]. - global_step_tensor = tf.reshape(tf.train.get_or_create_global_step(), [1]) - other_tensors = [tf.reshape(metric_dict[key], [1]) for key in metric_names] - - return host_call_fn, [global_step_tensor] + other_tensors - - -def embedding_matmul(embedding_table, values, mask, name="embedding_matmul"): - """Performs embedding lookup via a matmul. - - The matrix to be multiplied by the embedding table Tensor is constructed - via an implementation of scatter based on broadcasting embedding indices - and performing an equality comparison against a broadcasted - range(num_embedding_table_rows). All masked positions will produce an - embedding vector of zeros. - - Args: - embedding_table: Tensor of embedding table. - Rank 2 (table_size x embedding dim) - values: Tensor of embedding indices. Rank 2 (batch x n_indices) - mask: Tensor of mask / weights. Rank 2 (batch x n_indices) - name: Optional name scope for created ops - - Returns: - Rank 3 tensor of embedding vectors. - """ - - with tf.name_scope(name): - n_embeddings = embedding_table.get_shape().as_list()[0] - batch_size, padded_size = values.shape.as_list() - - emb_idcs = tf.tile( - tf.reshape(values, (batch_size, padded_size, 1)), (1, 1, n_embeddings)) - emb_weights = tf.tile( - tf.reshape(mask, (batch_size, padded_size, 1)), (1, 1, n_embeddings)) - col_idcs = tf.tile( - tf.reshape(tf.range(n_embeddings), (1, 1, n_embeddings)), - (batch_size, padded_size, 1)) - one_hot = tf.where( - tf.equal(emb_idcs, col_idcs), emb_weights, - tf.zeros((batch_size, padded_size, n_embeddings))) - - return tf.tensordot(one_hot, embedding_table, 1) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/accelerator/tpu_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/accelerator/tpu_test.py deleted file mode 100644 index 64ff0516..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/accelerator/tpu_test.py +++ /dev/null @@ -1,108 +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. -# ============================================================================== -"""Test TPU optimized matmul embedding.""" - -import numpy as np -import tensorflow as tf - -from official.utils.accelerator import tpu as tpu_utils - - -TEST_CASES = [ - dict(embedding_dim=256, vocab_size=1000, sequence_length=64, - batch_size=32, seed=54131), - dict(embedding_dim=8, vocab_size=15, sequence_length=12, - batch_size=256, seed=536413), - dict(embedding_dim=2048, vocab_size=512, sequence_length=50, - batch_size=8, seed=35124) -] - - -class TPUBaseTester(tf.test.TestCase): - def construct_embedding_and_values(self, embedding_dim, vocab_size, - sequence_length, batch_size, seed): - np.random.seed(seed) - - embeddings = np.random.random(size=(vocab_size, embedding_dim)) - embedding_table = tf.convert_to_tensor(embeddings, dtype=tf.float32) - - tokens = np.random.randint(low=1, high=vocab_size-1, - size=(batch_size, sequence_length)) - for i in range(batch_size): - tokens[i, np.random.randint(low=0, high=sequence_length-1):] = 0 - values = tf.convert_to_tensor(tokens, dtype=tf.int32) - mask = tf.to_float(tf.not_equal(values, 0)) - return embedding_table, values, mask - - def _test_embedding(self, embedding_dim, vocab_size, - sequence_length, batch_size, seed): - """Test that matmul embedding matches embedding lookup (gather).""" - - with self.test_session(): - embedding_table, values, mask = self.construct_embedding_and_values( - embedding_dim=embedding_dim, - vocab_size=vocab_size, - sequence_length=sequence_length, - batch_size=batch_size, - seed=seed - ) - - embedding = (tf.nn.embedding_lookup(params=embedding_table, ids=values) * - tf.expand_dims(mask, -1)) - - matmul_embedding = tpu_utils.embedding_matmul( - embedding_table=embedding_table, values=values, mask=mask) - - self.assertAllClose(embedding, matmul_embedding) - - def _test_masking(self, embedding_dim, vocab_size, - sequence_length, batch_size, seed): - """Test that matmul embedding properly zeros masked positions.""" - with self.test_session(): - embedding_table, values, mask = self.construct_embedding_and_values( - embedding_dim=embedding_dim, - vocab_size=vocab_size, - sequence_length=sequence_length, - batch_size=batch_size, - seed=seed - ) - - matmul_embedding = tpu_utils.embedding_matmul( - embedding_table=embedding_table, values=values, mask=mask) - - self.assertAllClose(matmul_embedding, - matmul_embedding * tf.expand_dims(mask, -1)) - - def test_embedding_0(self): - self._test_embedding(**TEST_CASES[0]) - - def test_embedding_1(self): - self._test_embedding(**TEST_CASES[1]) - - def test_embedding_2(self): - self._test_embedding(**TEST_CASES[2]) - - def test_masking_0(self): - self._test_masking(**TEST_CASES[0]) - - def test_masking_1(self): - self._test_masking(**TEST_CASES[1]) - - def test_masking_2(self): - self._test_masking(**TEST_CASES[2]) - - -if __name__ == "__main__": - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/data/__init__.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/data/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/data/file_io.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/data/file_io.py deleted file mode 100644 index b8180bce..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/data/file_io.py +++ /dev/null @@ -1,203 +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. -# ============================================================================== -"""Convenience functions for managing dataset file buffers.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import atexit -import multiprocessing -import os -import tempfile -import uuid - -import numpy as np -import six - -import tensorflow as tf - - -class _GarbageCollector(object): - """Deletes temporary buffer files at exit. - - Certain tasks (such as NCF Recommendation) require writing buffers to - temporary files. (Which may be local or distributed.) It is not generally safe - to delete these files during operation, but they should be cleaned up. This - class keeps track of temporary files created, and deletes them at exit. - """ - def __init__(self): - self.temp_buffers = [] - - def register(self, filepath): - self.temp_buffers.append(filepath) - - def purge(self): - try: - for i in self.temp_buffers: - if tf.gfile.Exists(i): - tf.gfile.Remove(i) - tf.logging.info("Buffer file {} removed".format(i)) - except Exception as e: - tf.logging.error("Failed to cleanup buffer files: {}".format(e)) - - -_GARBAGE_COLLECTOR = _GarbageCollector() -atexit.register(_GARBAGE_COLLECTOR.purge) - -_ROWS_PER_CORE = 50000 - - -def write_to_temp_buffer(dataframe, buffer_folder, columns): - if buffer_folder is None: - _, buffer_path = tempfile.mkstemp() - else: - tf.gfile.MakeDirs(buffer_folder) - buffer_path = os.path.join(buffer_folder, str(uuid.uuid4())) - _GARBAGE_COLLECTOR.register(buffer_path) - - return write_to_buffer(dataframe, buffer_path, columns) - - -def iter_shard_dataframe(df, rows_per_core=1000): - """Two way shard of a dataframe. - - This function evenly shards a dataframe so that it can be mapped efficiently. - It yields a list of dataframes with length equal to the number of CPU cores, - with each dataframe having rows_per_core rows. (Except for the last batch - which may have fewer rows in the dataframes.) Passing vectorized inputs to - a multiprocessing pool is much more effecient than iterating through a - dataframe in serial and passing a list of inputs to the pool. - - Args: - df: Pandas dataframe to be sharded. - rows_per_core: Number of rows in each shard. - - Returns: - A list of dataframe shards. - """ - n = len(df) - num_cores = min([multiprocessing.cpu_count(), n]) - - num_blocks = int(np.ceil(n / num_cores / rows_per_core)) - max_batch_size = num_cores * rows_per_core - for i in range(num_blocks): - min_index = i * max_batch_size - max_index = min([(i + 1) * max_batch_size, n]) - df_shard = df[min_index:max_index] - n_shard = len(df_shard) - boundaries = np.linspace(0, n_shard, num_cores + 1, dtype=np.int64) - yield [df_shard[boundaries[j]:boundaries[j+1]] for j in range(num_cores)] - - -def _shard_dict_to_examples(shard_dict): - """Converts a dict of arrays into a list of example bytes.""" - n = [i for i in shard_dict.values()][0].shape[0] - feature_list = [{} for _ in range(n)] - for column, values in shard_dict.items(): - if len(values.shape) == 1: - values = np.reshape(values, values.shape + (1,)) - - if values.dtype.kind == "i": - feature_map = lambda x: tf.train.Feature( - int64_list=tf.train.Int64List(value=x)) - elif values.dtype.kind == "f": - feature_map = lambda x: tf.train.Feature( - float_list=tf.train.FloatList(value=x)) - else: - raise ValueError("Invalid dtype") - for i in range(n): - feature_list[i][column] = feature_map(values[i]) - examples = [ - tf.train.Example(features=tf.train.Features(feature=example_features)) - for example_features in feature_list - ] - - return [e.SerializeToString() for e in examples] - - -def _serialize_shards(df_shards, columns, pool, writer): - """Map sharded dataframes to bytes, and write them to a buffer. - - Args: - df_shards: A list of pandas dataframes. (Should be of similar size) - columns: The dataframe columns to be serialized. - pool: A multiprocessing pool to serialize in parallel. - writer: A TFRecordWriter to write the serialized shards. - """ - # Pandas does not store columns of arrays as nd arrays. stack remedies this. - map_inputs = [{c: np.stack(shard[c].values, axis=0) for c in columns} - for shard in df_shards] - - # Failure within pools is very irksome. Thus, it is better to thoroughly check - # inputs in the main process. - for inp in map_inputs: - # Check that all fields have the same number of rows. - assert len(set([v.shape[0] for v in inp.values()])) == 1 - for val in inp.values(): - assert hasattr(val, "dtype") - assert hasattr(val.dtype, "kind") - assert val.dtype.kind in ("i", "f") - assert len(val.shape) in (1, 2) - shard_bytes = pool.map(_shard_dict_to_examples, map_inputs) - for s in shard_bytes: - for example in s: - writer.write(example) - -def write_to_buffer(dataframe, buffer_path, columns, expected_size=None): - """Write a dataframe to a binary file for a dataset to consume. - - Args: - dataframe: The pandas dataframe to be serialized. - buffer_path: The path where the serialized results will be written. - columns: The dataframe columns to be serialized. - expected_size: The size in bytes of the serialized results. This is used to - lazily construct the buffer. - - Returns: - The path of the buffer. - """ - if tf.gfile.Exists(buffer_path) and tf.gfile.Stat(buffer_path).length > 0: - actual_size = tf.gfile.Stat(buffer_path).length - if expected_size == actual_size: - return buffer_path - tf.logging.warning( - "Existing buffer {} has size {}. Expected size {}. Deleting and " - "rebuilding buffer.".format(buffer_path, actual_size, expected_size)) - tf.gfile.Remove(buffer_path) - - if dataframe is None: - raise ValueError( - "dataframe was None but a valid existing buffer was not found.") - - tf.gfile.MakeDirs(os.path.split(buffer_path)[0]) - - tf.logging.info("Constructing TFRecordDataset buffer: {}".format(buffer_path)) - - count = 0 - pool = multiprocessing.Pool(multiprocessing.cpu_count()) - try: - with tf.python_io.TFRecordWriter(buffer_path) as writer: - for df_shards in iter_shard_dataframe(df=dataframe, - rows_per_core=_ROWS_PER_CORE): - _serialize_shards(df_shards, columns, pool, writer) - count += sum([len(s) for s in df_shards]) - tf.logging.info("{}/{} examples written." - .format(str(count).ljust(8), len(dataframe))) - finally: - pool.terminate() - - tf.logging.info("Buffer write complete.") - return buffer_path diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/data/file_io_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/data/file_io_test.py deleted file mode 100644 index 8996b396..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/data/file_io_test.py +++ /dev/null @@ -1,193 +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. -# ============================================================================== -"""Tests for binary data file utilities.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import contextlib -import multiprocessing - -# pylint: disable=wrong-import-order -import numpy as np -import pandas as pd -import tensorflow as tf -# pylint: enable=wrong-import-order - -from official.utils.data import file_io - - -_RAW_ROW = "raw_row" -_DUMMY_COL = "column_0" -_DUMMY_VEC_COL = "column_1" -_DUMMY_VEC_LEN = 4 - -_ROWS_PER_CORE = 4 -_TEST_CASES = [ - # One batch of one - dict(row_count=1, cpu_count=1, expected=[ - [[0]] - ]), - - dict(row_count=10, cpu_count=1, expected=[ - [[0, 1, 2, 3]], [[4, 5, 6, 7]], [[8, 9]] - ]), - - dict(row_count=21, cpu_count=1, expected=[ - [[0, 1, 2, 3]], [[4, 5, 6, 7]], [[8, 9, 10, 11]], - [[12, 13, 14, 15]], [[16, 17, 18, 19]], [[20]] - ]), - - dict(row_count=1, cpu_count=4, expected=[ - [[0]] - ]), - - dict(row_count=10, cpu_count=4, expected=[ - [[0, 1], [2, 3, 4], [5, 6], [7, 8, 9]] - ]), - - dict(row_count=21, cpu_count=4, expected=[ - [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15]], - [[16], [17], [18], [19, 20]] - ]), - - dict(row_count=10, cpu_count=8, expected=[ - [[0], [1], [2], [3, 4], [5], [6], [7], [8, 9]] - ]), - - dict(row_count=40, cpu_count=8, expected=[ - [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15], - [16, 17, 18, 19], [20, 21, 22, 23], [24, 25, 26, 27], - [28, 29, 30, 31]], - [[32], [33], [34], [35], [36], [37], [38], [39]] - ]), -] - -_FEATURE_MAP = { - _RAW_ROW: tf.FixedLenFeature([1], dtype=tf.int64), - _DUMMY_COL: tf.FixedLenFeature([1], dtype=tf.int64), - _DUMMY_VEC_COL: tf.FixedLenFeature([_DUMMY_VEC_LEN], dtype=tf.float32) -} - - -@contextlib.contextmanager -def fixed_core_count(cpu_count): - """Override CPU count. - - file_io.py uses the cpu_count function to scale to the size of the instance. - However, this is not desirable for testing because it can make the test flaky. - Instead, this context manager fixes the count for more robust testing. - - Args: - cpu_count: How many cores multiprocessing claims to have. - - Yields: - Nothing. (for context manager only) - """ - old_count_fn = multiprocessing.cpu_count - multiprocessing.cpu_count = lambda: cpu_count - yield - multiprocessing.cpu_count = old_count_fn - - -class BaseTest(tf.test.TestCase): - - def _test_sharding(self, row_count, cpu_count, expected): - df = pd.DataFrame({_DUMMY_COL: list(range(row_count))}) - with fixed_core_count(cpu_count): - shards = list(file_io.iter_shard_dataframe(df, _ROWS_PER_CORE)) - result = [[j[_DUMMY_COL].tolist() for j in i] for i in shards] - self.assertAllEqual(expected, result) - - def test_tiny_rows_low_core(self): - self._test_sharding(**_TEST_CASES[0]) - - def test_small_rows_low_core(self): - self._test_sharding(**_TEST_CASES[1]) - - def test_large_rows_low_core(self): - self._test_sharding(**_TEST_CASES[2]) - - def test_tiny_rows_medium_core(self): - self._test_sharding(**_TEST_CASES[3]) - - def test_small_rows_medium_core(self): - self._test_sharding(**_TEST_CASES[4]) - - def test_large_rows_medium_core(self): - self._test_sharding(**_TEST_CASES[5]) - - def test_small_rows_large_core(self): - self._test_sharding(**_TEST_CASES[6]) - - def test_large_rows_large_core(self): - self._test_sharding(**_TEST_CASES[7]) - - def _serialize_deserialize(self, num_cores=1, num_rows=20): - np.random.seed(1) - df = pd.DataFrame({ - # Serialization order is only deterministic for num_cores=1. raw_row is - # used in validation after the deserialization. - _RAW_ROW: np.array(range(num_rows), dtype=np.int64), - _DUMMY_COL: np.random.randint(0, 35, size=(num_rows,)), - _DUMMY_VEC_COL: [ - np.array([np.random.random() for _ in range(_DUMMY_VEC_LEN)]) - for i in range(num_rows) # pylint: disable=unused-variable - ] - }) - - with fixed_core_count(num_cores): - buffer_path = file_io.write_to_temp_buffer( - df, self.get_temp_dir(), [_RAW_ROW, _DUMMY_COL, _DUMMY_VEC_COL]) - - with self.test_session(graph=tf.Graph()) as sess: - dataset = tf.data.TFRecordDataset(buffer_path) - dataset = dataset.batch(1).map( - lambda x: tf.parse_example(x, _FEATURE_MAP)) - - data_iter = dataset.make_one_shot_iterator() - seen_rows = set() - for i in range(num_rows+5): - row = data_iter.get_next() - try: - row_id, val_0, val_1 = sess.run( - [row[_RAW_ROW], row[_DUMMY_COL], row[_DUMMY_VEC_COL]]) - row_id, val_0, val_1 = row_id[0][0], val_0[0][0], val_1[0] - assert row_id not in seen_rows - seen_rows.add(row_id) - - self.assertEqual(val_0, df[_DUMMY_COL][row_id]) - self.assertAllClose(val_1, df[_DUMMY_VEC_COL][row_id]) - - self.assertLess(i, num_rows, msg="Too many rows.") - except tf.errors.OutOfRangeError: - self.assertGreaterEqual(i, num_rows, msg="Too few rows.") - - file_io._GARBAGE_COLLECTOR.purge() - assert not tf.gfile.Exists(buffer_path) - - def test_serialize_deserialize_0(self): - self._serialize_deserialize(num_cores=1) - - def test_serialize_deserialize_1(self): - self._serialize_deserialize(num_cores=2) - - def test_serialize_deserialize_2(self): - self._serialize_deserialize(num_cores=8) - - -if __name__ == "__main__": - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/export/__init__.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/export/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/export/export.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/export/export.py deleted file mode 100644 index 9ae7bca9..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/export/export.py +++ /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. -# ============================================================================== -"""Convenience functions for exporting models as SavedModels or other types.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import tensorflow as tf - - -def build_tensor_serving_input_receiver_fn(shape, dtype=tf.float32, - batch_size=1): - """Returns a input_receiver_fn that can be used during serving. - - This expects examples to come through as float tensors, and simply - wraps them as TensorServingInputReceivers. - - Arguably, this should live in tf.estimator.export. Testing here first. - - Args: - shape: list representing target size of a single example. - dtype: the expected datatype for the input example - batch_size: number of input tensors that will be passed for prediction - - Returns: - A function that itself returns a TensorServingInputReceiver. - """ - def serving_input_receiver_fn(): - # Prep a placeholder where the input example will be fed in - features = tf.placeholder( - dtype=dtype, shape=[batch_size] + shape, name='input_tensor') - - return tf.estimator.export.TensorServingInputReceiver( - features=features, receiver_tensors=features) - - return serving_input_receiver_fn diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/export/export_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/export/export_test.py deleted file mode 100644 index 2c555295..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/export/export_test.py +++ /dev/null @@ -1,63 +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. -# ============================================================================== -"""Tests for exporting utils.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.utils.export import export - - -class ExportUtilsTest(tf.test.TestCase): - """Tests for the ExportUtils.""" - - def test_build_tensor_serving_input_receiver_fn(self): - receiver_fn = export.build_tensor_serving_input_receiver_fn(shape=[4, 5]) - with tf.Graph().as_default(): - receiver = receiver_fn() - self.assertIsInstance( - receiver, tf.estimator.export.TensorServingInputReceiver) - - self.assertIsInstance(receiver.features, tf.Tensor) - self.assertEqual(receiver.features.shape, tf.TensorShape([1, 4, 5])) - self.assertEqual(receiver.features.dtype, tf.float32) - self.assertIsInstance(receiver.receiver_tensors, dict) - # Note that Python 3 can no longer index .values() directly; cast to list. - self.assertEqual(list(receiver.receiver_tensors.values())[0].shape, - tf.TensorShape([1, 4, 5])) - - def test_build_tensor_serving_input_receiver_fn_batch_dtype(self): - receiver_fn = export.build_tensor_serving_input_receiver_fn( - shape=[4, 5], dtype=tf.int8, batch_size=10) - - with tf.Graph().as_default(): - receiver = receiver_fn() - self.assertIsInstance( - receiver, tf.estimator.export.TensorServingInputReceiver) - - self.assertIsInstance(receiver.features, tf.Tensor) - self.assertEqual(receiver.features.shape, tf.TensorShape([10, 4, 5])) - self.assertEqual(receiver.features.dtype, tf.int8) - self.assertIsInstance(receiver.receiver_tensors, dict) - # Note that Python 3 can no longer index .values() directly; cast to list. - self.assertEqual(list(receiver.receiver_tensors.values())[0].shape, - tf.TensorShape([10, 4, 5])) - - -if __name__ == "__main__": - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/README.md b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/README.md deleted file mode 100644 index 18160f78..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/README.md +++ /dev/null @@ -1,97 +0,0 @@ -# Adding Abseil (absl) flags quickstart -## Defining a flag -absl flag definitions are similar to argparse, although they are defined on a global namespace. - -For instance defining a string flag looks like: -```$xslt -from absl import flags -flags.DEFINE_string( - name="my_flag", - default="a_sensible_default", - help="Here is what this flag does." -) -``` - -All three arguments are required, but default may be `None`. A common optional argument is -short_name for defining abreviations. Certain `DEFINE_*` methods will have other required arguments. -For instance `DEFINE_enum` requires the `enum_values` argument to be specified. - -## Key Flags -absl has the concept of a key flag. Any flag defined in `__main__` is considered a key flag by -default. Key flags are displayed in `--help`, others only appear in `--helpfull`. In order to -handle key flags that are defined outside the module in question, absl provides the -`flags.adopt_module_key_flags()` method. This adds the key flags of a different module to one's own -key flags. For example: -```$xslt -File: flag_source.py ---------------------------------------- - -from absl import flags -flags.DEFINE_string(name="my_flag", default="abc", help="a flag.") -``` - -```$xslt -File: my_module.py ---------------------------------------- - -from absl import app as absl_app -from absl import flags - -import flag_source - -flags.adopt_module_key_flags(flag_source) - -def main(_): - pass - -absl_app.run(main, [__file__, "-h"] -``` - -when `my_module.py` is run it will show the help text for `my_flag`. Because not all flags defined -in a file are equally important, `official/utils/flags/core.py` (generally imported as flags_core) -provides an abstraction for handling key flag declaration in an easy way through the -`register_key_flags_in_core()` function, which allows a module to make a single -`adopt_key_flags(flags_core)` call when using the util flag declaration functions. - -## Validators -Often the constraints on a flag are complicated. absl provides the validator decorator to allow -one to mark a function as a flag validation function. Suppose we want users to provide a flag -which is a palindrome. - -```$xslt -from absl import flags - -flags.DEFINE_string(name="pal_flag", short_name="pf", default="", help="Give me a palindrome") - -@flags.validator("pal_flag") -def _check_pal(provided_pal_flag): - return provided_pal_flag == provided_pal_flag[::-1] - -``` - -Validators take the form that returning True (truthy) passes, and all others -(False, None, exception) fail. - -## Testing -To test using absl, simply declare flags in the setupClass method of TensorFlow's TestCase. - -```$xslt -from absl import flags -import tensorflow as tf - -def define_flags(): - flags.DEFINE_string(name="test_flag", default="abc", help="an example flag") - - -class BaseTester(unittest.TestCase): - - @classmethod - def setUpClass(cls): - super(BaseTester, cls).setUpClass() - define_flags() - - def test_trivial(self): - flags_core.parse_flags([__file__, "test_flag", "def"]) - self.AssertEqual(flags.FLAGS.test_flag, "def") - -``` diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/__init__.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_base.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_base.py deleted file mode 100644 index ab90737b..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_base.py +++ /dev/null @@ -1,141 +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. -# ============================================================================== -"""Flags which will be nearly universal across models.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -from absl import flags -import tensorflow as tf - -from official.utils.flags._conventions import help_wrap -from official.utils.logs import hooks_helper - - -def define_base(data_dir=True, model_dir=True, clean=True, train_epochs=True, - epochs_between_evals=True, stop_threshold=True, batch_size=True, - num_gpu=True, hooks=True, export_dir=True): - """Register base flags. - - Args: - data_dir: Create a flag for specifying the input data directory. - model_dir: Create a flag for specifying the model file directory. - train_epochs: Create a flag to specify the number of training epochs. - epochs_between_evals: Create a flag to specify the frequency of testing. - stop_threshold: Create a flag to specify a threshold accuracy or other - eval metric which should trigger the end of training. - batch_size: Create a flag to specify the batch size. - num_gpu: Create a flag to specify the number of GPUs used. - hooks: Create a flag to specify hooks for logging. - export_dir: Create a flag to specify where a SavedModel should be exported. - - Returns: - A list of flags for core.py to marks as key flags. - """ - key_flags = [] - - if data_dir: - flags.DEFINE_string( - name="data_dir", short_name="dd", default="/tmp", - help=help_wrap("The location of the input data.")) - key_flags.append("data_dir") - - if model_dir: - flags.DEFINE_string( - name="model_dir", short_name="md", default="/tmp", - help=help_wrap("The location of the model checkpoint files.")) - key_flags.append("model_dir") - - if clean: - flags.DEFINE_boolean( - name="clean", default=False, - help=help_wrap("If set, model_dir will be removed if it exists.")) - key_flags.append("clean") - - if train_epochs: - flags.DEFINE_integer( - name="train_epochs", short_name="te", default=1, - help=help_wrap("The number of epochs used to train.")) - key_flags.append("train_epochs") - - if epochs_between_evals: - flags.DEFINE_integer( - name="epochs_between_evals", short_name="ebe", default=1, - help=help_wrap("The number of training epochs to run between " - "evaluations.")) - key_flags.append("epochs_between_evals") - - if stop_threshold: - flags.DEFINE_float( - name="stop_threshold", short_name="st", - default=None, - help=help_wrap("If passed, training will stop at the earlier of " - "train_epochs and when the evaluation metric is " - "greater than or equal to stop_threshold.")) - - if batch_size: - flags.DEFINE_integer( - name="batch_size", short_name="bs", default=32, - help=help_wrap("Batch size for training and evaluation. When using " - "multiple gpus, this is the global batch size for " - "all devices. For example, if the batch size is 32 " - "and there are 4 GPUs, each GPU will get 8 examples on " - "each step.")) - key_flags.append("batch_size") - - if num_gpu: - flags.DEFINE_integer( - name="num_gpus", short_name="ng", - default=1 if tf.test.is_gpu_available() else 0, - help=help_wrap( - "How many GPUs to use with the DistributionStrategies API. The " - "default is 1 if TensorFlow can detect a GPU, and 0 otherwise.")) - - if hooks: - # Construct a pretty summary of hooks. - hook_list_str = ( - u"\ufeff Hook:\n" + u"\n".join([u"\ufeff {}".format(key) for key - in hooks_helper.HOOKS])) - flags.DEFINE_list( - name="hooks", short_name="hk", default="LoggingTensorHook", - help=help_wrap( - u"A list of (case insensitive) strings to specify the names of " - u"training hooks.\n{}\n\ufeff Example: `--hooks ProfilerHook," - u"ExamplesPerSecondHook`\n See official.utils.logs.hooks_helper " - u"for details.".format(hook_list_str)) - ) - key_flags.append("hooks") - - if export_dir: - flags.DEFINE_string( - name="export_dir", short_name="ed", default=None, - help=help_wrap("If set, a SavedModel serialization of the model will " - "be exported to this directory at the end of training. " - "See the README for more details and relevant links.") - ) - key_flags.append("export_dir") - - return key_flags - - -def get_num_gpus(flags_obj): - """Treat num_gpus=-1 as 'use all'.""" - if flags_obj.num_gpus != -1: - return flags_obj.num_gpus - - from tensorflow.python.client import device_lib # pylint: disable=g-import-not-at-top - local_device_protos = device_lib.list_local_devices() - return sum([1 for d in local_device_protos if d.device_type == "GPU"]) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_benchmark.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_benchmark.py deleted file mode 100644 index 0c1494a8..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_benchmark.py +++ /dev/null @@ -1,99 +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. -# ============================================================================== -"""Flags for benchmarking models.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -from absl import flags - -from official.utils.flags._conventions import help_wrap - - -def define_benchmark(benchmark_log_dir=True, bigquery_uploader=True): - """Register benchmarking flags. - - Args: - benchmark_log_dir: Create a flag to specify location for benchmark logging. - bigquery_uploader: Create flags for uploading results to BigQuery. - - Returns: - A list of flags for core.py to marks as key flags. - """ - - key_flags = [] - - flags.DEFINE_enum( - name="benchmark_logger_type", default="BaseBenchmarkLogger", - enum_values=["BaseBenchmarkLogger", "BenchmarkFileLogger", - "BenchmarkBigQueryLogger"], - help=help_wrap("The type of benchmark logger to use. Defaults to using " - "BaseBenchmarkLogger which logs to STDOUT. Different " - "loggers will require other flags to be able to work.")) - flags.DEFINE_string( - name="benchmark_test_id", short_name="bti", default=None, - help=help_wrap("The unique test ID of the benchmark run. It could be the " - "combination of key parameters. It is hardware " - "independent and could be used compare the performance " - "between different test runs. This flag is designed for " - "human consumption, and does not have any impact within " - "the system.")) - - if benchmark_log_dir: - flags.DEFINE_string( - name="benchmark_log_dir", short_name="bld", default=None, - help=help_wrap("The location of the benchmark logging.") - ) - - if bigquery_uploader: - flags.DEFINE_string( - name="gcp_project", short_name="gp", default=None, - help=help_wrap( - "The GCP project name where the benchmark will be uploaded.")) - - flags.DEFINE_string( - name="bigquery_data_set", short_name="bds", default="test_benchmark", - help=help_wrap( - "The Bigquery dataset name where the benchmark will be uploaded.")) - - flags.DEFINE_string( - name="bigquery_run_table", short_name="brt", default="benchmark_run", - help=help_wrap("The Bigquery table name where the benchmark run " - "information will be uploaded.")) - - flags.DEFINE_string( - name="bigquery_run_status_table", short_name="brst", - default="benchmark_run_status", - help=help_wrap("The Bigquery table name where the benchmark run " - "status information will be uploaded.")) - - flags.DEFINE_string( - name="bigquery_metric_table", short_name="bmt", - default="benchmark_metric", - help=help_wrap("The Bigquery table name where the benchmark metric " - "information will be uploaded.")) - - @flags.multi_flags_validator( - ["benchmark_logger_type", "benchmark_log_dir"], - message="--benchmark_logger_type=BenchmarkFileLogger will require " - "--benchmark_log_dir being set") - def _check_benchmark_log_dir(flags_dict): - benchmark_logger_type = flags_dict["benchmark_logger_type"] - if benchmark_logger_type == "BenchmarkFileLogger": - return flags_dict["benchmark_log_dir"] - return True - - return key_flags diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_conventions.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_conventions.py deleted file mode 100644 index c486d386..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_conventions.py +++ /dev/null @@ -1,46 +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. -# ============================================================================== -"""Central location for shared arparse convention definitions.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import codecs -import functools - -from absl import app as absl_app -from absl import flags - - -# This codifies help string conventions and makes it easy to update them if -# necessary. Currently the only major effect is that help bodies start on the -# line after flags are listed. All flag definitions should wrap the text bodies -# with help wrap when calling DEFINE_*. -_help_wrap = functools.partial(flags.text_wrap, length=80, indent="", - firstline_indent="\n") - - -# Pretty formatting causes issues when utf-8 is not installed on a system. -try: - codecs.lookup("utf-8") - help_wrap = _help_wrap -except LookupError: - def help_wrap(text, *args, **kwargs): - return _help_wrap(text, *args, **kwargs).replace("\ufeff", "") - - -# Replace None with h to also allow -h -absl_app.HelpshortFlag.SHORT_NAME = "h" diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_device.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_device.py deleted file mode 100644 index 937e9d3e..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_device.py +++ /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. -# ============================================================================== -"""Flags for managing compute devices. Currently only contains TPU flags.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -from absl import flags -import tensorflow as tf - -from official.utils.flags._conventions import help_wrap - - -def require_cloud_storage(flag_names): - """Register a validator to check directory flags. - Args: - flag_names: An iterable of strings containing the names of flags to be - checked. - """ - msg = "TPU requires GCS path for {}".format(", ".join(flag_names)) - @flags.multi_flags_validator(["tpu"] + flag_names, message=msg) - def _path_check(flag_values): # pylint: disable=missing-docstring - if flag_values["tpu"] is None: - return True - - valid_flags = True - for key in flag_names: - if not flag_values[key].startswith("gs://"): - tf.logging.error("{} must be a GCS path.".format(key)) - valid_flags = False - - return valid_flags - - -def define_device(tpu=True): - """Register device specific flags. - Args: - tpu: Create flags to specify TPU operation. - Returns: - A list of flags for core.py to marks as key flags. - """ - - key_flags = [] - - if tpu: - flags.DEFINE_string( - name="tpu", default=None, - help=help_wrap( - "The Cloud TPU 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. Passing `local` will use the" - "CPU of the local instance instead. (Good for debugging.)")) - key_flags.append("tpu") - - flags.DEFINE_string( - name="tpu_zone", default=None, - help=help_wrap( - "[Optional] GCE zone where the Cloud TPU is located in. If not " - "specified, we will attempt to automatically detect the GCE " - "project from metadata.")) - - flags.DEFINE_string( - name="tpu_gcp_project", default=None, - help=help_wrap( - "[Optional] Project name for the Cloud TPU-enabled project. If not " - "specified, we will attempt to automatically detect the GCE " - "project from metadata.")) - - flags.DEFINE_integer(name="num_tpu_shards", default=8, - help=help_wrap("Number of shards (TPU chips).")) - - return key_flags diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_misc.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_misc.py deleted file mode 100644 index c6fa24b5..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_misc.py +++ /dev/null @@ -1,50 +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. -# ============================================================================== -"""Misc flags.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -from absl import flags - -from official.utils.flags._conventions import help_wrap - - -def define_image(data_format=True): - """Register image specific flags. - - Args: - data_format: Create a flag to specify image axis convention. - - Returns: - A list of flags for core.py to marks as key flags. - """ - - key_flags = [] - - if data_format: - flags.DEFINE_enum( - name="data_format", short_name="df", default=None, - enum_values=["channels_first", "channels_last"], - help=help_wrap( - "A flag to override the data format used in the model. " - "channels_first provides a performance boost on GPU but is not " - "always compatible with CPU. If left unspecified, the data format " - "will be chosen automatically based on whether TensorFlow was " - "built for CPU or GPU.")) - key_flags.append("data_format") - - return key_flags diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_performance.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_performance.py deleted file mode 100644 index cd9e0a51..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/_performance.py +++ /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. -# ============================================================================== -"""Register flags for optimizing performance.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import multiprocessing - -from absl import flags # pylint: disable=g-bad-import-order -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.utils.flags._conventions import help_wrap - - -# Map string to (TensorFlow dtype, default loss scale) -DTYPE_MAP = { - "fp16": (tf.float16, 128), - "fp32": (tf.float32, 1), -} - - -def get_tf_dtype(flags_obj): - return DTYPE_MAP[flags_obj.dtype][0] - - -def get_loss_scale(flags_obj): - if flags_obj.loss_scale is not None: - return flags_obj.loss_scale - return DTYPE_MAP[flags_obj.dtype][1] - - -def define_performance(num_parallel_calls=True, inter_op=True, intra_op=True, - synthetic_data=True, max_train_steps=True, dtype=True, - all_reduce_alg=True, tf_gpu_thread_mode=False, - datasets_num_private_threads=False, - datasets_num_parallel_batches=False): - """Register flags for specifying performance tuning arguments. - - Args: - num_parallel_calls: Create a flag to specify parallelism of data loading. - inter_op: Create a flag to allow specification of inter op threads. - intra_op: Create a flag to allow specification of intra op threads. - synthetic_data: Create a flag to allow the use of synthetic data. - max_train_steps: Create a flags to allow specification of maximum number - of training steps - dtype: Create flags for specifying dtype. - all_reduce_alg: If set forces a specific algorithm for multi-gpu. - tf_gpu_thread_mode: gpu_private triggers us of private thread pool. - datasets_num_private_threads: Number of private threads for datasets. - datasets_num_parallel_batches: Determines how many batches to process in - parallel when using map and batch from tf.data. - - Returns: - A list of flags for core.py to marks as key flags. - """ - - key_flags = [] - if num_parallel_calls: - flags.DEFINE_integer( - name="num_parallel_calls", short_name="npc", - default=multiprocessing.cpu_count(), - help=help_wrap("The number of records that are processed in parallel " - "during input processing. This can be optimized per " - "data set but for generally homogeneous data sets, " - "should be approximately the number of available CPU " - "cores. (default behavior)")) - - if inter_op: - flags.DEFINE_integer( - name="inter_op_parallelism_threads", short_name="inter", default=0, - help=help_wrap("Number of inter_op_parallelism_threads to use for CPU. " - "See TensorFlow config.proto for details.") - ) - - if intra_op: - flags.DEFINE_integer( - name="intra_op_parallelism_threads", short_name="intra", default=0, - help=help_wrap("Number of intra_op_parallelism_threads to use for CPU. " - "See TensorFlow config.proto for details.")) - - if synthetic_data: - flags.DEFINE_bool( - name="use_synthetic_data", short_name="synth", default=False, - help=help_wrap( - "If set, use fake data (zeroes) instead of a real dataset. " - "This mode is useful for performance debugging, as it removes " - "input processing steps, but will not learn anything.")) - - if max_train_steps: - flags.DEFINE_integer( - name="max_train_steps", short_name="mts", default=None, help=help_wrap( - "The model will stop training if the global_step reaches this " - "value. If not set, training will run until the specified number " - "of epochs have run as usual. It is generally recommended to set " - "--train_epochs=1 when using this flag." - )) - - if dtype: - flags.DEFINE_enum( - name="dtype", short_name="dt", default="fp32", - enum_values=DTYPE_MAP.keys(), - help=help_wrap("The TensorFlow datatype used for calculations. " - "Variables may be cast to a higher precision on a " - "case-by-case basis for numerical stability.")) - - flags.DEFINE_integer( - name="loss_scale", short_name="ls", default=None, - help=help_wrap( - "The amount to scale the loss by when the model is run. Before " - "gradients are computed, the loss is multiplied by the loss scale, " - "making all gradients loss_scale times larger. To adjust for this, " - "gradients are divided by the loss scale before being applied to " - "variables. This is mathematically equivalent to training without " - "a loss scale, but the loss scale helps avoid some intermediate " - "gradients from underflowing to zero. If not provided the default " - "for fp16 is 128 and 1 for all other dtypes.")) - - loss_scale_val_msg = "loss_scale should be a positive integer." - @flags.validator(flag_name="loss_scale", message=loss_scale_val_msg) - def _check_loss_scale(loss_scale): # pylint: disable=unused-variable - if loss_scale is None: - return True # null case is handled in get_loss_scale() - - return loss_scale > 0 - - if all_reduce_alg: - flags.DEFINE_string( - name="all_reduce_alg", short_name="ara", default=None, - help=help_wrap("Defines the algorithm to use for performing all-reduce." - "See tf.contrib.distribute.AllReduceCrossTowerOps for " - "more details and available options.")) - - if tf_gpu_thread_mode: - flags.DEFINE_string( - name="tf_gpu_thread_mode", short_name="gt_mode", default=None, - help=help_wrap( - "Whether and how the GPU device uses its own threadpool.") - ) - - if datasets_num_private_threads: - flags.DEFINE_integer( - name="datasets_num_private_threads", - default=None, - help=help_wrap( - "Number of threads for a private threadpool created for all" - "datasets computation..") - ) - - if datasets_num_parallel_batches: - flags.DEFINE_integer( - name="datasets_num_parallel_batches", - default=None, - help=help_wrap( - "Determines how many batches to process in parallel when using " - "map and batch from tf.data.") - ) - - return key_flags diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/core.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/core.py deleted file mode 100644 index 8bd15df9..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/core.py +++ /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. -# ============================================================================== -"""Public interface for flag definition. - -See _example.py for detailed instructions on defining flags. -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import functools -import sys - -from absl import app as absl_app -from absl import flags - -from official.utils.flags import _base -from official.utils.flags import _benchmark -from official.utils.flags import _conventions -from official.utils.flags import _device -from official.utils.flags import _misc -from official.utils.flags import _performance - - -def set_defaults(**kwargs): - for key, value in kwargs.items(): - flags.FLAGS.set_default(name=key, value=value) - - -def parse_flags(argv=None): - """Reset flags and reparse. Currently only used in testing.""" - flags.FLAGS.unparse_flags() - absl_app.parse_flags_with_usage(argv or sys.argv) - - -def register_key_flags_in_core(f): - """Defines a function in core.py, and registers its key flags. - - absl uses the location of a flags.declare_key_flag() to determine the context - in which a flag is key. By making all declares in core, this allows model - main functions to call flags.adopt_module_key_flags() on core and correctly - chain key flags. - - Args: - f: The function to be wrapped - - Returns: - The "core-defined" version of the input function. - """ - - def core_fn(*args, **kwargs): - key_flags = f(*args, **kwargs) - [flags.declare_key_flag(fl) for fl in key_flags] # pylint: disable=expression-not-assigned - return core_fn - - -define_base = register_key_flags_in_core(_base.define_base) -# Remove options not relevant for Eager from define_base(). -define_base_eager = register_key_flags_in_core(functools.partial( - _base.define_base, epochs_between_evals=False, stop_threshold=False, - hooks=False)) -define_benchmark = register_key_flags_in_core(_benchmark.define_benchmark) -define_device = register_key_flags_in_core(_device.define_device) -define_image = register_key_flags_in_core(_misc.define_image) -define_performance = register_key_flags_in_core(_performance.define_performance) - - -help_wrap = _conventions.help_wrap - - -get_num_gpus = _base.get_num_gpus -get_tf_dtype = _performance.get_tf_dtype -get_loss_scale = _performance.get_loss_scale -DTYPE_MAP = _performance.DTYPE_MAP -require_cloud_storage = _device.require_cloud_storage diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/flags_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/flags_test.py deleted file mode 100644 index 93711fd5..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/flags_test.py +++ /dev/null @@ -1,100 +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. -# ============================================================================== - -import unittest - -from absl import flags -import tensorflow as tf - -from official.utils.flags import core as flags_core # pylint: disable=g-bad-import-order - - -def define_flags(): - flags_core.define_base(num_gpu=False) - flags_core.define_performance() - flags_core.define_image() - flags_core.define_benchmark() - - -class BaseTester(unittest.TestCase): - - @classmethod - def setUpClass(cls): - super(BaseTester, cls).setUpClass() - define_flags() - - def test_default_setting(self): - """Test to ensure fields exist and defaults can be set. - """ - - defaults = dict( - data_dir="dfgasf", - model_dir="dfsdkjgbs", - train_epochs=534, - epochs_between_evals=15, - batch_size=256, - hooks=["LoggingTensorHook"], - num_parallel_calls=18, - inter_op_parallelism_threads=5, - intra_op_parallelism_threads=10, - data_format="channels_first" - ) - - flags_core.set_defaults(**defaults) - flags_core.parse_flags() - - for key, value in defaults.items(): - assert flags.FLAGS.get_flag_value(name=key, default=None) == value - - def test_benchmark_setting(self): - defaults = dict( - hooks=["LoggingMetricHook"], - benchmark_log_dir="/tmp/12345", - gcp_project="project_abc", - ) - - flags_core.set_defaults(**defaults) - flags_core.parse_flags() - - for key, value in defaults.items(): - assert flags.FLAGS.get_flag_value(name=key, default=None) == value - - def test_booleans(self): - """Test to ensure boolean flags trigger as expected. - """ - - flags_core.parse_flags([__file__, "--use_synthetic_data"]) - - assert flags.FLAGS.use_synthetic_data - - def test_parse_dtype_info(self): - for dtype_str, tf_dtype, loss_scale in [["fp16", tf.float16, 128], - ["fp32", tf.float32, 1]]: - flags_core.parse_flags([__file__, "--dtype", dtype_str]) - - self.assertEqual(flags_core.get_tf_dtype(flags.FLAGS), tf_dtype) - self.assertEqual(flags_core.get_loss_scale(flags.FLAGS), loss_scale) - - flags_core.parse_flags( - [__file__, "--dtype", dtype_str, "--loss_scale", "5"]) - - self.assertEqual(flags_core.get_loss_scale(flags.FLAGS), 5) - - with self.assertRaises(SystemExit): - flags_core.parse_flags([__file__, "--dtype", "int8"]) - - -if __name__ == "__main__": - unittest.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/guidelines.md b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/guidelines.md deleted file mode 100644 index 15130e98..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/flags/guidelines.md +++ /dev/null @@ -1,64 +0,0 @@ -# Using flags in official models - -1. **All common flags must be incorporated in the models.** - - Common flags (i.e. batch_size, model_dir, etc.) are provided by various flag definition functions, - and channeled through `official.utils.flags.core`. For instance to define common supervised - learning parameters one could use the following code: - - ```$xslt - from absl import app as absl_app - from absl import flags - - from official.utils.flags import core as flags_core - - - def define_flags(): - flags_core.define_base() - flags.adopt_key_flags(flags_core) - - - def main(_): - flags_obj = flags.FLAGS - print(flags_obj) - - - if __name__ == "__main__" - absl_app.run(main) - ``` -2. **Validate flag values.** - - See the [Validators](#validators) section for implementation details. - - Validators in the official model repo should not access the file system, such as verifying - that files exist, due to the strict ordering requirements. - -3. **Flag values should not be mutated.** - - Instead of mutating flag values, use getter functions to return the desired values. An example - getter function is `get_loss_scale` function below: - - ``` - # Map string to (TensorFlow dtype, default loss scale) - DTYPE_MAP = { - "fp16": (tf.float16, 128), - "fp32": (tf.float32, 1), - } - - - def get_loss_scale(flags_obj): - if flags_obj.loss_scale is not None: - return flags_obj.loss_scale - return DTYPE_MAP[flags_obj.dtype][1] - - - def main(_): - flags_obj = flags.FLAGS() - - # Do not mutate flags_obj - # if flags_obj.loss_scale is None: - # flags_obj.loss_scale = DTYPE_MAP[flags_obj.dtype][1] # Don't do this - - print(get_loss_scale(flags_obj)) - ... - ``` \ No newline at end of file diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/__init__.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/cloud_lib.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/cloud_lib.py deleted file mode 100644 index a2d9bd3d..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/cloud_lib.py +++ /dev/null @@ -1,34 +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. -# ============================================================================== - -"""Utilities that interact with cloud service. -""" - -import requests - -GCP_METADATA_URL = "http://metadata/computeMetadata/v1/instance/hostname" -GCP_METADATA_HEADER = {"Metadata-Flavor": "Google"} - - -def on_gcp(): - """Detect whether the current running environment is on GCP.""" - try: - # Timeout in 5 seconds, in case the test environment has connectivity issue. - # There is not default timeout, which means it might block forever. - response = requests.get( - GCP_METADATA_URL, headers=GCP_METADATA_HEADER, timeout=5) - return response.status_code == 200 - except requests.exceptions.RequestException: - return False diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/guidelines.md b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/guidelines.md deleted file mode 100644 index 408c3cd5..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/guidelines.md +++ /dev/null @@ -1,58 +0,0 @@ -# Logging in official models - -This library adds logging functions that print or save tensor values. Official models should define all common hooks -(using hooks helper) and a benchmark logger. - -1. **Training Hooks** - - Hooks are a TensorFlow concept that define specific actions at certain points of the execution. We use them to obtain and log - tensor values during training. - - hooks_helper.py provides an easy way to create common hooks. The following hooks are currently defined: - * LoggingTensorHook: Logs tensor values - * ProfilerHook: Writes a timeline json that can be loaded into chrome://tracing. - * ExamplesPerSecondHook: Logs the number of examples processed per second. - * LoggingMetricHook: Similar to LoggingTensorHook, except that the tensors are logged in a format defined by our data - anaylsis pipeline. - - -2. **Benchmarks** - - The benchmark logger provides useful functions for logging environment information, and evaluation results. - The module also contains a context which is used to update the status of the run. - -Example usage: - -``` -from absl import app as absl_app - -from official.utils.logs import hooks_helper -from official.utils.logs import logger - -def model_main(flags_obj): - estimator = ... - - benchmark_logger = logger.get_benchmark_logger() - benchmark_logger.log_run_info(...) - - train_hooks = hooks_helper.get_train_hooks(...) - - for epoch in range(10): - estimator.train(..., hooks=train_hooks) - eval_results = estimator.evaluate(...) - - # Log a dictionary of metrics - benchmark_logger.log_evaluation_result(eval_results) - - # Log an individual metric - benchmark_logger.log_metric(...) - - -def main(_): - with logger.benchmark_context(flags.FLAGS): - model_main(flags.FLAGS) - -if __name__ == "__main__": - # define flags - absl_app.run(main) -``` diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/hooks.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/hooks.py deleted file mode 100644 index c247a521..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/hooks.py +++ /dev/null @@ -1,127 +0,0 @@ -# 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. -# ============================================================================== - -"""Hook that counts examples per second every N steps or seconds.""" - - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.utils.logs import logger - - -class ExamplesPerSecondHook(tf.compat.v1.train.SessionRunHook): - """Hook to print out examples per second. - - Total time is tracked and then divided by the total number of steps - to get the average step time and then batch_size is used to determine - the running average of examples per second. The examples per second for the - most recent interval is also logged. - """ - - def __init__(self, - batch_size, - every_n_steps=None, - every_n_secs=None, - warm_steps=0, - metric_logger=None): - """Initializer for ExamplesPerSecondHook. - - Args: - batch_size: Total batch size across all workers used to calculate - examples/second from global time. - every_n_steps: Log stats every n steps. - every_n_secs: Log stats every n seconds. Exactly one of the - `every_n_steps` or `every_n_secs` should be set. - warm_steps: The number of steps to be skipped before logging and running - average calculation. warm_steps steps refers to global steps across all - workers, not on each worker - metric_logger: instance of `BenchmarkLogger`, the benchmark logger that - hook should use to write the log. If None, BaseBenchmarkLogger will - be used. - - Raises: - ValueError: if neither `every_n_steps` or `every_n_secs` is set, or - both are set. - """ - - if (every_n_steps is None) == (every_n_secs is None): - raise ValueError("exactly one of every_n_steps" - " and every_n_secs should be provided.") - - self._logger = metric_logger or logger.BaseBenchmarkLogger() - - self._timer = tf.train.SecondOrStepTimer( - every_steps=every_n_steps, every_secs=every_n_secs) - - self._step_train_time = 0 - self._total_steps = 0 - self._batch_size = batch_size - self._warm_steps = warm_steps - - def begin(self): - """Called once before using the session to check global step.""" - self._global_step_tensor = tf.train.get_global_step() - if self._global_step_tensor is None: - raise RuntimeError( - "Global step should be created to use StepCounterHook.") - - def before_run(self, run_context): # pylint: disable=unused-argument - """Called before each call to run(). - - Args: - run_context: A SessionRunContext object. - - Returns: - A SessionRunArgs object or None if never triggered. - """ - return tf.train.SessionRunArgs(self._global_step_tensor) - - def after_run(self, run_context, run_values): # pylint: disable=unused-argument - """Called after each call to run(). - - Args: - run_context: A SessionRunContext object. - run_values: A SessionRunValues object. - """ - global_step = run_values.results - - if self._timer.should_trigger_for_step( - global_step) and global_step > self._warm_steps: - elapsed_time, elapsed_steps = self._timer.update_last_triggered_step( - global_step) - if elapsed_time is not None: - self._step_train_time += elapsed_time - self._total_steps += elapsed_steps - - # average examples per second is based on the total (accumulative) - # training steps and training time so far - average_examples_per_sec = self._batch_size * ( - self._total_steps / self._step_train_time) - # current examples per second is based on the elapsed training steps - # and training time per batch - current_examples_per_sec = self._batch_size * ( - elapsed_steps / elapsed_time) - - self._logger.log_metric( - "average_examples_per_sec", average_examples_per_sec, - global_step=global_step) - - self._logger.log_metric( - "current_examples_per_sec", current_examples_per_sec, - global_step=global_step) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/hooks_helper.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/hooks_helper.py deleted file mode 100644 index b037798a..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/hooks_helper.py +++ /dev/null @@ -1,163 +0,0 @@ -# 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. -# ============================================================================== - -"""Hooks helper to return a list of TensorFlow hooks for training by name. - -More hooks can be added to this set. To add a new hook, 1) add the new hook to -the registry in HOOKS, 2) add a corresponding function that parses out necessary -parameters. -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.utils.logs import hooks -from official.utils.logs import logger -from official.utils.logs import metric_hook - -_TENSORS_TO_LOG = dict((x, x) for x in ['learning_rate', - 'cross_entropy', - 'train_accuracy']) - - -def get_train_hooks(name_list, use_tpu=False, **kwargs): - """Factory for getting a list of TensorFlow hooks for training by name. - - Args: - name_list: a list of strings to name desired hook classes. Allowed: - LoggingTensorHook, ProfilerHook, ExamplesPerSecondHook, which are defined - as keys in HOOKS - use_tpu: Boolean of whether computation occurs on a TPU. This will disable - hooks altogether. - **kwargs: a dictionary of arguments to the hooks. - - Returns: - list of instantiated hooks, ready to be used in a classifier.train call. - - Raises: - ValueError: if an unrecognized name is passed. - """ - - if not name_list: - return [] - - if use_tpu: - tf.logging.warning("hooks_helper received name_list `{}`, but a TPU is " - "specified. No hooks will be used.".format(name_list)) - return [] - - train_hooks = [] - for name in name_list: - hook_name = HOOKS.get(name.strip().lower()) - if hook_name is None: - raise ValueError('Unrecognized training hook requested: {}'.format(name)) - else: - train_hooks.append(hook_name(**kwargs)) - - return train_hooks - - -def get_logging_tensor_hook(every_n_iter=100, tensors_to_log=None, **kwargs): # pylint: disable=unused-argument - """Function to get LoggingTensorHook. - - Args: - every_n_iter: `int`, print the values of `tensors` once every N local - steps taken on the current worker. - tensors_to_log: List of tensor names or dictionary mapping labels to tensor - names. If not set, log _TENSORS_TO_LOG by default. - **kwargs: a dictionary of arguments to LoggingTensorHook. - - Returns: - Returns a LoggingTensorHook with a standard set of tensors that will be - printed to stdout. - """ - if tensors_to_log is None: - tensors_to_log = _TENSORS_TO_LOG - - return tf.train.LoggingTensorHook( - tensors=tensors_to_log, - every_n_iter=every_n_iter) - - -def get_profiler_hook(model_dir, save_steps=1000, **kwargs): # pylint: disable=unused-argument - """Function to get ProfilerHook. - - Args: - model_dir: The directory to save the profile traces to. - save_steps: `int`, print profile traces every N steps. - **kwargs: a dictionary of arguments to ProfilerHook. - - Returns: - Returns a ProfilerHook that writes out timelines that can be loaded into - profiling tools like chrome://tracing. - """ - return tf.train.ProfilerHook(save_steps=save_steps, output_dir=model_dir) - - -def get_examples_per_second_hook(every_n_steps=100, - batch_size=128, - warm_steps=5, - **kwargs): # pylint: disable=unused-argument - """Function to get ExamplesPerSecondHook. - - Args: - every_n_steps: `int`, print current and average examples per second every - N steps. - batch_size: `int`, total batch size used to calculate examples/second from - global time. - warm_steps: skip this number of steps before logging and running average. - **kwargs: a dictionary of arguments to ExamplesPerSecondHook. - - Returns: - Returns a ProfilerHook that writes out timelines that can be loaded into - profiling tools like chrome://tracing. - """ - return hooks.ExamplesPerSecondHook( - batch_size=batch_size, every_n_steps=every_n_steps, - warm_steps=warm_steps, metric_logger=logger.get_benchmark_logger()) - - -def get_logging_metric_hook(tensors_to_log=None, - every_n_secs=600, - **kwargs): # pylint: disable=unused-argument - """Function to get LoggingMetricHook. - - Args: - tensors_to_log: List of tensor names or dictionary mapping labels to tensor - names. If not set, log _TENSORS_TO_LOG by default. - every_n_secs: `int`, the frequency for logging the metric. Default to every - 10 mins. - - Returns: - Returns a LoggingMetricHook that saves tensor values in a JSON format. - """ - if tensors_to_log is None: - tensors_to_log = _TENSORS_TO_LOG - return metric_hook.LoggingMetricHook( - tensors=tensors_to_log, - metric_logger=logger.get_benchmark_logger(), - every_n_secs=every_n_secs) - - -# A dictionary to map one hook name and its corresponding function -HOOKS = { - 'loggingtensorhook': get_logging_tensor_hook, - 'profilerhook': get_profiler_hook, - 'examplespersecondhook': get_examples_per_second_hook, - 'loggingmetrichook': get_logging_metric_hook, -} diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/hooks_helper_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/hooks_helper_test.py deleted file mode 100644 index e6bc4df7..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/hooks_helper_test.py +++ /dev/null @@ -1,67 +0,0 @@ -# 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. -# ============================================================================== - -"""Tests for hooks_helper.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import unittest - -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.utils.logs import hooks_helper - - -class BaseTest(unittest.TestCase): - - def test_raise_in_non_list_names(self): - with self.assertRaises(ValueError): - hooks_helper.get_train_hooks( - 'LoggingTensorHook, ProfilerHook', model_dir="", batch_size=256) - - def test_raise_in_invalid_names(self): - invalid_names = ['StepCounterHook', 'StopAtStepHook'] - with self.assertRaises(ValueError): - hooks_helper.get_train_hooks(invalid_names, model_dir="", batch_size=256) - - def validate_train_hook_name(self, - test_hook_name, - expected_hook_name, - **kwargs): - returned_hook = hooks_helper.get_train_hooks( - [test_hook_name], model_dir="", **kwargs) - self.assertEqual(len(returned_hook), 1) - self.assertIsInstance(returned_hook[0], tf.train.SessionRunHook) - self.assertEqual(returned_hook[0].__class__.__name__.lower(), - expected_hook_name) - - def test_get_train_hooks_logging_tensor_hook(self): - self.validate_train_hook_name('LoggingTensorHook', 'loggingtensorhook') - - def test_get_train_hooks_profiler_hook(self): - self.validate_train_hook_name('ProfilerHook', 'profilerhook') - - def test_get_train_hooks_examples_per_second_hook(self): - self.validate_train_hook_name('ExamplesPerSecondHook', - 'examplespersecondhook') - - def test_get_logging_metric_hook(self): - test_hook_name = 'LoggingMetricHook' - self.validate_train_hook_name(test_hook_name, 'loggingmetrichook') - -if __name__ == '__main__': - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/hooks_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/hooks_test.py deleted file mode 100644 index 61813453..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/hooks_test.py +++ /dev/null @@ -1,157 +0,0 @@ -# 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. -# ============================================================================== - -"""Tests for hooks.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import time - -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.utils.logs import hooks -from official.utils.testing import mock_lib - -tf.logging.set_verbosity(tf.logging.DEBUG) - - -class ExamplesPerSecondHookTest(tf.test.TestCase): - """Tests for the ExamplesPerSecondHook. - - In the test, we explicitly run global_step tensor after train_op in order to - keep the global_step value and the train_op (which increase the glboal_step - by 1) consistent. This is to correct the discrepancies in reported global_step - value when running on GPUs. - """ - - def setUp(self): - """Mock out logging calls to verify if correct info is being monitored.""" - self._logger = mock_lib.MockBenchmarkLogger() - - self.graph = tf.Graph() - with self.graph.as_default(): - tf.train.create_global_step() - self.train_op = tf.assign_add(tf.train.get_global_step(), 1) - self.global_step = tf.train.get_global_step() - - def test_raise_in_both_secs_and_steps(self): - with self.assertRaises(ValueError): - hooks.ExamplesPerSecondHook( - batch_size=256, - every_n_steps=10, - every_n_secs=20, - metric_logger=self._logger) - - def test_raise_in_none_secs_and_steps(self): - with self.assertRaises(ValueError): - hooks.ExamplesPerSecondHook( - batch_size=256, - every_n_steps=None, - every_n_secs=None, - metric_logger=self._logger) - - def _validate_log_every_n_steps(self, every_n_steps, warm_steps): - hook = hooks.ExamplesPerSecondHook( - batch_size=256, - every_n_steps=every_n_steps, - warm_steps=warm_steps, - metric_logger=self._logger) - - with tf.train.MonitoredSession( - tf.train.ChiefSessionCreator(), [hook]) as mon_sess: - for _ in range(every_n_steps): - # Explicitly run global_step after train_op to get the accurate - # global_step value - mon_sess.run(self.train_op) - mon_sess.run(self.global_step) - # Nothing should be in the list yet - self.assertFalse(self._logger.logged_metric) - - mon_sess.run(self.train_op) - global_step_val = mon_sess.run(self.global_step) - - if global_step_val > warm_steps: - self._assert_metrics() - else: - # Nothing should be in the list yet - self.assertFalse(self._logger.logged_metric) - - # Add additional run to verify proper reset when called multiple times. - prev_log_len = len(self._logger.logged_metric) - mon_sess.run(self.train_op) - global_step_val = mon_sess.run(self.global_step) - - if every_n_steps == 1 and global_step_val > warm_steps: - # Each time, we log two additional metrics. Did exactly 2 get added? - self.assertEqual(len(self._logger.logged_metric), prev_log_len + 2) - else: - # No change in the size of the metric list. - self.assertEqual(len(self._logger.logged_metric), prev_log_len) - - def test_examples_per_sec_every_1_steps(self): - with self.graph.as_default(): - self._validate_log_every_n_steps(1, 0) - - def test_examples_per_sec_every_5_steps(self): - with self.graph.as_default(): - self._validate_log_every_n_steps(5, 0) - - def test_examples_per_sec_every_1_steps_with_warm_steps(self): - with self.graph.as_default(): - self._validate_log_every_n_steps(1, 10) - - def test_examples_per_sec_every_5_steps_with_warm_steps(self): - with self.graph.as_default(): - self._validate_log_every_n_steps(5, 10) - - def _validate_log_every_n_secs(self, every_n_secs): - hook = hooks.ExamplesPerSecondHook( - batch_size=256, - every_n_steps=None, - every_n_secs=every_n_secs, - metric_logger=self._logger) - - with tf.train.MonitoredSession( - tf.train.ChiefSessionCreator(), [hook]) as mon_sess: - # Explicitly run global_step after train_op to get the accurate - # global_step value - mon_sess.run(self.train_op) - mon_sess.run(self.global_step) - # Nothing should be in the list yet - self.assertFalse(self._logger.logged_metric) - time.sleep(every_n_secs) - - mon_sess.run(self.train_op) - mon_sess.run(self.global_step) - self._assert_metrics() - - def test_examples_per_sec_every_1_secs(self): - with self.graph.as_default(): - self._validate_log_every_n_secs(1) - - def test_examples_per_sec_every_5_secs(self): - with self.graph.as_default(): - self._validate_log_every_n_secs(5) - - def _assert_metrics(self): - metrics = self._logger.logged_metric - self.assertEqual(metrics[-2]["name"], "average_examples_per_sec") - self.assertEqual(metrics[-1]["name"], "current_examples_per_sec") - - -if __name__ == "__main__": - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/logger.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/logger.py deleted file mode 100644 index 0375e8ba..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/logger.py +++ /dev/null @@ -1,441 +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. -# ============================================================================== - -"""Logging utilities for benchmark. - -For collecting local environment metrics like CPU and memory, certain python -packages need be installed. See README for details. -""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import contextlib -import datetime -import json -import multiprocessing -import numbers -import os -import threading -import uuid - -from six.moves import _thread as thread -from absl import flags -import tensorflow as tf -from tensorflow.python.client import device_lib - -from official.utils.logs import cloud_lib - -METRIC_LOG_FILE_NAME = "metric.log" -BENCHMARK_RUN_LOG_FILE_NAME = "benchmark_run.log" -_DATE_TIME_FORMAT_PATTERN = "%Y-%m-%dT%H:%M:%S.%fZ" -GCP_TEST_ENV = "GCP" -RUN_STATUS_SUCCESS = "success" -RUN_STATUS_FAILURE = "failure" -RUN_STATUS_RUNNING = "running" - - -FLAGS = flags.FLAGS - -# Don't use it directly. Use get_benchmark_logger to access a logger. -_benchmark_logger = None -_logger_lock = threading.Lock() - - -def config_benchmark_logger(flag_obj=None): - """Config the global benchmark logger.""" - _logger_lock.acquire() - try: - global _benchmark_logger - if not flag_obj: - flag_obj = FLAGS - - if (not hasattr(flag_obj, "benchmark_logger_type") or - flag_obj.benchmark_logger_type == "BaseBenchmarkLogger"): - _benchmark_logger = BaseBenchmarkLogger() - elif flag_obj.benchmark_logger_type == "BenchmarkFileLogger": - _benchmark_logger = BenchmarkFileLogger(flag_obj.benchmark_log_dir) - elif flag_obj.benchmark_logger_type == "BenchmarkBigQueryLogger": - from official.benchmark import benchmark_uploader as bu # pylint: disable=g-import-not-at-top - bq_uploader = bu.BigQueryUploader(gcp_project=flag_obj.gcp_project) - _benchmark_logger = BenchmarkBigQueryLogger( - bigquery_uploader=bq_uploader, - bigquery_data_set=flag_obj.bigquery_data_set, - bigquery_run_table=flag_obj.bigquery_run_table, - bigquery_run_status_table=flag_obj.bigquery_run_status_table, - bigquery_metric_table=flag_obj.bigquery_metric_table, - run_id=str(uuid.uuid4())) - else: - raise ValueError("Unrecognized benchmark_logger_type: %s" - % flag_obj.benchmark_logger_type) - - finally: - _logger_lock.release() - return _benchmark_logger - - -def get_benchmark_logger(): - if not _benchmark_logger: - config_benchmark_logger() - return _benchmark_logger - - -@contextlib.contextmanager -def benchmark_context(flag_obj): - """Context of benchmark, which will update status of the run accordingly.""" - benchmark_logger = config_benchmark_logger(flag_obj) - try: - yield - benchmark_logger.on_finish(RUN_STATUS_SUCCESS) - except Exception: # pylint: disable=broad-except - # Catch all the exception, update the run status to be failure, and re-raise - benchmark_logger.on_finish(RUN_STATUS_FAILURE) - raise - - -class BaseBenchmarkLogger(object): - """Class to log the benchmark information to STDOUT.""" - - def log_evaluation_result(self, eval_results): - """Log the evaluation result. - - The evaluate result is a dictionary that contains metrics defined in - model_fn. It also contains a entry for global_step which contains the value - of the global step when evaluation was performed. - - Args: - eval_results: dict, the result of evaluate. - """ - if not isinstance(eval_results, dict): - tf.logging.warning("eval_results should be dictionary for logging. " - "Got %s", type(eval_results)) - return - global_step = eval_results[tf.GraphKeys.GLOBAL_STEP] - for key in sorted(eval_results): - if key != tf.GraphKeys.GLOBAL_STEP: - self.log_metric(key, eval_results[key], global_step=global_step) - - def log_metric(self, name, value, unit=None, global_step=None, extras=None): - """Log the benchmark metric information to local file. - - Currently the logging is done in a synchronized way. This should be updated - to log asynchronously. - - Args: - name: string, the name of the metric to log. - value: number, the value of the metric. The value will not be logged if it - is not a number type. - unit: string, the unit of the metric, E.g "image per second". - global_step: int, the global_step when the metric is logged. - extras: map of string:string, the extra information about the metric. - """ - metric = _process_metric_to_json(name, value, unit, global_step, extras) - if metric: - tf.logging.info("Benchmark metric: %s", metric) - - def log_run_info(self, model_name, dataset_name, run_params, test_id=None): - tf.logging.info("Benchmark run: %s", - _gather_run_info(model_name, dataset_name, run_params, - test_id)) - - def on_finish(self, status): - pass - - -class BenchmarkFileLogger(BaseBenchmarkLogger): - """Class to log the benchmark information to local disk.""" - - def __init__(self, logging_dir): - super(BenchmarkFileLogger, self).__init__() - self._logging_dir = logging_dir - if not tf.gfile.IsDirectory(self._logging_dir): - tf.gfile.MakeDirs(self._logging_dir) - self._metric_file_handler = tf.gfile.GFile( - os.path.join(self._logging_dir, METRIC_LOG_FILE_NAME), "a") - - def log_metric(self, name, value, unit=None, global_step=None, extras=None): - """Log the benchmark metric information to local file. - - Currently the logging is done in a synchronized way. This should be updated - to log asynchronously. - - Args: - name: string, the name of the metric to log. - value: number, the value of the metric. The value will not be logged if it - is not a number type. - unit: string, the unit of the metric, E.g "image per second". - global_step: int, the global_step when the metric is logged. - extras: map of string:string, the extra information about the metric. - """ - metric = _process_metric_to_json(name, value, unit, global_step, extras) - if metric: - try: - json.dump(metric, self._metric_file_handler) - self._metric_file_handler.write("\n") - self._metric_file_handler.flush() - except (TypeError, ValueError) as e: - tf.logging.warning("Failed to dump metric to log file: " - "name %s, value %s, error %s", name, value, e) - - def log_run_info(self, model_name, dataset_name, run_params, test_id=None): - """Collect most of the TF runtime information for the local env. - - The schema of the run info follows official/benchmark/datastore/schema. - - Args: - model_name: string, the name of the model. - dataset_name: string, the name of dataset for training and evaluation. - run_params: dict, the dictionary of parameters for the run, it could - include hyperparameters or other params that are important for the run. - test_id: string, the unique name of the test run by the combination of key - parameters, eg batch size, num of GPU. It is hardware independent. - """ - run_info = _gather_run_info(model_name, dataset_name, run_params, test_id) - - with tf.gfile.GFile(os.path.join( - self._logging_dir, BENCHMARK_RUN_LOG_FILE_NAME), "w") as f: - try: - json.dump(run_info, f) - f.write("\n") - except (TypeError, ValueError) as e: - tf.logging.warning("Failed to dump benchmark run info to log file: %s", - e) - - def on_finish(self, status): - self._metric_file_handler.flush() - self._metric_file_handler.close() - - -class BenchmarkBigQueryLogger(BaseBenchmarkLogger): - """Class to log the benchmark information to BigQuery data store.""" - - def __init__(self, - bigquery_uploader, - bigquery_data_set, - bigquery_run_table, - bigquery_run_status_table, - bigquery_metric_table, - run_id): - super(BenchmarkBigQueryLogger, self).__init__() - self._bigquery_uploader = bigquery_uploader - self._bigquery_data_set = bigquery_data_set - self._bigquery_run_table = bigquery_run_table - self._bigquery_run_status_table = bigquery_run_status_table - self._bigquery_metric_table = bigquery_metric_table - self._run_id = run_id - - def log_metric(self, name, value, unit=None, global_step=None, extras=None): - """Log the benchmark metric information to bigquery. - - Args: - name: string, the name of the metric to log. - value: number, the value of the metric. The value will not be logged if it - is not a number type. - unit: string, the unit of the metric, E.g "image per second". - global_step: int, the global_step when the metric is logged. - extras: map of string:string, the extra information about the metric. - """ - metric = _process_metric_to_json(name, value, unit, global_step, extras) - if metric: - # Starting new thread for bigquery upload in case it might take long time - # and impact the benchmark and performance measurement. Starting a new - # thread might have potential performance impact for model that run on - # CPU. - thread.start_new_thread( - self._bigquery_uploader.upload_benchmark_metric_json, - (self._bigquery_data_set, - self._bigquery_metric_table, - self._run_id, - [metric])) - - def log_run_info(self, model_name, dataset_name, run_params, test_id=None): - """Collect most of the TF runtime information for the local env. - - The schema of the run info follows official/benchmark/datastore/schema. - - Args: - model_name: string, the name of the model. - dataset_name: string, the name of dataset for training and evaluation. - run_params: dict, the dictionary of parameters for the run, it could - include hyperparameters or other params that are important for the run. - test_id: string, the unique name of the test run by the combination of key - parameters, eg batch size, num of GPU. It is hardware independent. - """ - run_info = _gather_run_info(model_name, dataset_name, run_params, test_id) - # Starting new thread for bigquery upload in case it might take long time - # and impact the benchmark and performance measurement. Starting a new - # thread might have potential performance impact for model that run on CPU. - thread.start_new_thread( - self._bigquery_uploader.upload_benchmark_run_json, - (self._bigquery_data_set, - self._bigquery_run_table, - self._run_id, - run_info)) - thread.start_new_thread( - self._bigquery_uploader.insert_run_status, - (self._bigquery_data_set, - self._bigquery_run_status_table, - self._run_id, - RUN_STATUS_RUNNING)) - - def on_finish(self, status): - self._bigquery_uploader.update_run_status( - self._bigquery_data_set, - self._bigquery_run_status_table, - self._run_id, - status) - - -def _gather_run_info(model_name, dataset_name, run_params, test_id): - """Collect the benchmark run information for the local environment.""" - run_info = { - "model_name": model_name, - "dataset": {"name": dataset_name}, - "machine_config": {}, - "test_id": test_id, - "run_date": datetime.datetime.utcnow().strftime( - _DATE_TIME_FORMAT_PATTERN)} - session_config = None - if "session_config" in run_params: - session_config = run_params["session_config"] - _collect_tensorflow_info(run_info) - _collect_tensorflow_environment_variables(run_info) - _collect_run_params(run_info, run_params) - #_collect_cpu_info(run_info) - _collect_gpu_info(run_info, session_config) - _collect_memory_info(run_info) - _collect_test_environment(run_info) - return run_info - - -def _process_metric_to_json( - name, value, unit=None, global_step=None, extras=None): - """Validate the metric data and generate JSON for insert.""" - if not isinstance(value, numbers.Number): - tf.logging.warning( - "Metric value to log should be a number. Got %s", type(value)) - return None - - extras = _convert_to_json_dict(extras) - return { - "name": name, - "value": float(value), - "unit": unit, - "global_step": global_step, - "timestamp": datetime.datetime.utcnow().strftime( - _DATE_TIME_FORMAT_PATTERN), - "extras": extras} - - -def _collect_tensorflow_info(run_info): - run_info["tensorflow_version"] = { - "version": tf.VERSION, "git_hash": tf.GIT_VERSION} - - -def _collect_run_params(run_info, run_params): - """Log the parameter information for the benchmark run.""" - def process_param(name, value): - type_check = { - str: {"name": name, "string_value": value}, - int: {"name": name, "long_value": value}, - bool: {"name": name, "bool_value": str(value)}, - float: {"name": name, "float_value": value}, - } - return type_check.get(type(value), - {"name": name, "string_value": str(value)}) - if run_params: - run_info["run_parameters"] = [ - process_param(k, v) for k, v in sorted(run_params.items())] - - -def _collect_tensorflow_environment_variables(run_info): - run_info["tensorflow_environment_variables"] = [ - {"name": k, "value": v} - for k, v in sorted(os.environ.items()) if k.startswith("TF_")] - - -# The following code is mirrored from tensorflow/tools/test/system_info_lib -# which is not exposed for import. -def _collect_cpu_info(run_info): - """Collect the CPU information for the local environment.""" - cpu_info = {} - - cpu_info["num_cores"] = multiprocessing.cpu_count() - - try: - # Note: cpuinfo is not installed in the TensorFlow OSS tree. - # It is installable via pip. - import cpuinfo # pylint: disable=g-import-not-at-top - - info = cpuinfo.get_cpu_info() - cpu_info["cpu_info"] = info["brand"] - cpu_info["mhz_per_cpu"] = info["hz_advertised_raw"][0] / 1.0e6 - - run_info["machine_config"]["cpu_info"] = cpu_info - except ImportError: - tf.logging.warn("'cpuinfo' not imported. CPU info will not be logged.") - - -def _collect_gpu_info(run_info, session_config=None): - """Collect local GPU information by TF device library.""" - gpu_info = {} - local_device_protos = device_lib.list_local_devices(session_config) - - gpu_info["count"] = len([d for d in local_device_protos - if d.device_type == "GPU"]) - # The device description usually is a JSON string, which contains the GPU - # model info, eg: - # "device: 0, name: Tesla P100-PCIE-16GB, pci bus id: 0000:00:04.0" - for d in local_device_protos: - if d.device_type == "GPU": - gpu_info["model"] = _parse_gpu_model(d.physical_device_desc) - # Assume all the GPU connected are same model - break - run_info["machine_config"]["gpu_info"] = gpu_info - - -def _collect_memory_info(run_info): - try: - # Note: psutil is not installed in the TensorFlow OSS tree. - # It is installable via pip. - import psutil # pylint: disable=g-import-not-at-top - vmem = psutil.virtual_memory() - run_info["machine_config"]["memory_total"] = vmem.total - run_info["machine_config"]["memory_available"] = vmem.available - except ImportError: - tf.logging.warn("'psutil' not imported. Memory info will not be logged.") - - -def _collect_test_environment(run_info): - """Detect the local environment, eg GCE, AWS or DGX, etc.""" - if cloud_lib.on_gcp(): - run_info["test_environment"] = GCP_TEST_ENV - # TODO(scottzhu): Add more testing env detection for other platform - - -def _parse_gpu_model(physical_device_desc): - # Assume all the GPU connected are same model - for kv in physical_device_desc.split(","): - k, _, v = kv.partition(":") - if k.strip() == "name": - return v.strip() - return None - - -def _convert_to_json_dict(input_dict): - if input_dict: - return [{"name": k, "value": v} for k, v in sorted(input_dict.items())] - else: - return [] diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/logger_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/logger_test.py deleted file mode 100644 index f02388d9..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/logger_test.py +++ /dev/null @@ -1,364 +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. -# ============================================================================== - -"""Tests for benchmark logger.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import json -import os -import tempfile -import time -import unittest - -import mock -from absl.testing import flagsaver -import tensorflow as tf # pylint: disable=g-bad-import-order - -try: - from google.cloud import bigquery -except ImportError: - bigquery = None - -from official.utils.flags import core as flags_core -from official.utils.logs import logger - - -class BenchmarkLoggerTest(tf.test.TestCase): - - @classmethod - def setUpClass(cls): # pylint: disable=invalid-name - super(BenchmarkLoggerTest, cls).setUpClass() - flags_core.define_benchmark() - - def test_get_default_benchmark_logger(self): - with flagsaver.flagsaver(benchmark_logger_type='foo'): - self.assertIsInstance(logger.get_benchmark_logger(), - logger.BaseBenchmarkLogger) - - def test_config_base_benchmark_logger(self): - with flagsaver.flagsaver(benchmark_logger_type='BaseBenchmarkLogger'): - logger.config_benchmark_logger() - self.assertIsInstance(logger.get_benchmark_logger(), - logger.BaseBenchmarkLogger) - - def test_config_benchmark_file_logger(self): - # Set the benchmark_log_dir first since the benchmark_logger_type will need - # the value to be set when it does the validation. - with flagsaver.flagsaver(benchmark_log_dir='/tmp'): - with flagsaver.flagsaver(benchmark_logger_type='BenchmarkFileLogger'): - logger.config_benchmark_logger() - self.assertIsInstance(logger.get_benchmark_logger(), - logger.BenchmarkFileLogger) - - @unittest.skipIf(bigquery is None, 'Bigquery dependency is not installed.') - @mock.patch.object(bigquery, "Client") - def test_config_benchmark_bigquery_logger(self, mock_bigquery_client): - with flagsaver.flagsaver(benchmark_logger_type='BenchmarkBigQueryLogger'): - logger.config_benchmark_logger() - self.assertIsInstance(logger.get_benchmark_logger(), - logger.BenchmarkBigQueryLogger) - - @mock.patch("official.utils.logs.logger.config_benchmark_logger") - def test_benchmark_context(self, mock_config_benchmark_logger): - mock_logger = mock.MagicMock() - mock_config_benchmark_logger.return_value = mock_logger - with logger.benchmark_context(None): - tf.logging.info("start benchmarking") - mock_logger.on_finish.assert_called_once_with(logger.RUN_STATUS_SUCCESS) - - @mock.patch("official.utils.logs.logger.config_benchmark_logger") - def test_benchmark_context_failure(self, mock_config_benchmark_logger): - mock_logger = mock.MagicMock() - mock_config_benchmark_logger.return_value = mock_logger - with self.assertRaises(RuntimeError): - with logger.benchmark_context(None): - raise RuntimeError("training error") - mock_logger.on_finish.assert_called_once_with(logger.RUN_STATUS_FAILURE) - - -class BaseBenchmarkLoggerTest(tf.test.TestCase): - - def setUp(self): - super(BaseBenchmarkLoggerTest, self).setUp() - self._actual_log = tf.logging.info - self.logged_message = None - - def mock_log(*args, **kwargs): - self.logged_message = args - self._actual_log(*args, **kwargs) - - tf.logging.info = mock_log - - def tearDown(self): - super(BaseBenchmarkLoggerTest, self).tearDown() - tf.logging.info = self._actual_log - - def test_log_metric(self): - log = logger.BaseBenchmarkLogger() - log.log_metric("accuracy", 0.999, global_step=1e4, extras={"name": "value"}) - - expected_log_prefix = "Benchmark metric:" - self.assertRegexpMatches(str(self.logged_message), expected_log_prefix) - - -class BenchmarkFileLoggerTest(tf.test.TestCase): - - def setUp(self): - super(BenchmarkFileLoggerTest, self).setUp() - # Avoid pulling extra env vars from test environment which affects the test - # result, eg. Kokoro test has a TF_PKG env which affect the test case - # test_collect_tensorflow_environment_variables() - self.original_environ = dict(os.environ) - os.environ.clear() - - def tearDown(self): - super(BenchmarkFileLoggerTest, self).tearDown() - tf.gfile.DeleteRecursively(self.get_temp_dir()) - os.environ.clear() - os.environ.update(self.original_environ) - - def test_create_logging_dir(self): - non_exist_temp_dir = os.path.join(self.get_temp_dir(), "unknown_dir") - self.assertFalse(tf.gfile.IsDirectory(non_exist_temp_dir)) - - logger.BenchmarkFileLogger(non_exist_temp_dir) - self.assertTrue(tf.gfile.IsDirectory(non_exist_temp_dir)) - - def test_log_metric(self): - log_dir = tempfile.mkdtemp(dir=self.get_temp_dir()) - log = logger.BenchmarkFileLogger(log_dir) - log.log_metric("accuracy", 0.999, global_step=1e4, extras={"name": "value"}) - - metric_log = os.path.join(log_dir, "metric.log") - self.assertTrue(tf.gfile.Exists(metric_log)) - with tf.gfile.GFile(metric_log) as f: - metric = json.loads(f.readline()) - self.assertEqual(metric["name"], "accuracy") - self.assertEqual(metric["value"], 0.999) - self.assertEqual(metric["unit"], None) - self.assertEqual(metric["global_step"], 1e4) - self.assertEqual(metric["extras"], [{"name": "name", "value": "value"}]) - - def test_log_multiple_metrics(self): - log_dir = tempfile.mkdtemp(dir=self.get_temp_dir()) - log = logger.BenchmarkFileLogger(log_dir) - log.log_metric("accuracy", 0.999, global_step=1e4, extras={"name": "value"}) - log.log_metric("loss", 0.02, global_step=1e4) - - metric_log = os.path.join(log_dir, "metric.log") - self.assertTrue(tf.gfile.Exists(metric_log)) - with tf.gfile.GFile(metric_log) as f: - accuracy = json.loads(f.readline()) - self.assertEqual(accuracy["name"], "accuracy") - self.assertEqual(accuracy["value"], 0.999) - self.assertEqual(accuracy["unit"], None) - self.assertEqual(accuracy["global_step"], 1e4) - self.assertEqual(accuracy["extras"], [{"name": "name", "value": "value"}]) - - loss = json.loads(f.readline()) - self.assertEqual(loss["name"], "loss") - self.assertEqual(loss["value"], 0.02) - self.assertEqual(loss["unit"], None) - self.assertEqual(loss["global_step"], 1e4) - self.assertEqual(loss["extras"], []) - - def test_log_non_number_value(self): - log_dir = tempfile.mkdtemp(dir=self.get_temp_dir()) - log = logger.BenchmarkFileLogger(log_dir) - const = tf.constant(1) - log.log_metric("accuracy", const) - - metric_log = os.path.join(log_dir, "metric.log") - self.assertFalse(tf.gfile.Exists(metric_log)) - - def test_log_evaluation_result(self): - eval_result = {"loss": 0.46237424, - "global_step": 207082, - "accuracy": 0.9285} - log_dir = tempfile.mkdtemp(dir=self.get_temp_dir()) - log = logger.BenchmarkFileLogger(log_dir) - log.log_evaluation_result(eval_result) - - metric_log = os.path.join(log_dir, "metric.log") - self.assertTrue(tf.gfile.Exists(metric_log)) - with tf.gfile.GFile(metric_log) as f: - accuracy = json.loads(f.readline()) - self.assertEqual(accuracy["name"], "accuracy") - self.assertEqual(accuracy["value"], 0.9285) - self.assertEqual(accuracy["unit"], None) - self.assertEqual(accuracy["global_step"], 207082) - - loss = json.loads(f.readline()) - self.assertEqual(loss["name"], "loss") - self.assertEqual(loss["value"], 0.46237424) - self.assertEqual(loss["unit"], None) - self.assertEqual(loss["global_step"], 207082) - - def test_log_evaluation_result_with_invalid_type(self): - eval_result = "{'loss': 0.46237424, 'global_step': 207082}" - log_dir = tempfile.mkdtemp(dir=self.get_temp_dir()) - log = logger.BenchmarkFileLogger(log_dir) - log.log_evaluation_result(eval_result) - - metric_log = os.path.join(log_dir, "metric.log") - self.assertFalse(tf.gfile.Exists(metric_log)) - - @mock.patch("official.utils.logs.logger._gather_run_info") - def test_log_run_info(self, mock_gather_run_info): - log_dir = tempfile.mkdtemp(dir=self.get_temp_dir()) - log = logger.BenchmarkFileLogger(log_dir) - run_info = {"model_name": "model_name", - "dataset": "dataset_name", - "run_info": "run_value"} - mock_gather_run_info.return_value = run_info - log.log_run_info("model_name", "dataset_name", {}) - - run_log = os.path.join(log_dir, "benchmark_run.log") - self.assertTrue(tf.gfile.Exists(run_log)) - with tf.gfile.GFile(run_log) as f: - run_info = json.loads(f.readline()) - self.assertEqual(run_info["model_name"], "model_name") - self.assertEqual(run_info["dataset"], "dataset_name") - self.assertEqual(run_info["run_info"], "run_value") - - def test_collect_tensorflow_info(self): - run_info = {} - logger._collect_tensorflow_info(run_info) - self.assertNotEqual(run_info["tensorflow_version"], {}) - self.assertEqual(run_info["tensorflow_version"]["version"], tf.VERSION) - self.assertEqual(run_info["tensorflow_version"]["git_hash"], tf.GIT_VERSION) - - def test_collect_run_params(self): - run_info = {} - run_parameters = { - "batch_size": 32, - "synthetic_data": True, - "train_epochs": 100.00, - "dtype": "fp16", - "resnet_size": 50, - "random_tensor": tf.constant(2.0) - } - logger._collect_run_params(run_info, run_parameters) - self.assertEqual(len(run_info["run_parameters"]), 6) - self.assertEqual(run_info["run_parameters"][0], - {"name": "batch_size", "long_value": 32}) - self.assertEqual(run_info["run_parameters"][1], - {"name": "dtype", "string_value": "fp16"}) - self.assertEqual(run_info["run_parameters"][2], - {"name": "random_tensor", "string_value": - "Tensor(\"Const:0\", shape=(), dtype=float32)"}) - self.assertEqual(run_info["run_parameters"][3], - {"name": "resnet_size", "long_value": 50}) - self.assertEqual(run_info["run_parameters"][4], - {"name": "synthetic_data", "bool_value": "True"}) - self.assertEqual(run_info["run_parameters"][5], - {"name": "train_epochs", "float_value": 100.00}) - - def test_collect_tensorflow_environment_variables(self): - os.environ["TF_ENABLE_WINOGRAD_NONFUSED"] = "1" - os.environ["TF_OTHER"] = "2" - os.environ["OTHER"] = "3" - - run_info = {} - logger._collect_tensorflow_environment_variables(run_info) - self.assertIsNotNone(run_info["tensorflow_environment_variables"]) - expected_tf_envs = [ - {"name": "TF_ENABLE_WINOGRAD_NONFUSED", "value": "1"}, - {"name": "TF_OTHER", "value": "2"}, - ] - self.assertEqual(run_info["tensorflow_environment_variables"], - expected_tf_envs) - - @unittest.skipUnless(tf.test.is_built_with_cuda(), "requires GPU") - def test_collect_gpu_info(self): - run_info = {"machine_config": {}} - logger._collect_gpu_info(run_info) - self.assertNotEqual(run_info["machine_config"]["gpu_info"], {}) - - def test_collect_memory_info(self): - run_info = {"machine_config": {}} - logger._collect_memory_info(run_info) - self.assertIsNotNone(run_info["machine_config"]["memory_total"]) - self.assertIsNotNone(run_info["machine_config"]["memory_available"]) - - -@unittest.skipIf(bigquery is None, 'Bigquery dependency is not installed.') -class BenchmarkBigQueryLoggerTest(tf.test.TestCase): - - def setUp(self): - super(BenchmarkBigQueryLoggerTest, self).setUp() - # Avoid pulling extra env vars from test environment which affects the test - # result, eg. Kokoro test has a TF_PKG env which affect the test case - # test_collect_tensorflow_environment_variables() - self.original_environ = dict(os.environ) - os.environ.clear() - - self.mock_bq_uploader = mock.MagicMock() - self.logger = logger.BenchmarkBigQueryLogger( - self.mock_bq_uploader, "dataset", "run_table", "run_status_table", - "metric_table", "run_id") - - def tearDown(self): - super(BenchmarkBigQueryLoggerTest, self).tearDown() - tf.gfile.DeleteRecursively(self.get_temp_dir()) - os.environ.clear() - os.environ.update(self.original_environ) - - def test_log_metric(self): - self.logger.log_metric( - "accuracy", 0.999, global_step=1e4, extras={"name": "value"}) - expected_metric_json = [{ - "name": "accuracy", - "value": 0.999, - "unit": None, - "global_step": 1e4, - "timestamp": mock.ANY, - "extras": [{"name": "name", "value": "value"}] - }] - # log_metric will call upload_benchmark_metric_json in a separate thread. - # Give it some grace period for the new thread before assert. - time.sleep(1) - self.mock_bq_uploader.upload_benchmark_metric_json.assert_called_once_with( - "dataset", "metric_table", "run_id", expected_metric_json) - - @mock.patch("official.utils.logs.logger._gather_run_info") - def test_log_run_info(self, mock_gather_run_info): - run_info = {"model_name": "model_name", - "dataset": "dataset_name", - "run_info": "run_value"} - mock_gather_run_info.return_value = run_info - self.logger.log_run_info("model_name", "dataset_name", {}) - # log_metric will call upload_benchmark_metric_json in a separate thread. - # Give it some grace period for the new thread before assert. - time.sleep(1) - self.mock_bq_uploader.upload_benchmark_run_json.assert_called_once_with( - "dataset", "run_table", "run_id", run_info) - self.mock_bq_uploader.insert_run_status.assert_called_once_with( - "dataset", "run_status_table", "run_id", "running") - - def test_on_finish(self): - self.logger.on_finish(logger.RUN_STATUS_SUCCESS) - # log_metric will call upload_benchmark_metric_json in a separate thread. - # Give it some grace period for the new thread before assert. - time.sleep(1) - self.mock_bq_uploader.update_run_status.assert_called_once_with( - "dataset", "run_status_table", "run_id", logger.RUN_STATUS_SUCCESS) - - -if __name__ == "__main__": - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/metric_hook.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/metric_hook.py deleted file mode 100644 index dd3cf466..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/metric_hook.py +++ /dev/null @@ -1,97 +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. -# ============================================================================== -"""Session hook for logging benchmark metric.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import tensorflow as tf # pylint: disable=g-bad-import-order - - -class LoggingMetricHook(tf.compat.v1.train.LoggingTensorHook): - """Hook to log benchmark metric information. - - This hook is very similar as tf.train.LoggingTensorHook, which logs given - tensors every N local steps, every N seconds, or at the end. The metric - information will be logged to given log_dir or via metric_logger in JSON - format, which can be consumed by data analysis pipeline later. - - Note that if `at_end` is True, `tensors` should not include any tensor - whose evaluation produces a side effect such as consuming additional inputs. - """ - - def __init__(self, tensors, metric_logger=None, - every_n_iter=None, every_n_secs=None, at_end=False): - """Initializer for LoggingMetricHook. - - Args: - tensors: `dict` that maps string-valued tags to tensors/tensor names, - or `iterable` of tensors/tensor names. - metric_logger: instance of `BenchmarkLogger`, the benchmark logger that - hook should use to write the log. - every_n_iter: `int`, print the values of `tensors` once every N local - steps taken on the current worker. - every_n_secs: `int` or `float`, print the values of `tensors` once every N - seconds. Exactly one of `every_n_iter` and `every_n_secs` should be - provided. - at_end: `bool` specifying whether to print the values of `tensors` at the - end of the run. - - Raises: - ValueError: - 1. `every_n_iter` is non-positive, or - 2. Exactly one of every_n_iter and every_n_secs should be provided. - 3. Exactly one of log_dir and metric_logger should be provided. - """ - super(LoggingMetricHook, self).__init__( - tensors=tensors, - every_n_iter=every_n_iter, - every_n_secs=every_n_secs, - at_end=at_end) - - if metric_logger is None: - raise ValueError("metric_logger should be provided.") - self._logger = metric_logger - - def begin(self): - super(LoggingMetricHook, self).begin() - self._global_step_tensor = tf.train.get_global_step() - if self._global_step_tensor is None: - raise RuntimeError( - "Global step should be created to use LoggingMetricHook.") - if self._global_step_tensor.name not in self._current_tensors: - self._current_tensors[self._global_step_tensor.name] = ( - self._global_step_tensor) - - def after_run(self, unused_run_context, run_values): - # should_trigger is a internal state that populated at before_run, and it is - # using self_timer to determine whether it should trigger. - if self._should_trigger: - self._log_metric(run_values.results) - - self._iter_count += 1 - - def end(self, session): - if self._log_at_end: - values = session.run(self._current_tensors) - self._log_metric(values) - - def _log_metric(self, tensor_values): - self._timer.update_last_triggered_step(self._iter_count) - global_step = tensor_values[self._global_step_tensor.name] - # self._tag_order is populated during the init of LoggingTensorHook - for tag in self._tag_order: - self._logger.log_metric(tag, tensor_values[tag], global_step=global_step) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/metric_hook_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/metric_hook_test.py deleted file mode 100644 index e7bfd07b..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/metric_hook_test.py +++ /dev/null @@ -1,217 +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. -# ============================================================================== -"""Tests for metric_hook.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import tempfile -import time - -import tensorflow as tf # pylint: disable=g-bad-import-order -from tensorflow.python.training import monitored_session # pylint: disable=g-bad-import-order - -from official.utils.logs import metric_hook -from official.utils.testing import mock_lib - - -class LoggingMetricHookTest(tf.test.TestCase): - """Tests for LoggingMetricHook.""" - - def setUp(self): - super(LoggingMetricHookTest, self).setUp() - - self._log_dir = tempfile.mkdtemp(dir=self.get_temp_dir()) - self._logger = mock_lib.MockBenchmarkLogger() - - def tearDown(self): - super(LoggingMetricHookTest, self).tearDown() - tf.gfile.DeleteRecursively(self.get_temp_dir()) - - def test_illegal_args(self): - with self.assertRaisesRegexp(ValueError, "nvalid every_n_iter"): - metric_hook.LoggingMetricHook(tensors=["t"], every_n_iter=0) - with self.assertRaisesRegexp(ValueError, "nvalid every_n_iter"): - metric_hook.LoggingMetricHook(tensors=["t"], every_n_iter=-10) - with self.assertRaisesRegexp(ValueError, "xactly one of"): - metric_hook.LoggingMetricHook( - tensors=["t"], every_n_iter=5, every_n_secs=5) - with self.assertRaisesRegexp(ValueError, "xactly one of"): - metric_hook.LoggingMetricHook(tensors=["t"]) - with self.assertRaisesRegexp(ValueError, "metric_logger"): - metric_hook.LoggingMetricHook(tensors=["t"], every_n_iter=5) - - def test_print_at_end_only(self): - with tf.Graph().as_default(), tf.Session() as sess: - tf.train.get_or_create_global_step() - t = tf.constant(42.0, name="foo") - train_op = tf.constant(3) - hook = metric_hook.LoggingMetricHook( - tensors=[t.name], at_end=True, metric_logger=self._logger) - hook.begin() - mon_sess = monitored_session._HookedSession(sess, [hook]) # pylint: disable=protected-access - sess.run(tf.global_variables_initializer()) - - for _ in range(3): - mon_sess.run(train_op) - self.assertEqual(self._logger.logged_metric, []) - - hook.end(sess) - self.assertEqual(len(self._logger.logged_metric), 1) - metric = self._logger.logged_metric[0] - self.assertRegexpMatches(metric["name"], "foo") - self.assertEqual(metric["value"], 42.0) - self.assertEqual(metric["unit"], None) - self.assertEqual(metric["global_step"], 0) - - def test_global_step_not_found(self): - with tf.Graph().as_default(): - t = tf.constant(42.0, name="foo") - hook = metric_hook.LoggingMetricHook( - tensors=[t.name], at_end=True, metric_logger=self._logger) - - with self.assertRaisesRegexp( - RuntimeError, "should be created to use LoggingMetricHook."): - hook.begin() - - def test_log_tensors(self): - with tf.Graph().as_default(), tf.Session() as sess: - tf.train.get_or_create_global_step() - t1 = tf.constant(42.0, name="foo") - t2 = tf.constant(43.0, name="bar") - train_op = tf.constant(3) - hook = metric_hook.LoggingMetricHook( - tensors=[t1, t2], at_end=True, metric_logger=self._logger) - hook.begin() - mon_sess = monitored_session._HookedSession(sess, [hook]) # pylint: disable=protected-access - sess.run(tf.global_variables_initializer()) - - for _ in range(3): - mon_sess.run(train_op) - self.assertEqual(self._logger.logged_metric, []) - - hook.end(sess) - self.assertEqual(len(self._logger.logged_metric), 2) - metric1 = self._logger.logged_metric[0] - self.assertRegexpMatches(str(metric1["name"]), "foo") - self.assertEqual(metric1["value"], 42.0) - self.assertEqual(metric1["unit"], None) - self.assertEqual(metric1["global_step"], 0) - - metric2 = self._logger.logged_metric[1] - self.assertRegexpMatches(str(metric2["name"]), "bar") - self.assertEqual(metric2["value"], 43.0) - self.assertEqual(metric2["unit"], None) - self.assertEqual(metric2["global_step"], 0) - - def _validate_print_every_n_steps(self, sess, at_end): - t = tf.constant(42.0, name="foo") - - train_op = tf.constant(3) - hook = metric_hook.LoggingMetricHook( - tensors=[t.name], every_n_iter=10, at_end=at_end, - metric_logger=self._logger) - hook.begin() - mon_sess = monitored_session._HookedSession(sess, [hook]) # pylint: disable=protected-access - sess.run(tf.global_variables_initializer()) - mon_sess.run(train_op) - self.assertRegexpMatches(str(self._logger.logged_metric), t.name) - for _ in range(3): - self._logger.logged_metric = [] - for _ in range(9): - mon_sess.run(train_op) - # assertNotRegexpMatches is not supported by python 3.1 and later - self.assertEqual(str(self._logger.logged_metric).find(t.name), -1) - mon_sess.run(train_op) - self.assertRegexpMatches(str(self._logger.logged_metric), t.name) - - # Add additional run to verify proper reset when called multiple times. - self._logger.logged_metric = [] - mon_sess.run(train_op) - # assertNotRegexpMatches is not supported by python 3.1 and later - self.assertEqual(str(self._logger.logged_metric).find(t.name), -1) - - self._logger.logged_metric = [] - hook.end(sess) - if at_end: - self.assertRegexpMatches(str(self._logger.logged_metric), t.name) - else: - # assertNotRegexpMatches is not supported by python 3.1 and later - self.assertEqual(str(self._logger.logged_metric).find(t.name), -1) - - def test_print_every_n_steps(self): - with tf.Graph().as_default(), tf.Session() as sess: - tf.train.get_or_create_global_step() - self._validate_print_every_n_steps(sess, at_end=False) - # Verify proper reset. - self._validate_print_every_n_steps(sess, at_end=False) - - def test_print_every_n_steps_and_end(self): - with tf.Graph().as_default(), tf.Session() as sess: - tf.train.get_or_create_global_step() - self._validate_print_every_n_steps(sess, at_end=True) - # Verify proper reset. - self._validate_print_every_n_steps(sess, at_end=True) - - def _validate_print_every_n_secs(self, sess, at_end): - t = tf.constant(42.0, name="foo") - train_op = tf.constant(3) - - hook = metric_hook.LoggingMetricHook( - tensors=[t.name], every_n_secs=1.0, at_end=at_end, - metric_logger=self._logger) - hook.begin() - mon_sess = monitored_session._HookedSession(sess, [hook]) # pylint: disable=protected-access - sess.run(tf.global_variables_initializer()) - - mon_sess.run(train_op) - self.assertRegexpMatches(str(self._logger.logged_metric), t.name) - - # assertNotRegexpMatches is not supported by python 3.1 and later - self._logger.logged_metric = [] - mon_sess.run(train_op) - self.assertEqual(str(self._logger.logged_metric).find(t.name), -1) - time.sleep(1.0) - - self._logger.logged_metric = [] - mon_sess.run(train_op) - self.assertRegexpMatches(str(self._logger.logged_metric), t.name) - - self._logger.logged_metric = [] - hook.end(sess) - if at_end: - self.assertRegexpMatches(str(self._logger.logged_metric), t.name) - else: - # assertNotRegexpMatches is not supported by python 3.1 and later - self.assertEqual(str(self._logger.logged_metric).find(t.name), -1) - - def test_print_every_n_secs(self): - with tf.Graph().as_default(), tf.Session() as sess: - tf.train.get_or_create_global_step() - self._validate_print_every_n_secs(sess, at_end=False) - # Verify proper reset. - self._validate_print_every_n_secs(sess, at_end=False) - - def test_print_every_n_secs_and_end(self): - with tf.Graph().as_default(), tf.Session() as sess: - tf.train.get_or_create_global_step() - self._validate_print_every_n_secs(sess, at_end=True) - # Verify proper reset. - self._validate_print_every_n_secs(sess, at_end=True) - - -if __name__ == "__main__": - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/mlperf_helper.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/mlperf_helper.py deleted file mode 100644 index 65a7435e..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/mlperf_helper.py +++ /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. -# ============================================================================== - -"""Wrapper for the mlperf logging utils. - -MLPerf compliance logging is only desired under a limited set of circumstances. -This module is intended to keep users from needing to consider logging (or -install the module) unless they are performing mlperf runs. -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -from collections import namedtuple -import json -import os -import re -import subprocess -import sys -import typing - -import tensorflow as tf - -_MIN_VERSION = (0, 0, 10) -_STACK_OFFSET = 2 - -SUDO = "sudo" if os.geteuid() else "" - -# This indirection is used in docker. -DROP_CACHE_LOC = os.getenv("DROP_CACHE_LOC", "/proc/sys/vm/drop_caches") - -_NCF_PREFIX = "NCF_RAW_" - -# TODO(robieta): move line parsing to mlperf util -_PREFIX = r"(?:{})?:::MLPv([0-9]+).([0-9]+).([0-9]+)".format(_NCF_PREFIX) -_BENCHMARK = r"([a-zA-Z0-9_]+)" -_TIMESTAMP = r"([0-9]+\.[0-9]+)" -_CALLSITE = r"\((.+):([0-9]+)\)" -_TAG = r"([a-zA-Z0-9_]+)" -_VALUE = r"(.*)" - -ParsedLine = namedtuple("ParsedLine", ["version", "benchmark", "timestamp", - "callsite", "tag", "value"]) - -LINE_PATTERN = re.compile( - "^{prefix} {benchmark} {timestamp} {callsite} {tag}(: |$){value}?$".format( - prefix=_PREFIX, benchmark=_BENCHMARK, timestamp=_TIMESTAMP, - callsite=_CALLSITE, tag=_TAG, value=_VALUE)) - - -def parse_line(line): # type: (str) -> typing.Optional[ParsedLine] - match = LINE_PATTERN.match(line.strip()) - if not match: - return - - major, minor, micro, benchmark, timestamp = match.groups()[:5] - call_file, call_line, tag, _, value = match.groups()[5:] - - return ParsedLine(version=(int(major), int(minor), int(micro)), - benchmark=benchmark, timestamp=timestamp, - callsite=(call_file, call_line), tag=tag, value=value) - - -def unparse_line(parsed_line): # type: (ParsedLine) -> str - version_str = "{}.{}.{}".format(*parsed_line.version) - callsite_str = "({}:{})".format(*parsed_line.callsite) - value_str = ": {}".format(parsed_line.value) if parsed_line.value else "" - return ":::MLPv{} {} {} {} {} {}".format( - version_str, parsed_line.benchmark, parsed_line.timestamp, callsite_str, - parsed_line.tag, value_str) - - -def get_mlperf_log(): - """Shielded import of mlperf_log module.""" - try: - import mlperf_compliance - - def test_mlperf_log_pip_version(): - """Check that mlperf_compliance is up to date.""" - import pkg_resources - version = pkg_resources.get_distribution("mlperf_compliance") - version = tuple(int(i) for i in version.version.split(".")) - if version < _MIN_VERSION: - tf.logging.warning( - "mlperf_compliance is version {}, must be >= {}".format( - ".".join([str(i) for i in version]), - ".".join([str(i) for i in _MIN_VERSION]))) - raise ImportError - return mlperf_compliance.mlperf_log - - mlperf_log = test_mlperf_log_pip_version() - - except ImportError: - mlperf_log = None - - return mlperf_log - - -class Logger(object): - """MLPerf logger indirection class. - - This logger only logs for MLPerf runs, and prevents various errors associated - with not having the mlperf_compliance package installed. - """ - class Tags(object): - def __init__(self, mlperf_log): - self._enabled = False - self._mlperf_log = mlperf_log - - def __getattr__(self, item): - if self._mlperf_log is None or not self._enabled: - return - return getattr(self._mlperf_log, item) - - def __init__(self): - self._enabled = False - self._mlperf_log = get_mlperf_log() - self.tags = self.Tags(self._mlperf_log) - - def __call__(self, enable=False): - if enable and self._mlperf_log is None: - raise ImportError("MLPerf logging was requested, but mlperf_compliance " - "module could not be loaded.") - - self._enabled = enable - self.tags._enabled = enable - return self - - def __enter__(self): - pass - - def __exit__(self, exc_type, exc_val, exc_tb): - self._enabled = False - self.tags._enabled = False - - @property - def log_file(self): - if self._mlperf_log is None: - return - return self._mlperf_log.LOG_FILE - - @property - def enabled(self): - return self._enabled - - def ncf_print(self, key, value=None, stack_offset=_STACK_OFFSET, - deferred=False, extra_print=False, prefix=_NCF_PREFIX): - if self._mlperf_log is None or not self.enabled: - return - self._mlperf_log.ncf_print(key=key, value=value, stack_offset=stack_offset, - deferred=deferred, extra_print=extra_print, - prefix=prefix) - - def set_ncf_root(self, path): - if self._mlperf_log is None: - return - self._mlperf_log.ROOT_DIR_NCF = path - - -LOGGER = Logger() -ncf_print, set_ncf_root = LOGGER.ncf_print, LOGGER.set_ncf_root -TAGS = LOGGER.tags - - -def clear_system_caches(): - if not LOGGER.enabled: - return - ret_code = subprocess.call( - ["sync && echo 3 | {} tee {}".format(SUDO, DROP_CACHE_LOC)], - shell=True) - - if ret_code: - raise ValueError("Failed to clear caches") - - -if __name__ == "__main__": - tf.logging.set_verbosity(tf.logging.INFO) - with LOGGER(True): - ncf_print(key=TAGS.RUN_START) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/misc/__init__.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/misc/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/misc/distribution_utils.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/misc/distribution_utils.py deleted file mode 100644 index 7f618dc2..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/misc/distribution_utils.py +++ /dev/null @@ -1,98 +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. -# ============================================================================== -"""Helper functions for running models in a distributed setting.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import tensorflow as tf - - -def get_distribution_strategy(num_gpus, - all_reduce_alg=None, - turn_off_distribution_strategy=False): - """Return a DistributionStrategy for running the model. - - Args: - num_gpus: Number of GPUs to run this model. - all_reduce_alg: Specify which algorithm to use when performing all-reduce. - See tf.contrib.distribute.AllReduceCrossDeviceOps for available - algorithms. If None, DistributionStrategy will choose based on device - topology. - turn_off_distribution_strategy: when set to True, do not use any - distribution strategy. Note that when it is True, and num_gpus is - larger than 1, it will raise a ValueError. - - Returns: - tf.contrib.distribute.DistibutionStrategy object. - Raises: - ValueError: if turn_off_distribution_strategy is True and num_gpus is - larger than 1 - """ - if num_gpus == 0: - if turn_off_distribution_strategy: - return None - else: - return tf.contrib.distribute.OneDeviceStrategy("device:CPU:0") - elif num_gpus == 1: - if turn_off_distribution_strategy: - return None - else: - return tf.contrib.distribute.OneDeviceStrategy("device:GPU:0") - elif turn_off_distribution_strategy: - raise ValueError("When {} GPUs are specified, " - "turn_off_distribution_strategy flag cannot be set to" - "True.".format(num_gpus)) - else: # num_gpus > 1 and not turn_off_distribution_strategy - devices = ["device:GPU:%d" % i for i in range(num_gpus)] - if all_reduce_alg: - return tf.distribute.MirroredStrategy( - devices=devices, - cross_device_ops=tf.contrib.distribute.AllReduceCrossDeviceOps( - all_reduce_alg, num_packs=2)) - else: - return tf.distribute.MirroredStrategy(devices=devices) - - -def per_device_batch_size(batch_size, num_gpus): - """For multi-gpu, batch-size must be a multiple of the number of GPUs. - - - Note that distribution strategy handles this automatically when used with - Keras. For using with Estimator, we need to get per GPU batch. - - Args: - batch_size: Global batch size to be divided among devices. This should be - equal to num_gpus times the single-GPU batch_size for multi-gpu training. - num_gpus: How many GPUs are used with DistributionStrategies. - - Returns: - Batch size per device. - - Raises: - ValueError: if batch_size is not divisible by number of devices - """ - if num_gpus <= 1: - return batch_size - - remainder = batch_size % num_gpus - if remainder: - err = ("When running with multiple GPUs, batch size " - "must be a multiple of the number of available GPUs. Found {} " - "GPUs with a batch size of {}; try --batch_size={} instead." - ).format(num_gpus, batch_size, batch_size - remainder) - raise ValueError(err) - return int(batch_size / num_gpus) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/misc/distribution_utils_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/misc/distribution_utils_test.py deleted file mode 100644 index 678ad060..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/misc/distribution_utils_test.py +++ /dev/null @@ -1,65 +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. -# ============================================================================== -""" Tests for distribution util functions.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.utils.misc import distribution_utils - - -class GetDistributionStrategyTest(tf.test.TestCase): - """Tests for get_distribution_strategy.""" - def test_one_device_strategy_cpu(self): - ds = distribution_utils.get_distribution_strategy(0) - self.assertEquals(ds.num_replicas_in_sync, 1) - self.assertEquals(len(ds.extended.worker_devices), 1) - self.assertIn('CPU', ds.extended.worker_devices[0]) - - def test_one_device_strategy_gpu(self): - ds = distribution_utils.get_distribution_strategy(1) - self.assertEquals(ds.num_replicas_in_sync, 1) - self.assertEquals(len(ds.extended.worker_devices), 1) - self.assertIn('GPU', ds.extended.worker_devices[0]) - - def test_mirrored_strategy(self): - ds = distribution_utils.get_distribution_strategy(5) - self.assertEquals(ds.num_replicas_in_sync, 5) - self.assertEquals(len(ds.extended.worker_devices), 5) - for device in ds.extended.worker_devices: - self.assertIn('GPU', device) - - -class PerDeviceBatchSizeTest(tf.test.TestCase): - """Tests for per_device_batch_size.""" - - def test_batch_size(self): - self.assertEquals( - distribution_utils.per_device_batch_size(147, num_gpus=0), 147) - self.assertEquals( - distribution_utils.per_device_batch_size(147, num_gpus=1), 147) - self.assertEquals( - distribution_utils.per_device_batch_size(147, num_gpus=7), 21) - - def test_batch_size_with_remainder(self): - with self.assertRaises(ValueError): - distribution_utils.per_device_batch_size(147, num_gpus=5) - - -if __name__ == "__main__": - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/misc/model_helpers.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/misc/model_helpers.py deleted file mode 100644 index 9c452235..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/misc/model_helpers.py +++ /dev/null @@ -1,93 +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. -# ============================================================================== -"""Miscellaneous functions that can be called by models.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import numbers - -import tensorflow as tf -from tensorflow.python.util import nest - - -def past_stop_threshold(stop_threshold, eval_metric): - """Return a boolean representing whether a model should be stopped. - - Args: - stop_threshold: float, the threshold above which a model should stop - training. - eval_metric: float, the current value of the relevant metric to check. - - Returns: - True if training should stop, False otherwise. - - Raises: - ValueError: if either stop_threshold or eval_metric is not a number - """ - if stop_threshold is None: - return False - - if not isinstance(stop_threshold, numbers.Number): - raise ValueError("Threshold for checking stop conditions must be a number.") - if not isinstance(eval_metric, numbers.Number): - raise ValueError("Eval metric being checked against stop conditions " - "must be a number.") - - if eval_metric >= stop_threshold: - tf.logging.info( - "Stop threshold of {} was passed with metric value {}.".format( - stop_threshold, eval_metric)) - return True - - return False - - -def generate_synthetic_data( - input_shape, input_value=0, input_dtype=None, label_shape=None, - label_value=0, label_dtype=None): - """Create a repeating dataset with constant values. - - Args: - input_shape: a tf.TensorShape object or nested tf.TensorShapes. The shape of - the input data. - input_value: Value of each input element. - input_dtype: Input dtype. If None, will be inferred by the input value. - label_shape: a tf.TensorShape object or nested tf.TensorShapes. The shape of - the label data. - label_value: Value of each input element. - label_dtype: Input dtype. If None, will be inferred by the target value. - - Returns: - Dataset of tensors or tuples of tensors (if label_shape is set). - """ - # TODO(kathywu): Replace with SyntheticDataset once it is in contrib. - element = input_element = nest.map_structure( - lambda s: tf.constant(input_value, input_dtype, s), input_shape) - - if label_shape: - label_element = nest.map_structure( - lambda s: tf.constant(label_value, label_dtype, s), label_shape) - element = (input_element, label_element) - - return tf.data.Dataset.from_tensors(element).repeat() - - -def apply_clean(flags_obj): - if flags_obj.clean and tf.gfile.Exists(flags_obj.model_dir): - tf.logging.info("--clean flag set. Removing existing model dir: {}".format( - flags_obj.model_dir)) - tf.gfile.DeleteRecursively(flags_obj.model_dir) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/misc/model_helpers_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/misc/model_helpers_test.py deleted file mode 100644 index ff7f9b4b..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/misc/model_helpers_test.py +++ /dev/null @@ -1,121 +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. -# ============================================================================== -""" Tests for Model Helper functions.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.utils.misc import model_helpers - - -class PastStopThresholdTest(tf.test.TestCase): - """Tests for past_stop_threshold.""" - - def test_past_stop_threshold(self): - """Tests for normal operating conditions.""" - self.assertTrue(model_helpers.past_stop_threshold(0.54, 1)) - self.assertTrue(model_helpers.past_stop_threshold(54, 100)) - self.assertFalse(model_helpers.past_stop_threshold(0.54, 0.1)) - self.assertFalse(model_helpers.past_stop_threshold(-0.54, -1.5)) - self.assertTrue(model_helpers.past_stop_threshold(-0.54, 0)) - self.assertTrue(model_helpers.past_stop_threshold(0, 0)) - self.assertTrue(model_helpers.past_stop_threshold(0.54, 0.54)) - - def test_past_stop_threshold_none_false(self): - """Tests that check None returns false.""" - self.assertFalse(model_helpers.past_stop_threshold(None, -1.5)) - self.assertFalse(model_helpers.past_stop_threshold(None, None)) - self.assertFalse(model_helpers.past_stop_threshold(None, 1.5)) - # Zero should be okay, though. - self.assertTrue(model_helpers.past_stop_threshold(0, 1.5)) - - def test_past_stop_threshold_not_number(self): - """Tests for error conditions.""" - with self.assertRaises(ValueError): - model_helpers.past_stop_threshold("str", 1) - - with self.assertRaises(ValueError): - model_helpers.past_stop_threshold("str", tf.constant(5)) - - with self.assertRaises(ValueError): - model_helpers.past_stop_threshold("str", "another") - - with self.assertRaises(ValueError): - model_helpers.past_stop_threshold(0, None) - - with self.assertRaises(ValueError): - model_helpers.past_stop_threshold(0.7, "str") - - with self.assertRaises(ValueError): - model_helpers.past_stop_threshold(tf.constant(4), None) - - -class SyntheticDataTest(tf.test.TestCase): - """Tests for generate_synthetic_data.""" - - def test_generate_synethetic_data(self): - input_element, label_element = model_helpers.generate_synthetic_data( - input_shape=tf.TensorShape([5]), - input_value=123, - input_dtype=tf.float32, - label_shape=tf.TensorShape([]), - label_value=456, - label_dtype=tf.int32).make_one_shot_iterator().get_next() - - with self.test_session() as sess: - for n in range(5): - inp, lab = sess.run((input_element, label_element)) - self.assertAllClose(inp, [123., 123., 123., 123., 123.]) - self.assertEquals(lab, 456) - - def test_generate_only_input_data(self): - d = model_helpers.generate_synthetic_data( - input_shape=tf.TensorShape([4]), - input_value=43.5, - input_dtype=tf.float32) - - element = d.make_one_shot_iterator().get_next() - self.assertFalse(isinstance(element, tuple)) - - with self.test_session() as sess: - inp = sess.run(element) - self.assertAllClose(inp, [43.5, 43.5, 43.5, 43.5]) - - def test_generate_nested_data(self): - d = model_helpers.generate_synthetic_data( - input_shape={'a': tf.TensorShape([2]), - 'b': {'c': tf.TensorShape([3]), 'd': tf.TensorShape([])}}, - input_value=1.1) - - element = d.make_one_shot_iterator().get_next() - self.assertIn('a', element) - self.assertIn('b', element) - self.assertEquals(len(element['b']), 2) - self.assertIn('c', element['b']) - self.assertIn('d', element['b']) - self.assertNotIn('c', element) - - with self.test_session() as sess: - inp = sess.run(element) - self.assertAllClose(inp['a'], [1.1, 1.1]) - self.assertAllClose(inp['b']['c'], [1.1, 1.1, 1.1]) - self.assertAllClose(inp['b']['d'], 1.1) - - -if __name__ == "__main__": - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/__init__.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/integration.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/integration.py deleted file mode 100644 index e6532c5c..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/integration.py +++ /dev/null @@ -1,65 +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. -# ============================================================================== -"""Helper code to run complete models from within python. -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os -import shutil -import sys -import tempfile - -from absl import flags - -from official.utils.flags import core as flags_core - - -def run_synthetic(main, tmp_root, extra_flags=None, synth=True, max_train=1): - """Performs a minimal run of a model. - - This function is intended to test for syntax errors throughout a model. A - very limited run is performed using synthetic data. - - Args: - main: The primary function used to exercise a code path. Generally this - function is ".main(argv)". - tmp_root: Root path for the temp directory created by the test class. - extra_flags: Additional flags passed by the caller of this function. - synth: Use synthetic data. - max_train: Maximum number of allowed training steps. - """ - - extra_flags = [] if extra_flags is None else extra_flags - - model_dir = tempfile.mkdtemp(dir=tmp_root) - - args = [sys.argv[0], "--model_dir", model_dir, "--train_epochs", "1", - "--epochs_between_evals", "1"] + extra_flags - - if synth: - args.append("--use_synthetic_data") - - if max_train is not None: - args.extend(["--max_train_steps", str(max_train)]) - - try: - flags_core.parse_flags(argv=args) - main(flags.FLAGS) - finally: - if os.path.exists(model_dir): - shutil.rmtree(model_dir) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/pylint.rcfile b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/pylint.rcfile deleted file mode 100644 index 009ee7d1..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/pylint.rcfile +++ /dev/null @@ -1,169 +0,0 @@ -[MESSAGES CONTROL] -disable=R,W, - bad-option-value - -[REPORTS] -# Tells whether to display a full report or only the messages -reports=no - -# Activate the evaluation score. -score=no - -[BASIC] - -# Regular expression matching correct argument names -argument-rgx=^[a-z][a-z0-9_]*$ - -# Regular expression matching correct attribute names -attr-rgx=^_{0,2}[a-z][a-z0-9_]*$ - -# Regular expression matching correct class attribute names -class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ - -# Regular expression matching correct class names -class-rgx=^_?[A-Z][a-zA-Z0-9]*$ - -# Regular expression matching correct constant names -const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=10 - -# Regular expression matching correct function names -function-rgx=^(?:(?P_?[A-Z][a-zA-Z0-9]*)|(?P_?[a-z][a-z0-9_]*))$ - -# Good variable names which should always be accepted, separated by a comma -good-names=main,_ - -# Regular expression matching correct inline iteration names -inlinevar-rgx=^[a-z][a-z0-9_]*$ - -# Regular expression matching correct method names -method-rgx=^(?:(?P__[a-z0-9_]+__|next)|(?P_{0,2}[A-Z][a-zA-Z0-9]*)|(?P_{0,2}[a-z][a-z0-9_]*)|(setUp|tearDown))$ - -# Regular expression matching correct module names -module-rgx=^(_?[a-z][a-z0-9_]*)|__init__|PRESUBMIT|PRESUBMIT_unittest$ - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=(__.*__|main|.*ArgParser) - -# Naming hint for variable names -variable-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression matching correct variable names -variable-rgx=^[a-z][a-z0-9_]*$ - -[TYPECHECK] - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis. It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules=absl, absl.*, official, official.*, tensorflow, tensorflow.*, LazyLoader, google, google.cloud.* - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict,_fields,_replace,_source,_make - -# This is deprecated, because it is not used anymore. -#ignore-iface-methods= - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls,class_ - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs - - -[DESIGN] - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore -ignored-argument-names=_.* - -# Maximum number of arguments for function / method -max-args=5 - -# Maximum number of attributes for a class (see R0902). -max-attributes=7 - -# Maximum number of branch for function / method body -max-branches=12 - -# Maximum number of locals for function / method body -max-locals=15 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - -# Maximum number of return / yield for function / method body -max-returns=6 - -# Maximum number of statements in function / method body -max-statements=50 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=2 - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=StandardError,Exception,BaseException - - -[FORMAT] - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Maximum number of characters on a single line. -max-line-length=80 - -# Maximum number of lines in a module -max-module-lines=99999 - -# List of optional constructs for which whitespace checking is disabled -no-space-check= - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=yes - -# Allow URLs and comment type annotations to exceed the max line length as neither can be easily -# split across lines. -ignore-long-lines=^\s*(?:(# )??$|# type:) - - -[VARIABLES] - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_,_cb - -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_) - -# Tells whether we should check for unused import in __init__ files. -init-import=no diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data.py deleted file mode 100644 index aaae9116..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data.py +++ /dev/null @@ -1,334 +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. -# ============================================================================== -"""TensorFlow testing subclass to automate numerical testing. - -Reference tests determine when behavior deviates from some "gold standard," and -are useful for determining when layer definitions have changed without -performing full regression testing, which is generally prohibitive. This class -handles the symbolic graph comparison as well as loading weights to avoid -relying on random number generation, which can change. - -The tests performed by this class are: - -1) Compare a generated graph against a reference graph. Differences are not - necessarily fatal. -2) Attempt to load known weights for the graph. If this step succeeds but - changes are present in the graph, a warning is issued but does not raise - an exception. -3) Perform a calculation and compare the result to a reference value. - -This class also provides a method to generate reference data. - -Note: - The test class is responsible for fixing the random seed during graph - definition. A convenience method name_to_seed() is provided to make this - process easier. - -The test class should also define a .regenerate() class method which (usually) -just calls the op definition function with test=False for all relevant tests. - -A concise example of this class in action is provided in: - official/utils/testing/reference_data_test.py -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import argparse -import hashlib -import json -import os -import shutil -import sys - -import numpy as np -import tensorflow as tf -from tensorflow.python import pywrap_tensorflow - - -class BaseTest(tf.test.TestCase): - """TestCase subclass for performing reference data tests.""" - - def regenerate(self): - """Subclasses should override this function to generate a new reference.""" - raise NotImplementedError - - @property - def test_name(self): - """Subclass should define its own name.""" - raise NotImplementedError - - @property - def data_root(self): - """Use the subclass directory rather than the parent directory. - - Returns: - The path prefix for reference data. - """ - return os.path.join(os.path.split( - os.path.abspath(__file__))[0], "reference_data", self.test_name) - - ckpt_prefix = "model.ckpt" - - @staticmethod - def name_to_seed(name): - """Convert a string into a 32 bit integer. - - This function allows test cases to easily generate random fixed seeds by - hashing the name of the test. The hash string is in hex rather than base 10 - which is why there is a 16 in the int call, and the modulo projects the - seed from a 128 bit int to 32 bits for readability. - - Args: - name: A string containing the name of a test. - - Returns: - A pseudo-random 32 bit integer derived from name. - """ - seed = hashlib.md5(name.encode("utf-8")).hexdigest() - return int(seed, 16) % (2**32 - 1) - - @staticmethod - def common_tensor_properties(input_array): - """Convenience function for matrix testing. - - In tests we wish to determine whether a result has changed. However storing - an entire n-dimensional array is impractical. A better approach is to - calculate several values from that array and test that those derived values - are unchanged. The properties themselves are arbitrary and should be chosen - to be good proxies for a full equality test. - - Args: - input_array: A numpy array from which key values are extracted. - - Returns: - A list of values derived from the input_array for equality tests. - """ - output = list(input_array.shape) - flat_array = input_array.flatten() - output.extend([float(i) for i in - [flat_array[0], flat_array[-1], np.sum(flat_array)]]) - return output - - def default_correctness_function(self, *args): - """Returns a vector with the concatenation of common properties. - - This function simply calls common_tensor_properties() for every element. - It is useful as it allows one to easily construct tests of layers without - having to worry about the details of result checking. - - Args: - *args: A list of numpy arrays corresponding to tensors which have been - evaluated. - - Returns: - A list of values containing properties for every element in args. - """ - output = [] - for arg in args: - output.extend(self.common_tensor_properties(arg)) - return output - - def _construct_and_save_reference_files( - self, name, graph, ops_to_eval, correctness_function): - """Save reference data files. - - Constructs a serialized graph_def, layer weights, and computation results. - It then saves them to files which are read at test time. - - Args: - name: String defining the run. This will be used to define folder names - and will be used for random seed construction. - graph: The graph in which the test is conducted. - ops_to_eval: Ops which the user wishes to be evaluated under a controlled - session. - correctness_function: This function accepts the evaluated results of - ops_to_eval, and returns a list of values. This list must be JSON - serializable; in particular it is up to the user to convert numpy - dtypes into builtin dtypes. - """ - data_dir = os.path.join(self.data_root, name) - - # Make sure there is a clean space for results. - if os.path.exists(data_dir): - shutil.rmtree(data_dir) - os.makedirs(data_dir) - - # Serialize graph for comparison. - graph_bytes = graph.as_graph_def().SerializeToString() - expected_file = os.path.join(data_dir, "expected_graph") - with tf.gfile.Open(expected_file, "wb") as f: - f.write(graph_bytes) - - with graph.as_default(): - init = tf.global_variables_initializer() - saver = tf.train.Saver() - - with self.test_session(graph=graph) as sess: - sess.run(init) - saver.save(sess=sess, save_path=os.path.join(data_dir, self.ckpt_prefix)) - - # These files are not needed for this test. - os.remove(os.path.join(data_dir, "checkpoint")) - os.remove(os.path.join(data_dir, self.ckpt_prefix + ".meta")) - - # ops are evaluated even if there is no correctness function to ensure - # that they can be evaluated. - eval_results = [op.eval() for op in ops_to_eval] - - if correctness_function is not None: - results = correctness_function(*eval_results) - with tf.gfile.Open(os.path.join(data_dir, "results.json"), "w") as f: - json.dump(results, f) - - with tf.gfile.Open(os.path.join(data_dir, "tf_version.json"), "w") as f: - json.dump([tf.VERSION, tf.GIT_VERSION], f) - - def _evaluate_test_case(self, name, graph, ops_to_eval, correctness_function): - """Determine if a graph agrees with the reference data. - - Args: - name: String defining the run. This will be used to define folder names - and will be used for random seed construction. - graph: The graph in which the test is conducted. - ops_to_eval: Ops which the user wishes to be evaluated under a controlled - session. - correctness_function: This function accepts the evaluated results of - ops_to_eval, and returns a list of values. This list must be JSON - serializable; in particular it is up to the user to convert numpy - dtypes into builtin dtypes. - """ - data_dir = os.path.join(self.data_root, name) - - # Serialize graph for comparison. - graph_bytes = graph.as_graph_def().SerializeToString() - expected_file = os.path.join(data_dir, "expected_graph") - with tf.gfile.Open(expected_file, "rb") as f: - expected_graph_bytes = f.read() - # The serialization is non-deterministic byte-for-byte. Instead there is - # a utility which evaluates the semantics of the two graphs to test for - # equality. This has the added benefit of providing some information on - # what changed. - # Note: The summary only show the first difference detected. It is not - # an exhaustive summary of differences. - differences = pywrap_tensorflow.EqualGraphDefWrapper( - graph_bytes, expected_graph_bytes).decode("utf-8") - - with graph.as_default(): - init = tf.global_variables_initializer() - saver = tf.train.Saver() - - with tf.gfile.Open(os.path.join(data_dir, "tf_version.json"), "r") as f: - tf_version_reference, tf_git_version_reference = json.load(f) # pylint: disable=unpacking-non-sequence - - tf_version_comparison = "" - if tf.GIT_VERSION != tf_git_version_reference: - tf_version_comparison = ( - "Test was built using: {} (git = {})\n" - "Local TensorFlow version: {} (git = {})" - .format(tf_version_reference, tf_git_version_reference, - tf.VERSION, tf.GIT_VERSION) - ) - - with self.test_session(graph=graph) as sess: - sess.run(init) - try: - saver.restore(sess=sess, save_path=os.path.join( - data_dir, self.ckpt_prefix)) - if differences: - tf.logging.warn( - "The provided graph is different than expected:\n {}\n" - "However the weights were still able to be loaded.\n{}".format( - differences, tf_version_comparison) - ) - except: # pylint: disable=bare-except - raise self.failureException( - "Weight load failed. Graph comparison:\n {}{}" - .format(differences, tf_version_comparison)) - - eval_results = [op.eval() for op in ops_to_eval] - if correctness_function is not None: - results = correctness_function(*eval_results) - with tf.gfile.Open(os.path.join(data_dir, "results.json"), "r") as f: - expected_results = json.load(f) - self.assertAllClose(results, expected_results) - - def _save_or_test_ops(self, name, graph, ops_to_eval=None, test=True, - correctness_function=None): - """Utility function to automate repeated work of graph checking and saving. - - The philosophy of this function is that the user need only define ops on - a graph and specify which results should be validated. The actual work of - managing snapshots and calculating results should be automated away. - - Args: - name: String defining the run. This will be used to define folder names - and will be used for random seed construction. - graph: The graph in which the test is conducted. - ops_to_eval: Ops which the user wishes to be evaluated under a controlled - session. - test: Boolean. If True this function will test graph correctness, load - weights, and compute numerical values. If False the necessary test data - will be generated and saved. - correctness_function: This function accepts the evaluated results of - ops_to_eval, and returns a list of values. This list must be JSON - serializable; in particular it is up to the user to convert numpy - dtypes into builtin dtypes. - """ - - ops_to_eval = ops_to_eval or [] - - if test: - try: - self._evaluate_test_case( - name=name, graph=graph, ops_to_eval=ops_to_eval, - correctness_function=correctness_function - ) - except: - tf.logging.error("Failed unittest {}".format(name)) - raise - else: - self._construct_and_save_reference_files( - name=name, graph=graph, ops_to_eval=ops_to_eval, - correctness_function=correctness_function - ) - - -class ReferenceDataActionParser(argparse.ArgumentParser): - """Minimal arg parser so that test regeneration can be called from the CLI.""" - - def __init__(self): - super(ReferenceDataActionParser, self).__init__() - self.add_argument( - "--regenerate", "-regen", - action="store_true", - help="Enable this flag to regenerate test data. If not set unit tests" - "will be run." - ) - - -def main(argv, test_class): - """Simple switch function to allow test regeneration from the CLI.""" - flags = ReferenceDataActionParser().parse_args(argv[1:]) - if flags.regenerate: - if sys.version_info[0] == 2: - raise NameError("\nPython2 unittest does not support being run as a " - "standalone class.\nAs a result tests must be " - "regenerated using Python3.\n" - "Tests can be run under 2 or 3.") - test_class().regenerate() - else: - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/dense/expected_graph b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/dense/expected_graph deleted file mode 100644 index 335628111e08e14eb90588b57998d1dd7e719c9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5996 zcmbW5O>Y}T7{@)f;$&&EEiVn zdtWVs#WZ|kAG>tz&e9Jf^D?Y`ZO>zu?(EZHkM?PI*xenHPN&nwKXV<{oahvXci-*7 zEuey_h36NBCWWDmeHaqjG5a{5GeBep?2{$~Pj9gi5QyQD5W%NuZV$55=!pAjNHaG_ z&<|bLp=%dkJpZq6UI21GNssfCd`Sk~pxui-_^6h67|&?$vv_9IDq^41pLu@ag~0uq ztwlViy?gQ8Xw0o<6+@`3-i1xRFtm5uacXnWRs9(o-IuW8`Nwf&MXnz%0}2oAz_Vv_ z_u&n5j3r684(ttan~-FynA@<94+Gb+e0zc01Q(v!GsN72mkyVHWP83FByZtI0%O+2 z^mZ6}k9|7GJ`K*lA!L=fi$m9%FAtAA|1s^pMamkOS72Zr&h0Ry6ByHN9JBWU>&e`K zP2B6AV@Ix)WrFkJ0c^9Qrh#iaba>#ne&j``d_3yT;5!1>&_?J^kKDj_=hFkDu=&c|t8l#mm)0!dMKkx@pU%^t8)Ylx--bD%T%OqTDkJdH*7u0w*hMLB+ zFAdciH;XT8Qm*up!y^%Yc4AHZ*_Z16QTwiDGiRP1a@z*ge;HXI>o+%xh|JSrqrQK=J^wbz;6UDH94gH zyWV_mXw}0|NTFJdmLy&V@O;NTu}Y3T1*jtC`a`4@+}b(w4E7QTUk) z<}-oj^xRiGcV71b(fv!{rusTGGM(;vt^_%S)Y#r<>F^5tRa_60*0UtL3`mvcERgMV zhO?B>o*E!y>sT-DxaVC(om4o=Q%L1Gfwz@6S4Mhzsp|xfty`&*Lh#=c*yMxfPBh`t zo*ZH%ky7!}-tLK&Bgwt~R{|404w3qVFb(yZ>l8$t{XWkjwdy9}>MsPW69t^X^`aVj z47d`#G0eo$3JN6b^7J5pH(K0t9N6l#enj8_#hp+D{v_~Pv(uafH7jE%rBbanfCVy$P6j`GXQWTW1WqD01;u9K$i%;D@ zGGf_c0ws>pjWjJNF1t;l_x!iC#D5bI^6yf9gfDW!MJu`4iV}q|aw283t+JAn;%;|n F{0|&%8MOca diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/dense/model.ckpt.data-00000-of-00001 b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/dense/model.ckpt.data-00000-of-00001 deleted file mode 100644 index 514037b565c0d5652887cb9d955f4859e3fde12f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76 zcmZQzKm`}%*6v%N)@QpVgmwP`+b+8c@uvG7vOR1o^n&b9)Hd6JR9AV_?r$h$w7bCh S+cqIq#NO$HuDyb(l^p>0{}#po diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/dense/model.ckpt.index b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/dense/model.ckpt.index deleted file mode 100644 index 29935cfdace6f1411cffb9504e2e35781b94dbb9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 254 zcmZQzVB=tvV&Y(A;Nnk7%_~mTPs&Uz=3o?J5n|$C(GW1*nIC?GjZHK=wJ0w&2Pn<~ z6lVg-DF|pNm`a@KOk-sij5kEq#;9PR!D70sTF{CEO(V!qMgYJl0PUD59)*6YMILUf$ej29ju`3 z+C7(8mX#g41y!EROu2pNzzN_QR4CO7sIvm9pMy)#q7~C$03g2su<2g_ZzuT)2)x4X z+5|TXZNsHB+%AOPugd8Lkh@Gz0Xhq`5c~-3u0tD8$pWl(=b!I$0#_yFIvt^Nn~uEF zO4ziVlWN6E;I4!frU5$FY2f`Su7qkv_(8pdMsYLfoN-?2F;wMTe!2&!OEpOoKM_ht zF~U8IB^w3e{)o2BiWc61W7qhEWQR6k%OqpLeZ@lK6Z7*q(-67?o8w3&Oezs)ukb;j zm8a;RX)T`=)|R(d;P=*4Wpt?)eh`gcqZJ4>>dUnU<46=Qax

ZWuZvz0WyA%-s diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/uniform_random/results.json b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/uniform_random/results.json deleted file mode 100644 index 76b54aab..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/uniform_random/results.json +++ /dev/null @@ -1 +0,0 @@ -[0.9872556924819946] \ No newline at end of file diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/uniform_random/tf_version.json b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/uniform_random/tf_version.json deleted file mode 100644 index 5674d7d6..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/reference_data_test/uniform_random/tf_version.json +++ /dev/null @@ -1 +0,0 @@ -["1.8.0-dev20180325", "v1.7.0-rc1-750-g6c1737e6c8"] \ No newline at end of file diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_projection_version-1_width-8_channels-4/expected_graph b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_projection_version-1_width-8_channels-4/expected_graph deleted file mode 100644 index 611e8aafe9c72422d2ec705a43a5c8c59c8150fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27274 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 i%kWZP@H$wty7BuNssBq3~s&=49zNE#DDOo&M~ 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 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_projection_version-1_width-8_channels-4/model.ckpt.index b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_projection_version-1_width-8_channels-4/model.ckpt.index deleted file mode 100644 index 056b74bec758b0d79b0a59cb3d668121cb436cd5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 824 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 Y`wKY((ER!}JuVysyE@*18MxB_0p1QP7g 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 N(y_%~SLc_){{rP!P%Que diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-1_width-8_channels-4/model.ckpt.data-00000-of-00001 b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-1_width-8_channels-4/model.ckpt.data-00000-of-00001 deleted file mode 100644 index 888b17101a4f910afba423b2ffaa9cb5ac42dfc7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32932 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 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-1_width-8_channels-4/model.ckpt.index b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-1_width-8_channels-4/model.ckpt.index deleted file mode 100644 index a89441ad472c9c23940bab147e0b439f465a37ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 628 zcmZQzVB=tvV&Y(Akl;^BEJ@CY&&w~$P0Y!xN-W9D&(lvzElK2H6k-tqD$!suT~;k< zCBiD0o|u~p7gk^aN;EFLr7glOn44dgnU@})o0^yh)1|-x)hEp%Se96nnV6TH3YX)7 zY87S|j5kEFOhEvuO$^;S5vU$9?AA#@^~qwfP6nz~giX*0*)|ph1q}hyo%!K6&}?H- zFaSy@o1fl|)ixFd2dF+w+gKC=pjsJtWs~#s%8XL#FL9CAje>R<+fXcmPjj6f569-i`M;8DoT zD=00AFGMrc`R_TxV))z7PD|{VBrS|;RoTr8@g3W-ERW`&!m1+ diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-1_width-8_channels-4/results.json b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-1_width-8_channels-4/results.json deleted file mode 100644 index b4086966..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-1_width-8_channels-4/results.json +++ /dev/null @@ -1 +0,0 @@ -[32, 8, 8, 4, 0.1677999496459961, 0.7767924070358276, 4089.44189453125, 32, 8, 8, 4, 0.8615571856498718, 1.1359407901763916, 5806.876953125] \ No newline at end of file diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-1_width-8_channels-4/tf_version.json b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-1_width-8_channels-4/tf_version.json deleted file mode 100644 index 8cf1ef08..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-1_width-8_channels-4/tf_version.json +++ /dev/null @@ -1 +0,0 @@ -["1.8.0-dev20180408", "v1.7.0-1345-gb874783ccd"] \ No newline at end of file diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-2_width-8_channels-4/expected_graph b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-2_width-8_channels-4/expected_graph deleted file mode 100644 index a1cdadead5d4c172abc78c49d48f684cdda4d12f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20432 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

OUiimkSO(9fblLJJhM) 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 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-2_width-8_channels-4/results.json b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-2_width-8_channels-4/results.json deleted file mode 100644 index fa61c203..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-2_width-8_channels-4/results.json +++ /dev/null @@ -1 +0,0 @@ -[32, 8, 8, 4, 0.8239736557006836, 0.3485994338989258, 4108.87548828125, 32, 8, 8, 4, 0.16798323392868042, -0.2975311279296875, 2860.068359375] \ No newline at end of file diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-2_width-8_channels-4/tf_version.json b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-2_width-8_channels-4/tf_version.json deleted file mode 100644 index 8cf1ef08..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_bottleneck_version-2_width-8_channels-4/tf_version.json +++ /dev/null @@ -1 +0,0 @@ -["1.8.0-dev20180408", "v1.7.0-1345-gb874783ccd"] \ No newline at end of file diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_projection_version-1_width-8_channels-4/expected_graph b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_projection_version-1_width-8_channels-4/expected_graph deleted file mode 100644 index f1fa1eb5de287004dcd6b0cdcec63c3e796a4802..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20709 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 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_projection_version-1_width-8_channels-4/model.ckpt.data-00000-of-00001 b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_projection_version-1_width-8_channels-4/model.ckpt.data-00000-of-00001 deleted file mode 100644 index 4cdfc7ea4474018e0e6b7956ebdb78597f1d47d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36736 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�O+hHlUQqu{R@l={UOxnHNEFu)K+ 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 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_projection_version-2_width-8_channels-4/model.ckpt.data-00000-of-00001 b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_projection_version-2_width-8_channels-4/model.ckpt.data-00000-of-00001 deleted file mode 100644 index ab8bffdee826bd55a61df9ca07745a0938234bed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36544 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಴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 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_version-2_width-8_channels-4/model.ckpt.index b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch-size-32_building_version-2_width-8_channels-4/model.ckpt.index deleted file mode 100644 index af3805389d77973168c45a1da6e2e3d85c49e81e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 477 zcmZQzVB=tvV&Y(Akl;^BEJ@CY&&w~$P0Y!xN-W9D&(lvzElK2H6k-u#;$YDbFx{CS zenW&+Fg-Ch7cQ(I0F+QRKfPOoTQE1jEHf`XJ~uTn52i~&0jf`$L$EBdC^IoHITbEv z0M#nYE*Ni!Vwr*iRGS#ObpcR4V%V)qfa;UQVqF1LD+8~5a(-T!QHp+cYEfQl4$wUU zKp!$gG1#{X4U8HGSWJ6`D;BeH$^%`8N9h4pkW!V~vKb6K3YmEYr6uttsd>fuMNoqk zfCh6wF^fV2mqtSalWF?gPsR*Dzz8Cae^&5>a}Io3xPgI@Ig6p487{Vbadun}GsjG3 RevlA;5dOQNTcyq!L&W^gX0;Nu|1 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

mWgj13f^V5F* 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

8A*~PNmBFNXC#CqNm9v3GLnoW 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 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch_norm/model.ckpt.index b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch_norm/model.ckpt.index deleted file mode 100644 index b082a6c8eee91959b0f1bf778d744bab52b7c959..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 275 zcmZQzVB=tvV&Y(Akl;^BEJ@CY&&w~$P0Y!xN-W9D&(lvzElK2H6k-u#;$YU`F`emX z%PYbvn4XxM3l~=40ZLTnH7JU33+CpRW#*;F=cXp+!E`A|K=ny;2$m%lWhUk&r^4k_ zpjsJtI?t(G5`T1h={q!VhraT o__S~X10!=5!(Ol)m{9YODm=uk diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch_norm/results.json b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch_norm/results.json deleted file mode 100644 index c5958251..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch_norm/results.json +++ /dev/null @@ -1 +0,0 @@ -[32, 16, 16, 3, 0.9722558259963989, 0.18413543701171875, 12374.20703125, 32, 16, 16, 3, 1.6126631498336792, -1.096894383430481, -0.041595458984375] \ No newline at end of file diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch_norm/tf_version.json b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch_norm/tf_version.json deleted file mode 100644 index 8cf1ef08..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data/resnet/batch_norm/tf_version.json +++ /dev/null @@ -1 +0,0 @@ -["1.8.0-dev20180408", "v1.7.0-1345-gb874783ccd"] \ No newline at end of file diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data_test.py deleted file mode 100644 index c5c18344..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/reference_data_test.py +++ /dev/null @@ -1,133 +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. -# ============================================================================== -"""This module tests generic behavior of reference data tests. - -This test is not intended to test every layer of interest, and models should -test the layers that affect them. This test is primarily focused on ensuring -that reference_data.BaseTest functions as intended. If there is a legitimate -change such as a change to TensorFlow which changes graph construction, tests -can be regenerated with the following command: - - $ python3 reference_data_test.py -regen -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import sys -import unittest -import warnings - -import tensorflow as tf # pylint: disable=g-bad-import-order -from official.utils.testing import reference_data - - -class GoldenBaseTest(reference_data.BaseTest): - """Class to ensure that reference data testing runs properly.""" - - @property - def test_name(self): - return "reference_data_test" - - def _uniform_random_ops(self, test=False, wrong_name=False, wrong_shape=False, - bad_seed=False, bad_function=False): - """Tests number generation and failure modes. - - This test is of a very simple graph: the generation of a 1x1 random tensor. - However, it is also used to confirm that the tests are actually checking - properly by failing in predefined ways. - - Args: - test: Whether or not to run as a test case. - wrong_name: Whether to assign the wrong name to the tensor. - wrong_shape: Whether to create a tensor with the wrong shape. - bad_seed: Whether or not to perturb the random seed. - bad_function: Whether to perturb the correctness function. - """ - name = "uniform_random" - - g = tf.Graph() - with g.as_default(): - seed = self.name_to_seed(name) - seed = seed + 1 if bad_seed else seed - tf.set_random_seed(seed) - tensor_name = "wrong_tensor" if wrong_name else "input_tensor" - tensor_shape = (1, 2) if wrong_shape else (1, 1) - input_tensor = tf.get_variable( - tensor_name, dtype=tf.float32, - initializer=tf.random_uniform(tensor_shape, maxval=1) - ) - - def correctness_function(tensor_result): - result = float(tensor_result[0, 0]) - result = result + 0.1 if bad_function else result - return [result] - - self._save_or_test_ops( - name=name, graph=g, ops_to_eval=[input_tensor], test=test, - correctness_function=correctness_function - ) - - def _dense_ops(self, test=False): - name = "dense" - - g = tf.Graph() - with g.as_default(): - tf.set_random_seed(self.name_to_seed(name)) - input_tensor = tf.get_variable( - "input_tensor", dtype=tf.float32, - initializer=tf.random_uniform((1, 2), maxval=1) - ) - layer = tf.layers.dense(inputs=input_tensor, units=4) - layer = tf.layers.dense(inputs=layer, units=1) - - self._save_or_test_ops( - name=name, graph=g, ops_to_eval=[layer], test=test, - correctness_function=self.default_correctness_function - ) - - def test_uniform_random(self): - self._uniform_random_ops(test=True) - - def test_tensor_name_error(self): - with self.assertRaises(AssertionError): - self._uniform_random_ops(test=True, wrong_name=True) - - def test_tensor_shape_error(self): - with self.assertRaises(AssertionError): - self._uniform_random_ops(test=True, wrong_shape=True) - - @unittest.skipIf(sys.version_info[0] == 2, - "catch_warning doesn't catch tf.logging.warn in py 2.") - def test_bad_seed(self): - with warnings.catch_warnings(record=True) as warn_catch: - self._uniform_random_ops(test=True, bad_seed=True) - assert len(warn_catch) == 1, "Test did not warn of minor graph change." - - def test_incorrectness_function(self): - with self.assertRaises(AssertionError): - self._uniform_random_ops(test=True, bad_function=True) - - def test_dense(self): - self._dense_ops(test=True) - - def regenerate(self): - self._uniform_random_ops(test=False) - self._dense_ops(test=False) - - -if __name__ == "__main__": - reference_data.main(argv=sys.argv, test_class=GoldenBaseTest) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/scripts/presubmit.sh b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/scripts/presubmit.sh deleted file mode 100644 index 5b2aebad..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/scripts/presubmit.sh +++ /dev/null @@ -1,97 +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. -# ============================================================================== - -# Presubmit script that run tests and lint under local environment. -# Make sure that tensorflow and pylint is installed. -# usage: models >: ./official/utils/testing/scripts/presubmit.sh -# usage: models >: ./official/utils/testing/scripts/presubmit.sh lint py2_test py3_test -set +x - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -cd "$SCRIPT_DIR/../../../.." -MODEL_ROOT="$(pwd)" - -export PYTHONPATH="$PYTHONPATH:${MODEL_ROOT}" - -cd official - -lint() { - local exit_code=0 - - RC_FILE="utils/testing/pylint.rcfile" - PROTO_SKIP="DO\sNOT\sEDIT!" - - echo "===========Running lint test============" - for file in `find . -name '*.py' ! -name '*test.py' -print` - do - if grep ${PROTO_SKIP} ${file}; then - echo "Linting ${file} (Skipped: Machine generated file)" - else - echo "Linting ${file}" - pylint --rcfile="${RC_FILE}" "${file}" || exit_code=$? - fi - done - - # More lenient for test files. - for file in `find . -name '*test.py' -print` - do - echo "Linting ${file}" - pylint --rcfile="${RC_FILE}" --disable=missing-docstring,protected-access "${file}" || exit_code=$? - done - - return "${exit_code}" -} - -py_test() { - local PY_BINARY="$1" - local exit_code=0 - - echo "===========Running Python test============" - - for test_file in `find . -name '*test.py' -print` - do - echo "Testing ${test_file}" - ${PY_BINARY} "${test_file}" || exit_code=$? - done - - return "${exit_code}" -} - -py2_test() { - local PY_BINARY=$(which python2) - py_test "$PY_BINARY" - return $? -} - -py3_test() { - local PY_BINARY=$(which python3) - py_test "$PY_BINARY" - return $? -} - -test_result=0 - -if [ "$#" -eq 0 ]; then - TESTS="lint py2_test py3_test" -else - TESTS="$@" -fi - -for t in "${TESTS}"; do - ${t} || test_result=$? -done - -exit "${test_result}" diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/README.md b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/README.md deleted file mode 100644 index 7296c589..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/README.md +++ /dev/null @@ -1,95 +0,0 @@ -# Predicting Income with the Census Income Dataset -## Overview -The [Census Income Data Set](https://archive.ics.uci.edu/ml/datasets/Census+Income) contains over 48,000 samples with attributes including age, occupation, education, and income (a binary label, either `>50K` or `<=50K`). The dataset is split into roughly 32,000 training and 16,000 testing samples. - -Here, we use the [wide and deep model](https://research.googleblog.com/2016/06/wide-deep-learning-better-together-with.html) to predict the income labels. The **wide model** is able to memorize interactions with data with a large number of features but not able to generalize these learned interactions on new data. The **deep model** generalizes well but is unable to learn exceptions within the data. The **wide and deep model** combines the two models and is able to generalize while learning exceptions. - -For the purposes of this example code, the Census Income Data Set was chosen to allow the model to train in a reasonable amount of time. You'll notice that the deep model performs almost as well as the wide and deep model on this dataset. The wide and deep model truly shines on larger data sets with high-cardinality features, where each feature has millions/billions of unique possible values (which is the specialty of the wide model). - -Finally, a key point. As a modeler and developer, think about how this dataset is used and the potential benefits and harm a model's predictions can cause. A model like this could reinforce societal biases and disparities. Is a feature relevant to the problem you want to solve, or will it introduce bias? For more information, read about [ML fairness](https://developers.google.com/machine-learning/fairness-overview/). - ---- - -The code sample in this directory uses the high level `tf.estimator.Estimator` API. This API is great for fast iteration and quickly adapting models to your own datasets without major code overhauls. It allows you to move from single-worker training to distributed training, and it makes it easy to export model binaries for prediction. - -The input function for the `Estimator` uses `tf.contrib.data.TextLineDataset`, which creates a `Dataset` object. The `Dataset` API makes it easy to apply transformations (map, batch, shuffle, etc.) to the data. [Read more here](https://www.tensorflow.org/guide/datasets). - -The `Estimator` and `Dataset` APIs are both highly encouraged for fast development and efficient training. - -## Running the code -First make sure you've [added the models folder to your Python path](/official/#running-the-models); otherwise you may encounter an error like `ImportError: No module named official.wide_deep`. - -### Setup -The [Census Income Data Set](https://archive.ics.uci.edu/ml/datasets/Census+Income) that this sample uses for training is hosted by the [UC Irvine Machine Learning Repository](https://archive.ics.uci.edu/ml/datasets/). We have provided a script that downloads and cleans the necessary files. - -``` -python census_dataset.py -``` - -This will download the files to `/tmp/census_data`. To change the directory, set the `--data_dir` flag. - -### Training -You can run the code locally as follows: - -``` -python census_main.py -``` - -The model is saved to `/tmp/census_model` by default, which can be changed using the `--model_dir` flag. - -To run the *wide* or *deep*-only models, set the `--model_type` flag to `wide` or `deep`. Other flags are configurable as well; see `census_main.py` for details. - -The final accuracy should be over 83% with any of the three model types. - -You can also experiment with `-inter` and `-intra` flag to explore inter/intra op parallelism for potential better performance as follows: - -``` -python census_main.py --inter= --intra= -``` -Please note the above optional inter/intra op does not affect model accuracy. These are TensorFlow framework configurations that only affect execution time. -For more details regarding the above inter/intra flags, please refer to [Optimizing_for_CPU](https://www.tensorflow.org/performance/performance_guide#optimizing_for_cpu) or [TensorFlow config.proto source code](https://github.com/tensorflow/tensorflow/blob/26b4dfa65d360f2793ad75083c797d57f8661b93/tensorflow/core/protobuf/config.proto#L165). - -### TensorBoard - -Run TensorBoard to inspect the details about the graph and training progression. - -``` -tensorboard --logdir=/tmp/census_model -``` - -## Inference with SavedModel -You can export the model into Tensorflow [SavedModel](https://www.tensorflow.org/guide/saved_model) format by using the argument `--export_dir`: - -``` -python census_main.py --export_dir /tmp/wide_deep_saved_model -``` - -After the model finishes training, use [`saved_model_cli`](https://www.tensorflow.org/guide/saved_model#cli_to_inspect_and_execute_savedmodel) to inspect and execute the SavedModel. - -Try the following commands to inspect the SavedModel: - -**Replace `${TIMESTAMP}` with the folder produced (e.g. 1524249124)** -``` -# List possible tag_sets. Only one metagraph is saved, so there will be one option. -saved_model_cli show --dir /tmp/wide_deep_saved_model/${TIMESTAMP}/ - -# Show SignatureDefs for tag_set=serve. SignatureDefs define the outputs to show. -saved_model_cli show --dir /tmp/wide_deep_saved_model/${TIMESTAMP}/ \ - --tag_set serve --all -``` - -### Inference -Let's use the model to predict the income group of two examples: -``` -saved_model_cli run --dir /tmp/wide_deep_saved_model/${TIMESTAMP}/ \ ---tag_set serve --signature_def="predict" \ ---input_examples='examples=[{"age":[46.], "education_num":[10.], "capital_gain":[7688.], "capital_loss":[0.], "hours_per_week":[38.]}, {"age":[24.], "education_num":[13.], "capital_gain":[0.], "capital_loss":[0.], "hours_per_week":[50.]}]' -``` - -This will print out the predicted classes and class probabilities. Class 0 is the <=50k group and 1 is the >50k group. - -## Additional Links - -If you are interested in distributed training, take a look at [Distributed TensorFlow](https://www.tensorflow.org/deploy/distributed). - -You can also [run this model on Cloud ML Engine](https://cloud.google.com/ml-engine/docs/getting-started-training-prediction), which provides [hyperparameter tuning](https://cloud.google.com/ml-engine/docs/getting-started-training-prediction#hyperparameter_tuning) to maximize your model's results and enables [deploying your model for prediction](https://cloud.google.com/ml-engine/docs/getting-started-training-prediction#deploy_a_model_to_support_prediction). diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/__init__.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/census_dataset.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/census_dataset.py deleted file mode 100644 index 4cf66f82..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/census_dataset.py +++ /dev/null @@ -1,204 +0,0 @@ -# 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. -# ============================================================================== -"""Download and clean the Census Income Dataset.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os -import sys - -# pylint: disable=wrong-import-order -from absl import app as absl_app -from absl import flags -from six.moves import urllib -import tensorflow as tf -# pylint: enable=wrong-import-order - -from official.utils.flags import core as flags_core - - -DATA_URL = 'https://archive.ics.uci.edu/ml/machine-learning-databases/adult' -TRAINING_FILE = 'adult.data' -TRAINING_URL = '%s/%s' % (DATA_URL, TRAINING_FILE) -EVAL_FILE = 'adult.test' -EVAL_URL = '%s/%s' % (DATA_URL, EVAL_FILE) - - -_CSV_COLUMNS = [ - 'age', 'workclass', 'fnlwgt', 'education', 'education_num', - 'marital_status', 'occupation', 'relationship', 'race', 'gender', - 'capital_gain', 'capital_loss', 'hours_per_week', 'native_country', - 'income_bracket' -] - -_CSV_COLUMN_DEFAULTS = [[0], [''], [0], [''], [0], [''], [''], [''], [''], [''], - [0], [0], [0], [''], ['']] - -_HASH_BUCKET_SIZE = 1000 - -_NUM_EXAMPLES = { - 'train': 32561, - 'validation': 16281, -} - - -def _download_and_clean_file(filename, url): - """Downloads data from url, and makes changes to match the CSV format.""" - temp_file, _ = urllib.request.urlretrieve(url) - with tf.gfile.Open(temp_file, 'r') as temp_eval_file: - with tf.gfile.Open(filename, 'w') as eval_file: - for line in temp_eval_file: - line = line.strip() - line = line.replace(', ', ',') - if not line or ',' not in line: - continue - if line[-1] == '.': - line = line[:-1] - line += '\n' - eval_file.write(line) - tf.gfile.Remove(temp_file) - - -def download(data_dir): - """Download census data if it is not already present.""" - tf.gfile.MakeDirs(data_dir) - - training_file_path = os.path.join(data_dir, TRAINING_FILE) - if not tf.gfile.Exists(training_file_path): - _download_and_clean_file(training_file_path, TRAINING_URL) - - eval_file_path = os.path.join(data_dir, EVAL_FILE) - if not tf.gfile.Exists(eval_file_path): - _download_and_clean_file(eval_file_path, EVAL_URL) - - -def build_model_columns(): - """Builds a set of wide and deep feature columns.""" - # Continuous variable columns - age = tf.feature_column.numeric_column('age') - education_num = tf.feature_column.numeric_column('education_num') - capital_gain = tf.feature_column.numeric_column('capital_gain') - capital_loss = tf.feature_column.numeric_column('capital_loss') - hours_per_week = tf.feature_column.numeric_column('hours_per_week') - - education = tf.feature_column.categorical_column_with_vocabulary_list( - 'education', [ - 'Bachelors', 'HS-grad', '11th', 'Masters', '9th', 'Some-college', - 'Assoc-acdm', 'Assoc-voc', '7th-8th', 'Doctorate', 'Prof-school', - '5th-6th', '10th', '1st-4th', 'Preschool', '12th']) - - marital_status = tf.feature_column.categorical_column_with_vocabulary_list( - 'marital_status', [ - 'Married-civ-spouse', 'Divorced', 'Married-spouse-absent', - 'Never-married', 'Separated', 'Married-AF-spouse', 'Widowed']) - - relationship = tf.feature_column.categorical_column_with_vocabulary_list( - 'relationship', [ - 'Husband', 'Not-in-family', 'Wife', 'Own-child', 'Unmarried', - 'Other-relative']) - - workclass = tf.feature_column.categorical_column_with_vocabulary_list( - 'workclass', [ - 'Self-emp-not-inc', 'Private', 'State-gov', 'Federal-gov', - 'Local-gov', '?', 'Self-emp-inc', 'Without-pay', 'Never-worked']) - - # To show an example of hashing: - occupation = tf.feature_column.categorical_column_with_hash_bucket( - 'occupation', hash_bucket_size=_HASH_BUCKET_SIZE) - - # Transformations. - age_buckets = tf.feature_column.bucketized_column( - age, boundaries=[18, 25, 30, 35, 40, 45, 50, 55, 60, 65]) - - # Wide columns and deep columns. - base_columns = [ - education, marital_status, relationship, workclass, occupation, - age_buckets, - ] - - crossed_columns = [ - tf.feature_column.crossed_column( - ['education', 'occupation'], hash_bucket_size=_HASH_BUCKET_SIZE), - tf.feature_column.crossed_column( - [age_buckets, 'education', 'occupation'], - hash_bucket_size=_HASH_BUCKET_SIZE), - ] - - wide_columns = base_columns + crossed_columns - - deep_columns = [ - age, - education_num, - capital_gain, - capital_loss, - hours_per_week, - tf.feature_column.indicator_column(workclass), - tf.feature_column.indicator_column(education), - tf.feature_column.indicator_column(marital_status), - tf.feature_column.indicator_column(relationship), - # To show an example of embedding - tf.feature_column.embedding_column(occupation, dimension=8), - ] - - return wide_columns, deep_columns - - -def input_fn(data_file, num_epochs, shuffle, batch_size): - """Generate an input function for the Estimator.""" - assert tf.gfile.Exists(data_file), ( - '%s not found. Please make sure you have run census_dataset.py and ' - 'set the --data_dir argument to the correct path.' % data_file) - - def parse_csv(value): - tf.logging.info('Parsing {}'.format(data_file)) - columns = tf.decode_csv(value, record_defaults=_CSV_COLUMN_DEFAULTS) - features = dict(zip(_CSV_COLUMNS, columns)) - labels = features.pop('income_bracket') - classes = tf.equal(labels, '>50K') # binary classification - return features, classes - - # Extract lines from input files using the Dataset API. - dataset = tf.data.TextLineDataset(data_file) - - if shuffle: - dataset = dataset.shuffle(buffer_size=_NUM_EXAMPLES['train']) - - dataset = dataset.map(parse_csv, num_parallel_calls=5) - - # We call repeat after shuffling, rather than before, to prevent separate - # epochs from blending together. - dataset = dataset.repeat(num_epochs) - dataset = dataset.batch(batch_size) - return dataset - - -def define_data_download_flags(): - """Add flags specifying data download arguments.""" - flags.DEFINE_string( - name="data_dir", default="/tmp/census_data/", - help=flags_core.help_wrap( - "Directory to download and extract data.")) - - -def main(_): - download(flags.FLAGS.data_dir) - - -if __name__ == '__main__': - tf.logging.set_verbosity(tf.logging.INFO) - define_data_download_flags() - absl_app.run(main) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/census_main.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/census_main.py deleted file mode 100644 index 87af0942..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/census_main.py +++ /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. -# ============================================================================== -"""Train DNN on census income dataset.""" - -import os - -from absl import app as absl_app -from absl import flags -import tensorflow as tf - -from official.utils.flags import core as flags_core -from official.utils.logs import logger -from official.wide_deep import census_dataset -from official.wide_deep import wide_deep_run_loop - - -def define_census_flags(): - wide_deep_run_loop.define_wide_deep_flags() - flags.adopt_module_key_flags(wide_deep_run_loop) - flags_core.set_defaults(data_dir='/tmp/census_data', - model_dir='/tmp/census_model', - train_epochs=40, - epochs_between_evals=2, - inter_op_parallelism_threads=0, - intra_op_parallelism_threads=0, - batch_size=40) - - -def build_estimator(model_dir, model_type, model_column_fn, inter_op, intra_op): - """Build an estimator appropriate for the given model type.""" - wide_columns, deep_columns = model_column_fn() - hidden_units = [100, 75, 50, 25] - - # Create a tf.estimator.RunConfig to ensure the model is run on CPU, which - # trains faster than GPU for this model. - run_config = tf.estimator.RunConfig().replace( - session_config=tf.ConfigProto(device_count={'GPU': 0}, - inter_op_parallelism_threads=inter_op, - intra_op_parallelism_threads=intra_op)) - - if model_type == 'wide': - return tf.estimator.LinearClassifier( - model_dir=model_dir, - feature_columns=wide_columns, - config=run_config) - elif model_type == 'deep': - return tf.estimator.DNNClassifier( - model_dir=model_dir, - feature_columns=deep_columns, - hidden_units=hidden_units, - config=run_config) - else: - return tf.estimator.DNNLinearCombinedClassifier( - model_dir=model_dir, - linear_feature_columns=wide_columns, - dnn_feature_columns=deep_columns, - dnn_hidden_units=hidden_units, - config=run_config) - - -def run_census(flags_obj): - """Construct all necessary functions and call run_loop. - - Args: - flags_obj: Object containing user specified flags. - """ - if flags_obj.download_if_missing: - census_dataset.download(flags_obj.data_dir) - - train_file = os.path.join(flags_obj.data_dir, census_dataset.TRAINING_FILE) - test_file = os.path.join(flags_obj.data_dir, census_dataset.EVAL_FILE) - - # Train and evaluate the model every `flags.epochs_between_evals` epochs. - def train_input_fn(): - return census_dataset.input_fn( - train_file, flags_obj.epochs_between_evals, True, flags_obj.batch_size) - - def eval_input_fn(): - return census_dataset.input_fn(test_file, 1, False, flags_obj.batch_size) - - tensors_to_log = { - 'average_loss': '{loss_prefix}head/truediv', - 'loss': '{loss_prefix}head/weighted_loss/Sum' - } - - wide_deep_run_loop.run_loop( - name="Census Income", train_input_fn=train_input_fn, - eval_input_fn=eval_input_fn, - model_column_fn=census_dataset.build_model_columns, - build_estimator_fn=build_estimator, - flags_obj=flags_obj, - tensors_to_log=tensors_to_log, - early_stop=True) - - -def main(_): - with logger.benchmark_context(flags.FLAGS): - run_census(flags.FLAGS) - - -if __name__ == '__main__': - tf.logging.set_verbosity(tf.logging.INFO) - define_census_flags() - absl_app.run(main) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/census_test.csv b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/census_test.csv deleted file mode 100644 index 374397db..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/census_test.csv +++ /dev/null @@ -1,30 +0,0 @@ -39,State-gov,77516,Bachelors,13,Never-married,Adm-clerical,Not-in-family,,,2174,0,40,,<=50K -50,Self-emp-not-inc,83311,Bachelors,13,Married-civ-spouse,Exec-managerial,Husband,,,0,0,13,,<=50K -38,Private,215646,HS-grad,9,Divorced,Handlers-cleaners,Not-in-family,,,0,0,40,,<=50K -53,Private,234721,11th,7,Married-civ-spouse,Handlers-cleaners,Husband,,,0,0,40,,<=50K -28,Private,338409,Bachelors,13,Married-civ-spouse,Prof-specialty,Wife,,,0,0,40,,<=50K -37,Private,284582,Masters,14,Married-civ-spouse,Exec-managerial,Wife,,,0,0,40,,<=50K -49,Private,160187,9th,5,Married-spouse-absent,Other-service,Not-in-family,,,0,0,16,,<=50K -52,Self-emp-not-inc,209642,HS-grad,9,Married-civ-spouse,Exec-managerial,Husband,,,0,0,45,,>50K -31,Private,45781,Masters,14,Never-married,Prof-specialty,Not-in-family,,,14084,0,50,,>50K -42,Private,159449,Bachelors,13,Married-civ-spouse,Exec-managerial,Husband,,,5178,0,40,,>50K -37,Private,280464,Some-college,10,Married-civ-spouse,Exec-managerial,Husband,,,0,0,80,,>50K -30,State-gov,141297,Bachelors,13,Married-civ-spouse,Prof-specialty,Husband,,,0,0,40,,>50K -23,Private,122272,Bachelors,13,Never-married,Adm-clerical,Own-child,,,0,0,30,,<=50K -32,Private,205019,Assoc-acdm,12,Never-married,Sales,Not-in-family,,,0,0,50,,<=50K -40,Private,121772,Assoc-voc,11,Married-civ-spouse,Craft-repair,Husband,,,0,0,40,,>50K -34,Private,245487,7th-8th,4,Married-civ-spouse,Transport-moving,Husband,,,0,0,45,,<=50K -25,Self-emp-not-inc,176756,HS-grad,9,Never-married,Farming-fishing,Own-child,,,0,0,35,,<=50K -32,Private,186824,HS-grad,9,Never-married,Machine-op-inspct,Unmarried,,,0,0,40,,<=50K -38,Private,28887,11th,7,Married-civ-spouse,Sales,Husband,,,0,0,50,,<=50K -43,Self-emp-not-inc,292175,Masters,14,Divorced,Exec-managerial,Unmarried,,,0,0,45,,>50K -40,Private,193524,Doctorate,16,Married-civ-spouse,Prof-specialty,Husband,,,0,0,60,,>50K -56,Local-gov,216851,Bachelors,13,Married-civ-spouse,Tech-support,Husband,,,0,0,40,,>50K -54,?,180211,Some-college,10,Married-civ-spouse,?,Husband,,,0,0,60,,>50K -22,State-gov,311512,Some-college,10,Married-civ-spouse,Other-service,Husband,,,0,0,15,,<=50K -31,Private,84154,Some-college,10,Married-civ-spouse,Sales,Husband,,,0,0,38,,>50K -57,Federal-gov,337895,Bachelors,13,Married-civ-spouse,Prof-specialty,Husband,,,0,0,40,,>50K -47,Private,51835,Prof-school,15,Married-civ-spouse,Prof-specialty,Wife,,,0,1902,60,,>50K -50,Federal-gov,251585,Bachelors,13,Divorced,Exec-managerial,Not-in-family,,,0,0,55,,>50K -25,Private,289980,HS-grad,9,Never-married,Handlers-cleaners,Not-in-family,,,0,0,35,,<=50K -42,Private,116632,Doctorate,16,Married-civ-spouse,Prof-specialty,Husband,,,0,0,45,,>50K diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/census_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/census_test.py deleted file mode 100644 index 9f8ed58b..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/census_test.py +++ /dev/null @@ -1,161 +0,0 @@ -# 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. -# ============================================================================== - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os - -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.utils.testing import integration -from official.wide_deep import census_dataset -from official.wide_deep import census_main -from official.wide_deep import wide_deep_run_loop - -tf.logging.set_verbosity(tf.logging.ERROR) - -TEST_INPUT = ('18,Self-emp-not-inc,987,Bachelors,12,Married-civ-spouse,abc,' - 'Husband,zyx,wvu,34,56,78,tsr,<=50K') - -TEST_INPUT_VALUES = { - 'age': 18, - 'education_num': 12, - 'capital_gain': 34, - 'capital_loss': 56, - 'hours_per_week': 78, - 'education': 'Bachelors', - 'marital_status': 'Married-civ-spouse', - 'relationship': 'Husband', - 'workclass': 'Self-emp-not-inc', - 'occupation': 'abc', -} - -TEST_CSV = os.path.join(os.path.dirname(__file__), 'census_test.csv') - - -class BaseTest(tf.test.TestCase): - """Tests for Wide Deep model.""" - - @classmethod - def setUpClass(cls): # pylint: disable=invalid-name - super(BaseTest, cls).setUpClass() - census_main.define_census_flags() - - def setUp(self): - # Create temporary CSV file - self.temp_dir = self.get_temp_dir() - self.input_csv = os.path.join(self.temp_dir, 'test.csv') - with tf.gfile.Open(self.input_csv, 'w') as temp_csv: - temp_csv.write(TEST_INPUT) - - with tf.gfile.Open(TEST_CSV, "r") as temp_csv: - test_csv_contents = temp_csv.read() - - # Used for end-to-end tests. - for fname in [census_dataset.TRAINING_FILE, census_dataset.EVAL_FILE]: - with tf.gfile.Open(os.path.join(self.temp_dir, fname), 'w') as test_csv: - test_csv.write(test_csv_contents) - - def test_input_fn(self): - dataset = census_dataset.input_fn(self.input_csv, 1, False, 1) - features, labels = dataset.make_one_shot_iterator().get_next() - - with self.test_session() as sess: - features, labels = sess.run((features, labels)) - - # Compare the two features dictionaries. - for key in TEST_INPUT_VALUES: - self.assertTrue(key in features) - self.assertEqual(len(features[key]), 1) - feature_value = features[key][0] - - # Convert from bytes to string for Python 3. - if isinstance(feature_value, bytes): - feature_value = feature_value.decode() - - self.assertEqual(TEST_INPUT_VALUES[key], feature_value) - - self.assertFalse(labels) - - def build_and_test_estimator(self, model_type): - """Ensure that model trains and minimizes loss.""" - model = census_main.build_estimator( - self.temp_dir, model_type, - model_column_fn=census_dataset.build_model_columns, - inter_op=0, intra_op=0) - - # Train for 1 step to initialize model and evaluate initial loss - def get_input_fn(num_epochs, shuffle, batch_size): - def input_fn(): - return census_dataset.input_fn( - TEST_CSV, num_epochs=num_epochs, shuffle=shuffle, - batch_size=batch_size) - return input_fn - - model.train(input_fn=get_input_fn(1, True, 1), steps=1) - initial_results = model.evaluate(input_fn=get_input_fn(1, False, 1)) - - # Train for 100 epochs at batch size 3 and evaluate final loss - model.train(input_fn=get_input_fn(100, True, 3)) - final_results = model.evaluate(input_fn=get_input_fn(1, False, 1)) - - print('%s initial results:' % model_type, initial_results) - print('%s final results:' % model_type, final_results) - - # Ensure loss has decreased, while accuracy and both AUCs have increased. - self.assertLess(final_results['loss'], initial_results['loss']) - self.assertGreater(final_results['auc'], initial_results['auc']) - self.assertGreater(final_results['auc_precision_recall'], - initial_results['auc_precision_recall']) - self.assertGreater(final_results['accuracy'], initial_results['accuracy']) - - def test_wide_deep_estimator_training(self): - self.build_and_test_estimator('wide_deep') - - def test_end_to_end_wide(self): - integration.run_synthetic( - main=census_main.main, tmp_root=self.get_temp_dir(), - extra_flags=[ - '--data_dir', self.get_temp_dir(), - '--model_type', 'wide', - '--download_if_missing=false' - ], - synth=False, max_train=None) - - def test_end_to_end_deep(self): - integration.run_synthetic( - main=census_main.main, tmp_root=self.get_temp_dir(), - extra_flags=[ - '--data_dir', self.get_temp_dir(), - '--model_type', 'deep', - '--download_if_missing=false' - ], - synth=False, max_train=None) - - def test_end_to_end_wide_deep(self): - integration.run_synthetic( - main=census_main.main, tmp_root=self.get_temp_dir(), - extra_flags=[ - '--data_dir', self.get_temp_dir(), - '--model_type', 'wide_deep', - '--download_if_missing=false' - ], - synth=False, max_train=None) - - -if __name__ == '__main__': - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/movielens_dataset.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/movielens_dataset.py deleted file mode 100644 index 278e6ac0..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/movielens_dataset.py +++ /dev/null @@ -1,163 +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. -# ============================================================================== -"""Prepare MovieLens dataset for wide-deep.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import functools -import os - -# pylint: disable=wrong-import-order -from absl import app as absl_app -from absl import flags -import numpy as np -import tensorflow as tf -# pylint: enable=wrong-import-order - -from official.datasets import movielens -from official.utils.data import file_io -from official.utils.flags import core as flags_core - - -_BUFFER_SUBDIR = "wide_deep_buffer" -_FEATURE_MAP = { - movielens.USER_COLUMN: tf.FixedLenFeature([1], dtype=tf.int64), - movielens.ITEM_COLUMN: tf.FixedLenFeature([1], dtype=tf.int64), - movielens.TIMESTAMP_COLUMN: tf.FixedLenFeature([1], dtype=tf.int64), - movielens.GENRE_COLUMN: tf.FixedLenFeature( - [movielens.N_GENRE], dtype=tf.int64), - movielens.RATING_COLUMN: tf.FixedLenFeature([1], dtype=tf.float32), -} - -_BUFFER_SIZE = { - movielens.ML_1M: {"train": 107978119, "eval": 26994538}, - movielens.ML_20M: {"train": 2175203810, "eval": 543802008} -} - -_USER_EMBEDDING_DIM = 16 -_ITEM_EMBEDDING_DIM = 64 - -def build_model_columns(dataset): - """Builds a set of wide and deep feature columns.""" - user_id = tf.feature_column.categorical_column_with_vocabulary_list( - movielens.USER_COLUMN, range(1, movielens.NUM_USER_IDS[dataset])) - user_embedding = tf.feature_column.embedding_column( - user_id, _USER_EMBEDDING_DIM, max_norm=np.sqrt(_USER_EMBEDDING_DIM)) - - item_id = tf.feature_column.categorical_column_with_vocabulary_list( - movielens.ITEM_COLUMN, range(1, movielens.NUM_ITEM_IDS)) - item_embedding = tf.feature_column.embedding_column( - item_id, _ITEM_EMBEDDING_DIM, max_norm=np.sqrt(_ITEM_EMBEDDING_DIM)) - - time = tf.feature_column.numeric_column(movielens.TIMESTAMP_COLUMN) - genres = tf.feature_column.numeric_column( - movielens.GENRE_COLUMN, shape=(movielens.N_GENRE,), dtype=tf.uint8) - - deep_columns = [user_embedding, item_embedding, time, genres] - wide_columns = [] - - return wide_columns, deep_columns - - -def _deserialize(examples_serialized): - features = tf.parse_example(examples_serialized, _FEATURE_MAP) - return features, features[movielens.RATING_COLUMN] / movielens.MAX_RATING - - -def _buffer_path(data_dir, dataset, name): - return os.path.join(data_dir, _BUFFER_SUBDIR, - "{}_{}_buffer".format(dataset, name)) - - -def _df_to_input_fn(df, name, dataset, data_dir, batch_size, repeat, shuffle): - """Serialize a dataframe and write it to a buffer file.""" - buffer_path = _buffer_path(data_dir, dataset, name) - expected_size = _BUFFER_SIZE[dataset].get(name) - - file_io.write_to_buffer( - dataframe=df, buffer_path=buffer_path, - columns=list(_FEATURE_MAP.keys()), expected_size=expected_size) - - def input_fn(): - dataset = tf.data.TFRecordDataset(buffer_path) - # batch comes before map because map can deserialize multiple examples. - dataset = dataset.batch(batch_size) - dataset = dataset.map(_deserialize, num_parallel_calls=16) - if shuffle: - dataset = dataset.shuffle(shuffle) - - dataset = dataset.repeat(repeat) - return dataset.prefetch(1) - - return input_fn - - -def _check_buffers(data_dir, dataset): - train_path = os.path.join(data_dir, _BUFFER_SUBDIR, - "{}_{}_buffer".format(dataset, "train")) - eval_path = os.path.join(data_dir, _BUFFER_SUBDIR, - "{}_{}_buffer".format(dataset, "eval")) - - if not tf.gfile.Exists(train_path) or not tf.gfile.Exists(eval_path): - return False - - return all([ - tf.gfile.Stat(_buffer_path(data_dir, dataset, "train")).length == - _BUFFER_SIZE[dataset]["train"], - tf.gfile.Stat(_buffer_path(data_dir, dataset, "eval")).length == - _BUFFER_SIZE[dataset]["eval"], - ]) - - -def construct_input_fns(dataset, data_dir, batch_size=16, repeat=1): - """Construct train and test input functions, as well as the column fn.""" - if _check_buffers(data_dir, dataset): - train_df, eval_df = None, None - else: - df = movielens.csv_to_joint_dataframe(dataset=dataset, data_dir=data_dir) - df = movielens.integerize_genres(dataframe=df) - df = df.drop(columns=[movielens.TITLE_COLUMN]) - - train_df = df.sample(frac=0.8, random_state=0) - eval_df = df.drop(train_df.index) - - train_df = train_df.reset_index(drop=True) - eval_df = eval_df.reset_index(drop=True) - - train_input_fn = _df_to_input_fn( - df=train_df, name="train", dataset=dataset, data_dir=data_dir, - batch_size=batch_size, repeat=repeat, - shuffle=movielens.NUM_RATINGS[dataset]) - eval_input_fn = _df_to_input_fn( - df=eval_df, name="eval", dataset=dataset, data_dir=data_dir, - batch_size=batch_size, repeat=repeat, shuffle=None) - model_column_fn = functools.partial(build_model_columns, dataset=dataset) - - train_input_fn() - return train_input_fn, eval_input_fn, model_column_fn - - -def main(_): - movielens.download(dataset=flags.FLAGS.dataset, data_dir=flags.FLAGS.data_dir) - construct_input_fns(flags.FLAGS.dataset, flags.FLAGS.data_dir) - -if __name__ == "__main__": - tf.logging.set_verbosity(tf.logging.INFO) - movielens.define_data_download_flags() - flags.adopt_module_key_flags(movielens) - flags_core.set_defaults(dataset="ml-1m") - absl_app.run(main) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/movielens_main.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/movielens_main.py deleted file mode 100644 index 3efedcb1..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/movielens_main.py +++ /dev/null @@ -1,115 +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. -# ============================================================================== -"""Train DNN on Kaggle movie dataset.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os - -from absl import app as absl_app -from absl import flags -import tensorflow as tf - -from official.datasets import movielens -from official.utils.flags import core as flags_core -from official.utils.logs import logger -from official.wide_deep import movielens_dataset -from official.wide_deep import wide_deep_run_loop - - -def define_movie_flags(): - """Define flags for movie dataset training.""" - wide_deep_run_loop.define_wide_deep_flags() - flags.DEFINE_enum( - name="dataset", default=movielens.ML_1M, - enum_values=movielens.DATASETS, case_sensitive=False, - help=flags_core.help_wrap("Dataset to be trained and evaluated.")) - flags.adopt_module_key_flags(wide_deep_run_loop) - flags_core.set_defaults(data_dir="/tmp/movielens-data/", - model_dir='/tmp/movie_model', - model_type="deep", - train_epochs=50, - epochs_between_evals=5, - inter_op_parallelism_threads=0, - intra_op_parallelism_threads=0, - batch_size=256) - - @flags.validator("stop_threshold", - message="stop_threshold not supported for movielens model") - def _no_stop(stop_threshold): - return stop_threshold is None - - -def build_estimator(model_dir, model_type, model_column_fn, inter_op, intra_op): - """Build an estimator appropriate for the given model type.""" - if model_type != "deep": - raise NotImplementedError("movie dataset only supports `deep` model_type") - _, deep_columns = model_column_fn() - hidden_units = [256, 256, 256, 128] - - run_config = tf.estimator.RunConfig().replace( - session_config=tf.ConfigProto(device_count={'GPU': 0}, - inter_op_parallelism_threads=inter_op, - intra_op_parallelism_threads=intra_op)) - return tf.estimator.DNNRegressor( - model_dir=model_dir, - feature_columns=deep_columns, - hidden_units=hidden_units, - optimizer=tf.train.AdamOptimizer(), - activation_fn=tf.nn.sigmoid, - dropout=0.3, - loss_reduction=tf.losses.Reduction.MEAN) - - -def run_movie(flags_obj): - """Construct all necessary functions and call run_loop. - - Args: - flags_obj: Object containing user specified flags. - """ - - if flags_obj.download_if_missing: - movielens.download(dataset=flags_obj.dataset, data_dir=flags_obj.data_dir) - - train_input_fn, eval_input_fn, model_column_fn = \ - movielens_dataset.construct_input_fns( - dataset=flags_obj.dataset, data_dir=flags_obj.data_dir, - batch_size=flags_obj.batch_size, repeat=flags_obj.epochs_between_evals) - - tensors_to_log = { - 'loss': '{loss_prefix}head/weighted_loss/value' - } - - wide_deep_run_loop.run_loop( - name="MovieLens", train_input_fn=train_input_fn, - eval_input_fn=eval_input_fn, - model_column_fn=model_column_fn, - build_estimator_fn=build_estimator, - flags_obj=flags_obj, - tensors_to_log=tensors_to_log, - early_stop=False) - - -def main(_): - with logger.benchmark_context(flags.FLAGS): - run_movie(flags.FLAGS) - - -if __name__ == '__main__': - tf.logging.set_verbosity(tf.logging.INFO) - define_movie_flags() - absl_app.run(main) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/movielens_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/movielens_test.py deleted file mode 100644 index 57129d19..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/movielens_test.py +++ /dev/null @@ -1,117 +0,0 @@ -# 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. -# ============================================================================== - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os - -import numpy as np -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.datasets import movielens -from official.utils.testing import integration -from official.wide_deep import movielens_dataset -from official.wide_deep import movielens_main -from official.wide_deep import wide_deep_run_loop - -tf.logging.set_verbosity(tf.logging.ERROR) - - -TEST_INPUT_VALUES = { - "genres": np.array( - [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - "user_id": [3], - "item_id": [4], -} - -TEST_ITEM_DATA = """item_id,titles,genres -1,Movie_1,Comedy|Romance -2,Movie_2,Adventure|Children's -3,Movie_3,Comedy|Drama -4,Movie_4,Comedy -5,Movie_5,Action|Crime|Thriller -6,Movie_6,Action -7,Movie_7,Action|Adventure|Thriller""" - -TEST_RATING_DATA = """user_id,item_id,rating,timestamp -1,2,5,978300760 -1,3,3,978302109 -1,6,3,978301968 -2,1,4,978300275 -2,7,5,978824291 -3,1,3,978302268 -3,4,5,978302039 -3,5,5,978300719 -""" - - -class BaseTest(tf.test.TestCase): - """Tests for Wide Deep model.""" - - @classmethod - def setUpClass(cls): # pylint: disable=invalid-name - super(BaseTest, cls).setUpClass() - movielens_main.define_movie_flags() - - def setUp(self): - # Create temporary CSV file - self.temp_dir = self.get_temp_dir() - tf.gfile.MakeDirs(os.path.join(self.temp_dir, movielens.ML_1M)) - - self.ratings_csv = os.path.join( - self.temp_dir, movielens.ML_1M, movielens.RATINGS_FILE) - self.item_csv = os.path.join( - self.temp_dir, movielens.ML_1M, movielens.MOVIES_FILE) - - with tf.gfile.Open(self.ratings_csv, "w") as f: - f.write(TEST_RATING_DATA) - - with tf.gfile.Open(self.item_csv, "w") as f: - f.write(TEST_ITEM_DATA) - - - def test_input_fn(self): - train_input_fn, _, _ = movielens_dataset.construct_input_fns( - dataset=movielens.ML_1M, data_dir=self.temp_dir, batch_size=8, repeat=1) - - dataset = train_input_fn() - features, labels = dataset.make_one_shot_iterator().get_next() - - with self.test_session() as sess: - features, labels = sess.run((features, labels)) - - # Compare the two features dictionaries. - for key in TEST_INPUT_VALUES: - self.assertTrue(key in features) - self.assertAllClose(TEST_INPUT_VALUES[key], features[key][0]) - - self.assertAllClose(labels[0], [1.0]) - - def test_end_to_end_deep(self): - integration.run_synthetic( - main=movielens_main.main, tmp_root=self.temp_dir, - extra_flags=[ - "--data_dir", self.temp_dir, - "--download_if_missing=false", - "--train_epochs", "1", - "--epochs_between_evals", "1" - ], - synth=False, max_train=None) - - -if __name__ == "__main__": - tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/wide_deep_run_loop.py b/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/wide_deep_run_loop.py deleted file mode 100644 index 7bc4c555..00000000 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/wide_deep/wide_deep_run_loop.py +++ /dev/null @@ -1,131 +0,0 @@ -# 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. -# ============================================================================== -"""Core run logic for TensorFlow Wide & Deep Tutorial using tf.estimator API.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import os -import shutil - -from absl import app as absl_app -from absl import flags -import tensorflow as tf # pylint: disable=g-bad-import-order - -from official.utils.flags import core as flags_core -from official.utils.logs import hooks_helper -from official.utils.logs import logger -from official.utils.misc import model_helpers - - -LOSS_PREFIX = {'wide': 'linear/', 'deep': 'dnn/'} - - -def define_wide_deep_flags(): - """Add supervised learning flags, as well as wide-deep model type.""" - flags_core.define_base() - flags_core.define_benchmark() - flags_core.define_performance( - num_parallel_calls=False, inter_op=True, intra_op=True, - synthetic_data=False, max_train_steps=False, dtype=False, - all_reduce_alg=False) - - flags.adopt_module_key_flags(flags_core) - - flags.DEFINE_enum( - name="model_type", short_name="mt", default="wide_deep", - enum_values=['wide', 'deep', 'wide_deep'], - help="Select model topology.") - flags.DEFINE_boolean( - name="download_if_missing", default=True, help=flags_core.help_wrap( - "Download data to data_dir if it is not already present.")) - - -def export_model(model, model_type, export_dir, model_column_fn): - """Export to SavedModel format. - - Args: - model: Estimator object - model_type: string indicating model type. "wide", "deep" or "wide_deep" - export_dir: directory to export the model. - model_column_fn: Function to generate model feature columns. - """ - wide_columns, deep_columns = model_column_fn() - if model_type == 'wide': - columns = wide_columns - elif model_type == 'deep': - columns = deep_columns - else: - columns = wide_columns + deep_columns - feature_spec = tf.feature_column.make_parse_example_spec(columns) - example_input_fn = ( - tf.estimator.export.build_parsing_serving_input_receiver_fn(feature_spec)) - model.export_savedmodel(export_dir, example_input_fn, - strip_default_attrs=True) - - -def run_loop(name, train_input_fn, eval_input_fn, model_column_fn, - build_estimator_fn, flags_obj, tensors_to_log, early_stop=False): - """Define training loop.""" - model_helpers.apply_clean(flags.FLAGS) - model = build_estimator_fn( - model_dir=flags_obj.model_dir, model_type=flags_obj.model_type, - model_column_fn=model_column_fn, - inter_op=flags_obj.inter_op_parallelism_threads, - intra_op=flags_obj.intra_op_parallelism_threads) - - run_params = { - 'batch_size': flags_obj.batch_size, - 'train_epochs': flags_obj.train_epochs, - 'model_type': flags_obj.model_type, - } - - benchmark_logger = logger.get_benchmark_logger() - benchmark_logger.log_run_info('wide_deep', name, run_params, - test_id=flags_obj.benchmark_test_id) - - loss_prefix = LOSS_PREFIX.get(flags_obj.model_type, '') - tensors_to_log = {k: v.format(loss_prefix=loss_prefix) - for k, v in tensors_to_log.items()} - train_hooks = hooks_helper.get_train_hooks( - flags_obj.hooks, model_dir=flags_obj.model_dir, - batch_size=flags_obj.batch_size, tensors_to_log=tensors_to_log) - - # Train and evaluate the model every `flags.epochs_between_evals` epochs. - for n in range(flags_obj.train_epochs // flags_obj.epochs_between_evals): - model.train(input_fn=train_input_fn, hooks=train_hooks) - - results = model.evaluate(input_fn=eval_input_fn) - - # Display evaluation metrics - tf.logging.info('Results at epoch %d / %d', - (n + 1) * flags_obj.epochs_between_evals, - flags_obj.train_epochs) - tf.logging.info('-' * 60) - - for key in sorted(results): - tf.logging.info('%s: %s' % (key, results[key])) - - benchmark_logger.log_evaluation_result(results) - - if early_stop and model_helpers.past_stop_threshold( - flags_obj.stop_threshold, results['accuracy']): - break - - # Export the model - if flags_obj.export_dir is not None: - export_model(model, flags_obj.model_type, flags_obj.export_dir, - model_column_fn) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/.gitignore b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/.gitignore similarity index 92% rename from TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/.gitignore rename to TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/.gitignore index 463aac4e..00eb5794 100644 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/.gitignore +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/.gitignore @@ -2,6 +2,7 @@ __pycache__/ *.py[cod] *$py.class +*.sw[op] # C extensions *.so @@ -15,7 +16,6 @@ dist/ downloads/ eggs/ .eggs/ -lib/ lib64/ parts/ sdist/ @@ -93,5 +93,3 @@ ENV/ # For mac .DS_Store - -samples/outreach/blogs/segmentation_blogpost/carvana-image-masking-challenge/ diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/LICENSE b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/LICENSE similarity index 99% rename from TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/LICENSE rename to TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/LICENSE index 43fcf7bf..8dada3ed 100644 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/LICENSE +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/LICENSE @@ -1,5 +1,3 @@ -Copyright 2016 The TensorFlow Authors. All rights reserved. - Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -180,7 +178,7 @@ Copyright 2016 The TensorFlow Authors. All rights reserved. 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 "[]" + 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 @@ -188,7 +186,7 @@ Copyright 2016 The TensorFlow Authors. All rights reserved. same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2016, The Authors. + Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/README.md b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/README.md new file mode 100644 index 00000000..39b36585 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/README.md @@ -0,0 +1,482 @@ +Table of Contents +================= + +* [Table of Contents](#table-of-contents) +* [Introduction](#introduction) +* [Executing tests](#executing-tests) + * [PerfZero on private GCE instance.](#perfzero-on-private-gce-instance) + * [Step one: Create GCE Instance](#step-one-create-gce-instance) + * [Step two: Build docker on GCE instance](#step-two-build-docker-on-gce-instance) + * [Step three: Start and "enter" the docker instance](#step-three-start-and-enter-the-docker-instance) + * [Step four: Run tests](#step-four-run-tests) + * [Step five: Delete the instance when done](#step-five-delete-the-instance-when-done) + * [PerfZero on local workstation or any server](#perfzero-on-local-workstation-or-any-server) + * [PerfZero without docker](#perfzero-without-docker) +* [Creating tests](#creating-tests) +* [Deep dive into individual tools](#deep-dive-into-individual-tools) + * [Build docker image](#build-docker-image) + * [Run benchmark](#run-benchmark) + * [Instructions for managing Google Cloud Platform computing instance](#instructions-for-managing-google-cloud-platform-computing-instance) + * [Understand the benchmark execution output](#understand-the-benchmark-execution-output) + * [Json formatted benchmark summary](#json-formatted-benchmark-summary) + * [Profiling](#profiling) + * [Visualize in TensorBoard](#visualize-in-tensorboard) + * [Visualize system metric values over time](#visualize-system-metric-values-over-time) +* [PerfZero development](#perfzero-development) + +# Introduction + +PerfZero is a benchmark framework for TensorFlow. It intends to address the +following use-cases: + +1) For user who wants to execute TensorFlow test to debug performance +regression. + +PerfZero makes it easy to execute the pre-defined test by consolidating the +docker image build, GPU driver installation, TensorFlow installation, benchmark +library checkout, data download, system statistics collection, benchmark metrics +collection, profiler data collection and so on into 2 to 3 commands. This allows +developer to focus on investigating the issue rather than setting up the test +environment. + +2) For user who wants to track the performance change of TensorFlow for a +variety of setup (e.g. GPU model, cudnn version, TensorFlow version) + +The developer can setup periodic job to execute these benchmark methods using +PerfZero. PerfZero will collect the information needed to identify the +benchmark (e.g. GPU model, TensorFlow version, dependent library git hash), get +benchmark execution result (e.g. wall time, accuracy, succeeded or not), +summarize the result in a easy-to-read json string and upload the result to +bigquery table. Using the data in the bigquery table, user can then visualize +the performance change in a dashboard, compare performance between different +setup in a table, and trigger alert on when there is performance regression. + + +# Executing tests + +There are multiple ways to use PerfZero to execute a test. Listed from highest +to lowest abstraction. + +* [PerfZero on private GCE instance](#perfzero-on-private-gce-instance) +* [PerfZero on local workstation or any server](#perfzero-on-local-workstation-or-any-server) +* [PerfZero without docker](#perfzero-without-docker) + +## PerfZero on private GCE instance. + +There are many variations on this approach, to get you started quickly the steps +below detail setting up an 8xV100 setup with local nvme drives where training +data would be stored. The only con to this setup is that it cannot be stopped +and can only be deleted due to the local nvme drives. + +### Step one: Create GCE Instance + +Creates an 8xV100 instance with 4 nvme drives. Output of the command will +provide the command to run to SSH to the machine. To set the project, zone, and +other features read +[cloud_manager tool details](https://github.com/tensorflow/benchmarks/tree/master/perfzero#instructions-for-managing-google-cloud-platform-computing-instance). + +```bash +python perfzero/lib/cloud_manager.py create --accelerator_count 8 --nvme_count 4 +``` + +### Step two: Build docker on GCE instance + +After logging into the instance run the following command to create a docker +instance with the latest nightly TF 2.0 build. For more options read the +[build docker image section](https://github.com/tensorflow/benchmarks/tree/master/perfzero#build-docker-image) + +```bash +python3 perfzero/lib/setup.py --dockerfile_path=docker/Dockerfile_ubuntu_1804_tf_v2 +``` + +For all options for building the docker image, including controlling the version +of TensorFlow installed, checkout the public +[README for PerfZero](https://github.com/tensorflow/benchmarks/tree/master/perfzero#build-docker-image). + +### Step three: Start and "enter" the docker instance + +```bash +nvidia-docker run -it --rm -v $(pwd):/workspace -v /data:/data perfzero/tensorflow bash +``` + +### Step four: Run tests + +The command below pulls GitHub official/models, downloads the cifar-10 dataset +from our internal Google Cloud storage bucket, and executes a ResNet56 benchmark +with TensorFlow 2.0 nightly build. For info on the args read the +[run benchmark section](https://github.com/tensorflow/benchmarks/tree/master/perfzero#run-benchmark). + +```bash +python3 /workspace/perfzero/lib/benchmark.py \ +--git_repos="https://github.com/tensorflow/models.git;benchmark" \ +--python_path=models \ +--data_downloads="gs://tf-perf-imagenet-uswest1/tensorflow/cifar10_data/cifar-10-batches-bin" \ +--benchmark_methods=official.benchmark.keras_cifar_benchmark.Resnet56KerasBenchmarkReal.benchmark_1_gpu_no_dist_strat +``` + +For all options that can be used when executing a test checkout the public +[README for PerfZero](https://github.com/tensorflow/benchmarks/tree/master/perfzero#run-benchmark). + +### Step five: Delete the instance when done + +```bash +python perfzero/lib/cloud_manager.py delete +``` + +## PerfZero on local workstation or any server + +This approach is the same as PerfZero on personal GCE instance, just jump to +Step two: Build docker on GCE instance. + +If the workstation does not have access to the PerfZero GCS bucket and does not +need access, e.g. data is already copied locally via another method, passing +`--gcloud_key_file_url=""` will skip attempting to download the key. + +A quick test that does not require accessing GCS for data is: + +```bash +python3 /workspace/perfzero/lib/benchmark.py \ +--git_repos="https://github.com/tensorflow/models.git;benchmark" \ +--python_path=models \ +--gcloud_key_file_url="" \ +--benchmark_methods=official.benchmark.keras_cifar_benchmark.Resnet56KerasBenchmarkSynth.benchmark_1_gpu_no_dist_strat +``` + +## PerfZero without docker + +PerfZero is not dependent on Docker. Docker is used to handle dependencies and +create a consistent environment. Most tests do not require much beyond +TensorFlow and PerfZero mostly depends on Google Cloud, but only for downloading +data and upload results if desired. While this approach works, we do not +maintain a clear list of the required libraries. The Docker build files are a +good starting point beyond. + +Once the requirements are met the command below can be executed which will pull +GitHub official/models, downloads the cifar-10 dataset from our internal Google +Cloud storage bucket, and executes a ResNet56 benchmark with TensorFlow 2.0 +nightly build. + +```bash +python3 /workspace/perfzero/lib/benchmark.py \ +--git_repos="https://github.com/tensorflow/models.git;benchmark" \ +--python_path=models \ +--data_downloads="gs://tf-perf-imagenet-uswest1/tensorflow/cifar10_data/cifar-10-batches-bin" \ +--benchmark_methods=official.r1.resnet.estimator_benchmark.Resnet50EstimatorBenchmarkReal.benchmark_graph_1_gpu +``` + +# Creating tests + +Here are the instructions that developers of benchmark method needs to follow in +order to run benchmark method in PerfZero. See +[estimator_benchmark.py](https://github.com/tensorflow/models/blob/master/official/r1/resnet/estimator_benchmark.py) +for example test code that supports PerfZero. + +1) The benchmark class should extend the TensorFlow python class +[tensorflow.test.Benchmark](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/platform/benchmark.py). The benchmark class constructor should have a +constructor with signature `__init__(self, output_dir, data_dir, **kwargs)`. +Below is the usage for each arguments: + +- Benchmark method should put all generated files (e.g. logs) in `output_dir` so that PerfZero can +upload these files to Google Cloud Storage when `--output_gcs_url` is specified. + +- Benchmark method should read data from `root_data_dir`. For example, the benchmark method can read data from e.g. `${root_data_dir}/cifar-10-binary` + +- `**kwargs` is useful to make the benchmark constructor forward compatible when PerfZero provides more named arguments to the benchmark constructor before + updating the benchmark class. + + +2) At the end of the benchmark method execution, the method should call [report_benchmark()](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/platform/benchmark.py) +with the following parameters: + +``` +tf.test.Benchmark.report_benchmark( + iters=num_iteration, # The number of iterations of the benchmark. + + wall_time=wall_time_sec, # Total wall time in sec for all iterations. + metrics = [ # List of metric entries + { + 'name': 'accuracy_top_5', # Metric name + 'value': 80, # Metric value + 'min_value': 90, # Optional. Minimum acceptable metric value for the benchmark to succeed. + 'max_value': 99 # Optional. Maximum acceptable metric value for the benchmark to succeed. + }, + { + 'name': 'accuracy_top_1', + 'value': 99.5 + } + ] +) +``` + +This format allows PerfZero to specify whether the benchmark has succeeded +(e.g. for convergence test) in its summary based on the logic determined by +the benchmark developer. + + +3) Include dependent libraries in `--git_repos` and `--python_path`. These +libraries will be checked-out in the directory +`path_to_perfzero/workspace/site-packages` by default. Developer can edit these +libraries directly and execute benchmark with the local change. + + +# Deep dive into individual tools + +The sections below go into details about the individual components of PerfZero. + +## Build docker image + +The command below builds the docker image named `perfzero/tensorflow` which contains the +libraries (e.g. TensorFlow) needed for benchmark. + +``` +python3 benchmarks/perfzero/lib/setup.py +``` + +Here are a few selected optional flags. Run `python3 setup.py -h` to see +detailed documentation for all supported flags. + +1) Use `--dockerfile_path=docker/Dockerfile_ubuntu_1804_tf_v2` to build docker image for TensorFlow v2 +2) Use `--tensorflow_pip_spec` to specify the tensorflow pip package name (and optionally version) to be +installed in the docker image, e.g. `--tensorflow_pip_spec=tensorflow==1.12.0`. + + +## Run benchmark + +The command below executes the benchmark method specified by `--benchmark_methods`. + +``` +export ROOT_DATA_DIR=/data + +nvidia-docker run -it --rm -v $(pwd):/workspace -v $ROOT_DATA_DIR:$ROOT_DATA_DIR perfzero/tensorflow \ +python3 /workspace/benchmarks/perfzero/lib/benchmark.py \ +--gcloud_key_file_url="" \ +--git_repos="https://github.com/tensorflow/models.git;benchmark" \ +--python_path=models \ +--benchmark_methods=official.r1.resnet.estimator_benchmark.Resnet50EstimatorBenchmarkSynth.benchmark_graph_1_gpu \ +--root_data_dir=$ROOT_DATA_DIR +``` + +`${ROOT_DATA_DIR}` should be the directory which contains the dataset files +required by the benchmark method. If the flag `--data_downloads` is specified, +PerfZero will download files from the specified url to the directory specified +by the flag `--root_data_dir`. Otherwise, user needs to manually download and +move the dataset files into the directory specified by `--root_data_dirs`. The +default `root_data_dir` is `/data`. Some benchmark methods, like the one run in +the sample command above, do not require any dataset files. + +Here are a few selected optional flags. Run `python3 benchmark.py -h` to see +detailed documentation for all supported flags. + +1) Use `--workspace=unique_workspace_name` if you need to run multiple benchmark +using different workspace setup. One example usecase is that you may want to +test a branch from a pull request without changing your existing workspace. + +2) Use `--debug` if you need to see the debug level logging + +3) Use `--git_repos="git_url;git_branch;git_hash"` to checkout a git repo with +the specified git_branch at the specified git_hash to the local folder with the +specified folder name. **Note that** the value of the flag `--git_repos` is +wrapped by the quotation mark `"` so that `;` will not be interpreted by the +bash as the end of the command. Specify the flag once for each repository you +want to checkout. + +5) Use `--profiler_enabled_time=start_time:end_time` to collect profiler data +during period `[start_time, end_time)` after the benchmark method execution +starts. Skip `end_time` in the flag value to collect data until the end of +benchmark method execution. See [here](#visualize-tensorflow-graph-etc-using-tensorboard) +for instructions on how to use the generated profiler data. + +## Instructions for managing Google Cloud Platform computing instance + +PerfZero aims to make it easy to run and debug TensorFlow which is usually run +with GPU. However, most users do not have dedicated machine with expensive +hardware. One cost-effective solution is for users to create machine with the +desired hardware on demand in a public cloud when they need to debug TensorFlow. + +We provide a script in PerfZero to make it easy to manage computing instance in +Google Cloud Platform. This assumes that you have access to an existing project +in GCP. + +Run `python perfzero/lib/cloud_manager.py -h` for list of commands supported +by the script. Run `cloud_manager.py -h` to see detailed documentation +for all supported flags for the specified `command`. + +In most cases, user only needs to run the following commands: + +``` +# Create a new instance that is unique to your username +python perfzero/lib/cloud_manager.py create --project=project_name + +# Query the status of the existing instanced created by your and its IP address +python perfzero/lib/cloud_manager.py status --project=project_name + +# Stop the instance +python perfzero/lib/cloud_manager.py stop --project=project_name + +# Start the instance +python perfzero/lib/cloud_manager.py start --project=project_name + +# Delete the instance +python perfzero/lib/cloud_manager.py delete --project=project_name +``` + +## Understand the benchmark execution output + +### Json formatted benchmark summary + +PerfZero outputs a json-formatted summary that provides the information needed +to understand the benchmark result. The summary is printed in the stdout and +in the file `path_to_perfzero/${workspace}/output/${execution_id}/perfzero.log`. + +Additionally, Perfzero outputs a pure json file containing the summary at +`path_to_perfzero/${workspace}/output/${execution_id}/perfzero_summary.json` + +Here is an example output from PerfZero. Explanation is provided inline for each +key when the name of the key is not sufficiently self-explanary. + +``` + { + "ml_framework_info": { # Summary of the machine learning framework + "version": "1.13.0-dev20190206", # Short version. It is tf.__version__ for TensorFlow + "name": "tensorflow", # Machine learning framework name such as PyTorch + "build_label": "ml_framework_build_label", # Specified by the flag --ml_framework_build_label + "build_version": "v1.12.0-7504-g9b32b5742b" # Long version. It is tf.__git_version__ for TensorFlow + }, + "execution_timestamp": 1550040322.8991697, # Timestamp when the benchmark is executed + "execution_id": "2019-02-13-06-45-22-899128", # A string that uniquely identify this benchmark execution + + "benchmark_info": { # Summary of the benchmark framework setup + "output_url": "gs://tf-performance/test-results/2019-02-13-06-45-22-899128/", # Google storage url that contains the log file from this benchmark execution + "has_exception": false, + "site_package_info": { + "models": { + "branch": "benchmark", + "url": "https://github.com/tensorflow/models.git", + "hash": "f788046ca876a8820e05b0b48c1fc2e16b0955bc" + }, + "benchmarks": { + "branch": "master", + "url": "https://github.com/tensorflow/benchmarks.git", + "hash": "af9e0ef36fc6867d9b63ebccc11f229375cd6a31" + } + }, + "harness_name": "perfzero", + "harness_info": { + "url": "https://github.com/tensorflow/benchmarks.git", + "branch": "master", + "hash": "75d2991b88630dde10ef65aad8082a6d5cd8b5fc" + }, + "execution_label": "execution_label" # Specified by the flag --execution_label + }, + + "system_info": { # Summary of the resources in the system that is used to execute the benchmark + "system_name": "system_name", # Specified by the flag --system_name + "accelerator_count": 2, # Number of GPUs in the system + "physical_cpu_count": 8, # Number of physical cpu cores in the system. Hyper thread CPUs are excluded. + "logical_cpu_count": 16, # Number of logical cpu cores in the system. Hyper thread CPUs are included. + "cpu_socket_count": 1, # Number of cpu socket in the system. + "platform_name": "platform_name", # Specified by the flag --platform_name + "accelerator_model": "Tesla V100-SXM2-16GB", + "accelerator_driver_version": "410.48", + "cpu_model": "Intel(R) Xeon(R) CPU @ 2.20GHz" + }, + + "process_info": { # Summary of the resources used by the process to execute the benchmark + "max_rss": 4269047808, # maximum physical memory in bytes used by the process + "max_vms": 39894450176, # maximum virtual memory in bytes used by the process + "max_cpu_percent": 771.1 # CPU utilization as a percentage. See psutil.Process.cpu_percent() for more information + }, + + "benchmark_result": { # Summary of the benchmark execution results. This is pretty much the same data structure defined in test_log.proto. + # Most values are read from test_log.proto which is written by tf.test.Benchmark.report_benchmark() defined in TensorFlow library. + + "metrics": [ # This is derived from `extras` [test_log.proto](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/util/test_log.proto) + # which is written by report_benchmark(). + # If the EntryValue is double, then name is the extra's key and value is extra's double value. + # If the EntryValue is string, then name is the extra's key. The string value will be a json formated string whose keys + # include `value`, `succeeded` and `description`. Benchmark method can provide arbitrary metric key/value pairs here. + { + "name": "accuracy_top_5", + "value": 0.7558000087738037 + }, + { + "name": "accuracy_top_1", + "value": 0.2639999985694885 + } + ], + "name": "official.resnet.estimator_cifar_benchmark.EstimatorCifar10BenchmarkTests.unit_test", # Full path to the benchmark method, i.e. module_path.class_name.method_name + "succeeded": true, # True iff benchmark method execution finishes without exception and no metric in metrics show succeeded = false + "wall_time": 14.552583694458008 # The value is determined by tf.test.Benchmark.report_benchmark() called by the benchmark method. It is -1 if report_benchmark() is not called. + } +} +``` + +### Profiling + +When the flag `--profiler_enabled_time=start_time:end_time` is specified, the +profiler data will be collected and stored in +`path_to_perfzero/${workspace}/output/${execution_id}/profiler_data`. + +#### Visualize in TensorBoard + +Firstly, install the profile plugin for TensorBoard. +``` +pip install -U tensorboard-plugin-profile +``` + +Run `tensorboard --logdir=path_to_perfzero/workspace/output/${execution_id}/profiler_data` or +`python3 -m tensorboard.main --logdir=path_to_perfzero/workspace/output/${execution_id}/profiler_data` to open +TensorBoard server. + +If PerfZero is executed on a remote machine, run `ssh -L +6006:127.0.0.1:6006 remote_ip` before opening `http://localhost:6006` in your +browser to access the TensorBoard UI. + +You can also run TensorBoard inside the docker, e.g. +`tensorboard --logdir=/workspace/perfzero/workspace/output/${execution_id}/profiler_data --bind_all` + +In this case, you have to start docker with port mapping, i.e. with "-p 6006:6006" flag, .e.g +``` +nvidia-docker run -it --rm -v $(pwd):/workspace -p 6006:6006 perfzero/tensorflow +``` + +Normally, the pages you see will look like: + +![Screenshot](screenshots/profiling_overview.png "Profiling Overview") +![Screenshot](screenshots/profiling_trace_view.png "Profiling Trace View") + +### Visualize system metric values over time + +PerfZero also records a few useful system metrics (e.g. rss, vms) over time in +the file `path_to_perfzero/${workspace}/output/${execution_id}/process_info.log`. +Run `python perfzero/scripts/plot_process_info.py process_info.log` to generate a +pdf showing the value of these metrics over time. + + +# PerfZero development + + +Avoid importing `tensorflow` package in any place that requires the `logging` +package because tensorflow package appears to prevent logging from working +properly. Importing `tensorflow` package only in the method that requires it. + +Here are the commands to run unit tests and check code style. + +``` +# Run all unit tests +# This must be executed in directory perfzero/lib +python3 -B -m unittest discover -p "*_test.py" + +# Format python code in place +find perfzero/lib -name *.py -exec pyformat --in_place {} \; + +# Check python code format and report warning and errors +find perfzero/lib -name *.py -exec gpylint3 {} \; +``` + +Here is the command to generate table-of-contents for this README. Run this +command and copy/paste it to the README.md. + +``` +./perfzero/scripts/generate-readme-header.sh perfzero/README.md +``` diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_build b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_build new file mode 100644 index 00000000..2a84858a --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_build @@ -0,0 +1,97 @@ +# Ubuntu 18.04 Python3 with CUDA 10 and the following: +# - Installs tf-nightly-gpu-2.0-preview +# - Installs requirements.txt for tensorflow/models +# - Install bazel for building TF from source + +FROM nvidia/cuda:10.0-base-ubuntu18.04 as base +ARG tensorflow_pip_spec="tf-nightly-gpu-2.0-preview" +ARG extra_pip_specs="" +ARG local_tensorflow_pip_spec="" + +COPY ${local_tensorflow_pip_spec} /${local_tensorflow_pip_spec} + +# Pick up some TF dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + cuda-command-line-tools-10-0 \ + cuda-cublas-dev-10-0 \ + cuda-cufft-dev-10-0 \ + cuda-curand-dev-10-0 \ + cuda-cusolver-dev-10-0 \ + cuda-cusparse-dev-10-0 \ + libcudnn7=7.6.2.24-1+cuda10.0 \ + libcudnn7-dev=7.6.2.24-1+cuda10.0 \ + libfreetype6-dev \ + libhdf5-serial-dev \ + libzmq3-dev \ + libpng-dev \ + pkg-config \ + software-properties-common \ + unzip \ + lsb-core \ + curl \ + && \ + find /usr/local/cuda-10.0/lib64/ -type f -name 'lib*_static.a' -not -name 'libcudart_static.a' -delete && \ + rm /usr/lib/x86_64-linux-gnu/libcudnn_static_v7.a + +RUN apt-get update && \ + apt-get install -y --no-install-recommends libnvinfer5=5.1.5-1+cuda10.0 \ + libnvinfer-dev=5.1.5-1+cuda10.0 \ + && apt-get clean + +# For CUDA profiling, TensorFlow requires CUPTI. +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH + +# See http://bugs.python.org/issue19846 +ENV LANG C.UTF-8 + +# Add google-cloud-sdk to the source list +RUN echo "deb http://packages.cloud.google.com/apt cloud-sdk-$(lsb_release -c -s) main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - + +# Install extras needed by most models +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + build-essential \ + ca-certificates \ + wget \ + htop \ + zip \ + google-cloud-sdk + +# Install / update Python +# (bulding TF needs py2 even if building for Python3 as of 06-AUG-2019) +RUN apt-get install -y --no-install-recommends \ + python3 \ + python3-dev \ + python3-pip \ + python3-setuptools \ + python3-venv \ + python + +# Upgrade pip, need to use pip3 and then pip after this or an error +# is thrown for no main found. +RUN pip3 install --upgrade pip +# setuptools upgraded to fix install requirements from model garden. +RUN pip install wheel +RUN pip install --upgrade setuptools google-api-python-client pyyaml google-cloud google-cloud-bigquery google-cloud-datastore mock +RUN pip install absl-py +RUN pip install --upgrade --force-reinstall ${tensorflow_pip_spec} ${extra_pip_specs} +RUN pip install tfds-nightly +RUN pip install -U scikit-learn + +RUN curl https://raw.githubusercontent.com/tensorflow/models/master/official/requirements.txt > /tmp/requirements.txt +RUN pip3 install -r /tmp/requirements.txt + +RUN pip3 freeze + +# Install bazel +ARG BAZEL_VERSION=0.24.1 +RUN mkdir /bazel && \ + wget -O /bazel/installer.sh "https://github.com/bazelbuild/bazel/releases/download/${BAZEL_VERSION}/bazel-${BAZEL_VERSION}-installer-linux-x86_64.sh" && \ + wget -O /bazel/LICENSE.txt "https://raw.githubusercontent.com/bazelbuild/bazel/master/LICENSE" && \ + chmod +x /bazel/installer.sh && \ + /bazel/installer.sh && \ + rm -f /bazel/installer.sh + +RUN git clone https://github.com/tensorflow/tensorflow.git /tensorflow_src diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_s4tf_cuda10.0 b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_s4tf_cuda10.0 new file mode 100644 index 00000000..e454f976 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_s4tf_cuda10.0 @@ -0,0 +1,124 @@ +# Ubuntu 18.04 Python3 with CUDA 10 and the following: +# - Installs tf-nightly-gpu (this is TF 2.0) +# - Installs requirements.txt for tensorflow/models +# Additionally also installs: +# - Latest S4TF development snapshot for cuda 10.0 + +FROM nvidia/cuda:10.0-base-ubuntu18.04 as base +ARG tensorflow_pip_spec="tf-nightly-gpu" +ARG local_tensorflow_pip_spec="" +ARG extra_pip_specs="" +ARG swift_tf_url=https://storage.googleapis.com/swift-tensorflow-artifacts/nightlies/latest/swift-tensorflow-DEVELOPMENT-cuda10.0-cudnn7-ubuntu18.04.tar.gz + +# setup.py passes the base path of local .whl file is chosen for the docker image. +# Otherwise passes an empty existing file from the context. +COPY ${local_tensorflow_pip_spec} /${local_tensorflow_pip_spec} + +# Pick up some TF dependencies +# cublas-dev and libcudnn7-dev only needed because of libnvinfer-dev which may not +# really be needed. +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + cuda-command-line-tools-10-0 \ + cuda-cublas-10-0 \ + cuda-cublas-dev-10-0 \ + cuda-cufft-10-0 \ + cuda-curand-10-0 \ + cuda-cusolver-10-0 \ + cuda-cusparse-10-0 \ + libcudnn7=7.6.2.24-1+cuda10.0 \ + libcudnn7-dev=7.6.2.24-1+cuda10.0 \ + libfreetype6-dev \ + libhdf5-serial-dev \ + libzmq3-dev \ + libpng-dev \ + pkg-config \ + software-properties-common \ + unzip \ + lsb-core \ + curl + +RUN apt-get update && \ + apt-get install -y --no-install-recommends libnvinfer5=5.1.5-1+cuda10.0 \ + libnvinfer-dev=5.1.5-1+cuda10.0 \ + && apt-get clean + +# For CUDA profiling, TensorFlow requires CUPTI. +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH + +# See http://bugs.python.org/issue19846 +ENV LANG C.UTF-8 + +# Add google-cloud-sdk to the source list +RUN echo "deb http://packages.cloud.google.com/apt cloud-sdk-$(lsb_release -c -s) main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - + +# Install extras needed by most models +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + ca-certificates \ + wget \ + htop \ + zip \ + google-cloud-sdk + +# Install / update Python and Python3 +RUN apt-get install -y --no-install-recommends \ + python3 \ + python3-dev \ + python3-pip \ + python3-setuptools \ + python3-venv + + +# Upgrade pip, need to use pip3 and then pip after this or an error +# is thrown for no main found. +RUN pip3 install --upgrade pip +# setuptools upgraded to fix install requirements from model garden. +RUN pip install wheel +RUN pip install --upgrade setuptools google-api-python-client pyyaml google-cloud google-cloud-bigquery mock +RUN pip install absl-py +RUN pip install --upgrade --force-reinstall ${tensorflow_pip_spec} ${extra_pip_specs} +RUN pip install tfds-nightly +RUN pip install -U scikit-learn + +RUN curl https://raw.githubusercontent.com/tensorflow/models/master/official/requirements.txt > /tmp/requirements.txt +RUN pip install -r /tmp/requirements.txt + +RUN pip freeze + +### Install Swift deps. +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + curl \ + git \ + python \ + python-dev \ + python-pip \ + python-setuptools \ + python-tk \ + python3 \ + python3-pip \ + python3-setuptools \ + clang \ + libcurl4-openssl-dev \ + libicu-dev \ + libpython-dev \ + libpython3-dev \ + libncurses5-dev \ + libxml2 \ + libblocksruntime-dev + +# Download and extract S4TF +WORKDIR /swift-tensorflow-toolchain +RUN if ! curl -fSsL --retry 5 $swift_tf_url -o swift.tar.gz; \ + then sleep 30 && curl -fSsL --retry 5 $swift_tf_url -o swift.tar.gz; \ + fi; + +RUN mkdir usr \ + && tar -xzf swift.tar.gz --directory=usr --strip-components=1 \ + && rm swift.tar.gz +ENV PATH="/swift-tensorflow-toolchain/usr/bin:${PATH}" +ENV LD_LIBRARY_PATH="/swift-tensorflow-toolchain/usr/lib/swift/linux/:${LD_LIBRARY_PATH}" diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_s4tf_cuda10.1 b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_s4tf_cuda10.1 new file mode 100644 index 00000000..bb7df8ed --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_s4tf_cuda10.1 @@ -0,0 +1,133 @@ +# Ubuntu 18.04 Python3 with CUDA 10.1 and the following: +# - Installs tf-nightly-gpu (this is TF 2.1) +# - Installs requirements.txt for tensorflow/models +# - TF 2.0 tested with cuda 10.0, but we need to test tf 2.1 with cuda 10.1. +# Additionally also installs +# - Latest S4TF development snapshot for cuda 10.1 + +FROM nvidia/cuda:10.1-base-ubuntu18.04 as base +ARG tensorflow_pip_spec="tf-nightly-gpu" +ARG local_tensorflow_pip_spec="" +ARG extra_pip_specs="" +ARG swift_tf_url=https://storage.googleapis.com/swift-tensorflow-artifacts/nightlies/latest/swift-tensorflow-DEVELOPMENT-cuda10.1-cudnn7-stock-ubuntu18.04.tar.gz + +# setup.py passes the base path of local .whl file is chosen for the docker image. +# Otherwise passes an empty existing file from the context. +COPY ${local_tensorflow_pip_spec} /${local_tensorflow_pip_spec} + +# Pick up some TF dependencies +# cublas-dev and libcudnn7-dev only needed because of libnvinfer-dev which may not +# really be needed. +# In the future, add the following lines in a shell script running on the +# benchmark vm to get the available dependent versions when updating cuda +# version (e.g to 10.2 or something later): +# sudo apt-cache search cuda-command-line-tool +# sudo apt-cache search cuda-cublas +# sudo apt-cache search cuda-cufft +# sudo apt-cache search cuda-curand +# sudo apt-cache search cuda-cusolver +# sudo apt-cache search cuda-cusparse + +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + cuda-command-line-tools-10-1 \ + cuda-cufft-10-1 \ + cuda-curand-10-1 \ + cuda-cusolver-10-1 \ + cuda-cusparse-10-1 \ + libcudnn7=7.6.4.38-1+cuda10.1 \ + libcudnn7-dev=7.6.4.38-1+cuda10.1 \ + libfreetype6-dev \ + libhdf5-serial-dev \ + libzmq3-dev \ + libpng-dev \ + pkg-config \ + software-properties-common \ + unzip \ + lsb-core \ + curl + +RUN apt-get update && \ + apt-get install -y --no-install-recommends libnvinfer5=5.1.5-1+cuda10.1 \ + libnvinfer-dev=5.1.5-1+cuda10.1 \ + libnvinfer6=6.0.1-1+cuda10.1 \ + && apt-get clean + +# For CUDA profiling, TensorFlow requires CUPTI. +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH + +# See http://bugs.python.org/issue19846 +ENV LANG C.UTF-8 + +# Add google-cloud-sdk to the source list +RUN echo "deb http://packages.cloud.google.com/apt cloud-sdk-$(lsb_release -c -s) main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - + +# Install extras needed by most models +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + ca-certificates \ + wget \ + htop \ + zip \ + google-cloud-sdk + +# Install / update Python and Python3 +RUN apt-get install -y --no-install-recommends \ + python3 \ + python3-dev \ + python3-pip \ + python3-setuptools \ + python3-venv + +# Upgrade pip, need to use pip3 and then pip after this or an error +# is thrown for no main found. +RUN pip3 install --upgrade pip +# setuptools upgraded to fix install requirements from model garden. +RUN pip install wheel +RUN pip install --upgrade setuptools google-api-python-client pyyaml google-cloud google-cloud-bigquery google-cloud-datastore mock +RUN pip install absl-py +RUN pip install --upgrade --force-reinstall ${tensorflow_pip_spec} ${extra_pip_specs} +RUN pip install tfds-nightly +RUN pip install -U scikit-learn + +RUN curl https://raw.githubusercontent.com/tensorflow/models/master/official/requirements.txt > /tmp/requirements.txt +RUN pip install -r /tmp/requirements.txt + +RUN pip freeze + +### Install Swift deps. +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + curl \ + git \ + python \ + python-dev \ + python-pip \ + python-setuptools \ + python-tk \ + python3 \ + python3-pip \ + python3-setuptools \ + clang \ + libcurl4-openssl-dev \ + libicu-dev \ + libpython-dev \ + libpython3-dev \ + libncurses5-dev \ + libxml2 \ + libblocksruntime-dev + +# Download and extract S4TF +WORKDIR /swift-tensorflow-toolchain +RUN if ! curl -fSsL --retry 5 $swift_tf_url -o swift.tar.gz; \ + then sleep 30 && curl -fSsL --retry 5 $swift_tf_url -o swift.tar.gz; \ + fi; + +RUN mkdir usr \ + && tar -xzf swift.tar.gz --directory=usr --strip-components=1 \ + && rm swift.tar.gz +ENV PATH="/swift-tensorflow-toolchain/usr/bin:${PATH}" +ENV LD_LIBRARY_PATH="/swift-tensorflow-toolchain/usr/lib/swift/linux/:${LD_LIBRARY_PATH}" diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_s4tf_cuda11.0 b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_s4tf_cuda11.0 new file mode 100644 index 00000000..ee328fd6 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_s4tf_cuda11.0 @@ -0,0 +1,128 @@ +# Ubuntu 18.04 Python3 with CUDA 11.0 and the following: +# - Installs tf-nightly-gpu (this is TF 2.4) +# - Installs requirements.txt for tensorflow/models +# Additionally also installs +# - Latest S4TF development snapshot for cuda 11.0 + +FROM nvidia/cuda:11.0-base-ubuntu18.04 as base +ARG tensorflow_pip_spec="tf-nightly-gpu" +ARG local_tensorflow_pip_spec="" +ARG extra_pip_specs="" +ARG swift_tf_url=https://storage.googleapis.com/swift-tensorflow-artifacts/nightlies/latest/swift-tensorflow-DEVELOPMENT-cuda11.0-cudnn8-stock-ubuntu18.04.tar.gz + +# setup.py passes the base path of local .whl file is chosen for the docker image. +# Otherwise passes an empty existing file from the context. +COPY ${local_tensorflow_pip_spec} /${local_tensorflow_pip_spec} + +# Pick up some TF dependencies +# cublas-dev and libcudnn8-dev only needed because of libnvinfer-dev which may not +# really be needed. +# In the future, add the following lines in a shell script running on the +# benchmark vm to get the available dependent versions when updating cuda +# version (e.g to 10.2 or something later): +# sudo apt-cache search cuda-command-line-tool +# sudo apt-cache search cuda-cublas +# sudo apt-cache search cuda-cufft +# sudo apt-cache search cuda-curand +# sudo apt-cache search cuda-cusolver +# sudo apt-cache search cuda-cusparse + +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + cuda-tools-11-0 \ + cuda-toolkit-11-0 \ + libcudnn8=8.0.4.30-1+cuda11.0 \ + libcudnn8-dev=8.0.4.30-1+cuda11.0 \ + libfreetype6-dev \ + libhdf5-serial-dev \ + libzmq3-dev \ + libpng-dev \ + pkg-config \ + software-properties-common \ + unzip \ + lsb-core \ + curl + +RUN apt-get update && \ + apt-get install -y --no-install-recommends libnvinfer7=7.2.0-1+cuda11.0 \ + libnvinfer-dev=7.2.0-1+cuda11.0 \ + && apt-get clean + +# For CUDA profiling, TensorFlow requires CUPTI. +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH + +# See http://bugs.python.org/issue19846 +ENV LANG C.UTF-8 + +# Add google-cloud-sdk to the source list +RUN echo "deb http://packages.cloud.google.com/apt cloud-sdk-$(lsb_release -c -s) main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - + +# Install extras needed by most models +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + ca-certificates \ + wget \ + htop \ + zip \ + google-cloud-sdk + +# Install / update Python and Python3 +RUN apt-get install -y --no-install-recommends \ + python3 \ + python3-dev \ + python3-pip \ + python3-setuptools \ + python3-venv + +# Upgrade pip, need to use pip3 and then pip after this or an error +# is thrown for no main found. +RUN pip3 install --upgrade pip +# setuptools upgraded to fix install requirements from model garden. +RUN pip install wheel +RUN pip install --upgrade setuptools google-api-python-client pyyaml google-cloud google-cloud-bigquery google-cloud-datastore mock +RUN pip install absl-py +RUN pip install --upgrade --force-reinstall ${tensorflow_pip_spec} ${extra_pip_specs} +RUN pip install tfds-nightly +RUN pip install -U scikit-learn + +RUN curl https://raw.githubusercontent.com/tensorflow/models/master/official/requirements.txt > /tmp/requirements.txt +RUN pip install -r /tmp/requirements.txt + +RUN pip freeze + +### Install Swift deps. +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + curl \ + git \ + python \ + python-dev \ + python-pip \ + python-setuptools \ + python-tk \ + python3 \ + python3-pip \ + python3-setuptools \ + clang \ + libcurl4-openssl-dev \ + libicu-dev \ + libpython-dev \ + libpython3-dev \ + libncurses5-dev \ + libxml2 \ + libblocksruntime-dev + +# Download and extract S4TF +WORKDIR /swift-tensorflow-toolchain +RUN if ! curl -fSsL --retry 5 $swift_tf_url -o swift.tar.gz; \ + then sleep 30 && curl -fSsL --retry 5 $swift_tf_url -o swift.tar.gz; \ + fi; + +RUN mkdir usr \ + && tar -xzf swift.tar.gz --directory=usr --strip-components=1 \ + && rm swift.tar.gz +ENV PATH="/swift-tensorflow-toolchain/usr/bin:${PATH}" +ENV LD_LIBRARY_PATH="/swift-tensorflow-toolchain/usr/lib/swift/linux/:${LD_LIBRARY_PATH}" diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_testing_apiclient b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_testing_apiclient new file mode 100644 index 00000000..0b8eadf4 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_testing_apiclient @@ -0,0 +1,94 @@ +# Ubuntu 18.04 Python3 with CUDA 10.1 and the following: +# - Installs tf-nightly-gpu (this is TF 2.1) +# - Installs requirements.txt for tensorflow/models +# - TF 2.0 tested with cuda 10.0, but we need to test tf 2.1 with cuda 10.1. + +FROM nvidia/cuda:10.1-base-ubuntu18.04 as base +ARG tensorflow_pip_spec="tf-nightly-gpu" +ARG local_tensorflow_pip_spec="" +ARG extra_pip_specs="" + +# setup.py passes the base path of local .whl file is chosen for the docker image. +# Otherwise passes an empty existing file from the context. +COPY ${local_tensorflow_pip_spec} /${local_tensorflow_pip_spec} + +# Pick up some TF dependencies +# cublas-dev and libcudnn7-dev only needed because of libnvinfer-dev which may not +# really be needed. +# In the future, add the following lines in a shell script running on the +# benchmark vm to get the available dependent versions when updating cuda +# version (e.g to 10.2 or something later): +# sudo apt-cache search cuda-command-line-tool +# sudo apt-cache search cuda-cublas +# sudo apt-cache search cuda-cufft +# sudo apt-cache search cuda-curand +# sudo apt-cache search cuda-cusolver +# sudo apt-cache search cuda-cusparse + +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + cuda-command-line-tools-10-1 \ + cuda-cufft-10-1 \ + cuda-curand-10-1 \ + cuda-cusolver-10-1 \ + cuda-cusparse-10-1 \ + libcudnn7=7.6.4.38-1+cuda10.1 \ + libcudnn7-dev=7.6.4.38-1+cuda10.1 \ + libfreetype6-dev \ + libhdf5-serial-dev \ + libzmq3-dev \ + libpng-dev \ + pkg-config \ + software-properties-common \ + unzip \ + lsb-core \ + curl + +RUN apt-get update && \ + apt-get install -y --no-install-recommends libnvinfer5=5.1.5-1+cuda10.1 \ + libnvinfer-dev=5.1.5-1+cuda10.1 \ + libnvinfer6=6.0.1-1+cuda10.1 \ + && apt-get clean + +# For CUDA profiling, TensorFlow requires CUPTI. +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH + +# See http://bugs.python.org/issue19846 +ENV LANG C.UTF-8 + +# Add google-cloud-sdk to the source list +RUN echo "deb http://packages.cloud.google.com/apt cloud-sdk-$(lsb_release -c -s) main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - + +# Install extras needed by most models +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + ca-certificates \ + wget \ + htop \ + zip \ + google-cloud-sdk + +# Install / update Python and Python3 +RUN apt-get install -y --no-install-recommends \ + python3 \ + python3-dev \ + python3-pip \ + python3-setuptools \ + python3-venv + +# Upgrade pip, need to use pip3 and then pip after this or an error +# is thrown for no main found. +RUN pip3 install --upgrade pip +# setuptools upgraded to fix install requirements from model garden. +RUN pip install wheel +RUN pip install --upgrade setuptools google-api-python-client==1.8.0 pyyaml google-cloud google-cloud-bigquery mock +RUN pip install absl-py +RUN pip install --upgrade --force-reinstall ${tensorflow_pip_spec} ${extra_pip_specs} +RUN pip install tfds-nightly +RUN pip install -U scikit-learn + +RUN curl https://raw.githubusercontent.com/tensorflow/models/master/official/requirements.txt > /tmp/requirements.txt +RUN pip install -r /tmp/requirements.txt + +RUN pip freeze diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_11 b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_11 new file mode 100644 index 00000000..4385d95a --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_11 @@ -0,0 +1,109 @@ +# Ubuntu 18.04 Python3 with CUDA 11 and the following: +# - Installs tf-nightly-gpu (this is TF 2.3) +# - Installs requirements.txt for tensorflow/models + +FROM nvidia/cuda:11.2.1-cudnn8-devel-ubuntu18.04 as base +ARG tensorflow_pip_spec="tf-nightly-gpu" +ARG local_tensorflow_pip_spec="" +ARG extra_pip_specs="" +ENV PIP_CMD="python3.9 -m pip" + +# setup.py passes the base path of local .whl file is chosen for the docker image. +# Otherwise passes an empty existing file from the context. +COPY ${local_tensorflow_pip_spec} /${local_tensorflow_pip_spec} + +# Pick up some TF dependencies +# cublas-dev and libcudnn7-dev only needed because of libnvinfer-dev which may not +# really be needed. +# In the future, add the following lines in a shell script running on the +# benchmark vm to get the available dependent versions when updating cuda +# version (e.g to 10.2 or something later): +# sudo apt-cache search cuda-command-line-tool +# sudo apt-cache search cuda-cublas +# sudo apt-cache search cuda-cufft +# sudo apt-cache search cuda-curand +# sudo apt-cache search cuda-cusolver +# sudo apt-cache search cuda-cusparse + +# Needed to disable prompts during installation. +ENV DEBIAN_FRONTEND noninteractive + + +RUN apt-get update && apt-get install -y --no-install-recommends \ + libfreetype6-dev \ + libhdf5-serial-dev \ + libzmq3-dev \ + libpng-dev \ + pkg-config \ + software-properties-common \ + unzip \ + lsb-core \ + curl + +# Python 3.9 related deps in this ppa. +RUN add-apt-repository ppa:deadsnakes/ppa + + +# Install / update Python and Python3 +RUN apt-get install -y --no-install-recommends \ + python3.9 \ + python3-pip \ + python3.9-dev \ + python3-setuptools \ + python3.9-venv \ + python3.9-distutils \ + python3.9-lib2to3 + +# Upgrade pip, need to use pip3 and then pip after this or an error +# is thrown for no main found. +RUN ${PIP_CMD} install --upgrade pip +RUN ${PIP_CMD} install --upgrade distlib +# setuptools upgraded to fix install requirements from model garden. +RUN ${PIP_CMD} install --upgrade setuptools + +# For CUDA profiling, TensorFlow requires CUPTI. +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:/usr/local/cuda-11.2/lib64:$LD_LIBRARY_PATH + +# See http://bugs.python.org/issue19846 +ENV LANG C.UTF-8 + +# Add google-cloud-sdk to the source list +RUN echo "deb http://packages.cloud.google.com/apt cloud-sdk-$(lsb_release -c -s) main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - + +# Install extras needed by most models +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + ca-certificates \ + wget \ + htop \ + zip \ + google-cloud-sdk + +RUN ${PIP_CMD} install --upgrade pyyaml +RUN ${PIP_CMD} install --upgrade google-api-python-client==1.8.0 +RUN ${PIP_CMD} install --upgrade google-cloud google-cloud-bigquery google-cloud-datastore mock + + +RUN ${PIP_CMD} install wheel +RUN ${PIP_CMD} install absl-py +RUN ${PIP_CMD} install --upgrade --force-reinstall ${tensorflow_pip_spec} ${extra_pip_specs} + +RUN ${PIP_CMD} install tfds-nightly +RUN ${PIP_CMD} install -U scikit-learn + +# Install dependnecies needed for tf.distribute test utils +RUN ${PIP_CMD} install dill tblib portpicker + +RUN curl https://raw.githubusercontent.com/tensorflow/models/master/official/requirements.txt > /tmp/requirements.txt +RUN ${PIP_CMD} install -r /tmp/requirements.txt + +RUN ${PIP_CMD} install tf-estimator-nightly +RUN ${PIP_CMD} install tensorflow-text-nightly + +# RUN nvidia-smi + +RUN nvcc --version + + +RUN pip freeze diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_11_0 b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_11_0 new file mode 100644 index 00000000..2ad70524 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_11_0 @@ -0,0 +1,94 @@ +# Ubuntu 18.04 Python3 with CUDA 11 and the following: +# - Installs tf-nightly-gpu (this is TF 2.3) +# - Installs requirements.txt for tensorflow/models + +FROM nvidia/cuda:11.0-cudnn8-devel-ubuntu18.04 as base +ARG tensorflow_pip_spec="tf-nightly-gpu" +ARG local_tensorflow_pip_spec="" +ARG extra_pip_specs="" + +# setup.py passes the base path of local .whl file is chosen for the docker image. +# Otherwise passes an empty existing file from the context. +COPY ${local_tensorflow_pip_spec} /${local_tensorflow_pip_spec} + +# Pick up some TF dependencies +# cublas-dev and libcudnn7-dev only needed because of libnvinfer-dev which may not +# really be needed. +# In the future, add the following lines in a shell script running on the +# benchmark vm to get the available dependent versions when updating cuda +# version (e.g to 10.2 or something later): +# sudo apt-cache search cuda-command-line-tool +# sudo apt-cache search cuda-cublas +# sudo apt-cache search cuda-cufft +# sudo apt-cache search cuda-curand +# sudo apt-cache search cuda-cusolver +# sudo apt-cache search cuda-cusparse + +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + cuda-tools-11-0 \ + cuda-toolkit-11-0 \ + libcudnn8=8.0.4.30-1+cuda11.0 \ + libcudnn8-dev=8.0.4.30-1+cuda11.0 \ + libfreetype6-dev \ + libhdf5-serial-dev \ + libzmq3-dev \ + libpng-dev \ + pkg-config \ + software-properties-common \ + unzip \ + lsb-core \ + curl + +# For CUDA profiling, TensorFlow requires CUPTI. +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:/usr/local/cuda-11.0/lib64:$LD_LIBRARY_PATH + +# See http://bugs.python.org/issue19846 +ENV LANG C.UTF-8 + +# Add google-cloud-sdk to the source list +RUN echo "deb http://packages.cloud.google.com/apt cloud-sdk-$(lsb_release -c -s) main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - + +# Install extras needed by most models +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + ca-certificates \ + wget \ + htop \ + zip \ + google-cloud-sdk + +# Install / update Python and Python3 +RUN apt-get install -y --no-install-recommends \ + python3 \ + python3-dev \ + python3-pip \ + python3-setuptools \ + python3-venv + +# Upgrade pip, need to use pip3 and then pip after this or an error +# is thrown for no main found. +RUN pip3 install --upgrade pip +# setuptools upgraded to fix install requirements from model garden. +RUN pip install wheel +RUN pip install --upgrade setuptools google-api-python-client==1.8.0 pyyaml google-cloud google-cloud-bigquery google-cloud-datastore mock +RUN pip install absl-py +RUN pip install --upgrade --force-reinstall ${tensorflow_pip_spec} ${extra_pip_specs} +RUN pip install tfds-nightly +RUN pip install -U scikit-learn +# Install dependnecies needed for tf.distribute test utils +RUN pip install dill tblib portpicker + +RUN curl https://raw.githubusercontent.com/tensorflow/models/master/official/requirements.txt > /tmp/requirements.txt +RUN pip install -r /tmp/requirements.txt + +RUN pip install tf-estimator-nightly +RUN pip install tensorflow-text-nightly + +# RUN nvidia-smi + +RUN nvcc --version + + +RUN pip freeze diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_11_py36 b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_11_py36 new file mode 100644 index 00000000..95980941 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_11_py36 @@ -0,0 +1,90 @@ +# Ubuntu 18.04 Python3 with CUDA 11 and the following: +# - Installs tf-nightly-gpu (this is TF 2.3) +# - Installs requirements.txt for tensorflow/models + +FROM nvidia/cuda:11.2.1-cudnn8-devel-ubuntu18.04 as base +ARG tensorflow_pip_spec="tf-nightly-gpu" +ARG local_tensorflow_pip_spec="" +ARG extra_pip_specs="" + +# setup.py passes the base path of local .whl file is chosen for the docker image. +# Otherwise passes an empty existing file from the context. +COPY ${local_tensorflow_pip_spec} /${local_tensorflow_pip_spec} + +# Pick up some TF dependencies +# cublas-dev and libcudnn7-dev only needed because of libnvinfer-dev which may not +# really be needed. +# In the future, add the following lines in a shell script running on the +# benchmark vm to get the available dependent versions when updating cuda +# version (e.g to 10.2 or something later): +# sudo apt-cache search cuda-command-line-tool +# sudo apt-cache search cuda-cublas +# sudo apt-cache search cuda-cufft +# sudo apt-cache search cuda-curand +# sudo apt-cache search cuda-cusolver +# sudo apt-cache search cuda-cusparse + +RUN apt-get update && apt-get install -y --no-install-recommends \ + libfreetype6-dev \ + libhdf5-serial-dev \ + libzmq3-dev \ + libpng-dev \ + pkg-config \ + software-properties-common \ + unzip \ + lsb-core \ + curl + +# For CUDA profiling, TensorFlow requires CUPTI. +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:/usr/local/cuda-11.2/lib64:$LD_LIBRARY_PATH + +# See http://bugs.python.org/issue19846 +ENV LANG C.UTF-8 + +# Add google-cloud-sdk to the source list +RUN echo "deb http://packages.cloud.google.com/apt cloud-sdk-$(lsb_release -c -s) main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - + +# Install extras needed by most models +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + ca-certificates \ + wget \ + htop \ + zip \ + google-cloud-sdk + +# Install / update Python and Python3 +RUN apt-get install -y --no-install-recommends \ + python3 \ + python3-dev \ + python3-pip \ + python3-setuptools \ + python3-venv + +# Upgrade pip, need to use pip3 and then pip after this or an error +# is thrown for no main found. +RUN pip3 install --upgrade pip +# setuptools upgraded to fix install requirements from model garden. +RUN pip install wheel +RUN pip install --upgrade setuptools google-api-python-client==1.8.0 pyyaml google-cloud google-cloud-bigquery google-cloud-datastore mock +RUN pip install absl-py +RUN pip install --upgrade --force-reinstall ${tensorflow_pip_spec} ${extra_pip_specs} +RUN pip install tfds-nightly +RUN pip install -U scikit-learn +# Install dependnecies needed for tf.distribute test utils +RUN pip install dill tblib portpicker + +RUN curl https://raw.githubusercontent.com/tensorflow/models/master/official/requirements.txt > /tmp/requirements.txt +RUN pip install -r /tmp/requirements.txt + +RUN pip install tf-estimator-nightly +RUN pip install tensorflow-text-nightly +RUN pip install psutil + +# RUN nvidia-smi + +RUN nvcc --version + + +RUN pip freeze diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_11_rollback b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_11_rollback new file mode 100644 index 00000000..0bc1769e --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_11_rollback @@ -0,0 +1,110 @@ +# Ubuntu 18.04 Python3 with CUDA 11 and the following: +# - Installs tf-nightly-gpu (this is TF 2.3) +# - Installs requirements.txt for tensorflow/models + +FROM nvidia/cuda:11.2.1-cudnn8-devel-ubuntu18.04 as base +ARG tensorflow_pip_spec="tf-nightly-gpu" +ARG local_tensorflow_pip_spec="" +ARG extra_pip_specs="" +ENV PIP_CMD="python3.9 -m pip" + +# setup.py passes the base path of local .whl file is chosen for the docker image. +# Otherwise passes an empty existing file from the context. +COPY ${local_tensorflow_pip_spec} /${local_tensorflow_pip_spec} + +# Pick up some TF dependencies +# cublas-dev and libcudnn7-dev only needed because of libnvinfer-dev which may not +# really be needed. +# In the future, add the following lines in a shell script running on the +# benchmark vm to get the available dependent versions when updating cuda +# version (e.g to 10.2 or something later): +# sudo apt-cache search cuda-command-line-tool +# sudo apt-cache search cuda-cublas +# sudo apt-cache search cuda-cufft +# sudo apt-cache search cuda-curand +# sudo apt-cache search cuda-cusolver +# sudo apt-cache search cuda-cusparse + +# Needed to disable prompts during installation. +ENV DEBIAN_FRONTEND noninteractive + + +RUN apt-get update && apt-get install -y --no-install-recommends \ + libfreetype6-dev \ + libhdf5-serial-dev \ + libzmq3-dev \ + libpng-dev \ + pkg-config \ + software-properties-common \ + unzip \ + lsb-core \ + curl + +# Python 3.9 related deps in this ppa. +RUN add-apt-repository ppa:deadsnakes/ppa + + +# Install / update Python and Python3 +RUN apt-get install -y --no-install-recommends \ + python3.9 \ + python3-pip \ + python3.9-dev \ + python3-setuptools \ + python3.9-venv \ + python3.9-distutils \ + python3.9-lib2to3 + +# Upgrade pip, need to use pip3 and then pip after this or an error +# is thrown for no main found. +RUN ${PIP_CMD} install --upgrade pip +RUN ${PIP_CMD} install --upgrade distlib +# setuptools upgraded to fix install requirements from model garden. +RUN ${PIP_CMD} install --upgrade setuptools + +# For CUDA profiling, TensorFlow requires CUPTI. +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:/usr/local/cuda-11.2/lib64:$LD_LIBRARY_PATH + +# See http://bugs.python.org/issue19846 +ENV LANG C.UTF-8 + +# Add google-cloud-sdk to the source list +RUN echo "deb http://packages.cloud.google.com/apt cloud-sdk-$(lsb_release -c -s) main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - + +# Install extras needed by most models +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + ca-certificates \ + wget \ + htop \ + zip \ + google-cloud-sdk + +RUN ${PIP_CMD} install --upgrade pyyaml +RUN ${PIP_CMD} install --upgrade google-api-python-client==1.8.0 +RUN ${PIP_CMD} install --upgrade google-cloud google-cloud-bigquery google-cloud-datastore mock + + +RUN ${PIP_CMD} install wheel +RUN ${PIP_CMD} install absl-py +RUN ${PIP_CMD} install --upgrade --force-reinstall ${tensorflow_pip_spec} ${extra_pip_specs} + +RUN ${PIP_CMD} install tfds-nightly +RUN ${PIP_CMD} install -U scikit-learn + +# Install dependnecies needed for tf.distribute test utils +RUN ${PIP_CMD} install dill tblib portpicker + +RUN curl https://raw.githubusercontent.com/tensorflow/models/master/official/requirements.txt > /tmp/requirements.txt +RUN ${PIP_CMD} install -r /tmp/requirements.txt + +RUN ${PIP_CMD} install tf-estimator-nightly +RUN ${PIP_CMD} install tensorflow-text-nightly +RUN ${PIP_CMD} install keras-nightly==2.7.0.dev2021082607 + +# RUN nvidia-smi + +RUN nvcc --version + + +RUN pip freeze diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_11_rollback_2 b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_11_rollback_2 new file mode 100644 index 00000000..a9ba568d --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_11_rollback_2 @@ -0,0 +1,110 @@ +# Ubuntu 18.04 Python3 with CUDA 11 and the following: +# - Installs tf-nightly-gpu (this is TF 2.3) +# - Installs requirements.txt for tensorflow/models + +FROM nvidia/cuda:11.2.1-cudnn8-devel-ubuntu18.04 as base +ARG tensorflow_pip_spec="tf-nightly-gpu" +ARG local_tensorflow_pip_spec="" +ARG extra_pip_specs="" +ENV PIP_CMD="python3.9 -m pip" + +# setup.py passes the base path of local .whl file is chosen for the docker image. +# Otherwise passes an empty existing file from the context. +COPY ${local_tensorflow_pip_spec} /${local_tensorflow_pip_spec} + +# Pick up some TF dependencies +# cublas-dev and libcudnn7-dev only needed because of libnvinfer-dev which may not +# really be needed. +# In the future, add the following lines in a shell script running on the +# benchmark vm to get the available dependent versions when updating cuda +# version (e.g to 10.2 or something later): +# sudo apt-cache search cuda-command-line-tool +# sudo apt-cache search cuda-cublas +# sudo apt-cache search cuda-cufft +# sudo apt-cache search cuda-curand +# sudo apt-cache search cuda-cusolver +# sudo apt-cache search cuda-cusparse + +# Needed to disable prompts during installation. +ENV DEBIAN_FRONTEND noninteractive + + +RUN apt-get update && apt-get install -y --no-install-recommends \ + libfreetype6-dev \ + libhdf5-serial-dev \ + libzmq3-dev \ + libpng-dev \ + pkg-config \ + software-properties-common \ + unzip \ + lsb-core \ + curl + +# Python 3.9 related deps in this ppa. +RUN add-apt-repository ppa:deadsnakes/ppa + + +# Install / update Python and Python3 +RUN apt-get install -y --no-install-recommends \ + python3.9 \ + python3-pip \ + python3.9-dev \ + python3-setuptools \ + python3.9-venv \ + python3.9-distutils \ + python3.9-lib2to3 + +# Upgrade pip, need to use pip3 and then pip after this or an error +# is thrown for no main found. +RUN ${PIP_CMD} install --upgrade pip +RUN ${PIP_CMD} install --upgrade distlib +# setuptools upgraded to fix install requirements from model garden. +RUN ${PIP_CMD} install --upgrade setuptools + +# For CUDA profiling, TensorFlow requires CUPTI. +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:/usr/local/cuda-11.2/lib64:$LD_LIBRARY_PATH + +# See http://bugs.python.org/issue19846 +ENV LANG C.UTF-8 + +# Add google-cloud-sdk to the source list +RUN echo "deb http://packages.cloud.google.com/apt cloud-sdk-$(lsb_release -c -s) main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - + +# Install extras needed by most models +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + ca-certificates \ + wget \ + htop \ + zip \ + google-cloud-sdk + +RUN ${PIP_CMD} install --upgrade pyyaml +RUN ${PIP_CMD} install --upgrade google-api-python-client==1.8.0 +RUN ${PIP_CMD} install --upgrade google-cloud google-cloud-bigquery google-cloud-datastore mock + + +RUN ${PIP_CMD} install wheel +RUN ${PIP_CMD} install absl-py +RUN ${PIP_CMD} install --upgrade --force-reinstall ${tensorflow_pip_spec} ${extra_pip_specs} + +RUN ${PIP_CMD} install tfds-nightly +RUN ${PIP_CMD} install -U scikit-learn + +# Install dependnecies needed for tf.distribute test utils +RUN ${PIP_CMD} install dill tblib portpicker + +RUN curl https://raw.githubusercontent.com/tensorflow/models/master/official/requirements.txt > /tmp/requirements.txt +RUN ${PIP_CMD} install -r /tmp/requirements.txt + +RUN ${PIP_CMD} install tf-estimator-nightly +RUN ${PIP_CMD} install tensorflow-text-nightly +RUN ${PIP_CMD} install keras-nightly==2.7.0.dev2021070900 + +# RUN nvidia-smi + +RUN nvcc --version + + +RUN pip freeze diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_testing b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_testing new file mode 100644 index 00000000..2ad70524 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_cuda_testing @@ -0,0 +1,94 @@ +# Ubuntu 18.04 Python3 with CUDA 11 and the following: +# - Installs tf-nightly-gpu (this is TF 2.3) +# - Installs requirements.txt for tensorflow/models + +FROM nvidia/cuda:11.0-cudnn8-devel-ubuntu18.04 as base +ARG tensorflow_pip_spec="tf-nightly-gpu" +ARG local_tensorflow_pip_spec="" +ARG extra_pip_specs="" + +# setup.py passes the base path of local .whl file is chosen for the docker image. +# Otherwise passes an empty existing file from the context. +COPY ${local_tensorflow_pip_spec} /${local_tensorflow_pip_spec} + +# Pick up some TF dependencies +# cublas-dev and libcudnn7-dev only needed because of libnvinfer-dev which may not +# really be needed. +# In the future, add the following lines in a shell script running on the +# benchmark vm to get the available dependent versions when updating cuda +# version (e.g to 10.2 or something later): +# sudo apt-cache search cuda-command-line-tool +# sudo apt-cache search cuda-cublas +# sudo apt-cache search cuda-cufft +# sudo apt-cache search cuda-curand +# sudo apt-cache search cuda-cusolver +# sudo apt-cache search cuda-cusparse + +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + cuda-tools-11-0 \ + cuda-toolkit-11-0 \ + libcudnn8=8.0.4.30-1+cuda11.0 \ + libcudnn8-dev=8.0.4.30-1+cuda11.0 \ + libfreetype6-dev \ + libhdf5-serial-dev \ + libzmq3-dev \ + libpng-dev \ + pkg-config \ + software-properties-common \ + unzip \ + lsb-core \ + curl + +# For CUDA profiling, TensorFlow requires CUPTI. +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:/usr/local/cuda-11.0/lib64:$LD_LIBRARY_PATH + +# See http://bugs.python.org/issue19846 +ENV LANG C.UTF-8 + +# Add google-cloud-sdk to the source list +RUN echo "deb http://packages.cloud.google.com/apt cloud-sdk-$(lsb_release -c -s) main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - + +# Install extras needed by most models +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + ca-certificates \ + wget \ + htop \ + zip \ + google-cloud-sdk + +# Install / update Python and Python3 +RUN apt-get install -y --no-install-recommends \ + python3 \ + python3-dev \ + python3-pip \ + python3-setuptools \ + python3-venv + +# Upgrade pip, need to use pip3 and then pip after this or an error +# is thrown for no main found. +RUN pip3 install --upgrade pip +# setuptools upgraded to fix install requirements from model garden. +RUN pip install wheel +RUN pip install --upgrade setuptools google-api-python-client==1.8.0 pyyaml google-cloud google-cloud-bigquery google-cloud-datastore mock +RUN pip install absl-py +RUN pip install --upgrade --force-reinstall ${tensorflow_pip_spec} ${extra_pip_specs} +RUN pip install tfds-nightly +RUN pip install -U scikit-learn +# Install dependnecies needed for tf.distribute test utils +RUN pip install dill tblib portpicker + +RUN curl https://raw.githubusercontent.com/tensorflow/models/master/official/requirements.txt > /tmp/requirements.txt +RUN pip install -r /tmp/requirements.txt + +RUN pip install tf-estimator-nightly +RUN pip install tensorflow-text-nightly + +# RUN nvidia-smi + +RUN nvcc --version + + +RUN pip freeze diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_custom_pip b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_custom_pip new file mode 100644 index 00000000..69abee6e --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_custom_pip @@ -0,0 +1,88 @@ +# Ubuntu 18.04 Python3.6 with CUDA 10 and the following: +# - Installs custom TensorFlow pip package +# - Installs requirements.txt for tensorflow/models + +# NOTE: Branched from Dockerfile_ubuntu_1804_tf_v1 with changes relevant to +# tensorflow_pip_spec. When updating please keep the difference minimal. + +FROM nvidia/cuda:10.0-base-ubuntu18.04 as base + +# Location of custom TF pip package, must be relative to docker context. +# Note that the version tag in the name of wheel file is meaningless. +ARG tensorflow_pip_spec="resources/tensorflow-0.0.1-cp36-cp36m-linux_x86_64.whl" +ARG extra_pip_specs="" +ARG local_tensorflow_pip_spec="" + +COPY ${local_tensorflow_pip_spec} /${local_tensorflow_pip_spec} + +COPY ${tensorflow_pip_spec} /tensorflow-0.0.1-cp36-cp36m-linux_x86_64.whl + +# Pick up some TF dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + cuda-command-line-tools-10-0 \ + cuda-cublas-10-0 \ + cuda-cufft-10-0 \ + cuda-curand-10-0 \ + cuda-cusolver-10-0 \ + cuda-cusparse-10-0 \ + libcudnn7=7.4.1.5-1+cuda10.0 \ + libfreetype6-dev \ + libhdf5-serial-dev \ + libzmq3-dev \ + libpng-dev \ + pkg-config \ + software-properties-common \ + unzip \ + lsb-core \ + curl + +RUN apt-get update && \ + apt-get install nvinfer-runtime-trt-repo-ubuntu1804-5.0.2-ga-cuda10.0 \ + && apt-get update \ + && apt-get install -y --no-install-recommends libnvinfer5=5.0.2-1+cuda10.0 \ + && apt-get clean + +# For CUDA profiling, TensorFlow requires CUPTI. +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH + +# See http://bugs.python.org/issue19846 +ENV LANG C.UTF-8 + +# Add google-cloud-sdk to the source list +RUN echo "deb http://packages.cloud.google.com/apt cloud-sdk-$(lsb_release -c -s) main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - + +# Install extras needed by most models +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + build-essential \ + ca-certificates \ + wget \ + htop \ + zip \ + google-cloud-sdk + +# Install / update Python and Python3 +RUN apt-get install -y --no-install-recommends \ + python3 \ + python3-dev \ + python3-pip \ + python3-setuptools \ + python3-venv + + +# Setup Python3 environment +RUN pip3 install --upgrade pip==9.0.1 +# setuptools upgraded to fix install requirements from model garden. +RUN pip3 install wheel +RUN pip3 install --upgrade setuptools google-api-python-client pyyaml google-cloud google-cloud-bigquery +RUN pip3 install absl-py +RUN pip3 install --upgrade --force-reinstall /tensorflow-0.0.1-cp36-cp36m-linux_x86_64.whl ${extra_pip_specs} +RUN pip3 install tfds-nightly +RUN pip3 install -U scikit-learn + +RUN curl https://raw.githubusercontent.com/tensorflow/models/master/official/requirements.txt > /tmp/requirements.txt +RUN pip3 install -r /tmp/requirements.txt + +RUN pip3 freeze diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_v1 b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_v1 new file mode 100644 index 00000000..9e89c1bb --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_v1 @@ -0,0 +1,88 @@ +# Ubuntu 18.04 Python3 with CUDA 10 and the following: +# - Installs tf-nightly-gpu +# - Installs requirements.txt for tensorflow/models +# +# This docker is not needed and is the same as the tf_v2 docker. The +# User can pass in the desired `ARG tensorflow_pip_spec` Remove +# one TF 1.0 testing is done or KOKORO jobs are updated to use the +# tensorfow_pip_spec rather than docker path to control TF version. + +FROM nvidia/cuda:10.0-base-ubuntu18.04 as base +ARG tensorflow_pip_spec="tf-nightly-gpu" +ARG extra_pip_specs="" +ARG local_tensorflow_pip_spec="" + +COPY ${local_tensorflow_pip_spec} /${local_tensorflow_pip_spec} + +# Pick up some TF dependencies +# cublas-dev and libcudnn7-dev only needed because of libnvinfer-dev which may not +# really be needed. +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + cuda-command-line-tools-10-0 \ + cuda-cublas-10-0 \ + cuda-cublas-dev-10-0 \ + cuda-cufft-10-0 \ + cuda-curand-10-0 \ + cuda-cusolver-10-0 \ + cuda-cusparse-10-0 \ + libcudnn7=7.6.0.64-1+cuda10.0 \ + libcudnn7-dev=7.6.0.64-1+cuda10.0 \ + libfreetype6-dev \ + libhdf5-serial-dev \ + libzmq3-dev \ + libpng-dev \ + pkg-config \ + software-properties-common \ + unzip \ + lsb-core \ + curl + +RUN apt-get update && \ + apt-get install -y --no-install-recommends libnvinfer5=5.1.5-1+cuda10.0 \ + libnvinfer-dev=5.1.5-1+cuda10.0 \ + && apt-get clean + +# For CUDA profiling, TensorFlow requires CUPTI. +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH + +# See http://bugs.python.org/issue19846 +ENV LANG C.UTF-8 + +# Add google-cloud-sdk to the source list +RUN echo "deb http://packages.cloud.google.com/apt cloud-sdk-$(lsb_release -c -s) main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - + +# Install extras needed by most models +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + ca-certificates \ + wget \ + htop \ + zip \ + google-cloud-sdk + +# Install / update Python and Python3 +RUN apt-get install -y --no-install-recommends \ + python3 \ + python3-dev \ + python3-pip \ + python3-setuptools \ + python3-venv + + +# Upgrade pip, need to use pip3 and then pip after this or an error +# is thrown for no main found. +RUN pip3 install --upgrade pip +# setuptools upgraded to fix install requirements from model garden. +RUN pip install wheel +RUN pip install --upgrade setuptools google-api-python-client pyyaml google-cloud google-cloud-bigquery mock +RUN pip install absl-py +RUN pip install --upgrade --force-reinstall ${tensorflow_pip_spec} ${extra_pip_specs} +RUN pip install tfds-nightly +RUN pip install -U scikit-learn + +RUN curl https://raw.githubusercontent.com/tensorflow/models/master/official/requirements.txt > /tmp/requirements.txt +RUN pip install -r /tmp/requirements.txt + +RUN pip freeze diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_v2 b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_v2 new file mode 100644 index 00000000..7bd56a37 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_v2 @@ -0,0 +1,85 @@ +# Ubuntu 18.04 Python3 with CUDA 10 and the following: +# - Installs tf-nightly-gpu (this is TF 2.0) +# - Installs requirements.txt for tensorflow/models + +FROM nvidia/cuda:10.0-base-ubuntu18.04 as base +ARG tensorflow_pip_spec="tf-nightly-gpu" +ARG local_tensorflow_pip_spec="" +ARG extra_pip_specs="" + +# setup.py passes the base path of local .whl file is chosen for the docker image. +# Otherwise passes an empty existing file from the context. +COPY ${local_tensorflow_pip_spec} /${local_tensorflow_pip_spec} + +# Pick up some TF dependencies +# cublas-dev and libcudnn7-dev only needed because of libnvinfer-dev which may not +# really be needed. +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + cuda-command-line-tools-10-0 \ + cuda-cublas-10-0 \ + cuda-cublas-dev-10-0 \ + cuda-cufft-10-0 \ + cuda-curand-10-0 \ + cuda-cusolver-10-0 \ + cuda-cusparse-10-0 \ + libcudnn7=7.6.2.24-1+cuda10.0 \ + libcudnn7-dev=7.6.2.24-1+cuda10.0 \ + libfreetype6-dev \ + libhdf5-serial-dev \ + libzmq3-dev \ + libpng-dev \ + pkg-config \ + software-properties-common \ + unzip \ + lsb-core \ + curl + +RUN apt-get update && \ + apt-get install -y --no-install-recommends libnvinfer5=5.1.5-1+cuda10.0 \ + libnvinfer-dev=5.1.5-1+cuda10.0 \ + && apt-get clean + +# For CUDA profiling, TensorFlow requires CUPTI. +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH + +# See http://bugs.python.org/issue19846 +ENV LANG C.UTF-8 + +# Add google-cloud-sdk to the source list +RUN echo "deb http://packages.cloud.google.com/apt cloud-sdk-$(lsb_release -c -s) main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - + +# Install extras needed by most models +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + ca-certificates \ + wget \ + htop \ + zip \ + google-cloud-sdk + +# Install / update Python and Python3 +RUN apt-get install -y --no-install-recommends \ + python3 \ + python3-dev \ + python3-pip \ + python3-setuptools \ + python3-venv + + +# Upgrade pip, need to use pip3 and then pip after this or an error +# is thrown for no main found. +RUN pip3 install --upgrade pip +# setuptools upgraded to fix install requirements from model garden. +RUN pip install wheel +RUN pip install --upgrade setuptools google-api-python-client pyyaml google-cloud google-cloud-bigquery mock +RUN pip install absl-py +RUN pip install --upgrade --force-reinstall ${tensorflow_pip_spec} ${extra_pip_specs} +RUN pip install tfds-nightly +RUN pip install -U scikit-learn + +RUN curl https://raw.githubusercontent.com/tensorflow/models/master/official/requirements.txt > /tmp/requirements.txt +RUN pip install -r /tmp/requirements.txt + +RUN pip freeze diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_v2_1 b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_v2_1 new file mode 100644 index 00000000..82016b03 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tf_v2_1 @@ -0,0 +1,96 @@ +# Ubuntu 18.04 Python3 with CUDA 10.1 and the following: +# - Installs tf-nightly-gpu (this is TF 2.1) +# - Installs requirements.txt for tensorflow/models +# - TF 2.0 tested with cuda 10.0, but we need to test tf 2.1 with cuda 10.1. + +FROM nvidia/cuda:10.1-base-ubuntu18.04 as base +ARG tensorflow_pip_spec="tf-nightly-gpu" +ARG local_tensorflow_pip_spec="" +ARG extra_pip_specs="" + +# setup.py passes the base path of local .whl file is chosen for the docker image. +# Otherwise passes an empty existing file from the context. +COPY ${local_tensorflow_pip_spec} /${local_tensorflow_pip_spec} + +# Pick up some TF dependencies +# cublas-dev and libcudnn7-dev only needed because of libnvinfer-dev which may not +# really be needed. +# In the future, add the following lines in a shell script running on the +# benchmark vm to get the available dependent versions when updating cuda +# version (e.g to 10.2 or something later): +# sudo apt-cache search cuda-command-line-tool +# sudo apt-cache search cuda-cublas +# sudo apt-cache search cuda-cufft +# sudo apt-cache search cuda-curand +# sudo apt-cache search cuda-cusolver +# sudo apt-cache search cuda-cusparse + +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + cuda-command-line-tools-10-1 \ + cuda-cufft-10-1 \ + cuda-curand-10-1 \ + cuda-cusolver-10-1 \ + cuda-cusparse-10-1 \ + libcudnn7=7.6.4.38-1+cuda10.1 \ + libcudnn7-dev=7.6.4.38-1+cuda10.1 \ + libfreetype6-dev \ + libhdf5-serial-dev \ + libzmq3-dev \ + libpng-dev \ + pkg-config \ + software-properties-common \ + unzip \ + lsb-core \ + curl + +RUN apt-get update && \ + apt-get install -y --no-install-recommends libnvinfer5=5.1.5-1+cuda10.1 \ + libnvinfer-dev=5.1.5-1+cuda10.1 \ + libnvinfer6=6.0.1-1+cuda10.1 \ + && apt-get clean + +# For CUDA profiling, TensorFlow requires CUPTI. +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH + +# See http://bugs.python.org/issue19846 +ENV LANG C.UTF-8 + +# Add google-cloud-sdk to the source list +RUN echo "deb http://packages.cloud.google.com/apt cloud-sdk-$(lsb_release -c -s) main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - + +# Install extras needed by most models +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + ca-certificates \ + wget \ + htop \ + zip \ + google-cloud-sdk + +# Install / update Python and Python3 +RUN apt-get install -y --no-install-recommends \ + python3 \ + python3-dev \ + python3-pip \ + python3-setuptools \ + python3-venv + +# Upgrade pip, need to use pip3 and then pip after this or an error +# is thrown for no main found. +RUN pip3 install --upgrade pip +# setuptools upgraded to fix install requirements from model garden. +RUN pip install wheel +RUN pip install --upgrade setuptools google-api-python-client==1.8.0 pyyaml google-cloud google-cloud-bigquery google-cloud-datastore mock +RUN pip install absl-py +RUN pip install --upgrade --force-reinstall ${tensorflow_pip_spec} ${extra_pip_specs} +RUN pip install tfds-nightly +RUN pip install -U scikit-learn +# Install dependnecies needed for tf.distribute test utils +RUN pip install dill tblib portpicker + +RUN curl https://raw.githubusercontent.com/tensorflow/models/master/official/requirements.txt > /tmp/requirements.txt +RUN pip install -r /tmp/requirements.txt + +RUN pip freeze diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tfx b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tfx new file mode 100644 index 00000000..6f6aac3a --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_1804_tfx @@ -0,0 +1,262 @@ +# Ubuntu 18.04 Python3 with CUDA 10 and the following: +# - Installs tf-nightly-gpu (this is TF 2.0) +# - Installs requirements.txt for tensorflow/models +# +# NOTE: Branched from Dockerfile_ubuntu_1804_tf_v2 with changes for +# TFX benchmarks. + +FROM nvidia/cuda:10.0-base-ubuntu18.04 as base +ARG tensorflow_pip_spec="tf-nightly-gpu" +ARG local_tensorflow_pip_spec="" +ARG extra_pip_specs="" + +# Specifies the default package version to use if no corresponding commit_id +# override is specified. +# If "head", uses the GitHub HEAD version. +# If "release", uses the latest released version from PyPI, REGARDLESS of +# package-compatibility requirements (e.g. even if tfx requires +# tensorflow-model-analysis<0.22, if tensorflow-model-analysis==0.22.0 is +# the latest released version on PyPI, we will install that). +# Packages include: tfx, tensorflow-transform, tensorflow-model-analysis, +# tensorflow-data-validation, tensorflow-metadata, tfx-bsl +ARG default_package_version="head" + +# Specifies the package version to use for the corresponding packages. +# If empty, uses the default specified by default_package_version. +# If "head", uses the GitHub HEAD version. +# If "release", uses the latest released version from PyPI, REGARDLESS of +# package-compatibility requirements. +# If "github_commit:", uses the given commit ID from GitHub. +# If "github_tag:" uses the given tag from GitHub. +# If "pypi:", uses the given package version from PyPI. +ARG tfx_package_version="" +ARG tensorflow_transform_package_version="" +ARG tensorflow_model_analysis_package_version="" +ARG tensorflow_data_validation_package_version="" +ARG tensorflow_metadata_package_version="" +ARG tfx_bsl_version="" + +# setup.py passes the base path of local .whl file is chosen for the docker image. +# Otherwise passes an empty existing file from the context. +COPY ${local_tensorflow_pip_spec} /${local_tensorflow_pip_spec} + +# Pick up some TF dependencies +# cublas-dev and libcudnn7-dev only needed because of libnvinfer-dev which may not +# really be needed. +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + cuda-command-line-tools-10-0 \ + cuda-cublas-10-0 \ + cuda-cublas-dev-10-0 \ + cuda-cufft-10-0 \ + cuda-curand-10-0 \ + cuda-cusolver-10-0 \ + cuda-cusparse-10-0 \ + libcudnn7=7.6.2.24-1+cuda10.0 \ + libcudnn7-dev=7.6.2.24-1+cuda10.0 \ + libfreetype6-dev \ + libhdf5-serial-dev \ + libzmq3-dev \ + libpng-dev \ + pkg-config \ + software-properties-common \ + unzip \ + lsb-core \ + curl + +RUN apt-get update && \ + apt-get install -y --no-install-recommends libnvinfer5=5.1.5-1+cuda10.0 \ + libnvinfer-dev=5.1.5-1+cuda10.0 \ + && apt-get clean + +# For CUDA profiling, TensorFlow requires CUPTI. +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:$LD_LIBRARY_PATH + +# See http://bugs.python.org/issue19846 +ENV LANG C.UTF-8 + +# Add google-cloud-sdk to the source list +RUN echo "deb http://packages.cloud.google.com/apt cloud-sdk-$(lsb_release -c -s) main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - + +# Install extras needed by most models +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + ca-certificates \ + wget \ + htop \ + zip \ + google-cloud-sdk + +# Install / update Python and Python3 +RUN apt-get install -y --no-install-recommends \ + python3 \ + python3-dev \ + python3-pip \ + python3-setuptools \ + python3-venv + + +# Upgrade pip, need to use pip3 and then pip after this or an error +# is thrown for no main found. +RUN pip3 install --upgrade pip +# setuptools upgraded to fix install requirements from model garden. +RUN pip install wheel +RUN pip install --upgrade setuptools google-api-python-client pyyaml google-cloud google-cloud-bigquery google-cloud-datastore mock +RUN pip install absl-py +RUN if [ ! -z "${extra_pip_specs}" ]; then pip install --upgrade --force-reinstall ${extra_pip_specs}; fi +RUN pip install tfds-nightly +RUN pip install -U scikit-learn + +RUN curl https://raw.githubusercontent.com/tensorflow/models/master/official/requirements.txt > /tmp/requirements.txt +RUN pip install -r /tmp/requirements.txt + +# Install yolk3k, for getting package versions from PyPI (so we can pull +# TFX from GitHub even when we need to install from the released version) +RUN pip install yolk3k + +# Install protoc +RUN PROTOC_ZIP=protoc-3.7.1-linux-x86_64.zip; \ + curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v3.7.1/$PROTOC_ZIP; \ + unzip -o $PROTOC_ZIP -d /usr/local bin/protoc; \ + unzip -o $PROTOC_ZIP -d /usr/local 'include/*'; \ + rm -f $PROTOC_ZIP; + +# Install Bazel +RUN curl https://bazel.build/bazel-release.pub.gpg | apt-key add - +RUN echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" | tee /etc/apt/sources.list.d/bazel.list +RUN apt update +RUN apt install bazel +# Create symlink to "python3" binary under the name "python" so Bazel doesn't complain about "python" not being found +RUN ln -s $(which python3) /usr/bin/python + +SHELL ["/bin/bash", "-c"] +RUN \ + function install_package { \ + # e.g. "head" or "release" \ + default_version="$1"; \ + # e.g "tensorflow-model-analysis" \ + package_name="$2"; \ + # e.g "model-analysis" \ + package_repo_name="$3"; \ + # How this package should be installed if pulled from GitHub. \ + # "none" for no extra installation steps required \ + # "bdist_wheel" for python setup.py bdist_wheel \ + package_install_type=$4; \ + # e.g. "head" or "release" or "pypi:0.21.4" or "github:[commit hash]" \ + package_version="$5"; \ + \ + # e.g. "tensorflow_model_analysis" \ + package_name_underscores=${package_name//-/_}; \ + if [ "$package_version" == "" ]; then \ + package_version="$default_version"; \ + fi; \ + \ + commit_id=""; \ + pypi_version=""; \ + if [ "$package_version" == "head" ]; then \ + commit_id=$(git ls-remote https://github.com/tensorflow/${package_repo_name} refs/heads/master | cut -f1); \ + echo ${package_name}: latest commit from GitHub: ${commit_id}; \ + elif [ "$package_version" == "release" ]; then \ + pypi_version=$(yolk -V $package_name | head -n 1 | cut -d' ' -f 2-); \ + echo ${package_name}: latest version from PyPI: ${pypi_version}; \ + elif [ "${package_version:0:5}" == "pypi:" ]; then \ + pypi_version="${package_version:5}"; \ + echo ${package_name}: using specified PyPI version: ${pypi_version}; \ + elif [ "${package_version:0:7}" == "github:" ]; then \ + commit_id="${package_version:7}"; \ + echo ${package_name}: using specified GitHub commit: ${commit_id}; \ + else \ + echo Unknown package version for ${package_name}: ${package_version}; \ + exit 1; \ + fi; \ + \ + if [ "$commit_id" != "" ]; then \ + if [ "$package_install_type" == "none" ]; then \ + # Package doesn't need extra installation steps - install directly from GitHub. \ + pip install -e git+https://github.com/tensorflow/${package_repo_name}.git@${commit_id}#egg=${package_name_underscores}; \ + install_commands+=("pip install --force --no-deps -e git+https://github.com/tensorflow/${package_repo_name}.git@${commit_id}#egg=${package_name_underscores}"); \ + echo Installed ${package_name} from GitHub commit ${commit_id}; \ + elif [ "$package_install_type" == "bdist_wheel" ]; then \ + # Package needs extra installation steps. Clone from GitHub, then build and install. \ + git clone https://github.com/tensorflow/${package_repo_name}.git; \ + pushd ${package_repo_name}; \ + git checkout ${commit_id}; \ + if [ "$package_name" == "tfx" ]; then \ + echo Building TFX pip package from source; \ + sed -i 's@packages=packages,@packages=packages, package_data={package_name: ["benchmarks/datasets/chicago_taxi/data/taxi_1M.tfrecords.gz"]},@' setup.py; \ + package_build/initialize.sh; \ + python package_build/ml-pipelines-sdk/setup.py bdist_wheel; \ + python package_build/tfx/setup.py bdist_wheel; \ + else \ + echo Using python setup.py bdist_wheel to build package; \ + python setup.py bdist_wheel; \ + fi; \ + pip install dist/*.whl; \ + install_commands+=("pip install --force --no-deps ${PWD}/dist/*.whl"); \ + popd; \ + echo Built and installed ${package_name} from GitHub commit ${commit_id}; \ + fi; \ + # Write GIT_COMMIT_ID attribute to the installed package. \ + package_path=$(python3 -c "import ${package_name_underscores}; print(list(${package_name_underscores}.__path__)[0])"); \ + echo "GIT_COMMIT_ID='${commit_id}'" >> ${package_path}/__init__.py; \ + install_commands+=("echo \"GIT_COMMIT_ID='${commit_id}'\" >> ${package_path}/__init__.py;"); \ + elif [ "$pypi_version" != "" ]; then \ + if [ "$package_name" == "tfx" ]; then \ + # Special handling for TFX - we want to install from GitHub, and get \ + # the data files as well (they are not included in the pip package). \ + # Install from the corresponding tag in GitHub. \ + echo Special handling for tfx: will install tfx from GitHub tag for version ${pypi_version}; \ + git clone --depth 1 --branch v${pypi_version} https://github.com/tensorflow/tfx.git; \ + pushd tfx; \ + echo Building TFX pip package from source; \ + sed -i 's@packages=packages,@packages=packages, package_data={package_name: ["benchmarks/datasets/chicago_taxi/data/taxi_1M.tfrecords.gz"]},@' setup.py; \ + package_build/initialize.sh; \ + python package_build/ml-pipelines-sdk/setup.py bdist_wheel; \ + python package_build/tfx/setup.py bdist_wheel; \ + pip install dist/*.whl; \ + install_commands+=("pip install --force --no-deps ${PWD}/dist/*.whl"); \ + popd; \ + echo Installed tfx from GitHub tag for version ${pypi_version}; \ + else \ + pip install ${package_name}==${pypi_version}; \ + install_commands+=("pip install --force --no-deps ${package_name}==${pypi_version}"); \ + echo Installed ${package_name} from PyPI version ${pypi_version}; \ + fi; \ + else \ + echo Neither commit_id nor pypi_version was set for ${package_name}; \ + exit 1; \ + fi; \ + }; \ + \ + # Array of commands to run post-installation. This is for forcing \ + # installation of packages without regard to the requirements of other \ + # packages. \ + # The first round of installations installs the packages and their \ + # requirements. This may result in some packages being re-installed at \ + # versions other than the requested versions due to requirements from \ + # other packages. \ + # The second round of installations via install_commands \ + # forces installations of the packages at the desired versions, ignoring \ + # any dependencies of these packages or other packages. Note that if there \ + # are incompatible package dependencies (e.g. tfx depends on \ + # apache-beam==2.21 and tensorflow-transform depends on apache-beam==2.22 \ + # then either could be installed depending on the installation order). \ + install_commands=(); \ + install_package "${default_package_version}" "tfx" "tfx" "bdist_wheel" "${tfx_package_version}"; \ + install_package "${default_package_version}" "tensorflow-transform" "transform" "none" "${tensorflow_transform_package_version}"; \ + install_package "${default_package_version}" "tensorflow-model-analysis" "model-analysis" "none" "${tensorflow_model_analysis_package_version}"; \ + install_package "${default_package_version}" "tensorflow-data-validation" "data-validation" "bdist_wheel" "${tensorflow_data_validation_package_version}"; \ + install_package "${default_package_version}" "tensorflow-metadata" "metadata" "bdist_wheel" "${tensorflow_metadata_package_version}"; \ + install_package "${default_package_version}" "tfx-bsl" "tfx-bsl" "bdist_wheel" "${tfx_bsl_package_version}"; \ + for cmd in "${install_commands[@]}"; do \ + echo Running "${cmd}"; \ + eval $cmd; \ + done; + +# Uninstall the TensorFlow version that TFX / the TFX components installed, and +# force install the version requested. +RUN pip uninstall -y tensorflow +RUN pip install --upgrade --force-reinstall ${tensorflow_pip_spec} + +RUN pip freeze diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_cuda11_8_0_0_180 b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_cuda11_8_0_0_180 new file mode 100644 index 00000000..9fe2c684 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_cuda11_8_0_0_180 @@ -0,0 +1,95 @@ +# Ubuntu 18.04 Python3 with CUDA 11 and the following: +# - Installs tf-nightly-gpu (this is TF 2.3) +# - Installs requirements.txt for tensorflow/models + +FROM nvidia/cuda:11.0-cudnn8-devel-ubuntu18.04 as base +ARG tensorflow_pip_spec="tf-nightly-gpu" +ARG local_tensorflow_pip_spec="" +ARG extra_pip_specs="" + +# setup.py passes the base path of local .whl file is chosen for the docker image. +# Otherwise passes an empty existing file from the context. +COPY ${local_tensorflow_pip_spec} /${local_tensorflow_pip_spec} + +# Pick up some TF dependencies +# cublas-dev and libcudnn7-dev only needed because of libnvinfer-dev which may not +# really be needed. +# In the future, add the following lines in a shell script running on the +# benchmark vm to get the available dependent versions when updating cuda +# version (e.g to 10.2 or something later): +# sudo apt-cache search cuda-command-line-tool +# sudo apt-cache search cuda-cublas +# sudo apt-cache search cuda-cufft +# sudo apt-cache search cuda-curand +# sudo apt-cache search cuda-cusolver +# sudo apt-cache search cuda-cusparse + +RUN apt-get update && apt-get install -y --no-install-recommends \ + --allow-downgrades --allow-change-held-packages \ + build-essential \ + cuda-tools-11-0 \ + cuda-toolkit-11-0 \ + libcudnn8=8.0.0.180-1+cuda11.0 \ + libcudnn8-dev=8.0.0.180-1+cuda11.0 \ + libfreetype6-dev \ + libhdf5-serial-dev \ + libzmq3-dev \ + libpng-dev \ + pkg-config \ + software-properties-common \ + unzip \ + lsb-core \ + curl + +# For CUDA profiling, TensorFlow requires CUPTI. +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:/usr/local/cuda-11.0/lib64:$LD_LIBRARY_PATH + +# See http://bugs.python.org/issue19846 +ENV LANG C.UTF-8 + +# Add google-cloud-sdk to the source list +RUN echo "deb http://packages.cloud.google.com/apt cloud-sdk-$(lsb_release -c -s) main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - + +# Install extras needed by most models +RUN apt-get update && apt-get install -y --no-install-recommends --allow-downgrades --allow-change-held-packages \ + git \ + ca-certificates \ + wget \ + htop \ + zip \ + google-cloud-sdk + +# Install / update Python and Python3 +RUN apt-get install -y --no-install-recommends --allow-downgrades --allow-change-held-packages \ + python3 \ + python3-dev \ + python3-pip \ + python3-setuptools \ + python3-venv + +# Upgrade pip, need to use pip3 and then pip after this or an error +# is thrown for no main found. +RUN pip3 install --upgrade pip +# setuptools upgraded to fix install requirements from model garden. +RUN pip install wheel +RUN pip install --upgrade setuptools google-api-python-client==1.8.0 pyyaml google-cloud google-cloud-bigquery google-cloud-datastore mock +RUN pip install absl-py +RUN pip install --upgrade --force-reinstall ${tensorflow_pip_spec} ${extra_pip_specs} +RUN pip install tfds-nightly +RUN pip install -U scikit-learn +# Install dependnecies needed for tf.distribute test utils +RUN pip install dill tblib portpicker + +RUN curl https://raw.githubusercontent.com/tensorflow/models/master/official/requirements.txt > /tmp/requirements.txt +RUN pip install -r /tmp/requirements.txt + +RUN pip install tf-estimator-nightly +RUN pip install tensorflow-text-nightly + +# RUN nvidia-smi + +RUN nvcc --version + + +RUN pip freeze diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_experimental_cuda11 b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_experimental_cuda11 new file mode 100644 index 00000000..4385d95a --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/docker/Dockerfile_ubuntu_experimental_cuda11 @@ -0,0 +1,109 @@ +# Ubuntu 18.04 Python3 with CUDA 11 and the following: +# - Installs tf-nightly-gpu (this is TF 2.3) +# - Installs requirements.txt for tensorflow/models + +FROM nvidia/cuda:11.2.1-cudnn8-devel-ubuntu18.04 as base +ARG tensorflow_pip_spec="tf-nightly-gpu" +ARG local_tensorflow_pip_spec="" +ARG extra_pip_specs="" +ENV PIP_CMD="python3.9 -m pip" + +# setup.py passes the base path of local .whl file is chosen for the docker image. +# Otherwise passes an empty existing file from the context. +COPY ${local_tensorflow_pip_spec} /${local_tensorflow_pip_spec} + +# Pick up some TF dependencies +# cublas-dev and libcudnn7-dev only needed because of libnvinfer-dev which may not +# really be needed. +# In the future, add the following lines in a shell script running on the +# benchmark vm to get the available dependent versions when updating cuda +# version (e.g to 10.2 or something later): +# sudo apt-cache search cuda-command-line-tool +# sudo apt-cache search cuda-cublas +# sudo apt-cache search cuda-cufft +# sudo apt-cache search cuda-curand +# sudo apt-cache search cuda-cusolver +# sudo apt-cache search cuda-cusparse + +# Needed to disable prompts during installation. +ENV DEBIAN_FRONTEND noninteractive + + +RUN apt-get update && apt-get install -y --no-install-recommends \ + libfreetype6-dev \ + libhdf5-serial-dev \ + libzmq3-dev \ + libpng-dev \ + pkg-config \ + software-properties-common \ + unzip \ + lsb-core \ + curl + +# Python 3.9 related deps in this ppa. +RUN add-apt-repository ppa:deadsnakes/ppa + + +# Install / update Python and Python3 +RUN apt-get install -y --no-install-recommends \ + python3.9 \ + python3-pip \ + python3.9-dev \ + python3-setuptools \ + python3.9-venv \ + python3.9-distutils \ + python3.9-lib2to3 + +# Upgrade pip, need to use pip3 and then pip after this or an error +# is thrown for no main found. +RUN ${PIP_CMD} install --upgrade pip +RUN ${PIP_CMD} install --upgrade distlib +# setuptools upgraded to fix install requirements from model garden. +RUN ${PIP_CMD} install --upgrade setuptools + +# For CUDA profiling, TensorFlow requires CUPTI. +ENV LD_LIBRARY_PATH /usr/local/cuda/extras/CUPTI/lib64:/usr/local/cuda-11.2/lib64:$LD_LIBRARY_PATH + +# See http://bugs.python.org/issue19846 +ENV LANG C.UTF-8 + +# Add google-cloud-sdk to the source list +RUN echo "deb http://packages.cloud.google.com/apt cloud-sdk-$(lsb_release -c -s) main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list +RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - + +# Install extras needed by most models +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + ca-certificates \ + wget \ + htop \ + zip \ + google-cloud-sdk + +RUN ${PIP_CMD} install --upgrade pyyaml +RUN ${PIP_CMD} install --upgrade google-api-python-client==1.8.0 +RUN ${PIP_CMD} install --upgrade google-cloud google-cloud-bigquery google-cloud-datastore mock + + +RUN ${PIP_CMD} install wheel +RUN ${PIP_CMD} install absl-py +RUN ${PIP_CMD} install --upgrade --force-reinstall ${tensorflow_pip_spec} ${extra_pip_specs} + +RUN ${PIP_CMD} install tfds-nightly +RUN ${PIP_CMD} install -U scikit-learn + +# Install dependnecies needed for tf.distribute test utils +RUN ${PIP_CMD} install dill tblib portpicker + +RUN curl https://raw.githubusercontent.com/tensorflow/models/master/official/requirements.txt > /tmp/requirements.txt +RUN ${PIP_CMD} install -r /tmp/requirements.txt + +RUN ${PIP_CMD} install tf-estimator-nightly +RUN ${PIP_CMD} install tensorflow-text-nightly + +# RUN nvidia-smi + +RUN nvcc --version + + +RUN pip freeze diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/dockertest/cuda_diff.sh b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/dockertest/cuda_diff.sh new file mode 100644 index 00000000..59a20515 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/dockertest/cuda_diff.sh @@ -0,0 +1,105 @@ +#!/bin/bash +# Copyright 2021 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 +set -x + +git clone https://github.com/tensorflow/benchmarks.git +cd benchmarks + +tf_spec="tf-nightly-gpu==2.5.0.dev20210212" + +CIFAR10_DATA="gs://tf-perf-imagenet-uswest1/tensorflow/cifar10_data/cifar-10-batches-bin" +CIFAR10_BENCHMARKS="official.benchmark.keras_cifar_benchmark.Resnet56KerasBenchmarkReal.benchmark_1_gpu_no_dist_strat" + +RESNET50_DATA="gs://tf-perf-imagenet-uswest1/tensorflow/imagenet" +RESNET50_BENCHMARKS="official.benchmark.resnet_ctl_imagenet_benchmark.Resnet50CtlBenchmarkReal.benchmark_1_gpu_fp16" + +function run_single_benchmark() { + docker_name=$1 + label=$2 + data_downloads=$3 + benchmark_methods=$4 + + perfzero_pwd=`pwd` + + nvidia-docker run \ + -v ${perfzero_pwd}:/workspace \ + -v /data:/data \ + -e PERFZERO_RUN_TAGS= \ + -e PERFZERO_TRACKING_ID= \ + -e PERFZERO_COMMIT_LABEL= \ + -e PERFZERO_EXECUTION_BRANCH=master \ + -e CUDNN_LOGINFO_DBG=1 \ + -e CUDNN_LOGDEST_DBG=stderr \ + ${docker_name} \ + python3 /workspace/perfzero/lib/benchmark.py \ + --bigquery_dataset_table_name="" \ + --data_downloads="${data_downloads}" \ + --ml_framework_build_label=v2-nightly-gpu \ + --execution_label="${label}" \ + --platform_name=kokoro-gcp \ + --system_name=n1-standard-8-1xA100 \ + --output_gcs_url="" \ + --benchmark_class_type= \ + --scratch_gcs_url= \ + --root_data_dir=/data \ + --benchmark_num_trials=2 \ + --bigquery_project_name="" \ + --git_repos="https://github.com/tensorflow/models.git;benchmark" \ + --python_path=models \ + --benchmark_methods=${benchmark_methods} \ + --result_upload_methods="" \ + --gcloud_key_file_url="${PERFZERO_GCLOUD_KEY_FILE_URL}" \ + --tpu_parameters= +} + +function run_benchmarks() { + docker_name=$1 + label=$2 + + run_single_benchmark ${docker_name} ${label} "${CIFAR10_DATA}" "${CIFAR10_BENCHMARKS}" + run_single_benchmark ${docker_name} ${label} "${RESNET50_DATA}" "${RESNET50_BENCHMARKS}" +} + +function setup_docker() { + label=$1 + dockerfile=$2 + + echo "`date` Setting up ${label} docker..." + sudo python3 perfzero/lib/setup.py \ + --gce_nvme_raid= \ + --docker_tag="${label}" \ + --gcloud_key_file_url= \ + --tensorflow_pip_spec=${tf_spec} \ + --dockerfile_path=${dockerfile} + echo "`date` Finished setting up ${label} docker." +} + +function diff_benchmarks() { + python3 perfzero/dockertest/diff_benchmarks.py `pwd` +} + +baseline_docker="docker/Dockerfile_ubuntu_cuda11_8_0_0_180" +experiment_docker="docker/Dockerfile_ubuntu_1804_tf_cuda_11" + +setup_docker "control/tensorflow" ${baseline_docker} +run_benchmarks "control/tensorflow" "control-8-0-0-180" + +setup_docker "experiment/tensorflow" ${experiment_docker} +run_benchmarks "experiment/tensorflow" "experiment-8-0-4-30" + +diff_benchmarks diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/dockertest/diff_benchmarks.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/dockertest/diff_benchmarks.py new file mode 100644 index 00000000..e5c0c75f --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/dockertest/diff_benchmarks.py @@ -0,0 +1,117 @@ +# Lint as: python3 +# Copyright 2021 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 script to diff benchmark results. + +This script will read all the summary files from a base output directory and +print a human readable diff report. +""" + +import json +import os +import sys + + +def _find_perfzero_logs(docker_output_dir): + """Finds pairs of json_file, output_log file from all methods.""" + summary_files = [] + for root, _, files in os.walk(docker_output_dir): + for summary_file in files: + if summary_file.endswith('perfzero_summary.json'): + full_summary_file = os.path.join(root, summary_file) + summary_files.append(full_summary_file) + sys.stdout.write('Found json {}\n'.format(full_summary_file)) + return summary_files + + +def _load_summaries(summary_files): + """Loads input json file paths and returns json objects.""" + summary_jsons = [] + for summary_file in summary_files: + with open(summary_file, 'r') as f: + summary_json = json.load(f) + summary_jsons.append(summary_json) + return summary_jsons + + +def _summarize_benchmarks(summary_files): + """Remaps list of json files -> summaries by benchmark method.""" + summary_jsons = _load_summaries(summary_files) + performance_by_method = {} + + for summary_json in summary_jsons: + method = summary_json['benchmark_result']['name'] + trial = summary_json['benchmark_result']['trial_id'] + metrics_list = summary_json['benchmark_result']['metrics'] + metrics = {} + for metric_info in metrics_list: + metrics[metric_info['name']] = metric_info['value'] + metrics['wall_time'] = summary_json['benchmark_result']['wall_time'] + label = summary_json['benchmark_info']['execution_label'] + + performance_by_method.setdefault(method, {}).setdefault(label, []) + performance_by_method[method][label].append((trial, metrics)) + + return performance_by_method + + +def _print_diff_report(performance_by_method): + """Prints a diff report of benchmark performance.""" + print('Performance report:') + print(json.dumps(performance_by_method, indent=2)) + + method_to_metric_to_perf = {} + for method in performance_by_method: + for label, label_data in performance_by_method[method].items(): + latest_trial_data = max(label_data, key=lambda x: x[0]) + latest_metrics = latest_trial_data[1] + for metric, value in latest_metrics.items(): + method_to_metric_to_perf.setdefault(method, {}).setdefault(metric, []) + method_to_metric_to_perf[method][metric].append((label, value)) + + print('Diff report:') + for method in sorted(method_to_metric_to_perf): + print('-- benchmark: {}'.format(method)) + for metric in sorted(method_to_metric_to_perf[method].keys()): + value_list = [] + for label, value in sorted( + method_to_metric_to_perf[method][metric], key=lambda x: x[0]): + print(' {}: {}: {}'.format(metric, label, value)) + value_list.append(value) + + if len(value_list) == 2: + control_val = float(value_list[0]) + expt_val = float(value_list[1]) + if abs(control_val) > 1e-5: + diff_pct = (expt_val / control_val - 1.0) * 100.0 + else: + diff_pct = -1.0 + print(' diff: {:2.2f}%'.format(diff_pct)) + + +def main(): + if len(sys.argv) != 2: + raise RuntimeError('Usage: {} '.format( + sys.argv[0])) + + perfzero_output_dir = sys.argv[1] + summary_files = _find_perfzero_logs(perfzero_output_dir) + performance_by_method = _summarize_benchmarks(summary_files) + _print_diff_report(performance_by_method) + + +if __name__ == '__main__': + main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/dockertest/requirements_temp.txt b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/dockertest/requirements_temp.txt new file mode 100644 index 00000000..a4249c32 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/dockertest/requirements_temp.txt @@ -0,0 +1,27 @@ +six +google-api-python-client>=1.6.7 +kaggle>=1.3.9 +numpy>=1.15.4 +oauth2client +pandas>=0.22.0 +psutil>=5.4.3 +py-cpuinfo>=3.3.0 +scipy>=0.19.1 +tensorflow-hub>=0.6.0 +tensorflow-model-optimization>=0.4.1 +tensorflow-datasets +tensorflow-addons +dataclasses;python_version<"3.7" +gin-config +tf_slim>=1.1.0 +Cython +matplotlib +pyyaml>=5.1 +# CV related dependencies +opencv-python-headless +Pillow +pycocotools +# NLP related dependencies +seqeval +sentencepiece +sacrebleu diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/dockertest/resnet50_synth.sh b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/dockertest/resnet50_synth.sh new file mode 100644 index 00000000..29497089 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/dockertest/resnet50_synth.sh @@ -0,0 +1,82 @@ +#!/bin/bash +set -e +set -x + +# To run this script from a GCP VM / host connected to GPUs: +# git clone https://github.com/tensorflow/benchmarks.git +# cd benchmarks +# bash perfzero/dockertest/resnet50_synth.sh +# Output log files/results will be stored at perfzero/workspace/output/ + +# Modify INPUT_PARAMS variables below to tweak the tf whl under test / benchmark methods / dataset paths. +# You can comment out "build_docker" call at the end, if the docker's already built. + +## INPUT PARAMS: start + +# Acceptable formats for TF_PIP_SPEC +# pypi nightlies: tf-nightly-gpu==2.6.0.dev20210521 +# gcs path to whls: gs://some-path-to-tf.whl +# Local path to whl: file://some-local-path-to-tf.whl +TF_PIP_SPEC="tf-nightly-gpu==2.6.0.dev20210624" + +# Path to GCS or local files containing the input datasets (if they need to be fetched into the docker). +DATA_DOWNLOADS="" + +# Comma separated list of strings. +BENCHMARK_METHODS="official.benchmark.keras_imagenet_benchmark.Resnet50KerasBenchmarkSynth.benchmark_1_gpu_fp16" + +# If either the tf_pip_spec or data downloads reference private GCP, then we +# need to set GCLOUD_KEY_FILE_URL to a credentials file. +GCLOUD_KEY_FILE_URL="" + +# Commit id under repository tensorflow/models, branch='benchmark' which has the benchmarks. +MODELS_GIT_HASH="169e4051aef247c27a95748a8015b2f35f509e1a" +## INPUT PARAMS: end + + + +build_docker() { + echo "building docker" +sudo python3 perfzero/lib/setup.py \ + --dockerfile_path=docker/Dockerfile_ubuntu_1804_tf_cuda_11 \ + --tensorflow_pip_spec="${TF_PIP_SPEC}" \ + --gcloud_key_file_url="${GCLOUD_KEY_FILE_URL}" \ + --extra_docker_build_args= +sudo docker images +} + + +run_benchmark() { + echo "running benchmark" + benchmark_tag=$1 + env_var=$2 + +sudo nvidia-docker run \ + -v ${PWD}:/workspace \ + -v /data:/data \ + -e PERFZERO_EXECUTION_MODE=test \ + -e TF_ENABLE_LEGACY_FILESYSTEM=1 \ + -e ${env_var} \ + perfzero/tensorflow python3 \ + /workspace/perfzero/lib/benchmark.py \ + --root_data_dir=/data \ + --bigquery_dataset_table_name="" \ + --benchmark_class_type= \ + --ml_framework_build_label=v2-nightly-gpu-${benchmark_tag} \ + --execution_label=test-benchmark \ + --platform_name=kokoro-gcp \ + --system_name=n1-standard-8-1xV100 \ + --output_gcs_url="" \ + --benchmark_num_trials=1 \ + --scratch_gcs_url= \ + --bigquery_project_name="" \ + --git_repos="https://github.com/tensorflow/models.git;benchmark;${MODELS_GIT_HASH}" \ + --data_downloads="${DATA_DOWNLOADS}"\ + --python_path=models \ + --benchmark_methods="${BENCHMARK_METHODS}" \ + --gcloud_key_file_url="${GCLOUD_KEY_FILE_URL}" +} + +build_docker +run_benchmark "control" "TF_CUDNN_USE_FRONTEND=false" +run_benchmark "experiment" "TF_CUDNN_USE_FRONTEND=true" diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/dockertest/run_single_benchmark.sh b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/dockertest/run_single_benchmark.sh new file mode 100644 index 00000000..f1466fe7 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/dockertest/run_single_benchmark.sh @@ -0,0 +1,74 @@ +#!/bin/bash +set -e +set -x + +# To run this script from a GCP VM / host connected to GPUs: +# git clone https://github.com/tensorflow/benchmarks.git +# cd benchmarks +# bash perfzero/dockertest/run_single_benchmark.sh +# Output log files/results will be stored at perfzero/workspace/output/ + +# Modify INPUT_PARAMS variables below to tweak the tf whl under test / benchmark methods / dataset paths. +# You can comment out "build_docker" call at the end, if the docker's already built. + +## INPUT PARAMS: start + +# Acceptable formats for TF_PIP_SPEC +# pypi nightlies: tf-nightly-gpu==2.6.0.dev20210521 +# gcs path to whls: gs://some-path-to-tf.whl +# Local path to whl: file://some-local-path-to-tf.whl +TF_PIP_SPEC="tf-nightly-gpu==2.6.0.dev20210521" + +# Path to GCS or local files containing the input datasets (if they need to be fetched into the docker). +DATA_DOWNLOADS="" + +# Comma separated list of strings. +BENCHMARK_METHODS="official.benchmark.keras_imagenet_benchmark.Resnet50KerasBenchmarkSynth.benchmark_xla_1_gpu_fp16" + +# If either the tf_pip_spec or data downloads reference private GCP, then we +# need to set GCLOUD_KEY_FILE_URL to a credentials file. +GCLOUD_KEY_FILE_URL="" +## INPUT PARAMS: end + + + +build_docker() { + echo "building docker" +sudo python3 perfzero/lib/setup.py \ + --dockerfile_path=docker/Dockerfile_ubuntu_1804_tf_cuda_11 \ + --tensorflow_pip_spec="${TF_PIP_SPEC}" \ + --gcloud_key_file_url="${GCLOUD_KEY_FILE_URL}" \ + --extra_docker_build_args= +sudo docker images +} + + +run_benchmark() { + echo "running benchmark" +sudo nvidia-docker run \ + -v ${PWD}:/workspace \ + -v /data:/data \ + -e PERFZERO_EXECUTION_MODE=test \ + -e TF_ENABLE_LEGACY_FILESYSTEM=1 \ + perfzero/tensorflow python3 \ + /workspace/perfzero/lib/benchmark.py \ + --root_data_dir=/data \ + --bigquery_dataset_table_name="" \ + --benchmark_class_type= \ + --ml_framework_build_label=v2-nightly-gpu \ + --execution_label=test-benchmark \ + --platform_name=kokoro-gcp \ + --system_name=n1-standard-8-1xV100 \ + --output_gcs_url="" \ + --benchmark_num_trials=1 \ + --scratch_gcs_url= \ + --bigquery_project_name="" \ + --git_repos='https://github.com/tensorflow/models.git;benchmark;f7938e6ad46fecfa1112eda579eb046eb3f3bf96' \ + --data_downloads="${DATA_DOWNLOADS}"\ + --python_path=models \ + --benchmark_methods="${BENCHMARK_METHODS}" \ + --gcloud_key_file_url="${GCLOUD_KEY_FILE_URL}" +} + +build_docker +run_benchmark diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/__init__.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/__init__.py similarity index 100% rename from TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/__init__.py rename to TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/__init__.py diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/benchmark.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/benchmark.py new file mode 100644 index 00000000..37291a4b --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/benchmark.py @@ -0,0 +1,193 @@ +# 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. +# ============================================================================== +"""Execute benchmark.""" +from __future__ import print_function + +import argparse +import json +import logging +import multiprocessing +import os +import re +import sys +import time + +import perfzero.benchmark_method_runner as benchmark_method_runner +import perfzero.perfzero_config as perfzero_config +import perfzero.tpu_runtime_utils as tpu_runtime_utils +import perfzero.utils as utils + + +class BenchmarkRunner(object): + """Execute benchmark and report results.""" + + def __init__(self, config): + self.config = config + self.project_dir = os.path.abspath( + os.path.dirname(os.path.dirname(__file__))) + self.workspace_dir = os.path.join(self.project_dir, config.workspace) + self.site_packages_dir = os.path.join(self.workspace_dir, 'site-packages') + self.root_output_dir = os.path.join(self.workspace_dir, 'output') + self.benchmark_execution_time = {} + + def _setup(self): + """Download data and checkout git repository.""" + + # Acticate gcloud service + start_time = time.time() + utils.setup_python_path(self.site_packages_dir, self.config.python_path_str) + utils.active_gcloud_service(self.config.gcloud_key_file_url, + self.workspace_dir) + utils.make_dir_if_not_exist(self.root_output_dir) + self.benchmark_execution_time['activate_gcloud_service'] = ( + time.time() - start_time) + + # Download data + start_time = time.time() + utils.download_data(utils.parse_data_downloads_str( + self.config.root_data_dir, self.config.gcs_downloads_str)) + utils.download_data(utils.parse_data_downloads_str( + self.config.root_data_dir, self.config.data_downloads_str)) + self.benchmark_execution_time['download_data'] = time.time() - start_time + + # Checkout git repositories + start_time = time.time() + site_package_info = utils.checkout_git_repos( + self.config.get_git_repos(self.site_packages_dir), + self.config.use_cached_site_packages) + self.benchmark_execution_time['checkout_repository'] = ( + time.time() - start_time) + + # Start cloud TPU. + if self.config.tpu_parameters is not None: + start_time = time.time() + utils.setup_tpu(self.config.tpu_parameters) + tpu_info = tpu_runtime_utils.configure_tpu(self.config.tpu_parameters) + site_package_info['tpu_version'] = tpu_info + self.benchmark_execution_time['start_tpu'] = time.time() - start_time + + self.stream_handler = logging.StreamHandler(sys.stdout) + self.stream_handler.setFormatter( + logging.Formatter('%(asctime)s %(levelname)s: %(message)s')) + logging.getLogger().addHandler(self.stream_handler) + return site_package_info + + def _get_benchmark_methods(self): + """Returns list of benchmark methods to execute.""" + filter_prefix = 'filter:' + benchmark_methods = [] + for benchmark_method_pattern in self.config.benchmark_method_patterns: + if filter_prefix not in benchmark_method_pattern: + benchmark_methods.append(benchmark_method_pattern) + else: + index = benchmark_method_pattern.find(filter_prefix) + benchmark_class = benchmark_method_pattern[:index - 1] + pattern = benchmark_method_pattern[index + len(filter_prefix):] + class_instance = utils.instantiate_benchmark_class( + benchmark_class, '/dev/null', '', None, {}, + benchmark_class_type=self.config.benchmark_class_type) + + for benchmark_method_name in dir(class_instance): + if re.match(pattern, benchmark_method_name): + benchmark_methods.append(benchmark_class + '.' + + benchmark_method_name) + + logging.info('The following benchmark methods will be executed: %s', + benchmark_methods) + return benchmark_methods + + def _run_benchmarks_trial(self, harness_info, site_package_info, + benchmark_methods, trial_id): + """Runs a single trial of all benchmark methods.""" + # Run the benchmark method in a separate process so that its memory usage + # will not affect the execution of other benchmark method + # This is a walkaround before we fix all memory leak issues in TensorFlow + has_exception = False + benchmark_success_results = {} + benchmark_output_dirs = {} + benchmark_execution_time = {} + for benchmark_method in benchmark_methods: + queue = multiprocessing.Queue() + process = multiprocessing.Process(target=benchmark_method_runner.run, + args=(benchmark_method, + harness_info, + site_package_info, + self.root_output_dir, + self.config, queue, trial_id)) + process.start() + process.join() + method_has_exception, method_execution_time, succeeded, output_dir = queue.get() # pylint: disable=line-too-long + has_exception |= method_has_exception + benchmark_execution_time[benchmark_method] = method_execution_time + benchmark_success_results[benchmark_method] = succeeded + benchmark_output_dirs[benchmark_method] = output_dir + return (has_exception, benchmark_success_results, + benchmark_output_dirs, benchmark_execution_time) + + def run_benchmark(self): + """Run benchmark.""" + harness_info = utils.get_git_repo_info(self.project_dir) + has_exception = False + benchmark_success_results = {} + benchmark_output_dirs = {} + num_trials = self.config.benchmark_num_trials + + try: + site_package_info = self._setup() + benchmark_methods = self._get_benchmark_methods() + + print('Setup complete. Running {} trials'.format(num_trials)) + for trial_id in range(1, num_trials + 1): + print('Running trial {} / {}'.format(trial_id, num_trials)) + (trial_has_exception, trial_success_results, + trial_output_dirs, trial_execution_time) = self._run_benchmarks_trial( + harness_info, site_package_info, benchmark_methods, trial_id) + + trial_key = 'trial_{}'.format(trial_id) + has_exception |= trial_has_exception + self.benchmark_execution_time[trial_key] = trial_execution_time + benchmark_success_results[trial_key] = trial_success_results + benchmark_output_dirs[trial_key] = trial_output_dirs + finally: + if self.config.tpu_parameters is not None: + has_exception |= utils.cleanup_tpu(self.config.tpu_parameters) + + print('Benchmark execution time in seconds by operation:\n {}'.format( + json.dumps(self.benchmark_execution_time, indent=2))) + print('Benchmark success results:\n{}'.format( + json.dumps(benchmark_success_results, indent=2))) + print('Benchmark local output directories:\n{}'.format( + json.dumps(benchmark_output_dirs, indent=2))) + if has_exception: + sys.exit(1) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + perfzero_config.add_benchmark_parser_arguments(parser) + FLAGS, unparsed = parser.parse_known_args() + + level = logging.DEBUG if FLAGS.debug else logging.INFO + logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s', + level=level) + + if unparsed: + logging.error('Arguments %s are not recognized', unparsed) + sys.exit(1) + + config_ = perfzero_config.PerfZeroConfig(mode='flags', flags=FLAGS) + benchmark_runner = BenchmarkRunner(config_) + benchmark_runner.run_benchmark() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/benchmark_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/benchmark_test.py new file mode 100644 index 00000000..926537e5 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/benchmark_test.py @@ -0,0 +1,57 @@ +# 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 benchmark.py.""" +from __future__ import print_function + +import sys +import unittest + +import benchmark +import mock + + +class TestBenchmarkRunner(unittest.TestCase): + + def test_get_benchmark_methods_filter(self): + """Tests returning methods on a class based on a filter.""" + config = mock.Mock() + config.workspace = 'workspace' + config.benchmark_method_patterns = ['new_foo.BenchmarkClass.filter:bench.*'] + benchmark_runner = benchmark.BenchmarkRunner(config) + + mock_benchmark_class = mock.Mock() + mock_benchmark_class.benchmark_method_1 = 'foo' + + mock_module = mock.Mock() + sys.modules['new_foo'] = mock_module + mock_module.BenchmarkClass.return_value = mock_benchmark_class + + methods = benchmark_runner._get_benchmark_methods() + + self.assertEqual(1, len(methods)) + self.assertEqual('new_foo.BenchmarkClass.benchmark_method_1', methods[0]) + + def test_get_benchmark_methods_exact_match(self): + """Tests returning methods on a class based on a filter.""" + config = mock.Mock() + config.workspace = 'workspace' + config.benchmark_method_patterns = [ + 'new_foo.BenchmarkClass.benchmark_method_1', + 'new_foo.BenchmarkClass.benchmark_method_2'] + benchmark_runner = benchmark.BenchmarkRunner(config) + + methods = benchmark_runner._get_benchmark_methods() + self.assertEqual(['new_foo.BenchmarkClass.benchmark_method_1', + 'new_foo.BenchmarkClass.benchmark_method_2'], methods) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/cloud_manager.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/cloud_manager.py new file mode 100644 index 00000000..e97d4316 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/cloud_manager.py @@ -0,0 +1,431 @@ +#!/usr/bin/python +# +# 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. +# ============================================================================== +"""Helper script to create, query and stop machine in GCP.""" + +from __future__ import print_function + +import argparse +import getpass +import logging +import subprocess +import sys +import time + + +INSTANCE_NAME_PREFIX = 'perfzero-dev-' + + +def run_command(cmd, is_from_user=False): + """Runs list of command and throw error if return code is non-zero. + + Args: + cmd: Command to execute + is_from_user: If true, log the command and the command output in INFO level. + Otherwise, log these in the DEBUG level. + + Returns: + a string representing the command output + + Raises: + Exception: raised when the command execution has non-zero exit code + """ + _log = logging.info if is_from_user else logging.debug + _log('Executing command: {}'.format(cmd)) + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, shell=True) + + exit_code = None + line = '' + stdout = '' + while exit_code is None or line: + exit_code = p.poll() + line = p.stdout.readline().decode('utf-8') + stdout += line + _log(line) + if exit_code and is_from_user: + sys.exit(exit_code) + elif exit_code: + raise Exception('Command:\n{}\nfailed with output:\n{}'.format(cmd, stdout)) + + return stdout + + +def get_instance_name(username): + return INSTANCE_NAME_PREFIX + username + + +def get_machine_type(machine_type, accelerator_count): + """Get machine type for the instance. + + - Use the user-specified machine_type if it is not None + - Otherwise, use the standard type with cpu_count = 8 x accelerator_count + if user-specified accelerator_count > 0 + - Otherwise, use the standard type with 8 cpu + + Args: + machine_type: machine_type specified by the user + accelerator_count: accelerator count + + Returns: + the machine type used for the instance + """ + if machine_type: + return machine_type + cpu_count = max(accelerator_count, 1) * 8 + return 'n1-standard-{}'.format(cpu_count) + + +def _ssh_prefix(project, zone, internal_ip, key_file): + if internal_ip: + ssh_prefix = 'gcloud beta compute ssh --internal-ip' + else: + ssh_prefix = 'gcloud compute ssh' + if key_file: + ssh_prefix = '{} --ssh-key-file={}'.format(ssh_prefix, key_file) + return '{} --project={} --zone={}'.format(ssh_prefix, project, zone) + + +def create(username, project, zone, machine_type, accelerator_count, + accelerator_type, image, nvme_count, ssh_internal_ip, ssh_key_file, + cpu_min_platform=None, boot_ssd_size=None): + """Create gcloud computing instance. + + Args: + username: the username of the current user + project: project name + zone: zone of the GCP computing instance + machine_type: the machine type used for the instance + accelerator_count: the number of pieces of the accelerator to attach to + the instance + accelerator_type: the specific type of accelerator to attach to the instance + image: the name of the image that the disk will be initialized with + nvme_count: the number of NVME local SSD devices to attach to the instance + ssh_internal_ip: internal ip to use for ssh. + ssh_key_file: ssh key file to use to connect to instance. + cpu_min_platform: minimum CPU platform to use, if None use default. + boot_ssd_size: If set boot disk is changed to SSD and this size(GB) is used. + """ + instance_name = get_instance_name(username) + machine_type = get_machine_type(machine_type, accelerator_count) + logging.debug('Creating gcloud computing instance %s', instance_name) + + cmd = '''gcloud compute instances create {} \ +--image={} \ +--project={} \ +--zone={} \ +--machine-type={} \ +--maintenance-policy=TERMINATE \ +'''.format(instance_name, image, project, zone, machine_type) + + if boot_ssd_size: + cmd += '--boot-disk-size={}GB --boot-disk-type=pd-ssd '.format( + boot_ssd_size) + + if accelerator_count > 0: + cmd += '--accelerator=count={},type={} '.format( + accelerator_count, accelerator_type) + + if cpu_min_platform: + cmd += '--min-cpu-platform="{}" '.format(cpu_min_platform) + + for _ in range(nvme_count): + cmd += '--local-ssd=interface=NVME ' + + run_command(cmd, is_from_user=True) + logging.info('Successfully created gcloud computing instance %s ' + 'with %s accelerator.\n', instance_name, accelerator_count) + + ssh_prefix = _ssh_prefix(project, zone, ssh_internal_ip, ssh_key_file) + # Wait until we can ssh to the newly created computing instance + cmd = '{} --strict-host-key-checking=no --command="exit" {}'.format( + ssh_prefix, instance_name) + ssh_remaining_retries = 12 + ssh_error = None + while ssh_remaining_retries > 0: + ssh_remaining_retries -= 1 + try: + run_command(cmd, is_from_user=False) + ssh_error = None + except Exception as error: # pylint: disable=broad-except + ssh_error = error + if ssh_remaining_retries: + logging.info('Cannot ssh to the computing instance. ' + 'Try again after 5 seconds') + time.sleep(5) + else: + logging.error('Cannot ssh to the computing instance after ' + '60 seconds due to error:\n%s', str(ssh_error)) + + if ssh_error: + logging.info('Run the commands below manually after ssh into the computing ' + 'instance:\n' + 'git clone https://github.com/tensorflow/benchmarks.git\n' + 'sudo usermod -a -G docker $USER\n') + else: + cmd = '{} --command="git clone {}" {}'.format( + ssh_prefix, 'https://github.com/tensorflow/benchmarks.git', + instance_name) + run_command(cmd, is_from_user=True) + logging.info('Successfully checked-out PerfZero code on the ' + 'computing instance\n') + + cmd = '{} --command="sudo usermod -a -G docker $USER" {}'.format( + ssh_prefix, instance_name) + run_command(cmd, is_from_user=True) + logging.info('Successfully added user to the docker group\n') + + cmd = '{} {} -- -L 6006:127.0.0.1:6006'.format(ssh_prefix, instance_name) + logging.info('Run the command below to ssh to the instance together with ' + 'port forwarding for tensorboard:\n%s\n', cmd) + + +def status(username, project, zone, ssh_internal_ip, ssh_key_file): + """Query the status of the computing instance. + + Args: + username: the username of the current user. + project: project name. + zone: zone of the GCP computing instance. + ssh_internal_ip: internal ip of the instance. + ssh_key_file: SSH key file to use to connect to the instance. + """ + instance_name = get_instance_name(username) + logging.debug('Querying status of gcloud computing instance %s of ' + 'project %s in zone %s', instance_name, project, zone) + + cmd = 'gcloud compute instances list --filter="name={} AND zone:{}" --project {}'.format( # pylint: disable=line-too-long + instance_name, zone, project) + stdout = run_command(cmd, is_from_user=True) + + num_instances = len(stdout.splitlines()) - 1 + logging.info('\nFound %s gcloud computing instance with name %s.\n', + num_instances, instance_name) + + if num_instances == 1: + cmd = '{} {} -- -L 6006:127.0.0.1:6006'.format( + _ssh_prefix(project, zone, ssh_internal_ip, ssh_key_file), + instance_name) + logging.info('Run the command below to ssh to the instance together with ' + 'port forwarding for tensorboard:\n%s\n', cmd) + + +def list_all(project): + logging.debug('Finding all gcloud computing instance of project %s created ' + 'for PerfZero test', project) + cmd = 'gcloud compute instances list --filter="name ~ {}" --project={}'.format( # pylint: disable=line-too-long + INSTANCE_NAME_PREFIX, project) + stdout = run_command(cmd, is_from_user=True) + num_instances = len(stdout.splitlines()) - 1 + logging.info('\nFound %s gcloud computing instance of project %s created ' + 'for PerfZero test', num_instances, project) + + +def start(username, project, zone): + instance_name = get_instance_name(username) + logging.debug('Starting gcloud computing instance %s of project %s ' + 'in zone %s', instance_name, project, zone) + + cmd = 'gcloud compute instances start {} --project={} --zone={}'.format( + instance_name, project, zone) + run_command(cmd, is_from_user=True) + logging.debug('\nSuccessfully started gcloud computing instance %s of ' + 'project %s in zone %s', instance_name, project, zone) + + +def stop(username, project, zone): + instance_name = get_instance_name(username) + logging.debug('Stopping gcloud computing instance %s of project %s in ' + 'zone %s', instance_name, project, zone) + + cmd = 'gcloud compute instances stop {} --project={} --zone={}'.format( + instance_name, project, zone) + run_command(cmd, is_from_user=True) + logging.debug('\nSuccessfully stopped gcloud computing instance %s of ' + 'project %s in zone %s', instance_name, project, zone) + + +def delete(username, project, zone): + instance_name = get_instance_name(username) + logging.debug('Deleting gcloud computing instance %s of project %s in ' + 'zone %s', instance_name, project, zone) + + cmd = 'echo Y | gcloud compute instances delete {} --project={} --zone={}'.format( # pylint: disable=line-too-long + instance_name, project, zone) + run_command(cmd, is_from_user=True) + logging.debug('\nSuccessfully deleted gcloud computing instance %s of ' + 'project %s in zone %s', instance_name, project, zone) + + +def parse_arguments(argv, command): # pylint: disable=redefined-outer-name + """Parse command line arguments and return parsed flags. + + Args: + argv: command line arguments + command: the subcommand requested by the user + + Returns: + parsed flags + """ + + # pylint: disable=redefined-outer-name + parser = argparse.ArgumentParser( + usage='cloud_manager.py {} []'.format(command), + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument( + '--debug', + action='store_true', + help='If set, use debug level logging. Otherwise, use info level logging' + ) + parser.add_argument( + '--project', + default='google.com:tensorflow-performance', + type=str, + help='Google Cloud Platform project name to use for this invocation' + ) + + if command in ['create', 'start', 'stop', 'delete', 'status']: + parser.add_argument( + '--username', + default=getpass.getuser(), + type=str, + help='''Username that uniquely identifies the name of computing instance created for PerfZero. + The default value is your ldap username. + ''') + parser.add_argument( + '--zone', + default='us-west1-b', + type=str, + help='Zone of the instance to create.' + ) + parser.add_argument( + '--ssh-internal-ip', + action='store_true', + help='If set, use internal IP for ssh with `gcloud beta compute ssh`.' + ) + parser.add_argument( + '--ssh-key-file', + default=None, + type=str, + help='The ssh key to use with with `gcloud (beta) compute ssh`.' + ) + + if command == 'create': + parser.add_argument( + '--accelerator_count', + default=1, + type=int, + help='The number of pieces of the accelerator to attach to the instance' + ) + parser.add_argument( + '--accelerator_type', + default='nvidia-tesla-v100', + type=str, + help='''The specific type (e.g. nvidia-tesla-v100 for nVidia Tesla V100) of + accelerator to attach to the instance. Use 'gcloud compute accelerator-types list --project=${project_name}' to + learn about all available accelerator types. + ''') + parser.add_argument( + '--cpu_min_platform', + default=None, + type=str, + help='''Minimum cpu platform, only needed for CPU only instances.''') + parser.add_argument( + '--machine_type', + default=None, + type=str, + help='''The machine type used for the instance. To get a list of available machine + types, run 'gcloud compute machine-types list --project=${project_name}' + ''') + parser.add_argument( + '--image', + default='tf-ubuntu-1604-20180927-410', + type=str, + help='''Specifies the name of the image that the disk will be initialized with. + A new disk will be created based on the given image. To view a list of + public images and projects, run 'gcloud compute images list --project=${project_name}'. It is best + practice to use image when a specific version of an image is needed. + ''') + parser.add_argument( + '--nvme_count', + default=0, + type=int, + help='''Specifies the number of NVME local SSD devices to attach to the instance. + ''' + ) + parser.add_argument( + '--boot_ssd_size', + default=None, + type=int, + help='''Specifies the size (GB) of the boot disk or size is the image + size. Setting this also changes boot disk to Persistent SSD. + ''' + ) + + flags, unparsed = parser.parse_known_args(argv) # pylint: disable=redefined-outer-name + if unparsed: + logging.error('Arguments %s are not recognized', unparsed) + sys.exit(1) + + level = logging.DEBUG if flags.debug else logging.INFO + logging.basicConfig(format='%(message)s', level=level) + + return flags +if __name__ == '__main__': + parser = argparse.ArgumentParser( + usage='''cloud_manager.py [] + +The supported commands are: + create: Create a computing instance in gcloud that is unique to the specified username, which is your ldap by default + start: Start the computing instance in gcloud that is unique to the specified username, which is your ldap by default + stop: Stop the computing instance in gcloud that is unique to the specified username, which is your ldap by default + delete: Delete the computing instance in gcloud that is unique to the specified username, which is your ldap by default + status: Query the status and information of the computing instance in gcloud that is unique to the specified username, which is your ldap by default + list_all: Query the status of all computing instances that are created by this script.''' + ) + parser.add_argument( + 'command', + type=str + ) + + flags = parser.parse_args(sys.argv[1:2]) + command = flags.command + if not hasattr(sys.modules[__name__], command): + print('Error: The command <{}> is not recognized\n'.format(command)) + parser.print_help() + sys.exit(1) + + flags = parse_arguments(sys.argv[2:], command) + + if command == 'create': + create(flags.username, flags.project, flags.zone, flags.machine_type, + flags.accelerator_count, flags.accelerator_type, flags.image, + flags.nvme_count, flags.ssh_internal_ip, flags.ssh_key_file, + cpu_min_platform=flags.cpu_min_platform, + boot_ssd_size=flags.boot_ssd_size) + elif command == 'start': + start(flags.username, flags.project, flags.zone) + elif command == 'stop': + stop(flags.username, flags.project, flags.zone) + elif command == 'delete': + delete(flags.username, flags.project, flags.zone) + elif command == 'status': + status(flags.username, flags.project, flags.zone, flags.ssh_internal_ip, + flags.ssh_key_file) + elif command == 'list_all': + list_all(flags.project) + + diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/__init__.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/__init__.py similarity index 100% rename from TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/benchmark/__init__.py rename to TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/__init__.py diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/benchmark_method_runner.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/benchmark_method_runner.py new file mode 100644 index 00000000..f5b87771 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/benchmark_method_runner.py @@ -0,0 +1,187 @@ +# 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. +# ============================================================================== +"""Execute a single benchmark method.""" +from __future__ import print_function + +import datetime +import json +import logging +import os +import time +import traceback + +from perfzero.process_info_tracker import ProcessInfoTracker +import perfzero.report_utils as report_utils +from perfzero.tensorflow_profiler import TensorFlowProfiler +import perfzero.utils as utils + + +def run(benchmark_method, harness_info, site_package_info, + root_output_dir, config, queue, trial_id): + try: + _run_internal(benchmark_method, harness_info, site_package_info, + root_output_dir, config, queue, trial_id) + except Exception: # pylint: disable=broad-except + logging.error('Benchmark execution for %s failed due to error:\n %s', + benchmark_method, traceback.format_exc()) + queue.put((True, None, False, None)) + + +def _set_file_contents(content_str, output_filename): + with open(output_filename, 'w') as f: + f.write(content_str) + logging.info('Wrote summary to file %s', output_filename) + + +def _run_internal(benchmark_method, harness_info, site_package_info, + root_output_dir, config, queue, trial_id): + """Run benchmark method and put result to the queue. + + Args: + benchmark_method: Canonical path to the benchmark method + harness_info: Description of the benchmark harness used in the benchmark + site_package_info: Description of the site-package used in the benchmark + root_output_dir: Directory under which to put the benchmark output + config: An instance of perfzero_config + queue: An interprocess queue to transfer benchmark result to the caller. + trial_id: An integer trial id to annotate in the benchmark result. + """ + start_timestamp = time.time() + execution_timestamp = start_timestamp + method_has_exception = False + execution_id = (config.execution_id if config.execution_id else + datetime.datetime.now().strftime('%Y-%m-%d-%H-%M-%S-%f')) + output_dir = os.path.join(root_output_dir, execution_id) + if config.scratch_gcs_url: + model_output_dir = os.path.join(config.scratch_gcs_url, execution_id) + else: + model_output_dir = output_dir + utils.make_dir_if_not_exist(output_dir) + benchmark_class, benchmark_method_name = benchmark_method.rsplit('.', 1) + benchmark_class_name = benchmark_class.rsplit('.', 1)[1] + + tensorflow_profiler = TensorFlowProfiler( + config.profiler_enabled_time_str, output_dir) + process_info_tracker = ProcessInfoTracker(output_dir) + process_info = None + + # Setup per-method file logger + filehandler = logging.FileHandler( + filename=os.path.join(output_dir, 'perfzero.log'), mode='w') + filehandler.setFormatter( + logging.Formatter('%(asctime)s %(levelname)s: %(message)s')) + logging.getLogger().addHandler(filehandler) + + try: + if config.tpu_parameters: + tpu = config.tpu_parameters.get('name') + else: + tpu = None + if config.perfzero_constructor_args: + constructor_args = json.loads(config.perfzero_constructor_args) + else: + constructor_args = {} + class_instance = utils.instantiate_benchmark_class( + benchmark_class=benchmark_class, + output_dir=model_output_dir, + root_data_dir=config.root_data_dir, + tpu=tpu, + constructor_args=constructor_args, + benchmark_class_type=config.benchmark_class_type) + # tf.test.Benchmark.report_benchmark() writes results to a file with + # path benchmark_result_file_path_prefix + benchmark_method + benchmark_result_file_path_prefix = os.path.join(output_dir, 'proto_') + os.environ['TEST_REPORT_FILE_PREFIX'] = benchmark_result_file_path_prefix + benchmark_result_file_path = '{}{}.{}'.format( + benchmark_result_file_path_prefix, + benchmark_class_name, + benchmark_method_name) + + # Start background threads for profiler and system info tracker + tensorflow_profiler.start() + process_info_tracker.start() + + # Run benchmark method + execution_timestamp = time.time() + logging.info('Starting benchmark execution: %s', benchmark_method) + getattr(class_instance, benchmark_method_name)() + logging.info('Stopped benchmark: %s', benchmark_method) + + # Read and build benchmark results + raw_benchmark_result = utils.read_benchmark_result( + benchmark_result_file_path) + # Explicitly overwrite the name to be the full path to benchmark method + raw_benchmark_result['name'] = benchmark_method + except Exception: # pylint: disable=broad-except + logging.error('Benchmark execution for %s failed due to error:\n %s', + benchmark_method, traceback.format_exc()) + method_has_exception = True + raw_benchmark_result = {} + raw_benchmark_result['name'] = benchmark_method + raw_benchmark_result['wall_time'] = -1 + raw_benchmark_result['extras'] = {} + finally: + # Stop background threads for profiler and system info tracker + process_info = process_info_tracker.stop() + tensorflow_profiler.stop() + + upload_timestamp = time.time() + benchmark_result = report_utils.build_benchmark_result( + raw_benchmark_result, method_has_exception, trial_id) + execution_summary = report_utils.build_execution_summary( + execution_timestamp, + execution_id, + config.ml_framework_build_label, + config.execution_label, + config.platform_name, + config.system_name, + config.output_gcs_url, + benchmark_result, + config.get_env_vars(), + config.get_flags(), + harness_info, + site_package_info, + process_info, + method_has_exception, + is_tpu_benchmark = (config.tpu_parameters != None)) + report_utils.upload_execution_summary( + config.bigquery_project_name, + config.bigquery_dataset_table_name, + execution_summary) + report_utils.execute_methods( + config.result_upload_methods, + execution_summary) + logging.info('Benchmark execution for %s completed with summary:\n %s', + benchmark_method, json.dumps(execution_summary, indent=2)) + _set_file_contents(json.dumps(execution_summary, indent=2), + os.path.join(output_dir, 'perfzero_summary.json')) + utils.maybe_upload_to_gcs(output_dir, config.output_gcs_url) + logging.getLogger().removeHandler(filehandler) + method_execution_time = { + 'class_initialization': execution_timestamp - start_timestamp, + 'method_execution': upload_timestamp - execution_timestamp, + 'log_upload': time.time() - upload_timestamp + } + + if config.profiler_enabled_time_str: + relative_output_dir = output_dir[output_dir.find('benchmark'):] + print('\nExecute the command below to start tensorboard server using ' + 'the collected profiler data:\ntensorboard --logdir={}\n\n' + 'Open localhost:6006 in your browser to access the Tensorbord ' + 'GUI. Use ssh with port forwarding if tensorboard is running on ' + 'a remote machine.\n'.format(relative_output_dir)) + + queue.put((method_has_exception, method_execution_time, + benchmark_result['succeeded'], output_dir)) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/device_utils.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/device_utils.py new file mode 100644 index 00000000..dd3e14be --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/device_utils.py @@ -0,0 +1,86 @@ +# 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. +# ============================================================================== +"""Setup the data drive with raid, RAM, or mount network drives.""" +from __future__ import print_function + +import logging + +import perfzero.utils as utils + + +def create_drive_from_devices(data_dir, gce_nvme_raid): + """Creates a drive at data directory.""" + if not gce_nvme_raid: + return + + devices = _get_nvme_devices() + cmd = 'mountpoint -q {}'.format(data_dir) + retcode, _ = utils.run_command(cmd) + if retcode: + if len(devices) > 1: + _create_drive_raid(data_dir, devices) + else: + _create_single_drive(data_dir, devices[0]) + + +def _get_nvme_devices(): + """Returns list paths to nvme devices.""" + devices = [] + cmd = 'lsblk' + retcode, log = utils.run_command(cmd) + if retcode: + raise Exception('"{}" failed with code:{} and log:\n{}'.format( + cmd, retcode, log)) + + lines = log.splitlines() + if lines: + for line in lines: + if line.startswith('nvme'): + parts = line.split() + devices.append('/dev/' + parts[0].strip()) + return devices + + +def _create_single_drive(data_dir, device): + """Creates a data drive out of a single device.""" + cmds = [] + cmds.append('mkfs.ext4 -F {}'.format(device)) + cmds.append('mkdir -p {}'.format(data_dir)) + cmds.append('mount {} {}'.format(device, data_dir)) + cmds.append('chmod a+w {}'.format(data_dir)) + + utils.run_commands(cmds) + logging.info('Created and mounted device %s at %s', device, data_dir) + + +def _create_drive_raid(data_dir, devices): + """Creates a raid zero array of nvme drives.""" + cmds = [] + # Passing 'yes' because GCE nvme drive are sometimes in an odd state and + # think they are in another raid. mdadm does not have -y option. + # Or the kokoro images were left dirty? and that is where the info + # comes from. + cmds.append('yes | mdadm --create /dev/md0 --level=0 ' + '--raid-devices={} {}'.format( + len(devices), ' '.join(devices))) + cmds.append('mkfs.ext4 -F /dev/md0') + cmds.append('mkdir -p {}'.format(data_dir)) + cmds.append('mount /dev/md0 {}'.format(data_dir)) + cmds.append('chmod a+w {}'.format(data_dir)) + + utils.run_commands(cmds) + logging.info('Created and mounted RAID array at %s', data_dir) + + diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/perfzero_config.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/perfzero_config.py new file mode 100644 index 00000000..2fa217c9 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/perfzero_config.py @@ -0,0 +1,367 @@ +# 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. +# ============================================================================== +"""PerfZero configs provided by user.""" +from __future__ import print_function + +import json +import logging +import os + + +def add_setup_parser_arguments(parser): + """Add arguments to the parser used by the setup.py.""" + parser.add_argument( + '--dockerfile_path', + default='docker/Dockerfile_ubuntu_1804_tf_v1', + type=str, + help='''Build the docker image using docker file located at the ${pwd}/${dockerfile_path} if + it exists, where ${pwd} is user's current work directory. Otherwise, build + the docker image using the docker file located at path_to_perfzero/${dockerfile_path}. + ''') + parser.add_argument( + '--workspace', + default='workspace', + type=str, + help='''The gcloud key file will be downloaded under directory path_to_perfzero/${workspace} + ''') + parser.add_argument( + '--gcloud_key_file_url', + default='', + type=str, + help='''DEPRECATED: Use --gcloud_key_file_url of setup.py instead. + The gcloud key file url. When specified, it will be downloaded to the + directory specified by the flag --workspace. Each url can start with file://, gcs://, http:// or https://. + ''') + parser.add_argument( + '--root_data_dir', + default='/data', + type=str, + help='The directory which should contain the dataset required by the becnhmark method.' + ) + parser.add_argument( + '--gce_nvme_raid', + default=None, + type=str, + help='If set to non-empty string, create raid 0 array with devices at the directory specified by the flag --root_data_dir' + ) + parser.add_argument( + '--tensorflow_pip_spec', + default=None, + type=str, + help='''The tensorflow pip package specfication. The format can be either ${package_name}, or ${package_name}==${package_version}. + Example values include tf-nightly-gpu, and tensorflow==1.12.0. If it is specified, the corresponding tensorflow pip package/version + will be installed. Otherwise, the default tensorflow pip package specified in the docker file will be installed. + ''') + parser.add_argument( + '--extra_pip_specs', + default='', + type=str, + help='''Additional specifications to pass to `pip install`. (e.g. pinning certain dependencies) + Specifications should be semicolon separated: e.g. `numpy==1.16.4;scipy==1.3.1` + ''') + parser.add_argument( + '--docker_tag', + default='perfzero/tensorflow', + type=str, + help='The docker tag to use if building a docker image.' + ) + parser.add_argument( + '--site_package_downloads', + default='', + type=str, + help='''Comma separated list of dirs in the external vm to copy to the docker\'s site package dir. + Format: /src/dir:new_base_dir_name,/src/dir2>:new_name,.... + This will copy /src/dir to /new_base_dir_name. + ''' + ) + parser.add_argument( + '--extra_docker_build_args', + nargs='*', + default='', + type=str, + help='''Additional build-args to pass to `docker build`. + Example: --extra_docker_build_args arg0 arg1=value1 "arg2=value with space" arg3=300. + Each string will be passed directly as a build-arg to docker, so the above example will be passed as follows: + --build-arg arg0 --build-arg arg1=value1 --build-arg "arg2=value with space" --build-arg arg3=300 + ''' + ) + + +def add_benchmark_parser_arguments(parser): + """Add arguments to the parser used by the benchmark.py.""" + parser.add_argument( + '--use_cached_site_packages', + action='store_true', + help='If set, skip git pull for dependent git repositories if it already exists in path_to_perfzero/${workspace}/site-packages' + ) + parser.add_argument( + '--gcs_downloads', + default=None, + type=str, + help='This flag is deprecated. Use the flag --data_downloads instead') + parser.add_argument( + '--git_repos', + default=None, + type=str, + help='''A string representing git repositories to checkout. The format is url_1;branch_1;hash_1,url_2;branch_2;hash_2,... + Git repositories will be checked-out under directory path_to_perfzero/${workspace}/site-packages, + where ${workspace} either defaults to 'workspace', or takes the value of the flag --workspace. + branch and hash can be skipped if user wants to use the head of the master branch, + which shortens the format to url_1,url_2,... + ''') + parser.add_argument( + '--benchmark_num_trials', + default=1, + type=int, + help='''Configures number of times to run each benchmark method + after setup completion.''') + parser.add_argument( + '--benchmark_methods', + action='append', + default=[], + type=str, + help='''This string specifies the benchmark_method to be executed. The flag can be specified multiple times in which case + the union of methods matched by these flags will be executed. The format can be module_path.class_name.method_name in which + case the corresponding method is executed. The format can also be module_path.class_name.filter:regex_pattern, in which case all methods + of the given class whose method name matches the given regular expression are executed. + ''') + parser.add_argument( + '--ml_framework_build_label', + default=None, + type=str, + help='A string that identified the machine learning framework build, e.g. nightly-gpu-build' + ) + parser.add_argument( + '--execution_label', + default=None, + type=str, + help='A string that identified the benchmark execution type, e.g. test, prod' + ) + parser.add_argument( + '--platform_name', + default=None, + type=str, + help='A string that identified the computing platform, e.g. gcp, aws' + ) + parser.add_argument( + '--system_name', + default=None, + type=str, + help='A string that identified the hardware system, e.g. n1-standard-64-8xV100' + ) + parser.add_argument( + '--output_gcs_url', + default=None, + type=str, + help='''If specified, log files generated by the benchmark execution will be uploaded to output_gcs_url/${execution_id}, + where ${execution_id} is a string that generated by PerfZero which uniquely identifies the execution of one benchmark method + ''') + parser.add_argument( + '--scratch_gcs_url', + default=None, + type=str, + help='''If specified, intermediate files like model outputs will be stored in scratch_gcs_url/${execution_id}, where + ${execution_id} is a string that is generated by PerfZero which uniquely identifies the execution of one benchmark method. + If not specified, intermediate files will be stored in a local folder on the host. + ''') + parser.add_argument( + '--bigquery_project_name', + default=None, + type=str, + help='''If both --bigquery_project_name and --bigquery_dataset_table_name are specified, for each benchmark method, the benchmark + summary will be uploaded to the specified bigquery table whose schema is defined in perfzero/scripts/create_big_table.txt. + The value of each field can in turn be a json-formatted string. See README.md for example output. + ''') + parser.add_argument( + '--bigquery_dataset_table_name', + default=None, + type=str, + help='''If both --bigquery_project_name and --bigquery_dataset_table_name are specified, for each benchmark method, the benchmark + summary will be uploaded to the specified bigquery table whose schema is defined in perfzero/scripts/create_big_table.txt. + The value of each field can in turn be a json-formatted string. See README.md for example output. + ''') + parser.add_argument( + '--python_path', + default=None, + type=str, + help='''A string of format path_1,path_2,... For each ${path} specified in the string, + path_to_perfzero/${workspace}/site-packages/${path} will be added to python path so that libraies downloaded by --git_repos can + be loaded and executed. + ''') + parser.add_argument( + '--workspace', + default='workspace', + type=str, + help='''The log files, gcloud key file and git repositories will be generated and downloaded under the + directory path_to_perfzero/${workspace} + ''') + parser.add_argument( + '--root_data_dir', + default='/data', + type=str, + help='The directory which should contain the dataset required by the becnhmark method.' + ) + parser.add_argument( + '--data_downloads', + default=None, + type=str, + help='''A string of format url_1;relative_path_1,url_2;relative_path_2,... + Data will be copied from ${url} to ${root_data_dir}/${relative_path}. ${relative_path} can be skipped if it is the same as the + base name of the url, which shortens the format to url_1,url_2,... ${root_data_dir} is specified by the flag --root_data_dir. + File will be de-compressed in ${root_data_dir} if its name ends with 'gz'. Only url prefixed with gcs, http or https are supported. + Each url can start with file://, gcs://, http:// or https://. + ''') + parser.add_argument( + '--gcloud_key_file_url', + default='gs://tf-performance/auth_tokens/benchmark_upload_gce.json', + type=str, + help='''The gcloud key file url. When specified, it will be downloaded to the + directory specified by the flag --workspace. Each url can start with file://, gcs://, http:// or https://. + The key file will then be activated and used as gcloud authentication credential. + ''') + parser.add_argument( + '--debug', + action='store_true', + help='If set, use debug level logging. Otherwise, use info level logging' + ) + parser.add_argument( + '--profiler_enabled_time', + default=None, + type=str, + help='''A string of format begin_time_1:end_time_1,begin_time_2:end_time_2,.... PerfZero will start to collect profiler + data ${begin_time} sec after benchmark method execution starts. The data collection continues for ${end_time - begin_time} + sec or until the benchmark method execution finishes, whichever occurs first. If ${end_time} is not explicitly + specified, it is assumed to be MAX_LONG. + ''') + parser.add_argument( + '--execution_id', + default=None, + type=str, + help='A string that uniquely identifies the benchmark execution.') + parser.add_argument( + '--result_upload_methods', + default=None, + type=str, + help='A comma separated list of class.method values to upload results.') + parser.add_argument( + '--tpu_parameters', + default=None, + type=str, + help='''A json dictionary of cloud tpu parameters. The format must look like the following: + {"name": "my-tpu-name", project": "my-gcp-project-id", "zone": "europe-west4-a", "size": "v3-8", "version": "nightly-2.x"} + It can have an optional key value pair "version_id" -> "nightly version" to change the tpu version id. + Example "version_id": "2.4.0-dev20200728". + ''') + parser.add_argument( + '--perfzero_constructor_args', + nargs='*', + default='', + type=str, + help='''A json dictionary of additional args to pass to the perfzero + constructor.''' + ) + parser.add_argument( + '--benchmark_class_type', + default=None, + type=str, + help='''The benchmark class type. If none, assumed perfzero_benchmark. Set to "tf_benchmark" + for tf.test.Benchmark benchmarks.''') + + +class PerfZeroConfig(object): + """Creates and contains config for PerfZero.""" + + def __init__(self, mode, flags=None): + self.mode = mode + self.flags = flags + if mode == 'flags': + self.gcs_downloads_str = flags.gcs_downloads + self.data_downloads_str = flags.data_downloads + self.git_repos_str = flags.git_repos + self.benchmark_method_patterns = [] + for value in flags.benchmark_methods: + self.benchmark_method_patterns.extend(value.split(',')) + self.ml_framework_build_label = flags.ml_framework_build_label + self.execution_label = flags.execution_label + self.platform_name = flags.platform_name + self.system_name = flags.system_name + self.output_gcs_url = flags.output_gcs_url + self.scratch_gcs_url = flags.scratch_gcs_url + self.bigquery_project_name = flags.bigquery_project_name + self.bigquery_dataset_table_name = flags.bigquery_dataset_table_name + self.python_path_str = flags.python_path + self.workspace = flags.workspace + self.benchmark_class_type = flags.benchmark_class_type + self.use_cached_site_packages = flags.use_cached_site_packages + self.root_data_dir = flags.root_data_dir + self.gcloud_key_file_url = flags.gcloud_key_file_url + self.profiler_enabled_time_str = flags.profiler_enabled_time + self.execution_id = flags.execution_id + self.result_upload_methods = flags.result_upload_methods + self.perfzero_constructor_args = flags.perfzero_constructor_args + self.benchmark_num_trials = flags.benchmark_num_trials + if flags.tpu_parameters: + self.tpu_parameters = json.loads(flags.tpu_parameters) + else: + self.tpu_parameters = None + + if not flags.benchmark_methods: + logging.warning('No benchmark method is specified by ' + '--benchmark_methods') + + if flags.bigquery_project_name and not flags.bigquery_dataset_table_name: + raise ValueError('--bigquery_project_name is specified but ' + '--bigquery_dataset_table_name is not') + + if not flags.bigquery_project_name and flags.bigquery_dataset_table_name: + raise ValueError('--bigquery_dataset_table_name is specified but ' + '--bigquery_project_name is not') + + def get_env_vars(self): + env_vars = {} + for key in os.environ.keys(): + if key.startswith('PERFZERO_'): + env_vars[key] = os.environ[key] + return env_vars + + def get_flags(self): + not_none_flags = {} + for key in vars(self.flags): + value = getattr(self.flags, key) + if value is not None: + not_none_flags[key] = value + return not_none_flags + + def get_git_repos(self, site_packages_dir): + """Parse git repository string.""" + git_repos = [] + if not self.git_repos_str: + return git_repos + + for repo_entry in self.git_repos_str.split(','): + parts = repo_entry.split(';') + git_repo = {} + git_repo['url'] = parts[0] + # Assume the git url has format */{dir_name}.git + git_repo['dir_name'] = parts[0].rsplit('/', 1)[-1].rsplit('.', 1)[0] + git_repo['local_path'] = os.path.join(site_packages_dir, + git_repo['dir_name']) + if len(parts) >= 2: + git_repo['branch'] = parts[1] + if len(parts) >= 3: + git_repo['git_hash'] = parts[2] + git_repos.append(git_repo) + + return git_repos diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/perfzero_config_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/perfzero_config_test.py new file mode 100644 index 00000000..e02cd9f4 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/perfzero_config_test.py @@ -0,0 +1,54 @@ +# 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 perfzero_config.py.""" +from __future__ import print_function + +import os +import unittest +import perfzero.perfzero_config as perfzero_config + + +class TestPerfZeroConfig(unittest.TestCase): + + def test_get_git_repos(self): + config = perfzero_config.PerfZeroConfig(mode='mock') + config.git_repos_str = 'https://github.com/tensorflow/benchmarks.git;branch_1;hash_1,https://github.com/tensorflow/models.git;branch_2' + git_repos = config.get_git_repos('/site_package_dir') + + git_repo_1 = {} + git_repo_1['url'] = 'https://github.com/tensorflow/benchmarks.git' + git_repo_1['dir_name'] = 'benchmarks' + git_repo_1['local_path'] = '/site_package_dir/benchmarks' + git_repo_1['branch'] = 'branch_1' + git_repo_1['git_hash'] = 'hash_1' + + git_repo_2 = {} + git_repo_2['url'] = 'https://github.com/tensorflow/models.git' + git_repo_2['dir_name'] = 'models' + git_repo_2['local_path'] = '/site_package_dir/models' + git_repo_2['branch'] = 'branch_2' + + self.assertEqual(2, len(git_repos)) + self.assertEqual(git_repo_1, git_repos[0]) + self.assertEqual(git_repo_2, git_repos[1]) + + def test_get_env_vars(self): + config = perfzero_config.PerfZeroConfig(mode='mock') + self.assertEqual({}, config.get_env_vars()) + + os.environ['PERFZERO_VAR1'] = 'value1' + self.assertEqual({'PERFZERO_VAR1': 'value1'}, config.get_env_vars()) + + diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/process_info_tracker.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/process_info_tracker.py new file mode 100644 index 00000000..4d8c2737 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/process_info_tracker.py @@ -0,0 +1,93 @@ +# 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. +# ============================================================================== + +"""Keep track of process information such as maximum memory usage with a separate thread.""" + +from __future__ import absolute_import + +import json +import logging +import os +import sched +import threading +import time +import traceback +import psutil + + +class ProcessInfoTracker(object): + """Keep track of process information such as maximum memory usage with separate thread.""" + + def __init__(self, output_dir): + self.process_info_log = open(os.path.join(output_dir, 'process_info.log'), + 'w') + self.scheduler = sched.scheduler(time.time, time.sleep) + self.process_info = {} + self.process_info['max_rss'] = 0 + self.process_info['max_vms'] = 0 + self.process_info['max_cpu_percent'] = 0 + self.exit_event = threading.Event() + self.last_exception = None + self.start_time = None + + def start(self): + self.start_time = time.time() + # 4th positional arg added to support Python2 for the short-term. + self.scheduler.enter(1, 1, self._update_process_info, ()) # pylint: disable=no-value-for-parameter + threading.Thread(target=self.scheduler.run).start() + logging.info('Started process information tracker.') + + def stop(self): + self.exit_event.set() + self.process_info_log.flush() + logging.info('Stopped process information tracker.') + + if self.last_exception is not None: + raise self.last_exception # pylint: disable=raising-bad-type + + return dict(self.process_info) + + def _update_process_info(self): + """Read and update process info using background thread every 1 second.""" + try: + p = psutil.Process(os.getpid()) + memory_info = p.memory_info() + # This is a blocking call which takes 0.1 second. + # This affects the interval # at which the metrics are reported + cpu_percent = p.cpu_percent(interval=0.1) + + self.process_info['max_rss'] = max(self.process_info['max_rss'], + memory_info.rss) + self.process_info['max_vms'] = max(self.process_info['max_vms'], + memory_info.vms) + self.process_info['max_cpu_percent'] = max( + self.process_info['max_cpu_percent'], cpu_percent) + + entry = {} + entry['time'] = time.time() - self.start_time + entry['rss'] = memory_info.rss + entry['vms'] = memory_info.vms + entry['cpu_percent'] = cpu_percent + self.process_info_log.write(json.dumps(entry) + '\n') + if not self.exit_event.is_set(): + # Schedule the next event to be run after 1 second + # 4th positional arg added to support Python2 for the short-term. + self.scheduler.enter(1, 1, self._update_process_info, ()) # pylint: disable=no-value-for-parameter + except Exception as e: # pylint: disable=broad-except + logging.error('Process info tracker failed due to error:\n %s', + traceback.format_exc()) + self.last_exception = e + + diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/report_utils.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/report_utils.py new file mode 100644 index 00000000..5a7441f0 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/report_utils.py @@ -0,0 +1,237 @@ +# 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. +# ============================================================================== +"""Upload test results.""" +from __future__ import print_function + +import importlib +import json +import logging +import perfzero.utils as utils +import psutil +import socket + +from six import u as unicode # pylint: disable=W0622 + + +def execute_methods(method_names_str, *args, **kwargs): + """Calls a list of method names on given function params. + + Args: + method_names_str: String - Comma-separated module.foo.bar.method strings. + This function imports module.foo.bar for each such method and calls it + with *args and **kwargs. + *args: Function params common to each method. + **kwargs: Function params common to each method. + + Raises: + RuntimeError: If any of the invoked methods raised an exception. + """ + if not method_names_str: + return + errors = [] + module_methods_list = method_names_str.split(',') + for module_method in module_methods_list: + try: + logging.info('Trying to call %s', module_method) + module_path, method_path = module_method.rsplit('.', 1) + this_module = importlib.import_module(module_path) + logging.info('Found module %s, looking for %s', module_path, method_path) + this_method = getattr(this_module, method_path) + logging.info('Found method %s', method_path) + this_method(*args, **kwargs) + except Exception as e: # pylint: disable=broad-except + errors.append(str(e)) + if errors: + raise RuntimeError('\n' + '\n'.join(errors)) + + +def upload_execution_summary(bigquery_project_name, bigquery_dataset_table_name, + execution_summary): + """Upload benchmark summary. + + Note: Using stream=False has a 1000 per day insert limit per table. Using + stream=True, the documented limit is 50K+. With streaming there can be + a small and possibly not noticeable delay to seeing the results the BigQuery + UI, but there can be a 90 minute more or less delay in the results being part + of exports. + + Note: BigQuery maps unicode() to STRING for python2. If str is used that is + mapped to BYTE. + + Args: + bigquery_project_name: Name of the gcp project. + bigquery_dataset_table_name: data_set and table name. + execution_summary: benchmark summary dictionary of results. + """ + + # pylint: disable=C6204 + import google.auth + from google.cloud import bigquery + + if not bigquery_project_name: + logging.info( + 'Skipped uploading benchmark result to bigquery because bigquery table name is not set.' + ) + return + + if not bigquery_dataset_table_name: + logging.info( + 'Skipped uploading benchmark result to bigquery because bigquery project name is not set.' + ) + return + + credentials = google.auth.default()[0] + dataset_name = bigquery_dataset_table_name.split('.')[0] + table_name = bigquery_dataset_table_name.split('.')[1] + client = bigquery.Client( + project=bigquery_project_name, credentials=credentials) + + benchmark_summary_input = {} + for key, value in execution_summary.items(): + if isinstance(value, dict): + benchmark_summary_input[key] = unicode(json.dumps(value)) + else: + benchmark_summary_input[key] = unicode(value) + logging.debug('Bigquery input for benchmark_summary table is %s', + json.dumps(benchmark_summary_input, indent=2)) + + errors = [] + # TODO(tobyboyd): Shim to direct results to new table until all jobs + # are updated. + if 'benchmark_results' in dataset_name: + if dataset_name == 'benchmark_results_dev': + table_ref = client.dataset('perfzero_dev').table('benchmark_summary') + table_obj = client.get_table(table_ref) + elif dataset_name == 'benchmark_results': + table_ref = client.dataset('perfzero').table('benchmark_summary') + table_obj = client.get_table(table_ref) + else: + table_ref = client.dataset(dataset_name).table(table_name) + table_obj = client.get_table(table_ref) + + errors.extend(client.insert_rows(table_obj, [benchmark_summary_input])) + + if errors: + logging.error( + 'Failed to upload benchmark result to bigquery due to errors %s', + errors) + else: + logging.info( + 'Uploaded benchmark result to the table %s of the bigquery project %s.', + bigquery_dataset_table_name, + bigquery_project_name) + + +def build_benchmark_result(raw_benchmark_result, has_exception, trial_id): + """Converts test_log.proto format to PerfZero format.""" + benchmark_result = {} + benchmark_result['name'] = raw_benchmark_result['name'] + benchmark_result['wall_time'] = raw_benchmark_result['wall_time'] + + succeeded = not has_exception + extras = [] + for name in raw_benchmark_result.get('extras', {}): + entry = {} + entry['name'] = name + + if 'double_value' in raw_benchmark_result['extras'][name]: + entry['value'] = raw_benchmark_result['extras'][name]['double_value'] + else: + entry['value'] = raw_benchmark_result['extras'][name]['string_value'] + extras.append(entry) + + metrics = [] + for metric in raw_benchmark_result.get('metrics', []): + value = metric['value'] + if 'min_value' in metric and metric['min_value'] > value: + succeeded = False + if 'max_value' in metric and metric['max_value'] < value: + succeeded = False + metrics.append(metric) + + benchmark_result['succeeded'] = succeeded + benchmark_result['extras'] = extras + benchmark_result['metrics'] = metrics + benchmark_result['trial_id'] = trial_id + + return benchmark_result + + +def build_execution_summary(execution_timestamp, execution_id, + ml_framework_build_label, execution_label, + platform_name, system_name, output_gcs_url, + benchmark_result, env_vars, flags, harness_info, + site_package_info, process_info, has_exception, + is_tpu_benchmark): + """Builds summary of the execution.""" + # Avoids module not found during setup phase when tf is not installed yet. + # pylint: disable=C6204 + import tensorflow as tf + + benchmark_info = {} + benchmark_info['harness_name'] = 'perfzero' + benchmark_info['harness_info'] = harness_info + benchmark_info['has_exception'] = has_exception + if execution_label: + benchmark_info['execution_label'] = execution_label + if output_gcs_url: + benchmark_info['output_url'] = '{}/{}/'.format(output_gcs_url, execution_id) + if env_vars: + benchmark_info['env_vars'] = env_vars + if flags: + benchmark_info['flags'] = flags + benchmark_info['site_package_info'] = site_package_info + + ml_framework_info = {} + ml_framework_info['name'] = 'tensorflow' + ml_framework_info['version'] = tf.__version__ + # tf.__git_version__ in Python3 has format b'version_string' + if tf.__git_version__[0] == 'b': + ml_framework_info['build_version'] = tf.__git_version__[2:-1] + else: + ml_framework_info['build_version'] = tf.__git_version__ + + if ml_framework_build_label: + ml_framework_info['build_label'] = ml_framework_build_label + + system_info = {} + if platform_name: + system_info['platform_name'] = platform_name + if system_name: + system_info['system_name'] = system_name + if not is_tpu_benchmark: + gpu_info = utils.get_gpu_info() + if gpu_info: + system_info['accelerator_driver_version'] = gpu_info['gpu_driver_version'] + system_info['accelerator_model'] = gpu_info['gpu_model'] + system_info['accelerator_count'] = gpu_info['gpu_count'] + system_info['cpu_model'] = utils.get_cpu_name() + system_info['physical_cpu_count'] = psutil.cpu_count(logical=False) + system_info['logical_cpu_count'] = psutil.cpu_count(logical=True) + system_info['cpu_socket_count'] = utils.get_cpu_socket_count() + system_info['hostname'] = socket.gethostname() + + execution_summary = {} + execution_summary['execution_id'] = execution_id + execution_summary['execution_timestamp'] = execution_timestamp + execution_summary['benchmark_result'] = benchmark_result + execution_summary['benchmark_info'] = benchmark_info + execution_summary['setup_info'] = {} + execution_summary['ml_framework_info'] = ml_framework_info + execution_summary['system_info'] = system_info + if process_info: + execution_summary['process_info'] = process_info + + return execution_summary diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/tensorflow_profiler.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/tensorflow_profiler.py new file mode 100644 index 00000000..bb1839c4 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/tensorflow_profiler.py @@ -0,0 +1,128 @@ +# 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. +# ============================================================================== + +"""Collect profiler data for Tensorboard with a separate thread.""" + +from __future__ import print_function + +import logging +import os +import sched +import threading +import time +import traceback + +import perfzero.utils as utils + + +def _start_profiler(output_dir): + """Start profiler. + + Args: + output_dir: log directory to place the profiler data + """ + import tensorflow as tf # pylint: disable=g-import-not-at-top + + profiler_data_dir = os.path.join(output_dir, 'profiler_data') + utils.make_dir_if_not_exist(profiler_data_dir) + logging.info('Starting TensorFlow profiler and saving data to dir %s', + profiler_data_dir) + try: + tf.profiler.experimental.start(profiler_data_dir) + logging.info('Started TensorFlow profiler') + except Exception: # pylint: disable=broad-except + logging.error('TensorFlow profiler failed to start due to error:\n %s', + traceback.format_exc()) + + +def _stop_profiler(): + """Stop profiler.""" + + import tensorflow as tf # pylint: disable=g-import-not-at-top + + try: + tf.profiler.experimental.stop() + logging.info('Stopped TensorFlow profiler.') + except Exception: # pylint: disable=broad-except + logging.error('TensorFlow profiler failed to stop due to error:\n %s', + traceback.format_exc()) + + +class TensorFlowProfiler(object): + """Collect profiler data for Tensorboard with a separate thread.""" + + def __init__(self, profiler_enabled_time_str, output_dir): + """Constructor. + + Args: + profiler_enabled_time_str: the value of the config --profiler_enabled_time + output_dir: log directory to place the profiler data + """ + + self.profiler_enabled_time_str = profiler_enabled_time_str + self.output_dir = output_dir + self.exit_event = threading.Event() + self.scheduler = sched.scheduler(time.time, self._sleep_until_exit) + + def _sleep_until_exit(self, timeout): + start_time = time.time() + cur_time = time.time() + while cur_time - start_time < timeout and not self.exit_event.is_set(): + time.sleep(min(1, timeout + start_time - cur_time)) + cur_time = time.time() + + def start(self): + """Schedule start/stop profiler event specified in profiler_enabled_time_str.""" + + if not self.profiler_enabled_time_str: + return + + last_end_time = -1 + for time_str in self.profiler_enabled_time_str.split(','): + begin_time = int(time_str.split(':')[0].strip()) + end_time_str = time_str.split(':')[1].strip() if ':' in time_str else None + end_time = int(end_time_str) if end_time_str else 365 * 24 * 60 * 60 + if begin_time <= last_end_time: + raise ValueError('begin_time {} is no larger than the last ' + 'end_time {}'.format(begin_time, last_end_time)) + if end_time <= begin_time: + raise ValueError('end_time {} is no larger than begin_time {}'.format( + end_time, begin_time)) + # 4th positional arg added to support Python2 for the short-term. + self.scheduler.enter(begin_time, 1, _start_profiler, + argument=(self.output_dir,)) + self.scheduler.enter(end_time, 1, _stop_profiler, ()) # pylint: disable=no-value-for-parameter + last_end_time = end_time + + threading.Thread(target=self.scheduler.run).start() + + def stop(self): + """Stop scheduler and save profiler data if any event is cancelled.""" + + event_canceled = False + for event in self.scheduler.queue: + try: + self.scheduler.cancel(event) + event_canceled = True + except ValueError: + # This is OK because the event may have been just canceled + pass + + # Signal the scheduler thread to stop sleeping + self.exit_event.set() + + # Save the profiler data if any event is canceled + if event_canceled: + _stop_profiler() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/test_files/example_nvidia-smi_no_processes.txt b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/test_files/example_nvidia-smi_no_processes.txt new file mode 100644 index 00000000..a467ea64 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/test_files/example_nvidia-smi_no_processes.txt @@ -0,0 +1,40 @@ +Tue Jan 9 09:34:25 2018 ++-----------------------------------------------------------------------------+ +| NVIDIA-SMI 384.81 Driver Version: 384.81 | +|-------------------------------+----------------------+----------------------+ +| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | +| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | +|===============================+======================+======================| +| 0 Tesla P100-SXM2... On | 00000000:06:00.0 Off | 0 | +| N/A 50C P0 196W / 300W | 15643MiB / 16276MiB | 97% Default | ++-------------------------------+----------------------+----------------------+ +| 1 Tesla P100-SXM2... On | 00000000:07:00.0 Off | 0 | +| N/A 41C P0 50W / 300W | 15483MiB / 16276MiB | 0% Default | ++-------------------------------+----------------------+----------------------+ +| 2 Tesla P100-SXM2... On | 00000000:0A:00.0 Off | 0 | +| N/A 33C P0 48W / 300W | 15483MiB / 16276MiB | 0% Default | ++-------------------------------+----------------------+----------------------+ +| 3 Tesla P100-SXM2... On | 00000000:0B:00.0 Off | 0 | +| N/A 34C P0 49W / 300W | 15483MiB / 16276MiB | 0% Default | ++-------------------------------+----------------------+----------------------+ +| 4 Tesla P100-SXM2... On | 00000000:85:00.0 Off | 0 | +| N/A 36C P0 50W / 300W | 15483MiB / 16276MiB | 0% Default | ++-------------------------------+----------------------+----------------------+ +| 5 Tesla P100-SXM2... On | 00000000:86:00.0 Off | 0 | +| N/A 33C P0 48W / 300W | 15483MiB / 16276MiB | 0% Default | ++-------------------------------+----------------------+----------------------+ +| 6 Tesla P100-SXM2... On | 00000000:89:00.0 Off | 0 | +| N/A 38C P0 48W / 300W | 15483MiB / 16276MiB | 0% Default | ++-------------------------------+----------------------+----------------------+ +| 7 Tesla P100-SXM2... On | 00000000:8A:00.0 Off | 0 | +| N/A 34C P0 49W / 300W | 15483MiB / 16276MiB | 0% Default | ++-------------------------------+----------------------+----------------------+ + ++-----------------------------------------------------------------------------+ +| Processes: GPU Memory | +| GPU PID Type Process name Usage | +|=============================================================================| +| No running processes found | ++-----------------------------------------------------------------------------+ + + diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/test_files/example_nvidia-smi_processes.txt b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/test_files/example_nvidia-smi_processes.txt new file mode 100644 index 00000000..2eedb2f2 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/test_files/example_nvidia-smi_processes.txt @@ -0,0 +1,43 @@ +Tue Jan 9 09:34:25 2018 ++-----------------------------------------------------------------------------+ +| NVIDIA-SMI 384.81 Driver Version: 384.81 | +|-------------------------------+----------------------+----------------------+ +| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | +| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | +|===============================+======================+======================| +| 0 Tesla P100-SXM2... On | 00000000:06:00.0 Off | 0 | +| N/A 50C P0 196W / 300W | 15643MiB / 16276MiB | 97% Default | ++-------------------------------+----------------------+----------------------+ +| 1 Tesla P100-SXM2... On | 00000000:07:00.0 Off | 0 | +| N/A 41C P0 50W / 300W | 15483MiB / 16276MiB | 0% Default | ++-------------------------------+----------------------+----------------------+ +| 2 Tesla P100-SXM2... On | 00000000:0A:00.0 Off | 0 | +| N/A 33C P0 48W / 300W | 15483MiB / 16276MiB | 0% Default | ++-------------------------------+----------------------+----------------------+ +| 3 Tesla P100-SXM2... On | 00000000:0B:00.0 Off | 0 | +| N/A 34C P0 49W / 300W | 15483MiB / 16276MiB | 0% Default | ++-------------------------------+----------------------+----------------------+ +| 4 Tesla P100-SXM2... On | 00000000:85:00.0 Off | 0 | +| N/A 36C P0 50W / 300W | 15483MiB / 16276MiB | 0% Default | ++-------------------------------+----------------------+----------------------+ +| 5 Tesla P100-SXM2... On | 00000000:86:00.0 Off | 0 | +| N/A 33C P0 48W / 300W | 15483MiB / 16276MiB | 0% Default | ++-------------------------------+----------------------+----------------------+ +| 6 Tesla P100-SXM2... On | 00000000:89:00.0 Off | 0 | +| N/A 38C P0 48W / 300W | 15483MiB / 16276MiB | 0% Default | ++-------------------------------+----------------------+----------------------+ +| 7 Tesla P100-SXM2... On | 00000000:8A:00.0 Off | 0 | +| N/A 34C P0 49W / 300W | 15483MiB / 16276MiB | 0% Default | ++-------------------------------+----------------------+----------------------+ + ++-----------------------------------------------------------------------------+ +| Processes: GPU Memory | +| GPU PID Type Process name Usage | +|=============================================================================| +| 0 44454 C /usr/bin/python 15631MiB | +| 1 44454 C /usr/bin/python 15471MiB | +| 2 44454 C /usr/bin/python 15471MiB | +| 3 44454 C /usr/bin/python 15471MiB | ++-----------------------------------------------------------------------------+ + + diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/test_files/nvme_device_log.txt b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/test_files/nvme_device_log.txt new file mode 100644 index 00000000..96965acf --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/test_files/nvme_device_log.txt @@ -0,0 +1,15 @@ +NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT +nvme0n8 259:7 0 375G 0 disk +nvme0n6 259:5 0 375G 0 disk +sdb 8:16 0 50G 0 disk + +└─sdb1 8:17 0 50G 0 part /tmpfs +nvme0n4 259:3 0 375G 0 disk +nvme0n2 259:1 0 375G 0 disk +nvme0n7 259:6 0 375G 0 disk +nvme0n5 259:4 0 375G 0 disk +sda 8:0 0 100G 0 disk + +└─sda1 8:1 0 100G 0 part / +nvme0n3 259:2 0 375G 0 disk +nvme0n1 259:0 0 375G 0 disk diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/tpu_runtime_utils.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/tpu_runtime_utils.py new file mode 100644 index 00000000..2cbcaa92 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/tpu_runtime_utils.py @@ -0,0 +1,89 @@ +"""Utility to manage the tpu version before starting the benchmark.""" + +import json +from absl import logging + +from six.moves.urllib import request + +try: + from cloud_tpu_client import client # pylint: disable=g-import-not-at-top +except ImportError: + print( + 'Falling back to TensorFlow client; we recommended you install the Cloud ' + 'TPU client directly with pip install cloud-tpu-client.') + from tensorflow.python.tpu.client import client # pylint: disable=g-import-not-at-top + + +def _as_text(s): + """Converts a byte/string into string.""" + if isinstance(s, bytes): + return s.decode('utf-8') + return s + + +def _get_content(url): + """Opens the url and loads the response into json.""" + logging.info('opening url %s', url) + req = request.Request(url) + resp = request.urlopen(req) + resp_text = _as_text(resp.read()) + logging.info('response text = %s', resp_text) + return json.loads(resp_text) + + +def _get_version_info(url, version_label): + """Constructs a version info from the response.""" + json_data = _get_content(url) + logging.info('json_data = %s', json_data) + if 'currentVersion' in json_data: + commit_id = json_data['currentVersion'] + elif 'buildLabel' in json_data: + commit_id = json_data['buildLabel'] + else: + commit_id = '' + + info = { + 'url': '', + 'hash': commit_id, + 'branch': version_label, + 'piper_id': json_data.get('piperOriginRevId', '') + } + return info + + + +def _configure_tpu_version(tpu_name, version_label, new_version_id): + """Returns the current tpu version after resetting to an optional version.""" + # The tpu_name is arbitrary / user chosen unique string for this tpu. + logging.info('Trying to connect to tpu %s', tpu_name) + tpu_client = client.Client(tpu=tpu_name) + tpu_client.wait_for_healthy() + + if new_version_id: + logging.info('Trying to reset tpu version to %s', new_version_id) + tpu_client.configure_tpu_version(version=new_version_id) + tpu_client.wait_for_healthy() + logging.info('TPU healthy after version reset.') + else: + logging.info('Using the default tpu version id.') + + workers = tpu_client.network_endpoints() + if workers: + ip_addr = workers[0]['ipAddress'] + url = 'http://{}:8475/requestversion'.format(ip_addr) + return _get_version_info(url, version_label) + else: + logging.error('No tpu endpoint info') + return { + 'url': '', + 'hash': '', + 'branch': version_label, + 'piper_id': '', + } + + +def configure_tpu(tpu_params): + return _configure_tpu_version( + tpu_params.get('name'), + version_label=tpu_params.get('version'), + new_version_id=tpu_params.get('version_id')) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/utils.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/utils.py new file mode 100644 index 00000000..3a4f28ed --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/utils.py @@ -0,0 +1,546 @@ +# 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. +# ============================================================================== +"""PerfZero utility methods.""" +from __future__ import print_function + +import importlib +import logging +import os +import shutil +import subprocess +import sys +import threading +import traceback +import requests +import json +import re + +def create_empty_file(parent_directory, file_basename): + """Creates an empty file with a given basename in a parent directory. + + Creates parent_directory and intermediate directories if it doesn't exist. + This is mostly used for creating no-op actions in the Dockerfile. + + Args: + parent_directory: The path to the parent directory. + file_basename: The basename for the empty file. + """ + if not os.path.isdir(parent_directory): + os.makedirs(parent_directory) + full_file_name = os.path.join(parent_directory, file_basename) + with open(full_file_name, 'w'): + print('Creating empty file: {}'.format(full_file_name)) + + +def checkout_git_repos(git_repos, use_cached_site_packages): + """Clone, update, or sync a repo. + + Args: + git_repos: array of dict containing attributes of the git repo to checkout. + use_cached_site_packages: If true, skip git pull if git_repo already exists. + + Returns: + A dict containing attributes of the git repositories + """ + site_package_info = {} + for repo in git_repos: + logging.info('Checking out repository from %s to %s', + repo['url'], repo['local_path']) + if not os.path.isdir(repo['local_path']): + run_commands(['git clone {} {}'.format(repo['url'], repo['local_path'])]) + if 'branch' in repo: + run_commands(['git -C {} checkout {}'.format( + repo['local_path'], repo['branch'])]) + if not use_cached_site_packages or 'git_hash' in repo: + run_commands(['git -C {} pull --rebase'.format(repo['local_path'])]) + if 'git_hash' in repo: + run_commands(['git -C {} reset --hard {}'.format( + repo['local_path'], repo['git_hash'])]) + logging.info('Checked-out repository from %s to %s', + repo['url'], repo['local_path']) + site_package_info[repo['dir_name']] = get_git_repo_info(repo['local_path']) + + return site_package_info + + +def get_git_repo_info(local_path): + """Get information of the git repository specified by the local_path.""" + git_repo_info = {} + + # Get git url + cmd = 'git -C {} config --get remote.origin.url'.format(local_path) + exit_code, result = run_command(cmd) + lines = result.splitlines() + if exit_code == 0 and lines: + git_repo_info['url'] = lines[0] + else: + logging.error('Error getting git url for repository %s due to %s', + local_path, result) + return {} + + # Get git branch + cmd = 'git -C {} rev-parse --abbrev-ref HEAD'.format(local_path) + exit_code, result = run_command(cmd) + lines = result.splitlines() + if exit_code == 0 and lines: + git_repo_info['branch'] = lines[0] + else: + logging.error('Error getting git branch for repository %s due to %s', + local_path, result) + return {} + + # Get git hash + cmd = 'git -C {} rev-parse HEAD'.format(local_path) + exit_code, result = run_command(cmd) + lines = result.splitlines() + if exit_code == 0 and lines: + git_repo_info['hash'] = lines[0] + else: + logging.error('Error getting git hash for repository %s due to %s', + local_path, result) + return {} + + return git_repo_info + + +def setup_python_path(site_packages_dir, python_path_str): + if python_path_str: + python_paths = python_path_str.split(',') + for python_path in python_paths: + logging.info('Adding path %s to sys.path', python_path) + sys.path.append(os.path.join(site_packages_dir, python_path)) + logging.debug('PYTHONPATH: %s', sys.path) + + +def active_gcloud_service(gcloud_key_file_url, workspace_dir, + download_only=False): + """Download key file and setup gcloud service credential using the key file. + + Args: + gcloud_key_file_url: gcloud key file url + workspace_dir: directory that the key file is downloaded to + download_only: skip setting up the gcloud service credential if this is true + """ + + if not gcloud_key_file_url: + return + + local_path = os.path.join(workspace_dir, + os.path.basename(gcloud_key_file_url)) + if not os.path.exists(local_path): + download_data([{'url': gcloud_key_file_url, 'local_path': local_path}]) + + if not download_only: + os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = local_path + run_commands(['gcloud auth activate-service-account --key-file {}'.format( + local_path)]) + logging.info('Activated gcloud service account credential') + + +def setup_gsutil_credential(): + run_commands(['gcloud config set pass_credentials_to_gsutil true']) + + +def download_data(download_infos): + """Download data from url to local_path for each (url, local_path) pair in the download_infos. + + Each url should start with either gs://, http:// or https:// + Downloaded file whose name ends with .gz will be decompressed in its + current directory + + Args: + download_infos: array of dict which specifies the url and local_path for + data download + """ + for info in download_infos: + if os.path.exists(info['local_path']): + continue + original_base_name = os.path.basename(info['url']) + expected_base_name = os.path.basename(info['local_path']) + local_path_parent = os.path.dirname(info['local_path']) + + logging.info('Downloading data from %s to %s', + info['url'], info['local_path']) + make_dir_if_not_exist(local_path_parent) + # Download data to the local path + if info['url'].startswith('http://') or info['url'].startswith('https://'): + request = requests.get(info['url'], allow_redirects=True) + f = open(info['local_path'], 'wb') + f.write(request.content) + f.close() + elif info['url'].startswith('gs://'): + cmd = ['gsutil', '-m', 'cp', '-r', '-n', info['url'], local_path_parent] + run_commands([cmd], shell=False) + elif info['url'].startswith('file://'): + cmd = ['cp', info['url'][7:], local_path_parent] + run_commands([cmd], shell=False) + else: + raise ValueError('Url {} with prefix {} is not supported.'.format( + info['url'], info['url'].split(':')[0])) + # Move data to the expected local path + if original_base_name != expected_base_name: + run_commands(['mv {} {}'.format( + os.path.join(local_path_parent, original_base_name), + os.path.join(local_path_parent, expected_base_name))]) + logging.info('Downloaded data from %s to %s', + info['url'], info['local_path']) + # Decompress file if file name ends with .gz unless caller sets 'decompress' + # to False in info. + if info['url'].endswith('.gz') and info.get('decompress', True): + run_commands(['tar xvf {} -C {}'.format( + info['local_path'], local_path_parent)]) + logging.info('Decompressed file %s', info['local_path']) + + +def parse_data_downloads_str(root_data_dir, data_downloads_str): + """Parse a comma separated string into array of dicts. + + Each dict specifies the url and local_path for a download. + + Args: + root_data_dir: the directory which should contain all the dataset files + data_downloads_str: a comma separated string specified by the + flag --data_downloads + + Returns: + An array of dict which specifies the url and local_path for data download + """ + + download_infos = [] + if not data_downloads_str: + return download_infos + + for entry in data_downloads_str.split(','): + info = {} + if ';' in entry: + info['url'] = entry.split(';')[0] + info['local_path'] = os.path.join(root_data_dir, entry.split(';')[1]) + else: + info['url'] = entry + info['local_path'] = os.path.join(root_data_dir, os.path.basename(entry)) + # Canonicalize url to remove trailing '/' and '*' + if info['url'].endswith('*'): + info['url'] = info['url'][:-1] + if info['url'].endswith('/'): + info['url'] = info['url'][:-1] + + download_infos.append(info) + + return download_infos + + +def maybe_upload_to_gcs(local_dir, output_gcs_url): + if not output_gcs_url: + return + run_commands(['gsutil -m cp -r {} {}'.format(local_dir, output_gcs_url)]) + logging.info('Uploaded data from local directory %s to gcs %s', + local_dir, output_gcs_url) + + +def make_dir_if_not_exist(local_path): + if not os.path.exists(local_path): + os.makedirs(local_path) + logging.info('Created directory %s', local_path) + + +def run_command(cmd, shell=True): + """Structures for a variety of different test results. + + Args: + cmd: Command to execute + shell: True to use shell, false otherwise. + + Returns: + Tuple of the command return value and the standard out in as a string. + """ + logging.debug('Executing command: %s', cmd) + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, shell=shell) + + exit_code = None + line = '' + stdout = '' + while exit_code is None or line: + exit_code = p.poll() + line = p.stdout.readline().decode('utf-8') + stdout += line + logging.debug(line) + + return exit_code, stdout + + +def run_commands(cmds, shell=True): + """Runs list of command and throw error if any fail.""" + for cmd in cmds: + exit_code, stdout = run_command(cmd, shell=shell) + if exit_code: + raise Exception('"{}" failed with code:{} and stdout:\n{}'.format( + cmd, exit_code, stdout)) + + +def get_cpu_name(): + cmd = "cat /proc/cpuinfo | grep 'model name' | sort --unique" + exit_code, result = run_command(cmd) + lines = result.splitlines() + if exit_code == 0 and lines: + model_name_parts = lines[0].split(':') + return model_name_parts[1].strip() + else: + logging.error('Error getting cpuinfo model name: %s', result) + return '' + + +def get_cpu_socket_count(): + cmd = 'grep -i "physical id" /proc/cpuinfo | sort -u | wc -l' + exit_code, result = run_command(cmd) + lines = result.splitlines() + if exit_code == 0 and lines: + return int(lines[0]) + else: + logging.error('Error getting cpuinfo scocket count: %s', result) + return -1 + + +def _get_amd_gpu_info(): + """Returns gpu information using rocm-smi. + + Note: Assumes if the system has multiple GPUs, that they are all the same + + Returns: + A dict containing gpu_driver_version, gpu_model and gpu_count or None if + `rocm-smi` is not found or fails. + """ + cmd = 'rocm-smi --json --showproductname --showdriverversion' + exit_code, result = run_command(cmd) + + if exit_code != 0: + logging.error('rocm-smi did not return as expected: %s', result) + return None + + def get_gpu_driver_version(rocm_smi_output): + return rocm_smi_output['system']['Driver version'] + + def get_gpu_model(rocm_smi_output): + gpu_model = "" + for key, value in rocm_smi_output.items(): + if re.match("card[0-9]+", key): + gpu_model = value['Card SKU'] + break + return gpu_model + + def get_gpu_count(rocm_smi_output): + gpu_count = 0 + for key, value in rocm_smi_output.items(): + if re.match("card[0-9]+", key): + gpu_count += 1 + return gpu_count + + rocm_smi_output= json.loads(result) + + gpu_info = {} + gpu_info['gpu_driver_version'] = get_gpu_driver_version(rocm_smi_output) + gpu_info['gpu_model'] = get_gpu_model(rocm_smi_output) + gpu_info['gpu_count'] = get_gpu_count(rocm_smi_output) + + return gpu_info + + +def _get_nvidia_gpu_info(): + """Returns gpu information using nvidia-smi. + + Note: Assumes if the system has multiple GPUs that they are all the same with + one exception. If the first result is a Quadro, the heuristic assumes + this may be a workstation and takes the second entry. + + Returns: + A dict containing gpu_driver_version, gpu_model and gpu_count or None if + `nvidia-smi` is not found or fails. + """ + cmd = 'nvidia-smi --query-gpu=driver_version,gpu_name --format=csv' + exit_code, result = run_command(cmd) + + if exit_code != 0: + logging.error('nvidia-smi did not return as expected: %s', result) + return None + + lines = result.splitlines() + gpu_info_line = lines[1] + if 'Quadro' in gpu_info_line and len(lines) >= 3: + gpu_info_line = lines[2] + + gpu_info = {} + gpu_info['gpu_driver_version'] = gpu_info_line.split(',')[0].strip() + gpu_info['gpu_model'] = gpu_info_line.split(',')[1].strip() + gpu_info['gpu_count'] = len(lines) - 1 + + return gpu_info + + +def get_gpu_info(): + """Returns gpu information using either nvidia-smi or rocm-smi. + + Returns: + A dict containing gpu_driver_version, gpu_model and gpu_count or None if + `nvidia-smi` is not found or fails. + """ + return _get_amd_gpu_info() if shutil.which("rocm-smi") \ + else _get_nvidia_gpu_info() + + +def _install_tpu_tool(): + """Installs the ctpu tool to managing cloud TPUs. + + Follows the instructions here: + https://github.com/tensorflow/tpu/tree/master/tools/ctpu + """ + if not os.path.exists('ctpu'): + logging.info('Installing TPU tool') + commands = [ + 'wget https://dl.google.com/cloud_tpu/ctpu/latest/linux/ctpu', + 'chmod a+x ctpu', + ] + run_commands(commands) + + +def setup_tpu(parameters): + """Sets up a TPU with a given set of parameters. + + Args: + parameters: dictionary of TPU parameters. + + Returns: + True if an error occurs during setup. + """ + try: + _install_tpu_tool() + + args = [ + '--name={}'.format(parameters.get('name')), + '--project={}'.format(parameters.get('project')), + '--zone={}'.format(parameters.get('zone')), + '--tpu-size={}'.format(parameters.get('size')), + '--tf-version={}'.format(parameters.get('version')), + '--tpu-only', + '-noconf', + ] + command = './ctpu up {}'.format(' '.join(args)) + logging.info('Setting up TPU: %s', command) + exit_code, output = run_command(command) + if exit_code != 0: + logging.error('Error in setup with output: %s', output) + return exit_code != 0 + except Exception: + logging.error('Unable to setup TPU') + run_command('rm -f ctpu') + sys.exit(1) + + +def cleanup_tpu(parameters): + """Cleans up an existing TPU. + + Args: + parameters: dictionary of TPU parameters. + + Returns: + True if an error occurs during cleanup. + """ + _install_tpu_tool() + + args = [ + '--name={}'.format(parameters.get('name')), + '--project={}'.format(parameters.get('project')), + '--zone={}'.format(parameters.get('zone')), + '--tpu-only', + '-noconf', + ] + command = './ctpu delete {}'.format(' '.join(args)) + logging.info('Cleaning up TPU: %s', command) + exit_code, output = run_command(command) + if exit_code != 0: + logging.error('Error in cleanup with output: %s', output) + return exit_code != 0 + + +def read_benchmark_result(benchmark_result_file_path): + """Read benchmark result from the protobuf file.""" + from google.protobuf import json_format # pylint: disable=g-import-not-at-top + from tensorflow.core.util import test_log_pb2 # pylint: disable=g-import-not-at-top + + if not os.path.isfile(benchmark_result_file_path): + logging.error('Failed to read benchmark result because ' + 'file %s does not exist', benchmark_result_file_path) + return {} + + with open(benchmark_result_file_path, 'rb') as f: + benchmark_entries = test_log_pb2.BenchmarkEntries() + benchmark_entries.ParseFromString(f.read()) + + return json_format.MessageToDict( + benchmark_entries, + preserving_proto_field_name=True, + including_default_value_fields=True)['entry'][0] + + +def print_thread_stacktrace(): + print('Here is the stacktrace for all threads:') + thread_names = {t.ident: t.name for t in threading.enumerate()} + for thread_id, frame in sys._current_frames().items(): # pylint: disable=protected-access + print('Thread {}'.format(thread_names.get(thread_id, thread_id))) + traceback.print_stack(frame) + + +def instantiate_benchmark_class( + benchmark_class, output_dir, root_data_dir, tpu, constructor_args, + benchmark_class_type=None): + """Return initialized benchmark class.""" + module_import_path, class_name = benchmark_class.rsplit('.', 1) + module = importlib.import_module(module_import_path) + class_ = getattr(module, class_name) + if benchmark_class_type == 'tf_benchmark': + # for benchmarks inheriting from tf.test.Benchmark, instantiate them directly. + instance = class_(**constructor_args) + else: + # Default instantiation for perfzero_benchmark classes. + instance = class_( + output_dir=output_dir, + root_data_dir=root_data_dir, + tpu=tpu, + **constructor_args) + + return instance + + +def copy_and_rename_dirs(dir_spec_string, dst_base_dir): + """Copies list of :new_name specs into a new dest dir. + + If a path /path1/path2/dir:new_dir is given, it copies /path1/path2/dir to + dst_base_dir/new_dir. + + Args: + dir_spec_string: Comma separated list of /path1/path2:new_name specs. + dst_base_dir: The base dir to contain the copies. + """ + if not dir_spec_string: + return + dir_specs = dir_spec_string.split(',') + for src_dir_with_name in dir_specs: + src_dir, final_basename = src_dir_with_name.split(':') + dst_dir = os.path.join(dst_base_dir, final_basename) + + if os.path.isdir(dst_dir): + logging.info('[DELETE] pre-existing %s', dst_dir) + shutil.rmtree(dst_dir) + logging.info('[COPY] %s -> %s', src_dir, dst_dir) + shutil.copytree(src_dir, dst_dir) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/utils_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/utils_test.py new file mode 100644 index 00000000..978a6aa0 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/perfzero/utils_test.py @@ -0,0 +1,219 @@ +# 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 utils.py.""" + +import os +import unittest +from mock import call +from mock import MagicMock +from mock import patch +import perfzero.utils as utils +import tensorflow as tf # pylint: disable=g-bad-import-order + + +class TestUtils(unittest.TestCase, tf.test.Benchmark): + + def test_protobuf_read(self): + output_dir = '/tmp/' + os.environ['TEST_REPORT_FILE_PREFIX'] = output_dir + benchmark_result_file_path = os.path.join(output_dir, + 'TestUtils.testReportBenchmark') + if os.path.exists(benchmark_result_file_path): + os.remove(benchmark_result_file_path) + + self.report_benchmark( + iters=2000, + wall_time=1000, + name='testReportBenchmark', + metrics=[{'name': 'metric_name_1', 'value': 0, 'min_value': 1}, + {'name': 'metric_name_2', 'value': 90, 'min_value': 0, + 'max_value': 95}]) + + actual_result = utils.read_benchmark_result( + benchmark_result_file_path) + os.remove(benchmark_result_file_path) + + expected_result = { + 'name': 'TestUtils.testReportBenchmark', + # google.protobuf.json_format.MessageToDict() will convert + # int64 field to string. + 'iters': '2000', + 'wall_time': 1000, + 'cpu_time': 0, + 'throughput': 0, + 'extras': {}, + 'metrics': [ + { + 'name': 'metric_name_1', + 'value': 0, + 'min_value': 1 + }, + { + 'name': 'metric_name_2', + 'value': 90, + 'min_value': 0, + 'max_value': 95 + } + ] + } + + self.assertDictEqual(expected_result, actual_result) + + @patch('perfzero.utils.get_git_repo_info') + @patch('perfzero.utils.run_commands') + def test_checkout_git_repos(self, run_commands_mock, get_git_repo_info_mock): + git_repo_1 = {} + git_repo_1['url'] = 'url_1' + git_repo_1['local_path'] = 'local_path_1' + git_repo_1['dir_name'] = 'dir_name_1' + git_repo_1['branch'] = 'branch_1' + git_repo_1['git_hash'] = 'git_hash_1' + + git_repo_2 = {} + git_repo_2['url'] = 'url_2' + git_repo_2['local_path'] = 'local_path_2' + git_repo_2['dir_name'] = 'dir_name_2' + git_repo_2['branch'] = 'branch_2' + + git_repo_info_1 = {'url': 'url_1'} + git_repo_info_2 = {'url': 'url_2'} + get_git_repo_info_mock.side_effect = \ + lambda local_path: git_repo_info_1 if local_path == 'local_path_1' else git_repo_info_2 # pylint: disable=line-too-long + site_package_info = utils.checkout_git_repos([git_repo_1, git_repo_2], + False) + + self.assertEqual(2, len(site_package_info)) + self.assertEqual(git_repo_info_1, site_package_info['dir_name_1']) + self.assertEqual(git_repo_info_2, site_package_info['dir_name_2']) + + run_commands_mock.assert_has_calls(any_order=False, calls=[ + call(['git clone url_1 local_path_1']), + call(['git -C local_path_1 checkout branch_1']), + call(['git -C local_path_1 pull --rebase']), + call(['git -C local_path_1 reset --hard git_hash_1']), + call(['git clone url_2 local_path_2']), + call(['git -C local_path_2 checkout branch_2']) + ]) + + @patch('perfzero.utils.run_command') + def test_get_git_repo_info(self, run_command_mock): + run_command_mock.side_effect = [ + [0, 'git_url'], + [0, 'branch_name'], + [0, 'git_hash'] + ] + + git_repo_info = utils.get_git_repo_info('local_path_1') + self.assertEqual( + {'url': 'git_url', 'branch': 'branch_name', 'hash': 'git_hash'}, + git_repo_info) + run_command_mock.assert_has_calls(any_order=False, calls=[ + call('git -C local_path_1 config --get remote.origin.url'), + call('git -C local_path_1 rev-parse --abbrev-ref HEAD'), + call('git -C local_path_1 rev-parse HEAD') + ]) + + @patch('builtins.open') + @patch('perfzero.utils.make_dir_if_not_exist') + @patch('requests.get') + @patch('perfzero.utils.run_commands') + def test_download_data(self, run_commands_mock, requests_get_mock, + make_dir_mock, open_mock): # pylint: disable=unused-argument + get_mock = MagicMock() + get_mock.content = 'content' + requests_get_mock.return_value = get_mock + + download_info_1 = {'url': 'gs://remote_path_1/name_1', + 'local_path': 'local_path_1/modified_name_1'} + download_info_2 = {'url': 'http://remote_path_2/name_2', + 'local_path': 'local_path_2/modified_name_2'} + utils.download_data([download_info_1, download_info_2]) + + make_dir_mock.assert_has_calls(any_order=False, calls=[ + call('local_path_1'), + call('local_path_2') + ]) + requests_get_mock.assert_called_once_with('http://remote_path_2/name_2', + allow_redirects=True) + run_commands_mock.assert_has_calls(any_order=False, calls=[ + call([['gsutil', '-m', 'cp', '-r', '-n', + 'gs://remote_path_1/name_1', 'local_path_1']], + shell=False), + call(['mv local_path_1/name_1 local_path_1/modified_name_1']), + call(['mv local_path_2/name_2 local_path_2/modified_name_2']) + ]) + + def test_parse_data_downloads_str(self): + data_downloads_str = 'url_1;relative_path_1,url_2;relative_path_2' + download_infos = utils.parse_data_downloads_str('/root_data_dir', + data_downloads_str) + self.assertEqual(2, len(download_infos)) + self.assertEqual(download_infos[0], + {'url': 'url_1', + 'local_path': '/root_data_dir/relative_path_1'}) + self.assertEqual(download_infos[1], + {'url': 'url_2', + 'local_path': '/root_data_dir/relative_path_2'}) + + @patch('perfzero.utils.run_command') + def test_get_cpu_name(self, run_command_mock): + """Tests extract the cpu model name.""" + run_command_mock.return_value = [ + 0, 'model name : Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz\n' + ] + cpu_name = utils.get_cpu_name() + self.assertEqual('Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz', cpu_name) + + @patch('perfzero.utils.run_command') + def test_get_cpu_socket_count(self, run_command_mock): + """Tests get socket count.""" + run_command_mock.return_value = [0, '2\n'] + cpu_socket_count = utils.get_cpu_socket_count() + self.assertEqual(2, cpu_socket_count) + + @patch('perfzero.utils.run_command') + def test_get_gpu_model(self, run_command_mock): + # Tests get gpu info parses expected value into expected components. + run_command_mock.return_value = [ + 0, 'driver_version, name\n381.99, GTX 1080 \n' + ] + gpu_model = utils.get_gpu_info()['gpu_model'] + self.assertEqual('GTX 1080', gpu_model) + + # Tests gpu info returns second entry if first entry is a Quadro. + run_command_mock.return_value = [ + 0, 'blah\n200.99, Quadro K900 \n381.99, GTX 1080\n' + ] + gpu_model = utils.get_gpu_info()['gpu_model'] + self.assertEqual('GTX 1080', gpu_model) + + @patch('perfzero.utils.run_command') + def test_get_gpu_count(self, run_command_mock): + """Tests gpu info returns second entry if first entry is a Quadro.""" + run_command_mock.return_value = [ + 0, 'blah\n200.99, Quadro K900 \n381.99, GTX 1080\n' + ] + gpu_count = utils.get_gpu_info()['gpu_count'] + self.assertEqual(2, gpu_count) + + + + + + + + + + diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/setup.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/setup.py new file mode 100644 index 00000000..63406092 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/lib/setup.py @@ -0,0 +1,197 @@ +# 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. +# ============================================================================== +"""Checkout repository, download data and build docker image.""" +from __future__ import print_function + +import argparse +import json +import logging +import os +import shutil +import sys +import tempfile +import time + +import perfzero.device_utils as device_utils +import perfzero.perfzero_config as perfzero_config +import perfzero.utils as utils + + +def _temporary_file_name(parent_dir, base_name): + """Returns a temp name of the form //.""" + if not os.path.isdir(parent_dir): + os.makedirs(parent_dir) + temp_dir = tempfile.mkdtemp(dir=parent_dir) + return os.path.join(temp_dir, base_name) + + +def _load_docker_image(FLAGS, workspace_dir, setup_execution_time): + """Runs docker load --input_image . + + Fetches FLAGS.dockerfile_path to workspace_dir//local_docker first. + Runs docker load --input . + Deletes workspace_dir/ after the docker image is loaded. + + Args: + FLAGS: parser.parse_known_args object. + workspace_dir: String - The path to use for intermediate artifacts. + setup_execution_time: Map from string->double containing wall times for + different operations. This will have insertions describing the docker + setup time. + """ + load_docker_start_time = time.time() + local_docker_image_path = _temporary_file_name(workspace_dir, 'local_docker') + utils.download_data([{'url': FLAGS.dockerfile_path, + 'local_path': local_docker_image_path, + 'decompress': False}]) + + setup_execution_time['fetch_docker'] = time.time() - load_docker_start_time + + docker_load_cmd = 'docker load --input {}'.format(local_docker_image_path) + try: + utils.run_commands( + [docker_load_cmd, + 'docker images' # Print loaded image list. + ]) + setup_execution_time['load_docker'] = time.time() - load_docker_start_time + finally: + logging.info('removing parent dir of local docker image copy %s', + local_docker_image_path) + shutil.rmtree(os.path.dirname(local_docker_image_path)) + + +def _create_docker_image(FLAGS, project_dir, workspace_dir, + setup_execution_time): + """Creates a docker image. + + Args: + FLAGS: parser.parse_known_args object. + project_dir: String - The current project path. + workspace_dir: String - The path to use for intermediate artifacts. + setup_execution_time: Map from string->double containing wall times for + different operations. This will have insertions describing the docker + setup time. + """ + # Create docker image + docker_start_time = time.time() + docker_context = os.path.join(workspace_dir, 'resources') + # Necessary in case we don't have a local .whl file. + utils.create_empty_file(docker_context, 'EMPTY') + + # Download TensorFlow pip package from Google Cloud Storage and modify package + # path accordingly, if applicable + local_tensorflow_pip_spec = None + + if (FLAGS.tensorflow_pip_spec and + (FLAGS.tensorflow_pip_spec.startswith('gs://') or + FLAGS.tensorflow_pip_spec.startswith('file://'))): + local_pip_filename = os.path.basename(FLAGS.tensorflow_pip_spec) + local_pip_path = os.path.join(docker_context, local_pip_filename) + utils.download_data([{'url': FLAGS.tensorflow_pip_spec, + 'local_path': local_pip_path}]) + # Update path to pip wheel file for the Dockerfile. Note that this path has + # to be relative to the docker context (absolute path will not work). + FLAGS.tensorflow_pip_spec = local_pip_filename + local_tensorflow_pip_spec = local_pip_filename + else: + local_tensorflow_pip_spec = 'EMPTY' + + dockerfile_path = FLAGS.dockerfile_path + if not os.path.exists(dockerfile_path): + # Fall back to the deprecated approach if the user-specified + # dockerfile_path does not exist + dockerfile_path = os.path.join(project_dir, FLAGS.dockerfile_path) + extra_pip_specs = (FLAGS.extra_pip_specs or '').replace(';', '') + docker_base_cmd = 'docker build --no-cache --pull' + # FLAGS.extra_docker_build_args will be a list of strings (e.g. ['a', 'b=c']). + # We treat the strings directly as build-args: --build-arg a --build-arg b=c + # Empty strings are ignored. + extra_docker_build_args = ' '.join([ + '--build-arg %s' % arg for arg in FLAGS.extra_docker_build_args if arg]) + cmd = '{docker_base_cmd} -t {docker_tag}{tf_pip}{local_tf_pip}{extra_pip}{extra_docker_build_args} {suffix}'.format( + docker_base_cmd=docker_base_cmd, + docker_tag=FLAGS.docker_tag, + tf_pip=( + ' --build-arg tensorflow_pip_spec={}'.format( + FLAGS.tensorflow_pip_spec) if FLAGS.tensorflow_pip_spec else ''), + # local_tensorflow_pip_spec is either string 'EMPTY' or basename of + # local .whl file. + local_tf_pip=' --build-arg local_tensorflow_pip_spec={}'.format( + local_tensorflow_pip_spec), + extra_pip=' --build-arg extra_pip_specs=\'{}\''.format(extra_pip_specs), + extra_docker_build_args=' ' + extra_docker_build_args, + suffix=( + '-f {} {}'.format(dockerfile_path, docker_context) + if docker_context else '- < {}'.format(dockerfile_path)) + ) + + utils.run_commands([cmd]) + logging.info('Built docker image with tag %s', FLAGS.docker_tag) + setup_execution_time['build_docker'] = time.time() - docker_start_time + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + perfzero_config.add_setup_parser_arguments(parser) + FLAGS, unparsed = parser.parse_known_args() + + logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s', + level=logging.DEBUG) + if unparsed: + logging.error('Arguments %s are not recognized', unparsed) + sys.exit(1) + + setup_execution_time = {} + project_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) + workspace_dir = os.path.join(project_dir, FLAGS.workspace) + site_package_dir = os.path.join(workspace_dir, 'site-packages') + utils.copy_and_rename_dirs(FLAGS.site_package_downloads, + site_package_dir) + + activate_gcloud = False + if FLAGS.dockerfile_path and FLAGS.dockerfile_path.startswith('gs://'): + # We might end up doing gsutil fetch later, so need to call + # active_gcloud_service(). + activate_gcloud = True + + if FLAGS.tensorflow_pip_spec and FLAGS.tensorflow_pip_spec.startswith('gs://'): + activate_gcloud = True + + # Download gcloud auth token. Remove this operation in the future when + # docker in Kokoro can accesss the GCP metadata server + start_time = time.time() + utils.active_gcloud_service(FLAGS.gcloud_key_file_url, + workspace_dir, download_only=not activate_gcloud) + setup_execution_time['download_token'] = time.time() - start_time + + # Set up the raid array. + start_time = time.time() + device_utils.create_drive_from_devices(FLAGS.root_data_dir, + FLAGS.gce_nvme_raid) + setup_execution_time['create_drive'] = time.time() - start_time + + if FLAGS.dockerfile_path: + if FLAGS.dockerfile_path.endswith('.tar.gz'): + logging.info('Assuming given file %s is a docker image to load', + FLAGS.dockerfile_path) + _load_docker_image(FLAGS, workspace_dir, + setup_execution_time) + else: + _create_docker_image(FLAGS, project_dir, workspace_dir, + setup_execution_time) + + logging.info('Setup time in seconds by operation:\n %s', + json.dumps(setup_execution_time, indent=2)) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/screenshots/profiling_overview.png b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/screenshots/profiling_overview.png new file mode 100644 index 0000000000000000000000000000000000000000..8a21129232fddd840595e2d5bb9bbe5eb8d5c5d4 GIT binary patch literal 373190 zcmeFZcTiJr-#3UA1qG!Dh=9^NNR=9-Nbg;mNE4AFy$3|#2U4Wh5Ron=(t8k*UZSA% zE)XC<=%KS0KKK3Xqux8SJM+%$%)6UELUPW@IoI{=LrwlV!EFLOJiP0Q3bI;w zc-M~b@GeeXxeSh=Q^%sfpNlTiiaJ-o%lC@qKj1r+tDJ$WwxhMHhq<#Ao{fW}y%m>> zg|n5FgUd5VSL}r*Nj$u}c#5(Qbv)BoCVYI!cVMR*xSn*Qn#(0qHbkjpfv zvS^h3+?aRq#pNo8Y9rg#st1;*S164qI2$R; z_Rg5(%3BxM{_*{{F3jlkxjAoXPYJqcaq%q}*0%{Ray86Ld;u2ya@tztJXq=IP=o@?I{yuzg^E~A5qf!v* zY5czrFUns5Jo)Q&eC2<^qQ4|z|Dx0MN7>F7tt`guKHEAeF^x)hpzcWJ^9NjZn60v) z7OOR1Rb;hZ$8&=OY%JdPjh~--oi|M`{<%C^$$gg;N#(!^yLm@{RpbT4d_Cj4sW-$K zABMjcghxoK6VPcq*@2+z8Sx|n{-Rt!!yDrx2*HYBVKdYy6{{7%FnB^*qW@y}+9mjb z5>olxS5<2y;h)cRa(&~wi6#&r**#Zh*MZ-p_hiaj&gQtuH|hMlw7+TCktW-jXL-PN zeNt%{fAMM%WG^A&RkKn=)7_~Yyv56Zb7+%Ua)$2`_0U57wI@Ar5!sFR z_4?mJHm5pUQ@&+#p7-uZJ3J0cg6DeSe}M%2KNDNCzo{N@&Ri#jjFMXpr&RxZ%j7!V z^p!_{i?OJkPKu7WJbOv}qeLrsb-muGF_*oW%#|y6Z~6bG^xMMDt(*Z0WtlfFX^=7% zQM<6ix>{3jx6%qPL+tm6Bw+(&gInKu&{#3X%W;bLh& z9^M6Fn!mL6odmgZPN(h&3OjjME;W0iat#_3>|g_bk}2TqeQ=c7+MiRH)B8iK_mwI8 z?N8snP-=3z?ClkyR~xT8-^Lu4ykmH6sKdRjJ zQT9d*f<^IFMe(Vaa>V_L;Z-65q}FW@ByN0;-Bu@Mdy!L^^&+0hlL%J5X5lK=2Z(tz zlf2tvJ12XbpjA{+zfy`E6%s-B)DfZk)UT3c$8PiXdHVGW+Z4I#Z6AM1$n*t9XAd`v zlmDTMPGsG;c5NNr4stsON!C6qNfP3I2~lFtNm0(uWHWj2D6#hN#=*I-a@OWA`?g5r z&8OS-CCyF-3?`RGNlx9Adi2P=l)zzhcK;)D zPoIb3AIy-?Q!$74c4r^HaUnjesIo*HlEXL|txUqh2Z6zdy^4p1(Zb_{61YqM1`( ztw=6hpxfa?QlmxCN(eefHe3Dq9~;N3D|%|{J)CD9SW#PNGl(dNts1I1ydzZBBT-`b zmET7}tC@0XWh}1U?!+`)&?dv@b8G+H{u;&)s{}nZ-5fVm&VFdYZ)Ve;y-sV3yS#MV zo}Ea{k5V@I8CjoW54-+=N^8Tazf)f!t1Cw;(}%}LND<4GGU2wF$MGK8i8<(y`o zw+`uZ)HUEYsaG@b1kzc|F&Yk(ERM*@1d{~9CC#simDs_XoZ@CNNo;k?dTc$!e1EWP zTZSp2e~uk%pdR8!B-UX1e9^`^VSA9z)<_k#GDEj~*^=n6TfZFNpMZr0z2^IadXMUq z%67Pu25TdqGqm>B^q|DD+Lr8XUE5%u7{%Vz$k-EyVCuRJN{;Jr)`XroYhQWqS-yh( z#__>)cZ)8cpEdWg7q~{y#9X_`q?9j4*+c&K8jt*NjZ#S|Oyrvhy-B)S_bg;z4tl9s zG?KV+kVCg_ju<^J>)T4u@1)c$8vf8KVQL3`ax1}2ynu8o%JNaZMssi>|D@e}|HzfP zgz%x__yGgBU*yf!%>=zJ&f<7oUnDvm(V(<2%qO=7W6DdqU}=pgA<8Bo?-Jg6L_WS3 z5@Dg<>mm3DVWjF(s=cCxdAhk)_&=!Bxw>s;**{~A>;18IP@r`|>R6MWzLl5}q7}qt zEhV~2-7m+%lojf&O%@TefLy$OSmDdM>MJMuQBXu^R>yYoP5(dIqNe;BtmG&~D&E+M zNaxw?aVV$A3dh?nB$G+iBleE|n^#x)2;PuCu6djpTsWVm`NLsYUX-lofnAI=p+1M_ z4gpp0(w5)6Er`dPHSdw`IUY&PUjeu1GX|AZfSQeuez(gkesCNr~W8 zS+HMv?q#_(cwvVGd3fW;?1_)jbC)}x@^EU-We|Yl9C-!JE|Z+>+DJ|gN$eIVY(AMS3P2B zP@Xwh3$s|a{!1b&GvOsCndvY`h1l~+6F&~tZB}?735}}>gI)z#&6W9jE62{_dN~_R zSW~HS*pNSb%NLK0S-%!$Ol0Tq_W1|rdLa$g$U+5LN29ngH$mMsHyL7ykC2Mt;;cL| zf}Qv#UtRC5A+`&-Iu8_6HzaIlE7!hWrBKWpzuV|9kHd)_?poTsQ^?ZUk}L4fVQi=2 z5LHjD?D3nD&dS+>autiq$HM%2uj(u6b%+VsBDcGWJKe@yo}{@>ZG;kCvt8fl?}R6* zHH{pLQf^&c`i!3~4;k^NSzqrp&i=$y?IwPou&_ewQcglRx7oYBgXbQ-3=%8aTA9;7 zI2aXAloMHz$j4zj-SNX(3=BH?pBi#t1dEixtpt8nzx2TbDWz7UsHYfQw7I{TpkLYd zW?W1;%w3duM?dAWE*EaU>}84EY+>vb}RTzC<08MsGdYbSg5Tznclsrd(F!_?d)d>>^k`F*xcOJs;1bh&E=MAh8}}@%BGXwh@m|h-r%|7ACd_#ojTPO! zKT$okw-otIk%fZm7C_o5`5wiN+c0ULhX65{oyc<`I&!U|^+AbU^baTzZR{;CwxihT zMcPp)mTPCUN*pJiQ12;PD62Xu6(aBMhbpv&e#X!9tvw8c-?vd_p*uN2D?n3O>=T@d5FS;IBv0>AX zudbMYQVukC_{VM(qL}7@o%5E@t1=JQ39FO0#O5ae{{{F2tdq57N>8a(nG!m?m$=7> zy?oDiWXCRQ@nbaOzO`2q%YIkk90wwHK!+!opnlxEjlEcBF_(aCJieBrzsM-6=9Bu9 z$^MFu>aMNlTZeRQ(j6mM6CJzm0Ar~IJcV%Qq*m5mTD0W~9^gAuJ@QdtZ6|`QJYOZy zOPSmRdDq@wy+b_|?m2wik(Mb z%NJ+njMU~~`6INwpea7Pnqjfe#No8)CWS0-y7zNfo2(YwUu5D=CdMifTF053IH&CX zls6ipeQ5|%m{@PqHFBJYt82E-_F3nK=5!x3R0N5iEx;Wb~J=d)5HRLLlE zq$%(FMBKHbJN&wQ7l1WwBO*m8AxoeF5-TpllhrwOX3E-4%e3k>>^{>rU)cH=`NxOq zg%v7K@A+-|<2<`wabNV;5B(A zLF|0W9$?-j@=-f{1#|GY_*%8!vnL7Z>G1W8OP&Rd zUth-ZFrud&3ap~*w*6T=k}gNaYdS@cP4J*U$(w*A@8lt0NK_oD!*fXb0v)5pvzSAX zsng84?HL~L-xU5U{`*s$ZmZd`oAW1LtBCY$saBk&YLi~9rD}r$lHWn&D5qzF?(teO zw+%|HlL_3@@j!4Bx*6)Qmp4s$iTj{{zNxXt#bkY;nx#dOD>A%jZ#FaZ)r3u7&jy;! zbDxijAv~u!i#+|%E*R>jHfS?KlIEY+L07UmO8t%Sz)0d#03Wj7XZ&mDM^x4AC5J4` zp>UDPWq-zMnVy(id$uDp61&%9-%ngQicsIDJ)M+UMNMov@vXZYz39BAP<+pneygSqbH(0ohW>N`noz9|Wf5S_{KVjXJhdAfVec^EEQHTQ~DfBXKNpg-2u2=aW3A z3il4Is9GfdD_$88Jy&@qQ$?}B1;Y~DPr*(2-f)PURRg{Ku~{BlG9`8Eg6O&htw+ca zzhk`M5=p@m*-cs3CK9Y<)?T*<+s1Aj6=KJ4)@~Dq{J1mhkF%A7ALZ)sYMpGI$RTa@ zJ1G67H(v$SlR^DzpB6${^Y$HAhk$UNrkKS5C)@unrjyiNdRHF7R=R`>Z}hFER#{ef z_WFqKG`y$)o&-Dt^*fO-BZo*5M~Tc~!s^bEN)Zf6VE0tfRDDYKE6s)ob$LfuL{ETOPIc-Z9GkH2))`blrq5 z>Hd3zz&B8CyIHwnEVLU6qqnhvp-ug4d<~HUh$In*M_uQA5{8fR-xoUcuG0P{tJ4Z| z!_MxF1s!zAcTb-DQ>2B<^gg9B>$|sUJ zi4PrQ=>_T5(%9`fJIgMa?TuS(b=4TVl!vwoU0b8|gVhiyu5HeagY~+k+H-%DSo^vn zDRC$>&C&#Z&fddmI7o72by z!Nj`0;Q(SI#E+4US5|N7&LxD8baSD;!pTEhWy+Se&O2g_J1vwhsXeiVw=15NAEA8* zsCQ$sA7;&o{Zu9Z0V`E|MruyU`AZOwCJ@PXryhtn4$H@?Cb>+4^V+n&+)oKPOluTzUwCb{Blk!TaSTUz;gCAb8t7O0HfKBI_$@!+s7)oIND5s7 zwUFEVwCR(9-h#R7VlZD8SBvpVxPi3!=BM;cFV+%I!lc-w3m_O3#h+k^clGmEs3Y4|G;zH9+Ajj6N2ofcDJzB8qjHCr_4KfMhd*X_;1YZqiV6&rc@C$A(Q4_Yb-OF@Am5vfmv(xtD&u;ZroZ z#^UH{95#YyUZ}A`D-%QOVU$}bjCfWjZRW`-kv$M{-!kGAydENP$0C`)Nz4uecWxQR->DXl?NZURs!?lzEdGuc3na;u%sWperPJBcRsmo)O_y%Y2E%8-m zSFsyMM?qRn&qDY^CpkFYeLZ3sCz4y;xHNxSf7($X`I~uo5G&Fv)^LI9q9qQMc?6UrEpY>(>NEBy=&5xL!_$bXP?ul{FK6F+n&&>pso;QV&fe9bs{CEdYkAJ5F36N<#Of+)?XAZ?tGl_3ji0 zkjq4`qxP}%@&r$`dfDl2_#9!E=nN@eJACZqcQN9X@}zwoQK>M~2&u6g&6oxPxEbgPaFqtnz zY>O>fJ-W@%ovp0T5^Ywi>+SXuJveNF2?^H1jQ$9I>^%@hQBODWB{k=VZhwi}#25?H zS2jgH7tiepg_O8wq&Em6W80vke)jD-9#FVAsAl146O~RUB{Gbej(5ah+#-gBQ}sgq zd!qtv{}h|h2iM>7q*gGeQ!K;SWvfj_p9R6BZ^+E_VeZ_T)mmY737a6+J)LFZ8x=2v zYfBvKj9un%PXtMfArsms>ZhSI`G)SBdAOIwAZB#Uh8zy(tNz@Zy8ZSX`QH6EUGs-~ z`r;O0!l(Hi*phWdQHw(8qB|<(Xld|1Ag^_qMoJ9L$* zoY`dM`cZ|V8z=#hnR;jD*&ENUvRGk<-+i)KQ&77j{uuZpCZX{mQL>utq)9r1uzY%A zP@xxYT+Mna2+_)4MrAm~oi{fKo^*dsbuEp! zH(BM?_AZ7mT10VhPI;hi`b$Qw+^$by|KL@f@s<5x<=CgFllrIXQ!CduiyjwEqv_rX z?(-MREGSZRq`K#jP-7iwK!qwN*qNT#xa7;ishvbs^>(HsrytW*H5qi%cB<8& z+7eSDJRUK9p7dDkyXj#Cvn@RiaJQVQDpztn3~%Hlz6Y(s)gxn;M&EL@Ay$oz66Q-YIZ!Ehxu)u2(-fBnEJnk5t97wt$!TLKQkDk&Z}CJAj$=SA9Q|NYZMxmz z`PpVw1E?KO5PYdEMKt+J0ph)bce3*^)R)XM|HIJYLAr?isgzfgb(vnTO!U_#Uaf zpy6*<+3LA3?=@D}iqW$^I(Op~1Lfd7WYKz}usK>fU~~auy|K^N4qK~bK0sr0g8djf zZGF{e4mot!6Wlrj-p(hTraQ793`4Ats201Dz4x=2HG2;=cC!&Ttr0hxK9^l+{;>9x zUW2>gLFXrpeaT%Wl*ZF7i}2(9dMbi(L`M+ZLq?Ph=5-$JC(p$eNVk<7C*D*6WG3UI zdSQoTt681)iTZJkTL-0xCIL|#{bAW;bfZPqXPl10Oxql7sOdqjS=``rp@T2N;^y$d zL*GM1IXbiKPvbsiYnOiWfbs;O`~o)pOhU-_9#wn#iaWZLNa6+TAGWH4P{W}sCXLc+ z^s}_d{wWOM6E<~3FD1QYzS;P{9nX5#E3;t_r|Ds&b?}S&dd12=f0B}4^+X7D(K~T? z?_Ij&wWAJ*!lN{s0WGSNSIELkez{_qbe|MbkU0{1Q;6Al`o?E0-LWhm2)&zGn zlwGZrQCnr9btGj&h%vB?9?NMbsBI6{&OTlhkf4-o_VIN}!+CDLn#26$ev7Pu96km~ zT06@9f(p!PFZ*U5b9CCWXj2=WO%zw0<^$x;15KPlQr+RTFCgL#;}Rs8GRy_FHlrXG zL-ZhGiFd`t0El1FCcC%ut^_inov+#=|N6SA)D}cJYJ0@~mJ7%k{zznXYy^9qC zg=v;Mb4!*Z7cK*NB*@J8HlLt=|duc}K zkjxxR>lW8;&% zCQ~1UL0f$$95;-il60@M8u`88qq@z5-Z4fI2VRa!i|BD~VOv6ACCuSEgPj!K_(W?C zI6u|zkTh-BZxZo!3PfIsf=jb2r1ZVvoQT*)Ka)YWw&a2gd6bbz6bUu7u!B_COBC}U zEZ-v|rG+7u%_ZIVy!3HfDAmfpu>b+(IKL4ivp#>Mmk*s@5b}G%nO#vE*2PHBj+7JI zql;FN_L$|OXKCmsgl$SQocCIre`=MCrFlg$wi#4BluS)Gh-2Ym6b!k*g^wiA( z6c6uca@PB+_6=?0)|HF^3qLox%0Tp%(u=lcVJobpP;K!reKEY$iQ(ZuJdKF4GKHskGO=?`K5z zwM4%e3?MW=MRAn|h{HPzC#FejEL_yl?*Xq?h_VlmJf&dIOYxb^!djGt@51Vvs zh3gC<_f2KOb%j{php0?Losv)U-JqEAC}v8p4zf;MIy#<4w3c}Mu`>0S(D z;huOae}%59#ig8SUaHXN0$O$KOoR@na0MT%?wFcN7%s!sp62&{xjal(Ww%e1Y0_VM zg97`u+|jOn00wg_zDL`lf7jwWBMwv;=%b+%XPFnM*kxESJZLz^=PiDRrmC(Qs!lvU zXbC@=(k~zKdSc^K{q5(<0h(;TrP8mCazimR1hGemg3;_;EASUKm#5OH*l;pqG0b5m{_Hz9zW4`&d1oi<2D_^Ef+@Y>!Gp z#Gcb>1C?DBc1#Aksg3PH@;FbE(AMYvjAphuc|nl~U!?E+*1UjYl1bB}umg0wCl2N8 zZgYChHrMrLEaPDdC&Wp@MJOkdilGeOzm9%$-A(C*H}COl@_0iX?o*Meco4dF&Ri`A zidp@h*et22V71Y2yH4~4gXc5@&SJf^(@pD@Zb8HhOPBERZ9`J)?mPUdw*|^t4ws2Q zkeFd{m?iA(_B(83k>`E1pX$cw6eB*#eKz-Z+IFw8^(uWC9^)4V5U=FYtRh%rUdfQ> z*BE|lr|{x=HuCf2oV{5`qvbXk)2@)E$rPnXv&)KrW4HKy+Qf3GeBN_>Z*A@~n`~0y zBU47$)4^x^tn|C_l*b`x)4A`BG-l%8s>}&#&kh@^38gbyp$#1q5M#~Yh)hcmpDcTi zzq@u0D}FZtDt}w@+jTC#Wn90uOOAmOn!bev=8>fHpjCnhn`qj^?=`utOo~vWZQ7Tuq!bF6xKZeb0gn|ruf-b%)mG$H`y5D%M7glZb==pTsYGaDzKF?9< zkquXDkrahvcAN?{1f?4yaQ`=ZdQG-8(E;MRWZq6@!U2hhaSH^7%+wPp(6cefmyf=O2o9SZcM|I%_U9R2+E-zy(HhbfKkHaLL z+@S$d(_{St%ZxdWw;}1QCi}VD3y7RH(N2i$ZGP=kCoAWZ!MvPe=yFP9d5UYXz=6dG zs@j*aaq|M(&BQ@t9+NsHKF*~Y!fX!V`iJipybGocmaRgh^<_@#v(Nl<4u{8itLK58wY)aDS~wEy6EoT&lm+rbbZ7VWO}k?lGT7wAg6n zb&YLHsq^yD74Hm$kx}Qj61((G=c(S57`)r7TaMy!D0fu9JPeGgd~Or8=o?Y}j7a6U zq}<6?6US*pu%TEFh*!1WZR&&arbZ0DBnzltu7!PHN13m>@{0EG_NWBG*c#=(=$(E& zvxRoc=vc0%(MdBR9JAtcA83XTKUu@F`C09|zGq}B1GGRzN`uyV?zMLT8$Q0fq7l+3 z>s}N(c!qfU?^i&Ux^Ii)90Uu(za%9+sX)S0-57{(Ge675flP5A-P95xxyhEJmU0MW z@WsI#WQ$I##Avdx@9=e5Gtxy2PZN$6ZFY_)hT-q{fueeCym7?7Ke%x`(jL4vKQ6Rx>FD(^B{F5_+- zP7{9=6l)Q9PoiAtznOOS;ZEcoBM^{~(xx?F#D39oN z-5L{*s_ln2GPC<9upbvLS7&7Oujg-TqzL@&l6qpoh8EQrC#|L@CmVvo#*b*rGWmF$8T2EXMKT{^;f?+-SYwXl38^Lj{Kxbig1KRt#{f zU3UaU?0X!>`+KPuZ?oJ?$g9>+(r2>4;%C%a6X9|-G6nVwu(WKr1?7FhYq6G&1)6is zC=Lt!4BCa2F=mUo*Mk-yb@B02|BY*u9V7?{r_Y#Z%1>*B5Vq45IO}E;1q;iUmhhq) zQviZs9EkPmzr=1(VHgH#QVvB)_vZ}u)sBlI;?c|W(LAw4Ys*_c8wn;HTQ$*E64*O? z3Z)<=5ptk~=AHou)<(k)Y!Ze&q1AR@V(uZ4b%J|AwCl@H3+P7#75N=8X`fIk*b8V- zTR~w^_RL8WD4#&{}%-H?f#rjP$k7ui9Vw;UE;_$4tl zim;Cqq#=V*vY=iM3b!x4^Csk@=iAEO@w`!TaPqWEY<&C(ZIb#qt>B`!o7u4?Ph)94obeNop{g=Dh`ByIppa` zBH1fdJFyHsG$OV3!KXi*Tk$U>^ypW;)ZX!ox=-X|`HtJ{F~ywNR>+{m&*V5OvRmD@ z17%#gqm7E7FjjW>c{{I?^o^sphTlyPK>8ed8-k>@um)BY^c&*??mP&W@GE~cB*Cp% z#%#)1o-OU(6_gL!E8#~A0|ch~K5P$E+TNCK!V)t1gJ|@(qlH;Z??H$i(^}EpFVMAo zHmG{43y`*su+7Ny;h6)_|K|qCLKyPF$h^(7Qfj;phF^Vr^f=vcB1N^$#aTnc4)XBE z#u_*OG^SjK?@E*+;GDOC*`*~RjyVI+c{!OD}B-&A0?c3Npcsz zAAI^$SYX)Hpj9~QgPSRLghu8o0LH0%`Y=zL*N?3f`PjNkVqb}a+{auoX{cO`$^oE` za(@iO_0XDdMW_4Gb?h%*MCB%nusZxT=f0tHVkaf;-QV?8XH;D64Bf4DI70K-0bA zuscPhu0kI78{{}3kac61w2V5#PJ*cCMDZZqBSWj3r<)vcY{usOuD3Ip#8L9Y`UF2Y zbSGnyo8nWfkl^lj_yF$*+>rnt`SWgqDWsu`SW?b_)uvXb+nesd7&UQi-Y9(#eii7WC z+A?{qhaN`0ATt(ZIQ!hdj6A5qovHwY!|A?INR;;CGC9E%L_a)RUL!Jw6m$~q zrC8)~h`OfFqE0LHmz5e$8+>h;pzG8ON|rD_^)TVYJawUCzV`G5sxq9tYKd+&Nc0`X$C)T$y-Z0x!Njk4i9>4(Ky)Z@=Q5|*-L8e8AbHOtyF z!>SO9r!bZCo%AQ&qFjExmy1tcGbhwZP!V+rJ(;zR=!+?z8r%I!DJT~xvRZBMTa6~* zq-ehC>O|cnfNbD5!N>TGX3XG28{Uyvwu5zFIelNvw){>$X@=}Y|B-qj<&8vlL}W>W z>9>R-w2rb7*Xezs!&+{n{6qo16vYceHhWWM)eQKZ!Ux~&vslEiDkVxKKEn%E4o+7n zr&qWHmA*^FUdww;Fky3wh5cBq4BGl2f%?j_OdHdfRW*j$O&DGV2s;$k^XcU?Fj9Qq zBMDt~sFfokVc`4StgowG)(JWtz(KuNeaA_iS85v8cEG)ApP=VGX$NfM?rz4hD78i) z+;jD`%cCvHiWX{(Ne2Bl@^OQ_J>@1HU}mz1AA%l45@>x_Z$&mq#gf%5LUJyMkt0k7 zf4VVE9%ZGsaq&Hh2c&}>Qn#M)nSjdf*~1ObrC6xNK?jK~BU0xYPa?@XFR)}Tl1^!? zFIyUPP(IFn1vN&1;^VlifjFm2kIp`1Ly*cM*L%o&_1*{;GW8R;2*DX`=3I0e|Exk4 z^r-8OPwE_DjtMDr-$6umC!=(6U`oGvPC4gmW}ix~db6D#JF?_4kgjgfb$B!r22fd@ zsYkY`N;U1Er*_&TZtlim9`LhD%HX}NJ$~}K6T?>11{t-1L%xv6IPCXsE7-wE#hOg4 zRBr3S#N zum|N&@S@ZNb^G)AZ0EM;3|h4c7JUn>AL51(2C0)HHn_`mhtv5ae!ejTT6Nz5B$uGa zo$)NAIXilB*<4Xn;A zO`%cd-WDVYyW+@*AJh_yv6@-&)sB%zeqSBy{KCne6yD{M;W&iQ-0n>4Z-eD}%O&z? ztanW*FJ$tlx5Q(&gB@6z(-!?fdAEp6Y>_HTV(Wq-sbqq&G5|8DTvAU?(T0K4KYJD9 zoSIfzWaEF=t}6#Ly+lMFcvgT!b8R#AH%jB-EnbM$Q%D;0l!3$*X1^rwA4Sx|0z?b5 zwNky(1S*EbkUl~6bWUV1L0=w#^#Z**KZXEz(sQ%*%vcc!&m)I3i5^;~tqFabC+ttv zxpQ@vhcm`;fu1Uq+IB7wbYAudl|?g>Z2%->6ZLp}biVxzBt5AmmxPmg!2bpy0)P2_ zSng#22*d43rf4(E^V&@51!G0Ef$v}NRRtwv0)12>{h@a;w+}{{;B;XCV6Wc#^BE1J zd*vr?C|~}@;^-V_5d|ANxsEEB&)5>fsUn0KpN31|=m_*rVPwQM8CuoSp!l?KwC}Ww zM6CA{S~>bKt)@4wk^=(2B`GDk^^(kJ>t}`eR?UFRhkJ$3%0K^rgP`%4D6dU#Ds@2o z52|ekD#THL9%*bAldLdEZIrHatrS>o+g?{YgY0`HmAE4SU_ESZU&QP#@hqm0+^ngG z-#Ss}*;iGgaU$W&&$|?7uvW~i>vguuAVWa5z6logRGl+64tw-4 zYSjJZnGOPvt-pKLwnB{8ySsYvtp4CATu2#cP@OS@aoH`CZ^GTOK<`q4_{Z>7rb(lm zJ&q==*`3gXZ0>sh$j1&m38yP*b7HIPR9ShJtdofo>*-JJpqXE&iL2{Pfl7n}@M)l7 zbq8o4&C$gp(N+57W`~5W!bi+#CynTrFWCAy0m^EG&OzrrP7Q2kNsb!o`)Zl`dSSsHfK(5(K%>st50dp7PnghC_Qlf z6Qh3)C`mB(GahHEe8^8^M&UB3^=JIkD`iJbFntpy9CB&dQkeo}Dg?9#jzVckih=;= z264Lh;7;OtLf#QIA$b?H`RP87L6Pi)(#V({F!%P?-<`pa-*j0f=vZF61Yjwc${UZ& z!Vratx~5Epdq)*6zz&kJujt|51>GD1;Y#;5#e{sb0S+2FZ{_z6IRKmPg$z0GGSR2G zIuT0hp!MEgb+abOK?6ypTa2N-W!9tus`juF5sJ3JwE69>u4BdK`+U(7x^2_nVqtFq zKX=zWvYjkUyZJ(*lQbVpUN0X8pk(*1$x_X5ArJri9IP%Yu2eGZO31hAi4tWVFJg&& zhqr=pA~GNz>bm3KKz0X=v2;%!C#Fw0diWPS%(A?d(9KGDPj0vKWL;3?uxi)0b7YUe zGAivJK*IsNqtB4Fh|lC|{qgT`54?kvAmO#|`JwXY`iQ6m9VPxG1(^Z5C~U>TW#Ps0 z;@m^KbA~H*zIm4?{4XAjiGI;H%>mj>qy9qO6Y9yhI#3G%*QmS z&rzE52tdXFju#+EUA-p&h%8)U&3(O8iQO_m-|I6Uz-H_`{wGH`7~ow1peCafK#Jv( zcoug@0s3V?^l(^Ai1n!3T^csw>iYL|(YCU@$~5|8r*8k!oBCTvU|_`5gwwucZ6r0| zT2#fSXhtf4JT@-NR=#%&CW$oe9|~`*2iIf z2hh_60Cg$|i$w5W<1_&cBD3cqp^@xyZMq6^AF^Wx%28vD54{UVR~O0)tJIY)WHa!u zyCfTG-2-LYqW&)TAWpCp63{e;nuo2>$YYxj(-d}=bU%4>FPkM@gJ9(1l2vzBk2-Uk^W(m!B=mc z67&2lE`bD4NKb^5gUlu{JvW5Act${w6?}f|uQ#4&P8UY>djjJ=0 zg#&d5Q&EN4(t2JApdDM@SMBja<7{9Fkk=7qh8FsJ+zSs+o)rX!To9fZLxXg;s0kgO zyiz3U`EoQEs{#`-?7N?12k!%t0Qpy|VMEMhsQf+72Wmgfpmod?wJD{pzg_)Sot=N` zx2XAFTUG!wQ-AFKu47S?1RP5ea4rmL^eLwIuc63KF?L^@MEAMRNvSKyx;ee%Wm3-W2dCI{cO z{9%(9%YvqaH%-n5Fr;IP$-^@?4ar~l@PdKF3y&D|3CPLA zcx+B-Z|_6gY?NQDY zY%;xHQ(`Pyj!ySNt@ZHn`%I#L)Sv; z&OW!q`)m8OZO8Ziiw@x3$z~I`v9aNL4SfDM`|~G5$-u93<8=S07GEmq{XKj81nZg_8XEeq2ZbXLh#IwDmeL7&<-e~PPdPe1IufTjJ5jW^zaNGX_;v2L z+jK&BdB6T^y;4#EziyLr<6OwUu>fGHzuz+G)vJHq^5UgS{};D>@L>2~w{&(c`;RO8 zB~0tmu8Y66tMJ>l+5cEam7pQ{pT9r*JUnkf88WkU*9k){@a%1WfB*LeFP9(1M*42c-#1(p@Sa%U z)+R-MA}@qcRpwy6F1@om`rx$&})>(sfgUtcUXuI(Sl{PXgFOIINsrI!eA<$G-{ zWDs4zbIy@_b1M}BMdr2qc`Y8{*{$o{SG6YU+$3l&*T=gv z_#(G%-pmwrTlNH}I}&`>YURz%&Cee4$BWbVS_`i@%|!=QNlD4s^>a>Q(}&k47rPQT z!vFba-Iem!<-|Y#Cq4M@{}~ycUG)Eg!`$-`{qVRrEb1BGEA57KK0bMv zz+-0Wcd#AP1W8){wFfM&lLh|l^yD~k{`7e2R5zG_T%7Xe+v=(pBk!sBtql^5Dxc@D zWO#lKy7}*fd5vtF{%mmHp{$}ppmF^(;lb=1p7@Lmhb`|sH5eH!?OI?59yTF8y+|gM zXle0VR`{DY1K{~{SS$jFB|9gl3Xn)WSJ#T8GrzpJ;Ikda4WjB?&1h z`JFpO;OY<0zPn@})Q8y*|s5{?M_-$NQ zM+L1cEm;KwbigjO_BZEsE1&CLx^&4)w8v<)IS60S@4&U&890pp4psuvUj2%c=H9eL zQrn?2BpiNRy>?B<)3b)2fkC%Wk4w;D+<->ZRU?dqX_HL}<+;&HA?USfsUCXHv$5O; z^}H>D>T%VJlq!db=fV%U3*WuFTlr!{3&@||6w+@A{ND?_=~nl(@q&?Z8#|!;qMqyT z1~y}2VxA9`z`S>sbL8p#wwYzGc(SzcR`~8a_ofM}7wQ%F?{A@L5^-Bw+F*O%GiC61 z)+USq&5(*Q|Bed+#y9U@Ygu=_Q6sp&xA#vA{-@OdEWc+KhkgkwJ^kka^EoA;5|uu< zHREd18#mrLOw=v45JPmpBDU`Gq3WDwUcG<+UL}kW$p7;9f;g~Ww~bjj)f7HCpnBU+ z;sb1Qa&wm>g(ts$2_sENPEO7e0zAl3jAJgaXpi!`Lj!zXubbF7oweKAxX5)HQ61$( z&d!aknwLMx$jKAhzfB;m`_c>Y;Qpg! z;;xGts;L5<|5GY^enq#4#MIOej!nmU-AOz{!c)Fk?X*5U1NY{;61j|2IwfajW)8Mc z5Zz4aOT>>Zc}5rNBR=>Y?v7U2#!U<nst!AUcsTE9E^;zLkP2>);AP8*rwA_ z#>@6oNR#W-Lf<#%yMI*JYKM-AdvE8n>lLvoE>F}~FZ86ud5F1EQ}bE=V~xt-QWSMv ze9Nx;A>Mw(b|BXM`Y)2gKreU%ChoC=*(_ z|Bm`LFZ`168nz7IO+`W5{x9Y!fchqNu8Lr}{lNal$H!ZGaR~|OP1Jh~0kig_!E5V? zNO&q;#HFobywXlT)-Qnb@-@mQaVrC5R?5Jtx_GZQ9}8oSl#m z^Ti+fX1c)CY)=OfPsX2R2v5w6jnjdlwN%>O0CEhM^!r}zXeRjlyFznwvpc5PwDEBq zvuZ!^c$^qg5|ZyZ$`q@UjYdq$i9PQ1O{XV5lWQF`pJBLS-~G*fiYu-QJydRsUmuFO zuYB|ZKL(&qWFuf&R(?J!IftIg!-o&UqoaoayDD7JYFuVW<1>Hr{P~9*Qx%m6QTLVn z9Oa}B_G4ATE*`(^9(W6&{uO?HQvC+}=ygioPxG7lCC1-?!P?nqr!%ttqNt`esI4xM z6NFF9&dbZ&B;Bh|!EMaTB`+`U!%Fh$u*k6d5g`S41?So4p>mWY=A$Wyp8<<9 zZ*e_-HvK7Z`D5yK$Q*$r&MnITzg zI`t3TA)w$c0xD4Gg6>P@vkDI(qIsUOfu3!BBB?O}`P5yr#s1={jJRJGHD>{kXkw6gyRCu4hmrkG$sfGP2&&||k=nAp&F zcVP1>ZBVocTt-4>xc_#8bJrTr0L9Y@^z`%s);*M}X+pgfC%_OK9AFR)~&|Y{+o}VdRwlYWuNQMj}LYRfX_Iyq}_EulximKF)=X6pi%H4 zzg9)dOK1INb5CN6FaR;Ht&p;0nch3-k=uk+k^f2~DBR`#`05;qqF z(qvqn27Kqw_Gk|9yE9L=G22Rh>sBKEO=XZPo>}e^)5f!fk@J`u17qD2R|OkE`qkU+ z7vyDhX^H_U*L`z~tHS@-TT!d@Y3ufo8FVQ|p0?BepM7RPj`k=T?H4bKUIhi|R=qGx zirWA3MV1EQZyXjDMnFiYox*3e>3X>WIFeu980gTM@RV6~*KN}{0Yhg}M$j~ zX?{X%Y>MbUn6{EqxKcc83<8%pC@#VgD{KN)62|dYSGi&IR5Qd!DoX5zN>ar<9HQV> zN2t@I=#!H}N57MObfO!FUeQPbdNhXBr7PF9wcot+&8 zx3xSAT54U;WR#ToBuq-I4<0;dx3{ABc@}5Pe7*#HZoWbcJ=p2%qvL(HTmO>9w=UvV zZT6kK*J#AtRnAy0E&YmsjP)I$-UrvEeuq$vb*rjMFiZtoct>{oy zdR3%%rAjwyBq~J*0qH6@NSEHhh=O$KWf-tfq$@~o|8>kY?|biizt7*zjT&a=dCqgr z-fOS5_MQlj}UGr&$Mx&vMv`pZ8UpF$Xdu|H*+tTt$4gI3)sKq`6L2dc8HTfc+$LQ_%K2D9K zmlbLViiv6Vde24=G{mMOP7LAj(AvQ`v>?y93^knq+`PnNd;!6lrt3#jjZQ)U+Rx3c zQ0~XvjuXVc+LmL_&_f>9%Cl!U54Z48nh^hOtR0r-K1E14#8@%#j#wkpcHZ>j z3&E!{J!kE`QH?EN!@2-o)_BOv%i{vn1WP_sJwAVU>l~Z8usqu<<}{$3YFMP@$IPBP zYD0VW>~Je0uwjvFt4M=P+;1mt9kN3J3nxICuLdBefP6GX!u;VbY1fo(0tQc}1{>g- zIiPWhUq816pZ$oY5?PqTj$3QeKIi=yfy)NRmS*z}J3D*plQYMFFx&3zJ!YVoR6YFW zuWJ!9?wO?O!mm*_J!pNU3z!C+4g8>D1 z`HhNoE?v45DvS+QI2hw8>NR(98e4)^$L=$C1d*$Kpe|BH#E)z)mytG)sXB>Pl!ktv zn0JdS>*v|$*g7CRPk#DzMnU05>B@o)pd6*hZ9<%tnZVJmoMfEO$2c?*T}23Y3PEhC z6Qzr)IQk+bT8U@5rYCyOVf(n*m}e~=@^u^Sc)0i24V}g|h=jXu4v z)Hh%MacZi0dZfJ-$)skP%IQ7bs-2{n99pxumvJg)w5O!VOmhGJQwVo`dOMgdo_tzb zfBV+0vxg5K4(!_S(@(9BPTbzIYgbZ)m`#@qUGdbZr?p;G9KMzojT#%gC_7y91LCX8 zoPiVYc7&XFVO#n+hb(rgMwYn>L5u*GnlEo{jjkv|JYF7@U(Mi?$2Eq2!@r45=EkY#utVti(DKf&3EOq&;Z5%E-{M5%a&&%A4lV!!i{!II>sx})AjSx8q?3oo8a~pu=f~a`CXyYy$I?C zUCiYN{o7LYrR~y<%WqTw&N6H zs^9>WF&%d4s?kzkUNhQTCf~kze>(l_V@28mY8l~2ad83b*e*Y#jny`g)1W%Lk5@a; z+tZgr!T+L-^!#7D%Nm&6R#GRmz~cyU>aLV73@5YXxx9Jyjk-QHs`Mn5T?|x?iMn7> z8I?P*RhDY6~#=iFs&4w#{NT`I{{-eIsqkDY_jd>ZKqAxKirMF;fC>!3ZVDV+=5H*71SAPzT2QVRh_Ni8+VF#F1fwGe&p0nZtM#Uc- zN5`M~Osp(THF$NZYJo!#vvb1z3$%OB_lFrK6(P&37lavkAkCI{ASrC#wQKmZ>_l%_ z+2d3&5+={Ce4{!tx45_)8|>1KRgNH;3rW9j-)<2fAHM@})3wXT)HEKI!X_nsdaj)a zsYbIA)fg616IsErgA=IOFs0C4R_fxL!;#|1WT1ll<-8Z}w;_Dh4!Nf2`eJBq4`U2I zb~hUR?aD>G4b68Qo=cPiz0k4$mVW!s04}a{NuQwk`<9}uLM4HWR-?78epH*7Cp*If)zNk38tzk+p&)&{sIj`3ztLP_((iKtv zrNvsOcO4lf;N7%h{ z=S~6kW-i5h2er}+hA!?t=ewMfSZ|YFV!q>sF*#!bvfqCE^htF{8E`A539J7ZC;tgDq+Z5_KOP6h#>OH9+0F{8pFVwu z(avB^d~N9C<0HNpJU=)xk_>cUU4G*ykGFqpZ^7~9;^H!+ho+}H_t*=Rs^!zh+HkmL4FVGEsx{@ zn(EJY6jA!+inL$JgwRQmcK|>t%(9>Fm}x~TKUNGmi!_G}@?+$g%P6#xNpPR+tJ-9e z!Dc89{7b68aYj+}m_i`yp^JwDc8Liaiky-tbwG8J?#JeF3V(AJn{{X!ReG{nO{iX$ zc|iG0*0?%Ki3=ts)3~mpE&6TYJiq+%%e^~y9-!v@0C;iN*<|3^hYyxTasA_6IZ6nT zVa_INKUl=LD+~<{QR9)*k^mK5Eu;t{`OI+ZmGtKo5KslNmSs?5MiMjX250Pzk@2F5 zT*=%tgMvq3kUCqOvU|f9OPz<#%C5}44-LP0^ypE-d4wF$tRCO!v${Otn{)B)Q9t_K zcvQ94s5Zq#G`?HA_Cs3Hc;?N16aXGGofgwbJkqujz<|j6jh}~_RkFUrg9Bsa;ql?2 zA7POrb?*<Iuq%E|HyJE5Rp?pfP#U+5Uh zv3t11x!T4YZ*T9dTem)T?62p2*JU z0RJthIY4$&I-={)p?r^T_No1SfAAcl*K_g5G zcq6-JK6da-1RhQI1*=I#g-eepOEv|}UO+iCOwUX2cTd8>xV2p{4ROCqX1fZ7(wLlH zn$#e-$d^~--t0ayop7%#)?Ib!bF*6LqRCK2iOWP!$yAEBTTI^IjN~g)S%KV$oJ7Hf zO6x4(vc_Y@t&wA+^gJ~JH6I@HW65=xi1HXo%~Kvcg~Nv;)NZQ#I2A8VsATsw@Ll@k zr?kky$nQOewey%Ax0~Adz_f}=dw-m|vHUeyAghR*|L{&>m!;nSkSE|E<%O+i8qOb& zJW>Jt9)Q5CPAI7NHG*ZLj>D5uH36;g1W0K!E32Jy)S*VJ&ZYZrD;xa< z6B83FR+bk7NS0`-$B)=n_dCz*a5Js<5pnE0Rg|9a`0-9@w{cNIk13^~<`)mCk}7W? zFe5kk?p@<3Iq$9=@;=39QuV~hm0E(Yt;?|3A)#=vmQOw1NY-S$yTJ7%Gb6*quMyj) zCb!pTag1dwPGnxTT+OwzG;fkNA+o1=?BG==qxz3o4!v@bjc3vgi_G&z)5}(@k)9%} zbCI0KmG_tkeXz^+vv0+%CX+i_5xWC{AD-oUxG@JW#8cIGuVZ`~3`xn#%5#T9-*Nli^cE8WO$ zs^{eK<2yjD)VaoH2Hs-9y7}*xsLxyiZ?_8&J59*=`C5~fCub_KF_c}v=vwjUgCwZ& z--n1O2IOV7OKk4h@84EnDyQkKI%v;x;BWmAOm;Q}gewEmT4t#=Ha5~cNPu4dAI&!{_sd#?aXed`<;eLFM+f zU^GR@9UMP?{QfYYCgZJ7ZHz2^V9pB|#2*n+KQQ?7XJ?benK9RsFO9&SFbQT<`08eMc83FzRO*G=NQnmX3IcK)QqMdzG?rbHg|j6Dpe)0ao zMo_q{6e>BvZ9F{nGef_U|y=EfjwFkW$j0&Br%ze3bLN5?UBycz-yi3B~*0w9V z>N9Ju@2XE6k_Jr*B6X*c$!>*}*s``&nhZ(=1>D$ALlyhS)a2&lg#fDG1Ck2`82KVD zM-BrDQ@v1FqMo2C zK6|cBz##wpJpscmjdaAwiXizF%c%)B5JI2+TF2gDaIFNIJBJqMJp_ux$LTS!^DKRe)}m`A!t5c?DXzK7o?^9lgt)<_xo7=8mfZ#@l#|_ zC`ll(vWE5|Y@aFda0#OtgFUuHd9oYJCx)bCLw=7(=F%Zl6CLk|ztz3q4qlceRBYpf z>Ri<@xNtE7`VpZzohxQ;T#r@8l_`LD_ebD*4<=5iWI*w`nik=X@AVeDXAR}V$8)|1 zHF1!<4z=%26#GscTHxM3A;s%=>7$qooE4JhT)OXGGrRLn>)7juyn?^_AU~Q}Ctv>b z&b8)oe7ug7qS*?_)sB|=%9zHulaa$32px1)wq}}&NC;@b)c!J*?W(h8eXs7)7GixC z63ZtEJ)+zX<-?3c)}zHZ8c?gNc(uf1x;odZEl>qqf&#LJ|C=}GNzsJj#pV~1Cf-g= zIEdPHwd4cCGbRD~#H*xHyWik^4?3j5RT|1jj&n!Vk~BaSBNJ4Z#F}rvRWS?W(|vbQ z9!{44XbzFXUf@_nCfb5#+FNzGKSZ0w-U3HYoVCq$qQ@?86at4o082zQRE}oj zMK#&!dx>7aR-;BmK9J*Rj-bv-uxHKnMnM_bU=OBg(?~uc#l>R{&G)6^S@jR)3N!q# z$AY8G_bx__)(ci3OHzBynl=6iIA;0{g(5~cM49~b1U2@^e571^7CEm&bg8yzQIUq; zlWguUPEjZs`ul{08V8Ep7d{WU*7W#K&3=4aPRq3G$`(R#^yTRyHg>ixA|0zt1MLhR zOiM|T-dj395Gm099n%}ve0kB0^hK#5h2JWmm$Ml8_En%zQl^hU1!|U(^_&rHwR-*F zca^inIl$?y&Og1Mn1~&7c}e(`vBkN$9aMjAm54*#_1La<*KLqXk$9eOXura zud_Q8lQIE}z$1;sv$Xqi^L@cGHgv-R_f46mRlle+0K=tOmuztc74Q(1D(|6M16ATM z8?{T$E5Ce*MR4Qn)*5}NG=I% z>oPvMEB}&7%nO7_Ds8$fD+mB0MM8k-`8i#D(BTv-e@X#@Zf1^p;XRgY%lPj z9)WZstYFnSDZb0oVM94c-NS&A)Oc+CWQaza=##Uv?WmG9f%7O$2*{2s+nKrrP{^3* z`4A~9G*Z?xS2ae)N;geCA&^@&YCp&|zp7xo%Vj|K!~ zG2az0uyn89x^_bneIi&o_dMJF-HV$<6VOS&`1W}OvbI&(c0Qd4z!Y8lu`aPl)yb%A zhjGtMCPA_Tq7^AF!NI}(5W^8TNeN*;kF`J`ZO9(SYFT$YeF%AG+BQ!eho10v+4iU% zTx-&FGLG`_@PzgwuIQ$zW&R3Qs@=%5jut(t!@h3)`t{a5#RYN;A2|HzR*jGTAelxl z&wJ6e$SgrEHjQMeP#~U^gcy%%<3++jqPQ{EmGhc=in`G10E)>pWW)Pyq{=4ZZFw%z ze z{tCiD8$q2R1-VeXGEE}745ISgpr9b&%6Ra1=KAT7LN{#Ml*_j|StBUG?uU$!wN!?m zi1Asl3LE~0>7w*db3zb7l+A^JpDOnc9zmUQ7NJcP7%&yfiD1Zf^Aj$MxJ%3mpl6Z7 z_2{u})ILP$<4C~&hkjGy2H z6CSvHEE||<;7~Y-=yIv>v~y)?IQAp&gXCRx(%^*&?Y$z*P%ZoJs}Ez6eZ74CV>ljk zd=6S8xI=AvbG8INhY{$I%@F&e?82d7C{iOttsbF(AjS()n{o~nO_bJ{^{uMG=fKH0 zD_<)Woa2;{wW%!zWRCIt>hdPZSd)daD&`eMKcT0c~c4t|SeS%)1r6#RGG9juaT z3aHoDl^zB~)k#emt_AOs0EDO$+T7Hn%vzsSU8sf?LGaoobNOQ6g>hIDn)}*PE}3~i zzlk=I&}o;x^zJ&tkrXdL%kjwSLnX64&9Clqu&!HPwL$%6HIGN;nrKA(LO$8V67ntE z3kpC{KgpMRBx^+H8sb)lzySmy13++Amd zct=cnDl1O{WEPi8eYauDX_WTLBbn0LDcF;Wh>dQ%+?3(QI6t1o+7OQz8Q(GQAUCKd z(A|T|?T#xbMAU}>hI*q`R5hXJIm9BQTIkw$WmhP$Z0JbD%waxW+7Z z8h~TV&Yg*{nOMzze5;p#@gb1kMfG=)qE=L4VPU=+l#wYQ*503Gfbk5cUPGyOQvJ6q z4wx=34t2VM+{NpybX8SUT0wssbls4cyfLDt<~ z0bE)`L|#5*_{$eB^0NcLGd4W7U~0P-W;**n zHZ9LnbAR!VA%TWAvOy$>wiv6ZtQ>*egUZ>={5o7-4)lo@kVHeUo!QE3ofMr68LNif zVDR;u0@&9?yUa~zBakRjk<|IBv?Vi8bD{jFP=_k#0k6gwMcUNGO3SuMa+`rB-(Jzz zyl~+Wv;{kn-O4!Ce3X3MXq0`E4tJbQD%PE>DrqAI1Hz1kq7+HSdn8Ol9J5u3spCaH zl;AIlxOcb8;|ANvi=a{G@$OEm-;TO=gJ%j8}sPo8VQAb4P4nyq#&>& z!!TSqcjY>p%)CqyI9ED4IVy&g(*#pwc)&(BBBSKR1S*9Hg$J9UJ|NpooL!7N8PtZt zkC>#85+l#hpi-IZxz5hjMVvNGFDd?5&6{osqG7lrBd&at1`5hn;(Yo2+jU7u7D*b3 zR3ooXCze+hC*0Z8jcTe2&c-SH1j!%Qm&TndKlzG7JkCV_$Mx%lxayQ76e{YI@5@|T zQFQET6m7=yP^lrDVY5`*w+f%Za`fjjG37I0`?cyRcGp9AC^w~JYmRkf9Q&19`7~5K zjYr4VGkjNzU7o?NSlg~Za4y0ODc#6n~!h)TA1)%wGpAKxTd5}WYznOD4#$7K+0`= z^lT?Kcz9qsl+!TtH$1wT@pb3fIx~8PiJV{@{GjhAqI^NBO6X4s zng?tyYVlER*s5LMTRW5GBVY7Om_%$Db)?r59GW($v1vrnQ`AkYQt?o9rHW%0v9bAA z*Iz|U%j=p0iJ~mBz2E}$JMk*bnZ7}%JIZab=U{%XaT#ezd_T!o3&AuAq?fpD$9Y{h zq$F9qXwyi66T2bIna!Efi(mbHOeD7(mUy&IuBI*h+@Dmt`98c)mWpetSPI6$Ew;|~ z`ldtmJjD9@>lotIz&f`Ab3MT$gCVN6|8BA@kAQ#xeRB#}CmIl_HHs>f+UhXC@KX-X zK>Sf4(!5m>kxgSge33XWW4A4gL?5|E}lck)zGT51dp4f&|05uEv*R;WA*%9itRHW0;dytK-ZtGGUV@NwjCfxW zbctsJtEmY9jWx(xdW;?2YOaM(bSp^VUA*++_1@-8-mJp>>IDNH2vqumx29dN#H-Q! zE}10&HtZ5H_lMat4klGgFu*8V;voEi;cSi}X1I(>HgyOgI1qf1s#-C9)`R(x-f4Xn z20dOYH*VZ0blJqra_Dq=@#HPDdAKI(#orEM6N^SW0gXBqWpOhxIcSWS#7%pK} zogp^;I7@kHnyC{vSq*>nlvX?E>oF|iR?*|s2Qj0l;YpT=b<2Jw;o@Y#mZ7Ezz5?$} zR9KFXW{^Ql3ccnt-@SXM+&%-(2>_8!ARTJvE&YBcN7XM|YX+K_a*bq(!&fBQ9Zpg= zzfGo!o}OMzb^)pmqGwQBAi+eUS#;Eb?H)RdO6l1xF02$Ld5BtuDS0`52irdwA8Cl z?wf(2JA^1lb14XKq&SYrRPiFHspC>Vqk>kX5@!yvs*n>6mc-@rhcl55#T-Wd0HDV^$48Dnc?5;N6q6|gIsY;3 zG6wTp_i)REV&qKz;%Ku}Cl>^8nlb=~p><6|gBqZVlg*-g)xSbwM%e2uzyA8ZiSMDr zG1ZuzkX7f?9Gx%Ld~Vi zPD&@fG0%tVCFb(G^{Kj970nO({&YF3YUIK_+2T$;M@-nb(K!T0cv!q?hpw@L109hvXvhszo~HqZg8;v1%uG@ zgpv9fD8>pwv`%+BHSi#I5+^#$*S6-jq3q30zv2i2v{xwTq9vq-Fv4?AWR^%gEp@vS zkCaf@5;|SW;kL`6ME;h)|2>>rE2c!SZBGz73+$x@^F5cBKi87dQtty*r%5LkHaKrsIRl1k^Lg<$$?VqTYnU93B^DG4|oZ zskY72P;5s-Z~pn`Yvh8lHbQc$Mjd(>B)9O26+OcLcFkwniw zyW(Zj>oE|B{<4Ji3ia_cniM~Z<=|vH)5@OOi{tjZBes7AcGZo!7OMbN07;}^A?~;J zy0jZwqh5ob@d+GZ`-O$i;tT)vtq$S8|CI+|A#r@U-CsN9NGTtn_ z)0>IuRY9%pOBl0^QRS+Tnyn}V(0`PTzNiEY6Ge_EJkM4j znms0~_8BIHft11_OU9Mc&TNGF`U&bEcvzBAux_GvfQ|%}Oh`F^Z9eVAyX?Xi*9+jM zRqAhuk~EoL1#euKlM1ql9OPlJL2W4HuoQo#z6jNCMPhhJ&J56w2R1md1w;zNi;{G% ztQ4Mr1SGo;z$b3*eYNL&=1%|tot{8mk_ljCvL}5*w0@$ftJ9P}ofa*yaL|eaabj!X zomJWM>UrBl^O)tUJ9}xSzat=E+o}RNXn@fwFYKX?nns@@B&m#59ymkD?Lsa~a37qC zsl7LZ0kW14Abk*TM5qf%g}9Vfu%yfi-&@#+Oy$7_kYrGh3HZxNL4g{)iCr!QK|#0L z3-=JZ%Di!-^SsYiCZ7=o%J+DP!w1@Cks0uv@x(!N1++-O9< zLD59;_cG}30y!=T`^r6m7EX}bY`r`RUAJT(*J~bTA-y*R0H228nB(b8uX+7U2%(GKfNAT2OKU2fmvazj2ncFwcI+jnc(&X^MnG zZX}_*%7CpYR9HwSw#GfvuZh|`fa5H{wVZ;IpQ-^f0?YFKHaKZ&ez-PKJQXNlSlH*f zJ1+v~$nwRXo2m9J>)=Sq)EAiAA&c_LYRkowWMcr0QO@E&*cYkhGu>fN`PCG-MrI48 z=LgiDFyLB67cH6^_CUoP_jk?!vA`)gx(tt4l;5I9O;JL@^&Hy#%;o{6weS9R z!d}l7&Vcdnnb~ahN@3Sp!P*<-3?+J2&&|ylgfRc~Qw~oSP*d0niL?eUeUhgJ27kb= z7vo-VTC90=tDMBvbV(D*i3NSNt4c`)s$l3o|&M7|OH%P%!_UFQ|Uf3IgA zzfV%<&UfYIYu!yddn)y5f!^V4@|WoZ(kChwj-hv6Ao zz)5EfWnhDGy-fgnEZH+a&I>GD@VCPob4JpBP^$?5kC<(#pV1dEf9L@ znFFd#rvQ}bLVG>)y5{L{3*j^MLbK{WWVw#%!T(jC*8KP z#922gnr1K}c^3B4X8(OeBF_pJXT3BlH2_tC)(@$)-{q9sMzCLLsPC?!UrJ>}ZgEtE z`9!b?44uJN1-`2*Uu@JaPEO2T0Bw2IPX8pi;+?4H`e85sfCQ}t>f&Fv6P0KwO;Dji zA=17p#XatWtfY|&Y9SJ3nvus4SIr3GVF)LBYcvTH*gOWVUKg<}opT73LS4CR^={;^ zwkzL!%gMO!Z4O;eT%NX%n|qja=QuUUqOT06=LB)ZO7@ty?R0=e@F5~dzt=kZ%_v3* zL$d|#VW8V>YVQk)n|XPAPY)%;9zb>?-gB_JpF1sLhtRh~b3{2n>YhT^u_Od#rNX<% zT_8pG-t+$EioT)w8gm-*C$BcgbB}LFys;O;^rxXS+311D_DM6DbZxu&TU*?3(%?_Z z`dki4YN5TZc@o=nlzSW934VW2y2>ycG_F?mc%M9ZW@44y*G?*t^Wkg{$8_=@!=D8- zBlfI0u;+&jkFFYR-n{!(Q@qZ>J=+DY{kFzNm{rK_YQ^mkM9lowIU*EKf>=OnC z2D!g%3B=~kvA0?CT@&wGumRse{98aCrE~9oBsMJ%_%4VS97$UhA*I!KWr`hY@dtE^ zG(r8}udS_3X~F{y*dURsF-r+$sxVLPzM2_Vee{5rP>$)cYU1A6HV;wGD{3t6Y#l##3N?up?$*;S2CljUD#{bEa zC&in1wIjsstaD0ntZk>urtAvgu`o?DDePs0e;ykdu~9FB!MiR-R$BgO^<9!(H4^}A zGUk6zKvH>vGieQd2sFfF_+<3P9H6(gE}*W>+^Vmrs2Ec=-BndZLG9HHH3rZA5sp{F z;);1YZ~$pVNVdartF_mcmzP^J+C~LU{2q;z<+* z+!wR!(!d`{zZ#qX1jWJkNjBFm7_+d0B%queXm_G%7GH97JPQsA`!7!B5OuK^g*$2R z)aBb2z$&c;qxMi2oDgTw(cZi-*6R^Sh-y>eA@p8#!7Gab0_~;5FK7jt8L2%IGRhgI zlWUlmbMg4}Q<3BcGj;gHr&W@fbP62J;ATGxP*EN9bJkIr(WZ`wt z)Ze#npX}<=lsnt|<%*h`8a&{256MwPVZq0k0YsF!$-55_9=N#F zHuOLUpb#^Wh1`v&og8Y%!uiTf13a9-jy8Ym4Xg)k8|lkJGYlE$_c{aeMFA?cVi zVyaU$Rd4eK)}Kz%75H*(R~FaMW&SMd1zn|6@(roN+E5e++%n(}=F!&F_=f57j?bf= z>h|{b6&~o>{C*S5{;bZZxK9K?!3~yd2i`D?=dYRiq{D87Q!$PRFrA8NJEWYS5Y`rN zX9ORM2GX#~GUAU#Grk)WMk-EK3iaBxV=0tFbyky*F0s;d|ZDJ8jbf?{MRBR(^dV715dQoyW<>*3J(Ts%5tOGk= z7RxNC-KQONx;_!@dC+>NxeAw{dB;A9-fPl}u-zRhcZJb9dkf47Nn>cM%w$;vf0ScA zF*+Lcc5ogxUcDS%z#P&f;uf$Bb4)1CwtfIH=FxxeeTgJvwKbSi2{JrCYy!1yhu|m( z#aqZSbb7s}RBnu+&BnO3zNe?h5#&AJpiZe*!AnzNU1jcI&0fgB;^i}LK7+f(Z7rKK zy5|k&XUkUY)Y0>@pgT=RC)kZK5^|DVVK2(HT9Wpi&F!|q>90;wLli-#)h_FmmJdQE zr8{?r&m&HxRwP4I$>>I)FbMdG>B`H=i{0!#XloiuYrrA3LF3-p+=>Elm5&kEwceva zPY0@nh;5n2v~_YWB5mmi3;sWMxEX1q$S7EcZ(9v+Idx%ve%`FZ9&GJU{U4k)XyVOe zA&zo}w-MmV#4c@BQQMB!)-ibg7M-O?m|9~ZMK{WN)8w1`W-Vg$!9=Oedh;0MKQ%*6 z3MGPECDtmV7|h8wZS8jF_h9m>f7!l0%(yJ7XSH;)npbNXzS(Fq&#X?{op4K>I9uqe zj`nU1+VXfYTbSu3m|bDW6|9*@4bF$tF3xc3z3uyz9AnzUp)vQ@u^Pz7NE?K|(|~+e zke|QB`P6syot>SB_L0;+dqcKcAE%heeF)c2uwugjv>xyf57eg)IUog@#9w$QFz&oY+H=T3D}ob&E9j(uK&(Odmww~EbR zc86VBg$;eO(D1$|8Ngo(-Vhs1*B35?*!VXb1jsN{?_SUf@U;fbd7|EE7 z=paLBGVEZ#CKwYv^NGl2H4KI!m|&1;Ex4)F1!+l16fr)e`+3~u!%hI=M#McV*1q?D zMPmo+<9VP#uTBHk;_l&b09`F8KZ*R6%J#F9i%TnhTC@1_r>j!eJ(JKLOU!Ya@7fVQ za^Q<4rwu(NXOMmHRFaT3Q!z$b2~#r!jJkrrm5z!4Ink)X=zGTQMehOGgC z-5EmDGHPWi=k1q|&FGFgT(~n^In*xy|q;nwKthD;lGX- z%NvhNOa|^H^cXJi62b8}ZVT8%TjAXiQvfje{ML_sVCa(%enb(P9?p1bF4B(?9#$A9 z(KZla9NpZs@q9t9yMTw<`0MYSLs&2Z49qOTJ}nB2j*U&X={pR;+%2Aq5h{r|X$0aU zxcr9k2Zp6yx{xDJ!}%1iUv(X;nXM=QWLMStNq(5ErPl#2-d6PFzITfN22#Qb)rLs| zX}mrpC1njC9q9sr>dtNA36xJ7hta5{R5-obfEBL6JtLliMpkxX^&mZIYOoc&axQ~i zySStzKnOHeYk`wR2y7L|MjO_z2b1X?znMc}hd2BJ{AiqpfBRK_=_0U+2D&W7Y&%Xv zt|YDcsESZ&r2&e?D_sePL_)k`k@YIbdKmR_l33sVHDX~4E>8g)Hmu6D2|7f!dEKrP zCr|26d!piPMdwrGz^gP^(JawbS+M}Sk4B~`MH3j4TsZ;;@bIMB_|=Q)@>8RZj*d;f ztBdCl@F~P|i|Y^YCd3|6k0>ZAlgMk=uBps&W3EFZGB-rCAg`N6FhG(8Z!Ie#PAW2Y zfpl^p>VH6zM3;OXjD@@u;c#Vg9!TA^x>&Z_jE(1CN}7XV$k`((C^!xNn{-uP>_MGY z{qp4rKsKpf z^m$i@@+YHg?6~~HK1oU4NC}5F#1fG)ZL|q2Bk2<(%Y^0=aVt>W!24=wNHN3*Qk{7- z&ZDkG>ei<%W1l4;4GgWKfpPpy{H!0uPy^WAn}H6^Y>Pamlc00nH(o?5oDH-iYJAu^ zA6)v&5fv@JTy%P>R@Uki8ST-Id{iWC`G4{UL;+T{$ zAr%OUTyFWZItUukX46} zQ|922|Wm|1;AS`ek=_E1Wyjpb?0Y_l&o;VI7CDj&7_FT?k^-Doi@! zhUC(s?@$twcGc$M#fzcs_rCpp;~iWFVTKs_m&!Xz;eq5!FE=zagi$o!R`V+8_eNOK z2&N`gBpE`3gqArfV)=Rx$?5}=nKrK<93f)7X6s|9!K{*w$D2}Z#{B9^mTH~8xK!aD zOa@tKu2-N?xOn>qA~+Gn8LD@1WTd{4pVh%|D_L0d2e~|15U)fC&cfI1P>W3U<^H)Q3UXeOISNN#1469IFCqZ!W2My z4dFp-_?(rUm#2-xPR0R|BfIY8S4eSqMMSsBX!B2|!BwWeoMUHV{&pqitq%s|-5t7C z|4I&d8PACfEQ~wZ`JLtiN8v}*HD(j&E5D7lx06K)={z`_Kdf>0S&$O7ECM4kB z0c1IgSu~u9P=iPZbZPy1rpwMUqqBbSJdoitFwrFv72w5-{TRoBf@AXYXCnB*i|=Tt z$qk50nhpQ_^A1>^zbD$rNB&9_n|OTuA2)7bzKSAw)yzyNGCIm2QzsV0gR>Y1_f4@Jv*YJs=Ei_A^pBS>`)|IOY z&zT+l*Y$lP!#C%7W3jP()G6w@lSVI%6Sb+KJveF@@Jq;Iq=PnkAyiGuK>cT<# zMq#Z?$GGww+BVcaL-^T#{`glQ+{apUb<^K}cyoksiin82M+gkX@V*}p{`U3f$iHPa zex1_$ir5{75C$t2NQN99*1Pi096$1tZ;TKBYs~NAO}3~sh}k{E2-}@lZob>Gnf%Px zUu@gH&h0flhI4_$6_2O7UsAHOLk_K)?w2obIdA&)hsnP$>m1tR{I9>5EkZ^{x5IHf zO)}jKO;)Kh#1n;-w*SG0|NXL$Prr^0ej`90G0f2x`Gh}Nz=}*<`e#MHe8m0EtzSQa z`4M1+4ZJVJYskWKIpHfi{^iXlH>t(_{VClqVdf!&nogWJQ3bFGNPHSCP8+27FhlAJ ziASM)x))Ny_Mdsg6!#6ASA)!yKLFPG6nW^~-);ZLu)a3B;fZ!9}` z+GlWjfKz|`djRF(Z~pg?$jhStbGiTfmYG)k{@cXnH*)`V2=hgDERbXy4(`=W-!M(C z^=|Gqmp6w-%5SNvH)>-XOK*KR$rz z=>^2G(5su)FqdLVj;4^15CTL9)SPsf4!2_@>YaLcpq-JS?K(40K-4jH`L+MNFPjSI(w+un;v=ZKTJc&9kn1SZa~>R9!*mR#`&Jn0@!sT)(Q;vT z#BZ(X&2|4}E_2*1@;t9B{><$BWeqvUng3bKD>nQGL-v4zZ`2XJq`?nAK@`wDOe+~7Wy@~3?3UfAgVKNXxw$|&n z|Nhe({`|XYzjCq$Ns=gtJ4cB5k?6)(Ht)@Y748XQur9b=$#pwxJkv600CJ^wfaLGLk%%6vKljEdTAJudMs|@5zS=P!y6t zM%3xJxw^tS5WL~wZ+(QOCZa89qEqHsl8hA?C5bJRbg#jwpb1+9I3bbdwdt^a8WOhy zu%3@!%pe(uii;Sq_J{sd1-1bwCYTc&dEozi{++4|89E!JNK3rVZKT1xJhvl$cLzz; z3c&oYd0W61AwXpq4a&pJ9CURPbEhaQnPglBRRg_ncy}fIL%h0X4i4#YM(zZy(}ZGNw$bQd15rA!8kru+k1O~Q3F~7R;r_OB5 z!@{5^ZR%$vsA`Zqkaen`rb78y$L{q2_JmW^825plDzS31<)07)*M801m^=@79M{rF=|rV27WIw?{>a12@sfO3Sy7j?t(PcU29G+Wu(V@EPL zt$=?kCShT?iz$Viopd_!pjRBb1+YSuPb9L8@}{N%IpG&Aq|a+g_>_qka1xT z2>`>SJs1;Dqx?%@1!LSWIg>Z52 zx+#ccgGoVWg4krqEELH@@ny%2knvWgVpm_83R|IC!U~0X>}cXj0=mqS2_>#L>^Cyw3wxEoR?r&5IqgR3$PHv#izW6S;WF^< zA}sGg6_W-_Qm}{ICwmqo*BMM2(ZJa*F3H__DtEJ`RW{B=KsRohq;RX+3m z+wmk7sh}mv+!&5}bX@2J{JL!$4WbaKj7e0;U^F*l{O0sM!mT0M(IVy1Xi6kQPGDg* zTzLzrlweN>sBN*-#;{*9jhHlwnF)v69{Sat7cv^ylu%BhVx#f&;0)PAC&9=e$1s0SzYyGc%mm%cHC$1U-f^q@v5`09bR-K2^?v z*r>a!Bme`&awF@^iCt}>KkS^p9RS|lgoFf85H2W~iJglKwZdlD15lfM$>hQX|4V`f z`43RhS?IY~AVqm#z*&GbZ+(3|p*@kF$yCcLSC~*55knd}4Q+3j6fe)vNm&`-0=W?q z0y!OBm?25#^Ff;<<KjiKfw!7C=hcg`f;St^=jlZZ$<|DkTw~d zG|XS;aBy(o;^rpp4TPS9M#1ap={Y^xDPp*>K=MByxv}de!dHT-5U+pp2sSwJO29`ViX|dD zgaliNpV=TZI0`BdrBPlfN|49pZyJktwuBqGtH>=KjdFz7BnD69bFri}u(Pz?WS$05 z*ik#db|gkzIY^JhX*e^{OL~^8a4gZZ1lGirIA$s)iG=g>&v(Xz;`KFc$p{cmOR@-r zQO738fgTsfK(;57;_!&8@$z%t(;>vSh`S|rGuVAkgS){%%XmWTk=m|RW@culViFLH zI0#720o-aPogAYbLP+1Q=IlvBG}2&oqfmcXw#lCuBq}{o9EWv;a&AbJebOO>n5W&8 z+WQ=0zZeGDBA%WjZv-}!Q1Nhh(4SnGMsSOd(D?V0%s;f+`x&=jhO2(5|j=kEi zVVtBZ^neggKv9wB4b4BBMj1uzJBW@5b)xR6A;Nb+-<>F}%z5;uO_R}hBN!>#21b`C z9)yBIS8*H3u4q@ewk~8BwthE=o9ttpyqJ>7wCV*%LQT` z2qz_K3%TW)L0-1$q4-z;#Q!@6KEv5MpHL+t3axiU>{)`jzTki)W9T_XcH8gvKkE zP2i#{93huvM795CtZ>@&9VX&~f3>%2_2x`Otk{`LMgc*X-iwfpQ^;c|d<}#bvOlqd zjUYL+0}rRpIA9RaDGdy&@M|s2zI7NqduTGW3eC{t+eRT@1OQ+nlJY(Dr7FJ4Q@aO2 zoNgupXmDph*>_HAO64cT*VU}nQ9s6H*2iy^`~ zh~l+k5>5b-)bE`o0C!09IwJj6G{KV|K|Botk5G{DP%vDfjo76i)DS5}m#sJ2UYfkF4)|k^#}E*d6quL!m{Fadl0=5#*y^0_B`Kp zQwuU*`TZ})lgTmg@9$rTL<1F>+TN79HWc%C(LJ`OJeP>Hm{LLp+rk9^FIA1TFS?5{ z0DTKsZDjyw6l@{>-oWLdoM23!KartwgI%Oz3JDDgM%NZMXq<4`Hoe?4ltvMPyQ&S! zzcbW=$bmvbh=CcS0|TLE^jb_9`^AFzHy{Thp*6O? z9>$=Nf|ZoO@Sf|z2Lqj+_fW*lPQ@lLOA`_KApn{1)f-7EA$msf-mxnsD1`UaAh{&Y7(6iDy12d5o!Xc;*;-vpllfkgm zjtZ5Z0yn-Vjlu~YnM{DF%ZLOMpVw=2q+gZXENsE|s8k}@BD*T$AZS4l2glEYzriuG z#^nQOdljyrpmB?q58$s7hAA*4F7CY{{mbZZTk9}ZoNzp8E!?>n=wM=PBNM$zZUqWY z#wv8OWq~Bs+Il(jEYk7DJd5d_in-BrKSQJc6GXn=D~uM3g&}iq`}&;QXAr~4y^&CS zJr-2?5KQy!h+jGIYjeCMvw)CC?s7uC<6zuJAR&Y7U^)qI~yvuGG_uA{1qIR z&~(1U3EME;{tz;gtOnjc-TDyS6EXDU_To#%I2{Nq*dg5 zt}kK{5=y{}2k1F7KV*({!9{^P1{kr%aRnn0BHz`%0>$IspOQ3;o{*PGS8K=Y)pS7d zo(?r3T5Nv$k!#;qIAWeM&E%A%e9yl$sTN>0lWED`I&k9t{dnq~-RT&MqVUI)B33fr z9VKX%trJRDkeH^0tB873drca#)6`v(`|w|$$lr0K0BspS3yM??K-Xl>dnp>2+k5J@ zE_@z^@f%SxA^&Pov*B7Lj39zM$|T|qx?$pq3ClVw1Cb8G(b9+^P$Sh*VQ0hc5&5np z8Yb0@DBbU3!Nd?5Ri{kEDgj)hLP#u1JRYzGj2+l&nEqo;yCjN1M$pyoa1xdY{e|JV z#8*J(AbX?RJUqAR6oQ!li=Sash@+}&L5?8f!%a$X^t2YoM(r69rW*5=t*xz=We^bg zekETI5=TZcLUPukQO;ob;jnbNvH1M?^K3MYPzXoTH#Qdi@{Fv(|M$wklCTQ~h}7|5 z?o@9S3}_p7x;+{L*^nSsbC}+nf*-+JK!(W3A5mmLTnPC zarRQkgnqO*Bmm7Fhtijsw98aHlDsz=Pfk1#NE*O=eZ(!7EQHu_;0tia+HsJ8ga1+E z5#e{XMt{*Fw_2heS@H zS2a`r=mB3c+AB%FzhxTcSa1PKRGgxKwXFJN5;#HPNJiNh#6^a*S^MIW@Kqqm5Yxsw zvrbZ5P_gn=l2MHKssOGr%rzl$DrwyTx_{N#sSClW5viMtp_os+D`e0^>iy=0K=_f_ zy*~a9l7V19VhKY&APfqjSqYd1x+0G<+nym6UT2PiRvo*x_5bnp7Eo1g-S_ap!UC=> zARvmOpdhF;Vk?LsDkY^-Dk28C*O0%a@-+$AWx-Fp#qeSE2z#R1z^s_ZN-B zU@-1EkYysX;|-%nckevhlw{tJ7D`C0rMTCRTOyk z-6$BXI()RDSjbDTPrSYyDnK@I(0acEJ-~b_w>a8RPlx~(IC8|b0ZW+BpHM|tz(0rN zPYA_)9F5gAnl|EMcqc_Z2hX^PPI%*5gJH*t((*i!L6(rXhy!IDb~Ig(lPxrHPPIg2 zf(?TZ@%cGzx)T}eg9j@eotzj8j$keQd|%+(+G6m+$nfl9!yQl}7#=DS92DcwkU@#~ zVFq^|QpcmZZf|P~##-(HsRt$BEMeE<5|M0!Ku+L2JiA#3Y@I9)()q{whk~9sk#R#; zu)U+>Ek0fsIEe+rmX@XM6_)?)EVcO;xK9Lc$4wew1OkjcGFR6DW+F5F9n0V2$&>A< zPNCCz3T-D#KD&z--N`^O$aSRcgd=KXXy|Me7dpS-hLhEcRDyR~FP1&8Us3mZPL##3 z!B=~SC5M1`$#eL=qhmK({E&F_^z-vW;ETqG>6oJ&L^GJ?cM};030m45;AQ`m&)o{J z3pa*5^=0fZL1AI-YA(!_qoR<*%HPJsM7Rp1B@GUKc`ULf4qS0U9|X(7UEfxY{2;`+ z($dl}sp%mp_V|p9RPjN4-LHY)o$0S_?+Uhco8%iz!Bx+tla%aoc;J<(M=NH>Ez4vW?{N}|yeCH64 z-2!xRi^OgLq?SReiBL!0O48%%Z!uAz3r6i;cs(}1wpIz)-<~c*@;rY3_Ei<} z{fRgw(G3&60EO}&!$phBf`50@DNmSC!xFKwSXHDpelw(rg-!kI_9xOZG7Y}VC=wfSCcR=2R z@Ps0P=XO}8C4SNGBX955!{N-cf3-6cen1f!H2pJvwbYRG#?e7W21!E!`x?Ja{)Svu zBni93acY+lcSqb!e@Y&ztgbMHX zkuAW~2SEpkDw22^!FrL|=NAvsu)6IV)K*2xdFTk`l_(&RH9O?PsrzGEx!NSaoZNGrIep+wRRK zdnPxvrrFFXW)vGw)LGKlwI3$`ojQHGqgNTfHQi0RazJc$h?6r7QDNW*`}XZw3i|le zo_aXE1FmJJJ$Y7b%n5**hzdYCto~8jnNz1QE_M!l)S?LyWQ_=Tzc@`cQaOJOYvbwb zvS0q{yyf)IIX-p|1^~3lkZsayw9_EK>(R80YFTlnS4ar-`Te7fah2M<5KKvj!aW$Y z8_1O&`vHLT{CwGS5Z1mV`~(7a?$}|p^=2+l8=ILDQ3}Isse3S)mPd!E>NlEC40BNA zxU+59vIT_2Q-o1TgjYX}qYno9s3xjhFE$^^-J;-K5K0?K0lL%B)-1(AJBEz73#e|c zh&yRZ5cN>POV}VS@{~Pq6}i~I9&S5+?(-+nT*_%|;$atD?+4Y6ch{e&59;FVlQJKHWNrJh&;Zq6=o$Q6`6+`x_|U8YH%FVy+TWFu)ukP8g8 z37~G|1IYCwjvxsj)iqf#KyejQrio=0QDT0CdA1h%p(y7~{$;9F^iWt(j!s z+OZ#HwM-4~n?XfMtjuh3{1b_|pGomg!N*C$e1QDWZt)*bS^#q7b+Z;-x2<33ccrwQ z)W4qixxe595-nos)rK=4$-g8~J;Qt<8Q9x<=`HbjBArJRmB0`P<*Ju76jAE74Gcha zRjM2LM;Y>TLTDl!V2FIi`f@|466Poe+--@hBCzUE9NSBKKiuRSu#i0a62=m{v)=!k}<=CUwt*ssm54fpTsm;7(yKBM7@ zPCb{faIGu2Rdx2z8vWVp{i#Zdj)d+wm?i0?*$KA6EkGaSmJ?oJR6Xh?(h3oG2e|g< zTKbDkv9&D|y`ND1=YtPGQm^4!_bglOt>uRrLCqzZ1yx7Qj_t6ffW8G!Jjt!SqvWl{%vuiI3 z6g)Yx{_KvGJ@d(2(TxWNg$G3r-kTNP-5KZv*1jRE2iyY!iqIq3G=)I_uSe+e<#xcU zPqw`m+{8rV5bZ`MF=I)SYBtEqv$v*tu(QhHNucOTrqU7)5CFd?=I4KH zixSpyD5Tr#)c4kobLgtj$q7i0}CJr!pZ5!{xUo z`o+#(yT(cZ0BfS#=(&!+{(+nH{A!)^iyD0Ep2|3HyS>3>j*-{6xbxHJ&*f=~msBk# zx*s=q#rN{RH>xO^nwc8B9tM^KCD686 z(_wXqNnUY%gkt8M4eTFXlE-^2zxB^Vtxdgc=$Ls9EK!lk7S4@c&xgi7tvY!s;QGMa zATYG%w~S5)ZfShx9tVXpKPU&s^U+(kIM9A1^KFM|kJnaL4}s6a1F;n3d6pP5QT%XY z9o7*z?N6UQ%Rx6*G0lPp9MzuWy>z3l*Ich!!;|rkoTsOA=Sl-A4sdi|3ZDB-|O4>9Ux5c$}FjhPUt4 zJ_Bs%Uyp^tjX&4V(=WSwp6?nmwhQqZdT@4Ows|dY+lH$SmEqmaf=V%IO=OHk@`>&Pzyeyquq9xE9pT zDIaV2?c!IhgDwoy8@84{NK3PE4HC%q)XR(&)@8XAXy=}reN0Y=NzlqUB0?8GH)uN~ zwt8gE(5TMQFPn?POx0YO#87^56!{+FnSf0nZ-2izj9fo`uJHlUsCl5{w452=8CPcQ zoLse4YijdGF9m+AstUQ%DJW~Th~`jEizerk>8RY?97Ab_iV}c(!3~UgDKvg(C8Rn+ z*%xn`>f+f4GZtBI53dps6e`QmV-yjod+73*U1W!QN#>mI&NFU26ECTqxqaWhd2>xi z{e-yq!}c^&k^S`nOBkL$fA0J^?x2s4A|)v~rKP2Bt%KUV6%MkJWb2`@EiP5p}t5dK(3$L9EwhvEg^lDk`4H+YYC zZ%Il`ZEn2;ZCnQVUR5YIMUTy_6o-~a?g(qR#8*v=*){h9oJV?bsUXQ9zRG+J7G~V zzGK&}HyS;X#uXt&~uftWG_U(po7Y+;-Vgt?=%J_JJ>njofTIcRowd zi)s&QW&WI<-4GUNpE=3;^r_4~)s(xjbYd6MN1rz=LJk#6vp|PC^XS#1uZ=sEW1h&0 zMK@<<=lBcIOO0xsr$UB&P-wSlU#Y7T^H%juF}S~8bRi`gvj)Q5-(QNVME9=E%2`Yb zLO2_#*WJ;^~8Y@ zQcDha^?yB-ukgvIP{{h2)+zi_!p@pKvSPUD#ZmReG=nWqupEx=w9h><=O9~6{o;JD zPWO+`>$lgW_4BJ|p6T}5Vr(3_R7@u1m7rCh0KanMCUbNC)sG*+QS`lU9F7Re+SUC| zSO5hDdu?Xt-6+nsU3TFybPm{ZlMSZAl-S$D{zsV9odaA2)pdo1g?O~a>JP7;ZEJ6T z`ow#gLZjD%{5=$_$-ZldmEoN%>I3a}cqVxb9Zb)594}lVmXKg3B03x-ca6oNnLoff zZgzMByG#Gz7OovQ{t%d)?fcf@<&K89GIiC3E~V7k)yi}W-dnO%P)$yb4?MIbl~uGc z>Ye&#{j@m+1rI~m&&RBurA*Dt9I&bWgOV^eUyI<(awyz&4E;RvwK zi^|Ghkq|?uXcb^GPj3yS=bSv?2097Wmnjp$|tX;VjYPE34!|y1(q(@#>T}Riuqb3$`7Z9hoaYJ4ULShAH8|TN3Aj{ zBqrvG)_K3uP<1B>ouNsC8xdR77?hWs%UZcl%{y&A;i;^uz3i*kC5}#amz0=J?X`Y3 zlh9~(0kNbx>9ot-s_u$=xi0s@O_}3rBu#E!*!#g=t;gD0-n+G>&pFkvJwr>olj2E> z2~LCMSHJJy8D?`j`%TG*xpb-rTr_k3EM0euuWf%;aGBNS!(2!OWo=g#^`MoiM^Kze zk-z^o-h*+@^D;7?+AXq)TuZ6P!p9WIA|CbTH(&;gxTFd6R@975JI-9z1IPr5GZngn zD-M8kg7os%IDH78s8;D-LeA;7=+J$2%_`A>-&o=6 zlTP`0K&kj~xW#kR{Mfj0Bg3qS2!|Qcbt$dRC{g9|_sgkV=58CAEZfe`Ut*y%9Trq| zmCuj9G@6d9xHv00>d>(}RQJ!Elv8waacSS*)xK0f^G?v_BEROwnC9h!$24EP+MlO1 z@Hjkn@$JidJ~)B;R@LS`=FXH_w6pCh3*8wQxW(?0wJeUO{E`yt7uJcr+(MZV=e`x! zUjt^@+;r9L^}Q1wlk7VWZiva?Z$ELmJx5Yg%0o33iKy$FlGWR}xEfZjo}T(1J>st! z@RTC)wX;~sOgB2P-sOoWKKPtq0QAIHHh=)@kUEeOUa13PvSC7owqK~qTrkak@dXBn zh2XXeSdOrPE=3d&0b#X2`fhhPE(VkFM2DlOUwZDC!kcO!ne5 zTSIhUU4*V9S^M|HF@uJkYzVJYiZQ$PbSV$DP8AqZfFfIEUp=km`NsO3VihCBt&AeA znw(YA6I68_*OeDcYUFuY$+TjMiZ!|oDKym7)+-#Z2xX#=2T!b3;SnmCPm%7bj$P^b zK71%a+GB;?j}Q8$-?ZNAT8ua>pGa4(PFw0`#S>#Z$5!h=c~fs0I$Q8x>+sOGy$ zGxQ>hTbr9*?N4M%bm9U(^$4JGkgpe_`0uejjRZ{6y0`vuV2EpHvNdwF8~2@yGc!gG z2Z+g;Nja2ccw2un_LbaSSHI7m1q!)mOY$kV7A485>KTT*g}chk^rvs%o}zgWTbRtl z(<#JG^klsyJ!2-kdDI~TEr{^4{q>mfaaeE1r}U#4;)@J)NepNN49o4fH2dvWKz6AP zRzBoEEXNpBtl*%30{SOt+Ov@YBha0o3XwLleA1Qw<`7~me^n}b*5oduUcoSnW)jtn zuBxWHdAYf{OPi{)s;cEHW3iCePZbmwYn69IPE8+vp`vsyal5wmxh8h@y!BIxsdh_o zA=YjyEfF}vBUqzgJd{OMk>Z)q zm7-ISf891P#mQeMBqXe*-*_tm;f}++VmVu$uKzutoH%Gcf(`Qj)X=Q9JEeYIJ@0ZJP%J5xOm_a~mI`WS2Nl9s z;wfZp7_B>9nVjbb2J}7EM@J-bL>STM4pB<%<%|gOd-laMvbNys*I*p?-y^LD9zQdC z^YY2uT^pbvVf8%J+rvHxyP2n3f$AbRo5ah*qs@`dEjv6m)Kpx1M&`nWER-kJ>Fg0R zcY?|c-6JFQJdB|*N_R9|=gL(B1G=mCwTz6z?M}-E#eJ}rx$ybG<+1Lqul-7=ukB53 zyj}cGLP(s{3PDw*9nhalVjkAc@+R~%`HozAe)GqNz1Wo4u79wwNS{2p40OxnCf_rz z+jtT{*#5zA+3u!JCWEGy)_Y9*l530f1J!5g@=kut#aDC9Zrk&()B#^s$j=+_|BR=X zFI~P_{i(csskAhmy8Tgy_H*_C0%Vy}O{S$O%cz%^r70p-zA;cJ;od{iW?UqVqnStK zR8;)^0}$e3tcrvrcW*f1q+q}9TS+t(1w+LC4_#jKcGJ__93EtBkw_hWzIV;HWtFc| z=`P!;J$if&Np#7|sLQ$0ciwc2rgmK3wO6pF%Xh;DZ+!_Ej_pDn?w72@QN7pPAW5IK4SFz z^Evq^9v*t}nGxX^^n|#`l~wb5{CEvmFFXr#P<)_o?)@nwdeUo!cY1nyGVnIeuRcPi z902$Bk^i8cl-_n&tq9nekHGC65YcD>eU@$i{>Z{)tvDaF80qankD6wYdG+rT39s{L zi5R)kMg<;@r-}zt%EAvR7|l(In+?_|H79w5+wInEwA0nqO&V-SjmQO^8JQaL{3mLa zddU3)?QD-nbO;CwFa0CIoaOfI;X1>4)2Z*13BIjWSzbu5sHSl2fEgE&3nv+i#e3Zm zN|_HgiOuWUs%X}0aCBe2#N<)$dq4`2cyG^N+c6+R4TLd$*v5ZbOox7wsp3A)5aRVgM+-tTX3YToH@P(O42>*9cQ zhuu64v@dcOK7KsTd+;EYp$PySWh05z6j~2w6j-#v!oo0IQGBnf>4=;>$A`z5_OTb2 z_Ta+O+ywvy7fY_B9Fwh-J&SFs0S;QO_9sx^4uGyq8qJ${j~I8X1#N~40BKt6ppSN7 z_P1}bAFV(ixQ>R^K7|;E>gwt*_EhpPJ7ht>is~ptX~0ux0YuIQ?ymPG1qBjLETEo* zs7yy8`WIjT$OmivH_(b5Onof<^RGgE-d~lx@WiyK^M_njX}GqzU45E|o7UIhOL?@* zmha=EW4)Qq2HOcuQL+)O+`eL^}ZL`jMmfbQ^{~&>O@=qO7T*k#L{z4ZVG{& zFG?52`t0r@x?KvPoa=o3!%Ss8fQZZl^!5*c#80HZv0$p-Yp%hxJ{mft@*qcMMV4|OSOTXz5qQc8i(|BQ5;`#lk2x{-|j#d;qtG@ z(Dy%&r(&YN^A&eYJFxEVSbM>e-Jd$I?r~-i3#a_z*T9(czEyE1Gms&C3E0dzW%?~g z4kH{kg5QLw^ZIPvDD>iEQe51#*!tv4)N_n8vMEw=Z_isaCl`KV32z)B^*V z$7s5XTuxA5GcX@$9qIPo3dTcw$`T#ssnDJb92r9`#w9ULPN({+<7>k<$K3ZnPCcgi zDTa}AnSa>D%~LmPUJ0U_$*Iis>=!nBkNb2rxr#~nLo4vz@KUM*4c_6Y*K~0v(-L-y zsdoVF#+Wx!sYevWMVIsf+4V~NO=9$MU{+r0pCHs8UY`g+-f^|>BbFp_rl@bR$xLV(X ztMG}P4fuy6@B|tg+uMdQi#femAG`PNec0l@9>Y5C231PJT8mgPtp5bt%gD$$76H{Y zgk4qDlv`XBloYnzyKpau#)o59pOO8n%eP@m&`~opJ{+f0GvD(Hx8c$z|c%&cX2rIm-L@?@>6v0xJ^Z zS=+2<#TMeaf>e$lUxIE?a7c(cpWkg`9{lBO8zrO3-GlVwe|NSu9B=ak${Oig7{bw7;t6u=Z ze-5zU-{i}lyuofiPwf_D(YRBri|LgUB{+;rvUl`?o zU+wpI$&danum?kegO|n8>*`8+OTOh)ahp!a#B z50kyur(#|rp|=vhl?h01mR8lulz~$&>eZRa-q68*+c)PBSpWBt+*6%_*|TWn?j7V! zeY&Em%8au@KE?Umt%?$zsph<0m~L?D4D`95r}iRF`c{u(!`!5GUwtAAqY|Ug2ZJ*t zcjfhg&U8y@^J|BPI#&|jP51)7LkX(_i2qJN024V&Ov8z8&-Non?woLPy8uKiJ~ArV zW{VlUr9~3Cgg9Y9RsZ(Vww6=Hom`ZB+5YIMMyLO*9;4emzt*u=eS%H!0iJ7qhnMj@Dl9JAQ zZdy+~Jx@RGD6*LQ<}^Gevc>twdsE>!u~~QIN|Mpc^~2-i$@{9zlE(*X0_v?NMrvIH z>TL7$1O%$py4}g;`kzM>$3KYaxpUIm#hq$P414p}p=G7q{D{ki#cSVAoG<774s|>n zc2jh;rRZ-HdSGX3W!$Nk2)>t6*_{7gIFZ+a3czOo8i2r@$H#!Ra+*tXV z<3)*L$bQeaHf>)))2G&+7Tl&`Ht^#Fkb0zVZ52H_2c!jq;Mu%Lp z=m!mY7|aWKTl@Mr>>?6kW1BOq2gAOlC?)7qzmP8(Acc=m$f?n{eYN%u5ArWns7`8F zEMqB|*{IFV{x)NVTW;~aXOP_daim5-(Xd#qe(g%V(bW66MpvsSg=X%<%$AFnN^mVs z;J$1%9#lbz8FM8OyHBsye5kil@8p@hCthCE4$q!#0Gg8hqLd2Yn5Cl?vgMVuv~5$v z&j5p!g{sfqQp3dg|32~?@BGfZYjCa&*A}Z#>MUpXNbbJmC}=r7{dG}`DpSAa&l=qo zSvfgO=KSU(YevMSq_WF%mKqFNVSxf18lE(;$Q}hkIaH=oe(SQ%Y=T}GQ)$gmuNK(& zU1qlO3{x|eT_UmtUrIUw@>BLJ9SS!JjEsDvbU{u|F1`xs2}N%FstjIO z#%UeW?aD(wENC>gbGq%OAO3Zs<&aHC-@cXZ0e%^TM<(~DO(%;Oe3x$So3q?Z&E=l6}c-7T>lF9{&}`lXc~y7M45Uw!F}M{3t*b2c_j`|DvH#$6D}J5nCkAMl*j8?_8eM z?R{{r*CVuAxO%k3_!%e7TG_Xsl#feD+;X{9l%F3lQ5RWjxAcX!n?A6bSmP?@9j~RH z`uRB}nu|vD);-P2a$pD$J2+Z5Em~I}?(gOOe3?Q@+vO^sY6Z&0izP;FT|5ne6ATRv zhqz8v+D-Ew+*w?U{c%=bpUcm#{^Q#srD@9|AsgGC@=Ud$Tamp31Fmk;w(B=MLvios+Fz^a zf(h@8sawp@)2DnNod~N;!7al*_j8e#2M>1`odSE6wCX{lFM?D#SAj1; z5e7^$b%2MH6Ja1vAUsVdzg(k4=~b*3PJJXedOlO1Dyue?xjt1ztzD>C%CngN=FM)e zmxaEMU1LqYh@X=togYcd8Sxa=D*DAg?GEYuxKBOEaz4b>)1iDMNUrKeKkL2YRMlyk zjORQfw~LD2yL)#Dj>T;IhQ$XsqnidgR-h|+G``Z=P5Q-)7Ze9qk7d#Bmo7RF7Y7Jk z-YP9FemHlYafW`DdTj79f}VImfyiQX)zs}_UbGWsXRjEK8r?B2k^g^h?#bfhU?w|Y zeU_k7a|xAb@HTgJ{F#`@kMX6~z>RQNC>LH_7{feU4(=nuT*pf%=2s|&WsCRk-P?>o zL5;7 zz$7oDmG~AS&6qbk^__o$e@rKFw>E&rpR?Xy)fQoKgBb^Swn+M;noH$)!ejx2WjScZvssaq3~IR|aeJREC~t&CT8t z;N|B>eKgOtKWyy>3oEOwwT_Ob4u`4Z2*up*PSnTjM_8$>sjaUZ(vi5aXtzzoWNTcF zGc-{S4UI=uT7^g{E)l+SjLO5V@`xzv9m z{b=m)fNu|1U9m|rqrGy~DyaOaB4TZG@Y7FuXg!eJDdDN8v&BkIRz=;;?g&hIcsf8L zbi~Nll0P@f=^jx7}mnmY{lyxQ282o)z`*~9WM0g*u z6~cJ6SA^7GsqNFuxN{6sd!r$;1&2;KAvpzwv&zauF8EV!uDh>qp-yyqE?9{`@_O1i zx~0`IQ-siPO%jBW%`y*XKrU$CV!m$)0!b9_KTt$-V3N_<_q~?HTqs%K;zpO|CqC zMgFdWOOHBa+Q&!#E+*Te_wn+&L$n|k>_*H19J3-h*eXm2fCN{74(L{X{cV_uPbzy; zSqP@V6vS?HZ2W_QSTJ5G3L3u+^j;|tM4pyrnwmzt+FR6`kAg|cw+JSb2-S37G&7c# zqJ$UIYwA2+5*DVj4?P4|swD7ZU9w6*Y>&t#^IKUZyJ9ScucIqO=|UY3o&cVN6zd2* zRHSeI)#vamnY#G>xIFNFxm}@9f$~@Iem`Qk8VBE15cx1W5`>uw)u{JTR-l0AatjZn zjY@&PEprW?oJXLCp^Z>SfeFSVcykakGF_QjFpc-SeHn@@}_Sw%7`-aCi~6MD&|ymbC1hz;|V| zM`fAR0`J`uou8S=Eh_S$KiFy5H?<0U4#08ozFXGOpEBz<&V(yrm^th3M8*CZw4txT zXB|nX+}3mF&ufD4tXrGlLORh<;QU6^ao~R>2;+3uv@{XGXP?jznLOj6s)rE@Er^;N zKEkypf8-!5hV9^@M0XT_#eGxHKDfGCfzZiXOJIP*j6BbwJ>}7(ef6*A7e8@}Cx0)2 zN=3N>-Mja_3Ce=xM=&+<3S>1Ha}8mMlmkuWR3SaypO5^Y2F*2dm*sEV*hA1ZdN@5$ zz;@_oVV=S^din0%N=WgxQukCm_3}C+B}I#@3W&V2`;{ZpZ5tq|6O)+OiJ{kM6Dd&b zz{LEVlXH&@dg@jmXx)Fcf(*suW`2gyvyZSj7e;R109o;BSx-VzM23IM1@WfO6&0J% zrYI{v_`q4{RLKDUW$#NDF649TR4y$IKQqaZ$}E?Q_UP=HMwQ2f+)~W^E29&X$n)E9=fuer7B|4Sho#fxoV~IQv=;f8ysb)uW^u}Taa$(s> z(+$tu3-mT7Fj zz~>{mDK}?gfI2HxRg_?cy;pw6)P*oio@L`a##9g$ijaF4%Uic~>)F$%S3qU@z+GPW zQPtsk;>VksZ%2g>p#i>%j*faCw`(ecpn$+-i@htarfm$?EuPK{=M>gz`SeiIUtoA- z^E9;u_ODA&ULDv!aw@Xm6Pdp$ObO;1k0Uc3*u&`>y#;ToAS(cop<>XK@eZ2Nl)sLx zCZDqJ@9-)n>i1YWd2VKM(9O?}kpdxwr~^k3@G%FN1+R6BY*UE-LJ)1~)`MD-jK-l$Yp%yNIji!odG@qh{(CX$DRv{lSb1hb#*@RERl3>Rim z{=_KI0X#cCaw3Cray+hW#^QoC1w6^S(a{DT#vo7^Vw8cNPgfxl&EO!I^-hC5rIYT8 z^_V~H?_X>>zV}_rx!n+SU+#c}4iK+%1>s*F?6f(3(TEoSHN9PV=@)rj8W5vQlLUk} z0K}eLvjxzcOKj5AvWD5g#XWoW1k3ESxAN-!bV*tHBWHAGL8hut;fzb>mB{X{uB;#9 zj3AA%Aap-#61HUkp-q1^)10vwWO&z{*;hIR{t+W>H5(0V0 zB*x{Gl&t?Mc$?S1>TP;agmmVCl%*c>=wOa)^3++m_Qqw_t!f|!f&IFqG*y0liOgRS zK@$;y=i?Dbm~Rua^G8F<8HyZ^2)1sRf*XYrIQ{6Qix)X;o-Sa3EOcTT5CW#ufK^t6 zHGuU6QxojS&~LZFL*2p2`K2caimBm(XhWMh3WA&zCL3^dnn?kB(u4fq>8ZCuC_<1X z@&(GD#|X;^X6Clq+l_sD#BCQEihd8Ckh7n5+!`5@JUuhqP}x~oUsg^w8x`i~Z|CDn z$jwgtAySK11`X(}bacuF3(dj>CJ_t~R+MbkdG)qLl%L-VC~{?wGNvtYSgVdTdcY3g zYqe^UAuA$$yim~ZDD&Wfzp?Bx(js$CeBxpMSRRK<@gM8f9WyZ*TV4fKqfjGN#1E`l z&}NMxlYxemtBTn;{!kaU^GLZ4wE*KTl;mokJFP&tw3JQKBrB4@$-PF_3+r()saim zO)Y(MXFTj=!0M^9ZxjXEVs~YJ4R^+zP~4IEyXFg{RNQG}3}CccSXZ?IM7pjP#VMn! zyp1sJn2c9|W!HOHMS1ICBLRxoT+;n5s#Y#OablhIlSed0j2Bxz6&Nxc9?@>OpLg5v zo!yzjOK*Z6FJ(9!R{HRrP^(PHzB_?v9|9Y9#_-v56d*Vg^Lh=^a-5|*V$fKSi&V`) z`(UK+ar6+d698ccX99&eBa-J61A?_Rt{P~cIU5~VL3Aa_<7s9X#$(iHu^pqw91X!X z6o8QLGDwNFBE>tL@^P+}WpTTJfcGX&xvjP$c3Wdf!zq*KucpQa5>bqq)}mn8 zAk)l7TO!Gxn3X!3a*svaQpX5{Tvd>9LkS}sJ#$cEtXjD;tGs*z&U1f%|CK9OUWGS> z$3nxFTZ_7oJ1Bu|BMP<8wY6})=xIWC1+!mLHa0dMc^HrN$7FePOUrR+vki@me5|PO zz>CLW(sT7F_h(}@^mD*t%l-U$IgV=#s9AQu@j?L<_FgLHzD#R^v?HU8qPHkx0?|_AV_ieK+K}OS>8X8;0ZtS-=-NNi>Bn)Up6x}ZT{TJKH7iRv9B)PsYof2UL9>K5Lw*F zBt&@lIY;&A4^dU7xivKpryJ+9e5wUdi&J?XxB#%$aCGR}rXH?F8g_vLF&};J9iMIO zYRYnN=Zvg0F*kl~5Y$j7ps^hw;Nh^iK7SlT9y^(uY!uMG`>TmizoMxSC>8;icK$U@|z*&rVcycO(}N>yAfo?7^rJN*Yh|! z<<7B?a8d6^_IwaFtPtnZGo6E84k4vNKI0lxLJlYQF(3gw+%r5e5oNSbNXQpibRb5T z)kOpaeb8w4ht10$OiV?Xn{?rcs{z$m2Oe2_kYappSg89V2a5#>SVVD{a1@l3Xk)sK z6c5<98yFa_MC$CBRoQ+N{~VE`u@r{@L)BB*P4$_>qtvPx$5IQJ8S$&VfGc7GTkVfc zn?6Hj2{Qa!BGK^qq>zZr6yjb_G55GtMH@#Rrhv3yvl*e58AygCg>d*lriy)NVxj{} zmTy9>OR?g#l$2|Q8ImBK=X z7^DI`0{;Q@!aBoN&S8Eo&*JfgvuE=)1J4T0u$PfhwtqslDNGy|cViaBXe6<2BRo_(Kk>X`tb zdzkqr`+m?X_vNqsup3P5v9Yxvw+xMk8rTeos3B7?1&wcua-8&8?&DUIY`ezy0T)b4 zNrJYRL43vw8K02Axz6Qz>{46Jvxir?iCyWoQ&M6)?YcF$D93;eEM@1fl9al-I`AJ< z+C>#Ij5dMd5Zc;Z5W`3<-@5v*6nh4P?K!9*sLo1Ayh8AR$G7NOMm}d`q=@JN9Bu&z zt}THWcg*S-Ld=7t*f$KeQjn!4!GhK$rUVPy5-7%=ef!#gY&2ocb~~;%e962aY*C;P zVl$+PNeyVU4P%I~JW`1t#vM7Na4jw-V?J=53|dRi8(bK)N?E^MhfF#m!Dfe7*+yeIfClH6sq!sss+`K+Y6ZBawGX} z(-rWveE=d+P+q-AN}75!KX~`AEHhs%dlStZVk9r74Y3TCD>Sqbf2}We4WNk2&X}3- z^YcTWxd`+3NIGK~zU-S{uU?Z@h%+$;)7@7bP0^}5D=tp8?@o}I#I!MlSG(whqw)N6 zsks2cs48d$YUY1D}D4Q6JV zdV7t(C1a!QQ;J;;LI)61x5sg#EEit>de8Y4gLkNzuVNX@A?tE@a}P=DL6Ck|pxcIX zal`ucAK^5X4~vWlRd-&D*&;R2*ffReX)BZ-4lT@K!9GKO?I_BssS^7I^)zJ-4B?XK ziCY=-{?e zrf{qmAhYlhvp<5x0+B{H6a{^E55ElwVGNMYGNRKlwxwX8dz*lvLsl7A8GnPtP7GNL zoH~dP5hm9ZGM9o>3RvI>);#3^PBmae*PP)d_7ozs^d(8aS+Nb409&L&3oj;aw$*-I zdE)q~m}N~kTUH!3G(14UB9ex;$eJ+2O+R{TRu^)=rs^)Z^Ng9TE1)B~sY=y)KLwOZ zb&Mz1S+u%nXlsvx1&o;6$D;0>AhHt_uq1h;5CIqb!{f-v{z0n&U@>t$s$)3O z4&bJ|p1`t^5Z)AEFHN#y9H$y{)3|S0x_qeU?jqR*&?9#zXk;8CQbs6mTL0W$C-rdyKknRtp}Nen z_WVuH0|T?2eT-p-(d_AG&;q`k^52c}Ch_9GfxM94x(*%b2!+_YSZUuNOhbV+ zjP#W&zC459&TyK2w;Rf)cIihPqJ{lKcYI}AvR5>Q&r=9+ZCO0(kr_e)J2Vp6IXDWd zC$XAFpoTdL#c_sAC2r$|^nENsf3rHC`ljkeD;5?OUdP4T5&+SY)5v&X7#3p{SJr-A zN9P<4b}j*0PZmipeylPQxX=izPP04+rpfokAB2NI;A zt=jBpiy7ctGug?=w2_x|G52L|g+pOw(*)$7|6pdORY)2Cep+*K0OC(sj)RN}jj3W~ zVhh7`P@-Rzh1uQY{)Ad2mQW#b2h3~)cO<3d7B9?7CQw6SvRK_7gkO&D7b}^<(r`92 z)FQ-UM&h!!u^qKPd~+_(IO~kmeQ~nK1c3ggv87)|VJn$1!({4%4u}&0p8?ZiY?U6E z7qacVk`gy0sKB*8GMU%idHq`5Q5|l>FvL7G&dP-6#~`Y%0+kcvHGh)2s5Isp3QYQswv+W| zpho9X#l3jKP+Pn6N`-m|&juTBgc6!RxvEMbGy*etJwag;ks3w|k=|LP>+| zX?dA=dzQsFCm2>8UUq%SOY>#jvg_W|QaDc~9lH>=aS?sC6RTGC(Cm4!ooCE-d%F7B zu5`BNrQ&xEm5rw5KT9rQUB$-ADw7cuub@-dU_Ie4$nJkIF4}fJC_%q|CTjvIC!~+A zMw~f)+IRU>9VfaF<^8~!ee|X6bJ;cpc1liaiNBAJKdi)Gr0QO?U;<&fTqD@HKs3m` zohQz!Ub22RrLoq{KZ!s5QLyAaZlfN%XcaTH#4~!3rzOf5NZj?7r6DoE{=bAH^!Z!q zpeX!FRCB^q(^G=$RB9nK>aADO$Don>LG|g5Q@plv{m@oO2&~XP#9mc@Xeuu|o3)(G zR`{%#1I{52VgE{;%O*q-ZWc36AFgIEA>zU4P*>s*AA=RN);l%md+hvUsNpF8Z^aA2 zX9iSa(1DYIZtKDNDx^3>KLp9h46@32c(B)0SGUQ_k2pgGmDto^FqBv-{6GajRHu=0 z5xpFOxrJ^yNPiwiSy@+p2WAR z;v9INkRS&&DzI(*BdM6TR-z|{? zd~^s<5Ag`aLL*}1M7RUqtweo&21zrdq=|f-X6pHJ#hCY2H{8|A-11I9W#tJ=Pi54b zlz5#@Cj1vDVX~1Cw`4iRlD7Z6#BQNLqI8f${Z?s;NBrJLJu3zo^3z$;h}{S+afcr& z!uoXU+J*yGiOce=O*CLs6k#iX99CZcW-Xv_-_aG=^+xktK~b+3 zeBM6m&ZD_apTIf^ujQ8rf(#oj{(1lY4P=DAP~9U^YVb$xZ_HGKT6bYZ{P@HKpOP2= zT^ucX33}KJpk+LJ{rWYL<3^Vu9CAmw3ph7140r{??+3!oA+!`XJ|=mR4hHZ!3bdKTOpS;j(bqV_$^dfh-1h?XMp-;)e-`ka?X?m%t|j7>bVk4$Ry z-zb0U5}SsPxpqSrF*k(_W*Cy#ukT-Z!^QJ^S>uhnh{vSnFGUJY<&C^v-=jTtP~gSDM7Ji(hn zjl~~bkj9D#|FcZ>*bE%CE~S9nB(RZMZZY>l6W5@2^WOxwCvp=`2H62RlP__4GwFlYzwQD>@X}Q(F*r7S12)7ZPRv(Pg<$0B`OTWK?Y@YO8Z`~rP$q~) zX@l<`60^3vJniI6)K$b+kd5ff3EoPD+6{yiIOUBhGV9LbNf4cOy{Uiqw+vp^>tPg| zVc4{}Gw59GhHZf@aw^{=wkgEF0iT7)_Ye~hnJD|kg$GIiaZhw4xLG zO$R5`;I2kgmtZ>g5^n}-_)3e@CH9AGrVQ~GIZqt8Ahc%98XBZ&XiCD*wTf5@Kw9QU zwHlt%UL~sp^=SmAw;6i@AXP!^2r7lq2pe4}I=&;ts0fi}VG^^OyVDN?jmmvf&G@_N zvcWuYNX?L1z=cIjm4tc-h(_TJh~F$RU7TvA1KJ{S@k)a4*iqj))mkWJH!7$?r>^4Lnbo6Loi%XFFP-O`iU$2o>4x8g5=c$DY`1W7y{&eaX@J9 zMAZoQg<(9ZQVr+8=i+}VpLZd^7tVOpw!;21ZP z$F6##-wtxj-f~{lBwvH1IQsuI#6p*aUOw@@%+M^q=MGHU$Yd?(3F^BOBDAEx&;fwT z{VItlWuuYzw`P~a2Ow&&Z75gBgdyKB z%G_x~caNVqvFi;@&mzo)HEt@taAO+vN2Ugp90o<)d;1lE6daa?EjTjyD>(O_v0(+B z!&sT+RqM^0H{YsDijB21$lcWDpO~148_B(Q|H1QkTWwukdl(rRp+?<y@7mSA@FN30C070MRnFsN<3o}!OeHk|pzV&mdS&OJU>JlApUq-G`zmcs6Z4xQzfP8Jg&Syw1ux8fWjtt;RPIBx1>%vtV5w z8X^oYlUQYp*lrvD$G?Iu1j@WbfD{0nY^I%UTm)k`(Y(fydlRB7AiwyakN9;01)~Zv zBB_;wAf>t`?74`ZIij@J%YK|lI=G3jQ&aP_m4x;*aDneA=i;)Mamk32I^piW#-;i6oi8-$)B-WRu+ zSf)r&lM&SAAe9#Vb$8f@xVWD}g(zo<7-a4QW^L4yaS9Mw!W5J49U!oZ^L|;7h5b4) zD22#6eZ%bA!Coxi_U`Flca-9`^YQnwxjIWQ_f-LAdp+MA90xfO-3ldGjvtvDIw5|O zZWB7Ix)U)Nc#**UDaDWN5=UN3Y%THTh;Jb%CJ@1tA|U{02N66&@bO_%hs_>5(9^-= zf)ahfzFBwG;=)1+jG-S+aJqm09WG=F60mR7hyoBLLjy_znysfFO+kj9DEV?+3_hDs zEC!7{+YqP$pL0h6z#%PNBKc*!yZj6Qy?mKk&{PKOYMTR)D461+d)P zNVHwywYJ&hS3bSmxzQ=Bl^u$xP^?3a`#d5dq7AN@xXFr&iuH+LM88M>5e7N0!Mi{_ z5G^;ta=Qpk4x;PW+bbz5ue&b|FL@-xBa`1dk|VB)8X8@9JLn`_Yovp=vD zkb{2$GDi>Ark%ooz%JuPyb%keS-J9n-_##;ov7r9%p0)0*N|BagJKy>kZrD};~YSt zNX&;o`^H5su8d{t#yTb1XypCkU2OLEVi{;i4@Y#(|M+o7ZM1j_ZUL_Od8j$R|L`Gg zu+bUM0dd{zm9!+=C90R~oxkt7NK^!iGTR~%Mw`J`rJg8I|j5h49VK*DAs~SBPW2?;1H@;u82=e^g?(h#%&5_I1En5NaMLyP5h2@ zBS!4{o(1()wRBfJ-BQ zJpqme4EwE(Xd;HyZ~PR51jkMVKVOolPDOF>zqaD1HG;#Z!4s6_d53bO@2#MLu&Y9r z=!`ie1Om_j-LrSU_l`Y#K(*FiUdP$geDchhFZjR^e16V`^XDAaJZF>Y@3*^XmV9fIKn+5(82{pY!vpW+IIBGkACW98kzIl8=V=k`g6 zo`oa1l5qe3*!#|~DzB|uqETZ{>>@>F0|EkyfJ(JQbc6Jc6eC4Jq)8Ew5^F3JH%&wZ zrFRh#>6Qpm6zS4YM4EtwUhkO1oO8eL_x*W!a-L&|u=if?T64`g#+YN0wh*R%IF_sJ zQvdleZ?6T{s_}KM|K~EAN(;gqVx8NR!ax-xs@k}G@!~2>OH8GV7z5B-k#^mE3~?@( zF$R?y8W}Cl-ax;}drl#(J1=TRXa!gdaMZYz*Vy?@8v%3*3Jhd0q1D?l$J^WM7Otu0 zJa=7|nWd!w5R^InKjy@K8QA+&brX#mLPlF$Sf~K{}_%}Hl9Rs^W2DucX5PC5cMaD6V2H;@008E;b!TNXX75#~AkLS_Qdzka2GApiSo)#m^ItN+KE{J&Wcy-w}@$opJtFkAL14@h@|w?_ZtW$-Uz zuhPA_uk1~SNox4{qvrqXKd;hpWuJXuGBy|vm<*6X!m{#rwE3o_c_3KB^8E_K5`skd zQj5lke0ql%xa!wmHzMbSIeTnG&UZhgvtpD@v8ejRRPY#Y_aDLI$4ia!Z)~Ho+@CTe z0v=cp0z@OdG*4`SD?hT{tr!`e-Up+^27t?qZe+I0n+qUGIfaqV;Dcz45Og2+03qPs zFM0i18Hq{lj&hkuOoNjdOjEfd6^#O}^v#?8dyV7#@SLpxbu+1U3P@5Hsr|o?{D`SP z-cj4vJ+ph4%N+nr4LrbCtzR#|#2s4=iOPK(t<`qrQ2_oZ8UEf`u8YHZX$qA*jTFK= zrU^bYh=creF+J#Pt8*Q$OiUA@`Hf>WLtaUUFv*5l-|jWoVPqQA=YrrIkz%Q`xEk36 z|Hl*9^X9kN0Xju3a80GR0kZ`#Ujkxc8jIJ8R0BL&{K$kRCxC$F{-?0qNN%guKn-aG zJJJcXi&TLVpi%KJ3k$8Fl%~uXAZHDPIfonJ#Git2Du5JVj;9zT^RWPE%(J|}cmdrD z0>%jB=Z@ui*bp)`05sJASUlU!|3BVkTkAD%nkyhPJdOI6N!4LTTU!J&WtzDO+$=6B zdGRNq(-=@d2K4BtU{(gG%Ed2S$rlGsF*F!DD#C35tI)`KFmjgoIhp`}=^^Aj>L?@f zhiAgeo;}-(>bKZo23H&H3k;W?2%Zfm9}k0S^qd|$P6HHJ7y=Xz?^QE>Iox5$7Hfd~ zIpVRw?i*9xjKJYgt%%Yt+%gXZx)nigo|8|Y20+0Uhg}ngv^I3w_dl4#mS1M$Q(Kxd z5J?&`&%*lg;|8ILj+DK7^3I;h7xB3xaq-7>1aSL3!H?ECJbI2b}`s z7AKG)k##tgDJ5JoXSV5jV70aJA@Es|p5D6Gya0*)V;UI%yL0m7PgssEMyAGBR`7~7 zhocw-rto7l5V{bvq*P`{A7ESsuD$f!PoQq;ogopY-xoQIVVaJeZ#Y3S0x1&^&7Pg? zvHTR03aSe+y5ssO{0IGDKO}X?8CG#{SU;XZN=#tyaDTH9+&3UbHrwUv=cjtG<%8|!4mV`HY-nY@w z(Wag6imKb5ou^+1fm#nMg@t~x znl04OWV3Jf#~=5Ar*;9ZsfF+(4(PoitKK^WX*QvDUejZ-RQSMKn8^b4KqES!YluUP zn75)y41-!J+0%0dOgHSaeun~R7ByrC5{L#Up$?&BWEB(?Y>!JumC7#b_6Yg;CV{@8 z+j#HL2-)0@MQ+{VF=9lMph`W_zG4Z936)YJhEI-K)0k((DmokSh(N9$GO+c%5N7G(d^-U|24urz zVY;U{#UC2hdOrO>_wy^+IiB<)?4kZB3fF`v@bjidO%46+<_CCjV~_L#rp`Q;*o<<0hCn1TXRX#6GHsm`(U=_)$;Ds=g(UK`@wMfAiwI}YE&|qUAux< z8Km92-Gpg|;01fG?mBTJ*y*A&q!HD4Cxo|R2$F-7lQsgW7r94CHuL->vd32uzrE<9aQ_tCIu~eGvI;9F_?*Yk2!?xF$ugF z1dUvZHDnQt*}jt^6Ag|9Sp@Q)iLAZ`lp~6l*|1DinP&>U((KpnKYpZqi%PJxP9yzB zfDwFI`L}P~O2F`9nQxv`QzNZ~4j7%_U|4{=nZO-JH?AMtrZ*UQ&`?nk1X@&Nb}R+% z8^GzCF!5VzzchB_4`csFXU7~kF;ol_nd)#+UN1X{pg?C+tQyZFmgaclbkTGZq}COX z9c5rZ73VIyJ@S)ZXkyoDc6O_u({2*c7u<=qBT!v9u!pgJ>41ieRrHQS$b7mi% zfUw#LWaGFH0)fY*!u`n-qEsn|kAKk&2>m0muiL-0&P?xof$q2`_fQ0E5f|5j*TOg? zH?%zkHQc$|}!MI#0s?Cksl zCEu2iPdS=ixP-1ZkQq2vex~aw(P-Z=>`kzX0Blm1EVAA zOX}FfL@(G|R$2N)lv3*&%)&&n*)jH&-phSN10@!O;ii%&1o)epn|(UIeEDKHLac`- z(bCpX)^0ne-oI3A;gTiQc&EHfVu~nlIF*A}r5j3#P0{}pNes`9VC4m=w4vb-#EJrl9EUc< zP_7)PE8RMRbQ#eeBA6v{Hz$C$rMF=mfU1}R>4BobOjS2YZp|3|bD-uoGYR0*4}~>m zn0{BwxBqNmzgt7+uhGAa*@BLja{Xo&Gf_F$3wt&AcoDQKL4m={j_45Mk_QE}bRaH~ zyV1;yb^2Q0jvPLG9iNC%{3;?~Lrka*@`lsRD!3ogn?mGv^|W|zuDUJ{ZPMAJ6VPM6 zrRrhLnqW&iHJwv>1_lY3tcWj~rBZ_lNuZx4_V2E`E_qSgO(EVLkmb747=6~^&}AAs z?)*7U5fFT6U|#Pww3{Hmv_gO&1;OKqW%U02Zgty+mz}$KI5S#xmA!4@U3}D>yT+mg z(K9x)_pFQ@_5|oF?YGVN>rdtPyyqT6+UZ^N93Qqn#|xrU*EL@-JKln0pgT7Fjrgl5 zT$@Oeu2HH@lhrU9Dlj(o_+5?}&#P;Mt+uv8K3~^&bD3GmsW$(N45yeKys_zz4lAv@ zg-PCzr*N+nego6zb6q)f`|D+B{>(a@dH{mX^d$LXfB!<0PiXWUx-2BH>@?k$;3Xh* z{b6G;bplAT_$^5NB*y{ks_w(w^P!kvFO8|&1l$TYVcEmw$nLfUSM4yN0JrB4i~_OU zdR%f(iQ^GB{VJ)@njDv@mTzO5Fh(9G_~j$*v`TD?)}jIkLS&A}!H;=+vCSqPYFo^L zn)#cj&M7!hijKL|IF zqG`Rj(qWa4&N}g+IM$UCKJ>V^Ip0INsxNfjPtm>a(jtlCr^&`NV+$>6gu756LWr@d zjJjfsJQSQ#{M$FIpk~jGM>8>@cd*@~(4c9ac(;3qo}%~jSFl0Vle{y!@lN;Ot59^U z?=jOEXer)W+1tU7Yy(#sZ>!(z1K(mOR;sfYKV)|4pD?`$nOA}BTUEC&uU{a&zXAFn ztshu3H+T4g+9N2i(YrT&X#ON-KGF~*8jgzmO*{YW7)Ry}u8ro%T0u^CIIT@jM(A2U zU)!8FQ9tQ$dXw3wtj@a>OHq{b(N%=FfhNv4bVp^N7=%DVlWtC`nzC|oI!8ICm(mQH zzSES|%Z1$_`LUgGB$^7V zP6Kla-(5h$gb<>N>1yn|9nAiOVV-S44c_(;l9WW_Jhyq3DEZYM{4zV2=-ylC5GLkN zKC*3=Z{DLc>u5 zNn|$U(INuEXX4I${ovYCG_jpCiRfO8Yv`ERo}8342wiwLtqLwDz>~?yXK|ViP~i>) zk~AHN3!ne2n{0hPDCG#{2^6$jpSA#;a7S%K7eD0_cqhOp;}Pm;el86Nwk?9(E^Zma zj}zPXA722#>tJ(Vp=8B`VWKOzi(NLbJ)MFibA4&3xquHeZNVs%!|tiL$yQzAKiqq> zc6NiX&By?vB<%?(U<_KZB-Us|xBwDFO4gBeKm6^>X-JAFHzxA-fp<7`54{LFm=`{m zz&M$h9}Bw;J+`ObJ=jzg5e_!tBsx7@9XLQNGA%=GalKAH{k$7(S(IkrwLW%sjvSif ziBvLXmU;1T9h+7rI zDFt6VwP$-8sfnb;lnUqd!(lcU*~nrPBez8

IU=x>(Ggk$~g31*5oE{`3!6IsJ^7 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 z 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≧}6!EuXS;B5L?&#`b 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%0DCsDMCTvx7FnyC%LPb6Q(Q|&DGBeicRk~q5fpm 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Ξ9w7|EfhW}bm_mwl?M-={Uil9ulMlqu(Gmh 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

vk6}{cER+jGn(}zW4;Qc)C&h1K}0yi{SV7 zXVN+wpi`y?sEW7Ch)d}&9(x7N{A;Gyl#+jT_BwgQEi?%xXY*1v0ZH4JG((Mnc=S9+ zF(@(;6O=TM_z&pP|CSP66IJat>Gl^)tA2r^{_5F`s|MaBzscDMB+=>8N6Y`CX@ z+`^L2x1i-8=^&GGO{TQK+2A4ek z_2a)M0MASn`=*(B{{(z!1`b1&CzROJeXMk*!hoc`-6)IO_@>0u^+r4hn&{PUY27>a zp6!vtv*32JgH{$@Vzn&G`uwPIJQ+MX|5I@ztU;Wt4 z32d-|6sga0adB7CwA6gxhYO4!^g%BS{MumtGjzcHM005)Z|c*~H4Ud7VC)BbF9WS!=^sq$ATJ0xx z+E!HY18K47qt^x>Ou)KP;hLNG6|H5x4CB}Hr zwxk4*G}4xn27Hp5H+BuOMY;~UZ$}(TNu+JH+$0h3?m8gHg~%IWY1 zhSdaQJ9_@Lq4L=B^FbkLvAA#S=4n5COxp`#!E33EYdJacd(I@|Tr?`#H%)5aE5h#j zv4Eutkw$!Yq@%sik2K>0?eO$8=#3`6u>27lx+dxfV)tjh-gZ~{9#cY`oCt5J?G1^M z{$-OL8LfpEa647Kv<{{fhaeC(=m5ms#~${>5y{fB&^?p@CIv{UX&qJ210mQY2#`WZ z8$I%JT=)XhaF5o^Lp5yLwxjLz@-fEYT5vWnzxI8K0>mp&6i@EkaGN3?uFq?))*CM- zb!@E7;g-+myo5nJ8Q>zL&%s`S?{y0CI*ut3`FAM0CK^A05E7y?T~!@8Q)`lpy}sDR+#j zTf9zm;EsFXy!rcbqLg^q3urw3KKK<3x`)A_-3fSkgYnI$Hty?vI;$SdH=aP$zqv$+ zN}n3A7XfR${Qkl1ppoXw?;wd=K!zG#S`u0x*xqIvN;)T$jvH6`G&^5ie?L37bj8?o zvtZ%kyudr?HCW$pBx|$Q$>d3A!C2>Q$>}!i{&(7!5;7=Y0PA#bN3nFdzc6omfZ%yy z#U!!7n0!=!Trr__vd4E-`_K?-bAPC9Hxsd$Qtw?@;*P#_f5db(bguY8u3TP)h>OS^PBR-&+$o`H%{>4_8|C zrLZ@yZ#mA+6{W4eG?rG>^VtMT#~9N1rmP?n27D$F!RW7~0(fm-@uOsjqgjjl10wo- z(!E@#>~zZ%o6!wVkVFa56~Sxs~W<9 zkWT*rLSTZkd5=Ljk3P`YWjd*;Z46~b4R=yOpX1p~=>rdx@x`}-w}-se4J!yujsvQX zps9!B&cYslvh>UGJPg;Orl!T~W}&-$`Qt5lkK>FRAQ&lw%n5Xdk{xdX0%oJF0}&8+ zNP0H+39|;Suphv*xfK$SnbHGghlJ~etv)2M929j59REH^EmQg+$yE+ zz?5GZsf_|6{mEl8ZWi!fxA*Z;eFzR!IIzwa$xLZ>GT!t|ngie_bZ05$J}^S?+P_n! z{*{LAglV6jg+}yT_9sqsWL;UYTIuAW@ry? zSGRH=3g>aR3(xl%e#23CGA&NOzL({6fo1r#I4w+qcNU3AoE4Lks%#aL`o$kJ{v8w- zsub%l{Q3gatoI)EeP(BQq31wp7ZKoA0N?aBf>_jxHtc%0AH#haUi{|8<78n)vv^ae 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 K;lzXIuKW*mO^pcv literal 0 HcmV?d00001 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/scripts/create_big_table.txt b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/scripts/create_big_table.txt new file mode 100644 index 00000000..52b13b02 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/scripts/create_big_table.txt @@ -0,0 +1,43 @@ +[ + { + "name": "execution_timestamp", + "type": "TIMESTAMP", + "mode": "REQUIRED" + }, + { + "name": "execution_id", + "type": "STRING", + "mode": "REQUIRED" + }, + + { + "name": "ml_framework_info", + "type": "STRING", + "mode": "NULLABLE" + }, + { + "name": "benchmark_result", + "type": "STRING", + "mode": "NULLABLE" + }, + { + "name": "benchmark_info", + "type": "STRING", + "mode": "NULLABLE" + }, + { + "name": "setup_info", + "type": "STRING", + "mode": "NULLABLE" + }, + { + "name": "system_info", + "type": "STRING", + "mode": "NULLABLE" + }, + { + "name": "process_info", + "type": "STRING", + "mode": "NULLABLE" + } +] diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/scripts/generate-readme-header.sh b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/scripts/generate-readme-header.sh new file mode 100644 index 00000000..ab51a5fc --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/scripts/generate-readme-header.sh @@ -0,0 +1,275 @@ +#!/usr/bin/env bash + +# +# Steps: +# +# 1. Download corresponding html file for some README.md: +# curl -s $1 +# +# 2. Discard rows where no substring 'user-content-' (github's markup): +# awk '/user-content-/ { ... +# +# 3.1 Get last number in each row like ' ... sitemap.js.*<\/h/)+2, RLENGTH-5) +# +# 5. Find anchor and insert it inside "(...)": +# substr($0, match($0, "href=\"[^\"]+?\" ")+6, RLENGTH-8) +# + +gh_toc_version="0.6.0" + +gh_user_agent="gh-md-toc v$gh_toc_version" + +# +# Download rendered into html README.md by its url. +# +# +gh_toc_load() { + local gh_url=$1 + + if type curl &>/dev/null; then + curl --user-agent "$gh_user_agent" -s "$gh_url" + elif type wget &>/dev/null; then + wget --user-agent="$gh_user_agent" -qO- "$gh_url" + else + echo "Please, install 'curl' or 'wget' and try again." + exit 1 + fi +} + +# +# Converts local md file into html by GitHub +# +# ➥ curl -X POST --data '{"text": "Hello world github/linguist#1 **cool**, and #1!"}' https://api.github.com/markdown +#

Hello world github/linguist#1 cool, and #1!

'" +gh_toc_md2html() { + local gh_file_md=$1 + URL=https://api.github.com/markdown/raw + if [ -z "$GH_TOC_TOKEN" ]; then + TOKEN=$GH_TOC_TOKEN + else + TOKEN="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/token.txt" + fi + if [ -f "$TOKEN" ]; then + URL="$URL?access_token=$(cat $TOKEN)" + fi + # echo $URL 1>&2 + OUTPUT="$(curl -s --user-agent "$gh_user_agent" \ + --data-binary @"$gh_file_md" -H "Content-Type:text/plain" \ + $URL)" + + if [ "$?" != "0" ]; then + echo "XXNetworkErrorXX" + fi + if [ "$(echo "${OUTPUT}" | awk '/API rate limit exceeded/')" != "" ]; then + echo "XXRateLimitXX" + else + echo "${OUTPUT}" + fi +} + + +# +# Is passed string url +# +gh_is_url() { + case $1 in + https* | http*) + echo "yes";; + *) + echo "no";; + esac +} + +# +# TOC generator +# +gh_toc(){ + local gh_src=$1 + local gh_src_copy=$1 + local gh_ttl_docs=$2 + local need_replace=$3 + + if [ "$gh_src" = "" ]; then + echo "Please, enter URL or local path for a README.md" + exit 1 + fi + + + # Show "TOC" string only if working with one document + if [ "$gh_ttl_docs" = "1" ]; then + + echo "Table of Contents" + echo "=================" + echo "" + gh_src_copy="" + + fi + + if [ "$(gh_is_url "$gh_src")" == "yes" ]; then + gh_toc_load "$gh_src" | gh_toc_grab "$gh_src_copy" + if [ "${PIPESTATUS[0]}" != "0" ]; then + echo "Could not load remote document." + echo "Please check your url or network connectivity" + exit 1 + fi + if [ "$need_replace" = "yes" ]; then + echo + echo "!! '$gh_src' is not a local file" + echo "!! Can't insert the TOC into it." + echo + fi + else + local rawhtml=$(gh_toc_md2html "$gh_src") + if [ "$rawhtml" == "XXNetworkErrorXX" ]; then + echo "Parsing local markdown file requires access to github API" + echo "Please make sure curl is installed and check your network connectivity" + exit 1 + fi + if [ "$rawhtml" == "XXRateLimitXX" ]; then + echo "Parsing local markdown file requires access to github API" + echo "Error: You exceeded the hourly limit. See: https://developer.github.com/v3/#rate-limiting" + TOKEN="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/token.txt" + echo "or place github auth token here: $TOKEN" + exit 1 + fi + local toc=`echo "$rawhtml" | gh_toc_grab "$gh_src_copy"` + echo "$toc" + if [ "$need_replace" = "yes" ]; then + local ts="<\!--ts-->" + local te="<\!--te-->" + local dt=`date +'%F_%H%M%S'` + local ext=".orig.${dt}" + local toc_path="${gh_src}.toc.${dt}" + local toc_footer="" + # http://fahdshariff.blogspot.ru/2012/12/sed-mutli-line-replacement-between-two.html + # clear old TOC + sed -i${ext} "/${ts}/,/${te}/{//!d;}" "$gh_src" + # create toc file + echo "${toc}" > "${toc_path}" + echo -e "\n${toc_footer}\n" >> "$toc_path" + # insert toc file + if [[ "`uname`" == "Darwin" ]]; then + sed -i "" "/${ts}/r ${toc_path}" "$gh_src" + else + sed -i "/${ts}/r ${toc_path}" "$gh_src" + fi + echo + echo "!! TOC was added into: '$gh_src'" + echo "!! Origin version of the file: '${gh_src}${ext}'" + echo "!! TOC added into a separate file: '${toc_path}'" + echo + fi + fi +} + +# +# Grabber of the TOC from rendered html +# +# $1 — a source url of document. +# It's need if TOC is generated for multiple documents. +# +gh_toc_grab() { + # if closed is on the new line, then move it on the prev line + # for example: + # was: The command foo1 + #
+ # became: The command foo1 + sed -e ':a' -e 'N' -e '$!ba' -e 's/\n<\/h/<\/h/g' | + # find strings that corresponds to template + grep -E -o '//g' | sed 's/<\/code>//g' | + # now all rows are like: + # ... .*<\/h/)+2, RLENGTH-5) + href = substr($0, match($0, "href=\"[^\"]+?\"")+6, RLENGTH-7) + print sprintf("%*s", level*3, " ") "* [" text "](" gh_url href ")" }' | + sed 'y/+/ /; s/%/\\x/g')" +} + +# +# Returns filename only from full path or url +# +gh_toc_get_filename() { + echo "${1##*/}" +} + +# +# Options hendlers +# +gh_toc_app() { + local app_name=$(basename $0) + local need_replace="no" + + if [ "$1" = '--help' ] || [ $# -eq 0 ] ; then + echo "GitHub TOC generator ($app_name): $gh_toc_version" + echo "" + echo "Usage:" + echo " $app_name [--insert] src [src] Create TOC for a README file (url or local path)" + echo " $app_name - Create TOC for markdown from STDIN" + echo " $app_name --help Show help" + echo " $app_name --version Show version" + return + fi + + if [ "$1" = '--version' ]; then + echo "$gh_toc_version" + echo + echo "os: `lsb_release -d | cut -f 2`" + echo "kernel: `cat /proc/version`" + echo "shell: `$SHELL --version`" + echo + for tool in curl wget grep awk sed; do + printf "%-5s: " $tool + echo `$tool --version | head -n 1` + done + return + fi + + if [ "$1" = "-" ]; then + if [ -z "$TMPDIR" ]; then + TMPDIR="/tmp" + elif [ -n "$TMPDIR" -a ! -d "$TMPDIR" ]; then + mkdir -p "$TMPDIR" + fi + local gh_tmp_md + gh_tmp_md=$(mktemp $TMPDIR/tmp.XXXXXX) + while read input; do + echo "$input" >> "$gh_tmp_md" + done + gh_toc_md2html "$gh_tmp_md" | gh_toc_grab "" + return + fi + + if [ "$1" = '--insert' ]; then + need_replace="yes" + shift + fi + + for md in "$@" + do + echo "" + gh_toc "$md" "$#" "$need_replace" + done + + echo "" + echo "Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)" +} + +# +# Entry point +# +gh_toc_app "$@" diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/scripts/plot_process_info.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/scripts/plot_process_info.py new file mode 100644 index 00000000..1d62a2b7 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/perfzero/scripts/plot_process_info.py @@ -0,0 +1,74 @@ +#!/usr/bin/python +# +# 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. +# ============================================================================== + +"""Plot graph showing process metric values over time""" + +from __future__ import print_function + +import argparse +import sys +import json +import matplotlib.pyplot as plt +import matplotlib.backends.backend_pdf as backend_pdf +import matplotlib.ticker as tick + +colors=['b', 'r', 'g', 'c', 'pink'] + +def visualize(file_path): + + entries = [] + with open(file_path) as f: + entries = [json.loads(line) for line in f.readlines() if line.strip()] + + if not entries: + print('There is no data in file {}'.format(file_path)) + return + + pdf = backend_pdf.PdfPages("process_info.pdf") + idx = 0 + names = [name for name in entries[0].keys() if name != 'time'] + times = [entry['time'] for entry in entries] + + for name in names: + values = [entry[name] for entry in entries] + fig = plt.figure() + ax = plt.gca() + ax.yaxis.set_major_formatter(tick.ScalarFormatter(useMathText=True)) + plt.ticklabel_format(style='sci', axis='y', scilimits=(-2,3)) + plt.plot(times, values, colors[idx % len(colors)], marker='x', label=name) + plt.xlabel('Time (sec)') + plt.ylabel(name) + plt.ylim(ymin=0) + plt.legend(loc = 'upper left') + pdf.savefig(fig) + idx += 1 + + plt.show() + pdf.close() + print('Generated process_info.pdf from {}'.format(file_path)) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(usage='plot_process_info.py ' ) + parser.add_argument('file_path', type=str) + flags = parser.parse_args(sys.argv[1:]) + + + visualize(flags.file_path) + + + diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/README.md b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/README.md new file mode 100644 index 00000000..e7b74648 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/README.md @@ -0,0 +1,88 @@ +# tf_cnn_benchmarks: High performance benchmarks + +**Note: tf_cnn_benchmarks is no longer maintained.** + +tf_cnn_benchmarks contains TensorFlow 1 implementations of several popular +convolutional models, and is designed to be as fast as possible. +tf_cnn_benchmarks supports both running on a single machine or running in +distributed mode across multiple hosts. + +tf_cnn_benchmarks is no longer maintained. Although it will run with TensorFlow +2, it was written and optimized for TensorFlow 1, and has not been maintained +since TensorFlow 2 was released. For clean and easy-to-read TensorFlow 2 models, +please see the [TensorFlow Official +Models](https://github.com/tensorflow/models/tree/master/official). + +## Getting Started + +To run ResNet50 with synthetic data without distortions with a single GPU, run + +``` +python tf_cnn_benchmarks.py --num_gpus=1 --batch_size=32 --model=resnet50 --variable_update=parameter_server +``` + +Note that the master branch of tf_cnn_benchmarks occasionally requires the +latest nightly version of TensorFlow. You can install the nightly version by +running `pip install tf-nightly-gpu` in a clean environment, or by installing +TensorFlow from source. We sometimes will create a branch of tf_cnn_benchmarks, +in the form of cnn_tf_vX.Y_compatible, that is compatible with TensorFlow +version X.Y. For example, branch +[cnn_tf_v1.9_compatible](https://github.com/tensorflow/benchmarks/tree/cnn_tf_v1.9_compatible/scripts/tf_cnn_benchmarks) +works with TensorFlow 1.9. However, as tf_cnn_benchmarks is no longer +maintained, we will likely no longer create new branches. + +Some important flags are + +* model: Model to use, e.g. resnet50, inception3, vgg16, and alexnet. +* num_gpus: Number of GPUs to use. +* data_dir: Path to data to process. If not set, synthetic data is used. To + use Imagenet data use these + [instructions](https://github.com/tensorflow/models/tree/master/research/inception#getting-started) + as a starting point. +* batch_size: Batch size for each GPU. +* variable_update: The method for managing variables: parameter_server + ,replicated, distributed_replicated, independent +* local_parameter_device: Device to use as parameter server: cpu or gpu. + +To see the full list of flags, run `python tf_cnn_benchmarks.py --help`. + +To run ResNet50 with real data with 8 GPUs, run: + +``` +python tf_cnn_benchmarks.py --data_format=NCHW --batch_size=256 \ +--model=resnet50 --optimizer=momentum --variable_update=replicated \ +--nodistortions --gradient_repacking=8 --num_gpus=8 \ +--num_epochs=90 --weight_decay=1e-4 --data_dir=${DATA_DIR} --use_fp16 \ +--train_dir=${CKPT_DIR} +``` +This will train a ResNet-50 model on ImageNet with 2048 batch size on 8 +GPUs. The model should train to around 76% accuracy. + +## Running the tests + +To run the tests, run + +```bash +pip install portpicker +python run_tests.py && python run_tests.py --run_distributed_tests +``` + +Note the tests require portpicker. + +The command above runs a subset of tests that is both fast and fairly +comprehensive. Alternatively, all the tests can be run, but this will take a +long time: + +```bash +python run_tests.py --full_tests && python run_tests.py --full_tests --run_distributed_tests +``` + +We will run all tests on every PR before merging them, so it is not necessary +to pass `--full_tests` when running tests yourself. + +To run an individual test, such as method `testParameterServer` of test class +`TfCnnBenchmarksTest` of module `benchmark_cnn_test`, run + +```bash +python -m unittest -v benchmark_cnn_test.TfCnnBenchmarksTest.testParameterServer +``` diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/all_reduce_benchmark.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/all_reduce_benchmark.py new file mode 100644 index 00000000..b61838d7 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/all_reduce_benchmark.py @@ -0,0 +1,290 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Benchmarks the all-reduce algorithms of tf_cnn_benchmarks. + +tf_cnn_benchmarks uses all-reduce to aggregate gradients. This benchmark is +useful for benchmarking the performance of just this gradient aggregation, +instead of the entire model. All the flags that tf_cnn_benchmarks accepts are +also accepted by this script, although many are silently ignored. + +The number and shapes of the tensors all-reduced are those of the variables of +the model specified by the --model flag. +TODO(reedwm): Allow custom sizes to be specified. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + + +import os +import time + +from absl import app +from absl import flags as absl_flags +import tensorflow.compat.v1 as tf + +from tensorflow.python.ops import control_flow_ops +import benchmark_cnn +import cnn_util +import flags +from cnn_util import log_fn + + +absl_flags.DEFINE_integer('iters_per_step', 5, + 'Number of iterations to run all-reduce for, per ' + 'step. Every step, a session will be run on a Graph ' + 'that contains this many copies of the all-reduce. ' + 'The copies are run sequentially. Setting this above ' + '1 is useful to lower the overhead of starting the ' + 'session run, running the VariableV2 ops at the ' + 'start of the step, etc.') + + +flags.define_flags() +for name in flags.param_specs.keys(): + absl_flags.declare_key_flag(name) + + +def get_var_shapes(model): + """Returns the list of variable shapes for a tf_cnn_benchmarks Model.""" + with tf.Graph().as_default(): + # The variable shapes do not depend on the batch size. + images = tf.placeholder(tf.float32, model.get_input_shapes('train')[0]) + model.build_network([images]) + return [[int(d) for d in v.shape.dims] for v in tf.trainable_variables()] + + +def all_reduce(all_device_tensors, variable_mgr): + """Performs a single batch all-reduce. + + Args: + all_device_tensors: List of lists of tensors. all_device_tensors[t][i] is + a tensor, where t is the tower the tensor is on and i is the index of + the tensor. + variable_mgr: The VariableMgr to perform the all-reduce. + Returns: + List of list of tensors in the same form as `all_device_tensors`, except the + tensors are aggregated across towers. + """ + tower_grads = [[(g, None) for g in device_tensors] for + device_tensors in all_device_tensors] + _, aggregated_tower_grads = variable_mgr.preprocess_device_grads(tower_grads) + return [ + [g for g, _ in agg_device_tensors] + for agg_device_tensors in aggregated_tower_grads] + + +def build_all_reduce_iterations(all_device_tensors, tower_devices, variable_mgr, + num_iters): + """Builds the all-reduce ops for multiple iterations to aggregate tensors. + + The tensors in `all_device_tensors` are aggregated `num_iters` times. Each + iteration aggregates the results from the previous iteration. The iterations + are run sequentially, so the aggregations for an iteration do not start + running until the previous iteration has completed. Each iteration after the + first is aggregating already-aggregated values, but it does not matter because + we are only aggregating for benchmarking purposes. + + Args: + all_device_tensors: List of lists of tensors. all_device_tensors[t][i] is + a tensor, where t is the tower the tensor is on and i is the index of + the tensor. + tower_devices: A list of device strings. tower_devices[t] is the device + of the tensors in all_device_tensors[t]. + variable_mgr: The VariableMgr to perform the all-reduce. + num_iters: Number of iterations to aggregate tensors for. + Returns: + An op that when run, causes the all-reduce ops to run. + """ + for i in range(num_iters): + with tf.name_scope('iteration_%d' % i): + # Step 1: Do the aggregation. + with tf.name_scope('tensor_aggregation'): + all_device_tensors = all_reduce(all_device_tensors, variable_mgr) + + # Step 2. Create identity ops, to bring the aggregated results back to + # each device. + new_all_device_tensors = [] + for device, device_tensors in zip(tower_devices, all_device_tensors): + with tf.device(device): + new_all_device_tensors.append([ + tf.identity(t, name='identity_after_allreduce') + for t in device_tensors + ]) + all_device_tensors = new_all_device_tensors + + # Step 3. Add control dependencies to delay the next iteration until this + # iteration is complete. To avoid extra overhead, we do not have any + # cross-device control dependencies, which means it's possible for two + # iterations to slightly overlap. + new_all_device_tensors = [] + for device_tensors in all_device_tensors: + new_all_device_tensors.append([ + control_flow_ops.with_dependencies( + device_tensors, t, name='identity_after_dependencies') + for t in device_tensors + ]) + all_device_tensors = new_all_device_tensors + + # To prevent the dependency optimizer from removing every op we created, + # we store the results in variables. + ops_to_run = [] + for device, device_tensors in zip(tower_devices, all_device_tensors): + with tf.device(device): + for t in device_tensors: + # The placeholder initial value is never run. + var = tf.Variable(tf.placeholder(tf.float32, t.shape), collections=[]) + ops_to_run.append(var.assign(t)) + return tf.group(*ops_to_run) + + +def build_graph(tower_devices, tensor_shapes, variable_mgr, num_iters): + """Builds the graph for the benchmark. + + Args: + tower_devices: A list of device strings of the devices to run the all-reduce + benchmark on. + tensor_shapes: A list of shapes of the tensors that will be aggregated for + the all-reduce. + variable_mgr: The VariableMgr to perform the all-reduce. + num_iters: Number of iterations to aggregate tensors for. + Returns: + An op that runs the benchmark. + """ + all_device_tensors = [] + for i, tower_device in enumerate(tower_devices): + with tf.device(tower_device): + device_tensors = [] + for j, shape in enumerate(tensor_shapes): + tensor = tf.Variable(tf.random_normal(shape, dtype=tf.float32), + name='tensor_%d_on_device_%d' % (j, i)) + device_tensors.append(tensor) + all_device_tensors.append(device_tensors) + + log_fn('Building all-reduce ops') + benchmark_op = build_all_reduce_iterations(all_device_tensors, tower_devices, + variable_mgr, num_iters) + log_fn('Done building all-reduce ops') + return benchmark_op + + +def run_graph(benchmark_op, bench_cnn, init_ops, dummy_loss_op): + """Runs the graph for the benchmark. + + Args: + benchmark_op: An op that runs the benchmark. + bench_cnn: The BenchmarkCNN where params and other attributes are obtained. + init_ops: A list of ops that are run before `benchmark_op` for + initialization. + dummy_loss_op: Any op. We must pass a loss op to + `benchmark_cnn.benchmark_one_step`, but the result of the op is never + actually used. + """ + config = benchmark_cnn.create_config_proto(bench_cnn.params) + with tf.Session(config=config) as sess: + for op in init_ops: + sess.run(op) + step_train_times = [] + fetches = {'average_loss': dummy_loss_op, 'benchmark_op': benchmark_op} + log_fn('Running warmup') + for i in range(-bench_cnn.num_warmup_batches, bench_cnn.num_batches): + if i == 0: + log_fn('Running all-reduce ops') + start = time.perf_counter() + if i > 0 and i % bench_cnn.params.display_every == 0: + log_fn('Iteration: %d. Average time per step so far: %s' % + (i, (time.perf_counter() - start) / i)) + # Call benchmark_one_step instead of directly calling sess.run(...), to + # potentially get a trace file, partitioned graphs, etc. + benchmark_cnn.benchmark_one_step( + sess=sess, + fetches=fetches, + step=i, + # The batch size is only used for the images/sec calculation, which is + # not actually calculated because we pass show_images_per_sec=False. + batch_size=None, + step_train_times=step_train_times, + trace_filename=bench_cnn.trace_filename, + partitioned_graph_file_prefix=( + bench_cnn.params.partitioned_graph_file_prefix), + profiler=None, + image_producer=None, + params=bench_cnn.params, + show_images_per_sec=False) + log_fn('Average time per step: %s' % + ((time.perf_counter() - start) / bench_cnn.num_batches)) + + +def run_benchmark(bench_cnn, num_iters): + """Runs the all-reduce benchmark. + + Args: + bench_cnn: The BenchmarkCNN where params, the variable manager, and other + attributes are obtained. + num_iters: Number of iterations to do all-reduce for for. + + Raises: + ValueError: Invalid params of bench_cnn. + """ + if bench_cnn.params.variable_update != 'replicated': + raise ValueError('--variable_update=replicated must be specified to use' + 'the all-reduce benchmark') + if bench_cnn.params.variable_consistency == 'relaxed': + raise ValueError('--variable_consistency=relaxed is not supported') + + benchmark_op = build_graph(bench_cnn.raw_devices, + get_var_shapes(bench_cnn.model), + bench_cnn.variable_mgr, num_iters) + init_ops = [ + tf.global_variables_initializer(), + bench_cnn.variable_mgr.get_post_init_ops() + ] + loss_op = tf.no_op() + + if bench_cnn.graph_file: + path, filename = os.path.split(bench_cnn.graph_file) + as_text = filename.endswith('txt') + log_fn('Writing GraphDef as %s to %s' % ( + 'text' if as_text else 'binary', bench_cnn.graph_file)) + tf.train.write_graph(tf.get_default_graph().as_graph_def(add_shapes=True), + path, filename, as_text) + + run_graph(benchmark_op, bench_cnn, init_ops, loss_op) + + +# TODO(reedwm): Reduce redundancy with tf_cnn_benchmarks +def main(positional_arguments): + # Command-line arguments like '--distortions False' are equivalent to + # '--distortions=True False', where False is a positional argument. To prevent + # this from silently running with distortions, we do not allow positional + # arguments. + assert len(positional_arguments) >= 1 + if len(positional_arguments) > 1: + raise ValueError('Received unknown positional arguments: %s' + % positional_arguments[1:]) + + params = benchmark_cnn.make_params_from_flags() + params = benchmark_cnn.setup(params) + bench = benchmark_cnn.BenchmarkCNN(params) + + tfversion = cnn_util.tensorflow_version_tuple() + log_fn('TensorFlow: %i.%i' % (tfversion[0], tfversion[1])) + + run_benchmark(bench, absl_flags.FLAGS.iters_per_step) + +if __name__ == '__main__': + tf.disable_v2_behavior() + app.run(main) # Raises error on invalid flags, unlike tf.app.run() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/all_reduce_benchmark_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/all_reduce_benchmark_test.py new file mode 100644 index 00000000..c8efd53f --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/all_reduce_benchmark_test.py @@ -0,0 +1,52 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 all_reduce_benchmark.py.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import tensorflow.compat.v1 as tf + +import all_reduce_benchmark +import benchmark_cnn +import test_util + + +class AllReduceBenchmarkTest(tf.test.TestCase): + """Tests the all-reduce benchmark.""" + + def _test_run_benchmark(self, params): + """Tests that run_benchmark() runs successfully with the params.""" + logs = [] + with test_util.monkey_patch(all_reduce_benchmark, + log_fn=test_util.print_and_add_to_list(logs)): + bench_cnn = benchmark_cnn.BenchmarkCNN(params) + all_reduce_benchmark.run_benchmark(bench_cnn, num_iters=5) + self.assertRegex(logs[-1], '^Average time per step: [0-9.]+$') + + def test_run_benchmark(self): + """Tests that run_benchmark() runs successfully.""" + params = benchmark_cnn.make_params(num_batches=10, + variable_update='replicated', + num_gpus=2) + self._test_run_benchmark(params) + params = params._replace(hierarchical_copy=True, gradient_repacking=8, + num_gpus=8) + self._test_run_benchmark(params) + +if __name__ == '__main__': + tf.disable_v2_behavior() + tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/allreduce.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/allreduce.py new file mode 100644 index 00000000..10560328 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/allreduce.py @@ -0,0 +1,649 @@ +# 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. +# ============================================================================== +"""Utilities for allreduce.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import collections as pycoll +import re + +from six.moves import xrange # pylint: disable=redefined-builtin +import tensorflow.compat.v1 as tf + +# pylint: disable=g-direct-tensorflow-import,g-import-not-at-top +try: + from tensorflow.python.distribute.v1 import all_reduce +except ImportError: + # Compatibility with TF 2.4 and below + from tensorflow.python.distribute import all_reduce +from tensorflow.python.framework import device as pydev +from tensorflow.python.framework import ops +from tensorflow.python.ops import collective_ops + +AllReduceSpecTuple = pycoll.namedtuple('AllReduceSpecTuple', 'alg shards limit') + + +def parse_general_int(s): + """Parse integer with power-of-2 suffix eg. 32k.""" + mo = re.match(r'(\d+)([KkMGT]?)$', s) + if mo: + i, suffix = mo.group(1, 2) + v = int(i) + if suffix: + if suffix == 'K' or suffix == 'k': + v *= 1024 + elif suffix == 'M': + v *= (1024 * 1024) + elif suffix == 'G': + v *= (1024 * 1024 * 1024) + elif suffix == 'T': + v *= (1024 * 1024 * 1024 * 1024) + else: + raise ValueError('invalid integer string %s' % s) + return v + else: + v = int(s) + return v + + +def parse_all_reduce_spec(all_reduce_spec): + """Parse all_reduce_spec. + + Args: + all_reduce_spec: a string specifying a combination of all-reduce + algorithms to apply for gradient reduction. + + Returns: + a list of AllReduceSpecTuple. + + Raises: + ValueError: all_reduce_spec is not well-formed. + + An all_reduce_spec has BNF form: + int ::= positive whole number + g_int ::= int[KkMGT]? + alg_spec ::= alg | alg#int + range_spec ::= alg_spec | alg_spec/alg_spec + spec ::= range_spec | range_spec:g_int:range_spec + + Not all syntactically correct specifications are supported. + Examples of supported all_reduce_spec strings, with semantics explained: + + 'collective' == apply tf.collective_reduce operator to all tensors. + 'collective#2' == apply tf.collective_reduce operator to all tensors, + requesting up to 2 simultaneous transfers at each node, if + feasible, by subdividing tensor by an additional factor of 2. + 'xring' == apply ring all-reduce to all tensors + 'xring#2' == apply ring all-reduce to all tensors, using two simultaneous + transfer rings, each operating on 1/2 of each tensor. + 'nccl' == apply NCCL all-reduce to all tensors (only works within + a single worker process where all devices are GPUs) + 'nccl/xring' == apply NCCL all-reduce to all tensors within each worker + to produce at least one full-reduced (locally) value, + then apply ring all-reduce to one such value from each + worker, then apply NCCL broadcast to propagate those globally + reduced values back to every device within each worker. + 'pscpu' == Shuffle reduce using worker CPUs as the gather devices: each + distributed tensor is reduced by copying all instances to + one of the worker CPUs, computing the reduction there, then + copying back to each participating device. Tensor reductions + are assigned to specific CPUs round-robin. + 'psgpu#4' == Arrange all GPUs across all workers into groups of 4. + Each distributed tensor is shuffle reduced against one + such group of 4 GPUs, selected round-robin. That is, each + tensor is split across 4 shards for the reduction. + 'pscpu:2k:pscpu#2:64k:xring' == Apply single-shard pscpu to + tensors of size <= 2048 elements, apply 2-shard pscpu to + tensors up to size 64k elements, apply xring to larger tensors. + 'pscpu/pscpu#2' == Use shuffle gather to locally reduce each tensor on + the worker's CPU, then use 2-shard shuffle to reduce those + locally reduced tensors across workers (on the worker CPUs), then + scatter the globally reduced values locally from each worker CPU. + """ + range_parts = all_reduce_spec.split(':') + ['-1'] + if len(range_parts) % 2: + raise ValueError('all_reduce_spec not well formed: %s' % all_reduce_spec) + limit = 0 + spec = [] + alg = None + shards = 1 + for i, range_part in enumerate(range_parts): + if i % 2 == 1: + try: + limit = parse_general_int(range_part) + spec.append(AllReduceSpecTuple(alg=alg, shards=shards, limit=limit)) + except ValueError: + raise ValueError('all_reduce_spec (%s) contains non-integer range %s' % + (all_reduce_spec, range_part)) + else: + alg = range_part + alg_parts = range_part.split('#') + alg = alg_parts[0] + if len(alg_parts) > 1: + try: + shards = int(alg_parts[1]) + except ValueError: + raise ValueError('all_reduce_spec (%s) contains non-integer ' + 'shards %s' % all_reduce_spec, alg_parts[1]) + else: + shards = 1 + if alg not in [ + 'nccl', 'nccl/xring', 'nccl/rechd', 'nccl/pscpu', 'xring', 'pscpu', + 'psgpu', 'pscpu/pscpu', 'collective' + ]: + raise ValueError('all_reduce_spec (%s) contains invalid alg %s' % + (all_reduce_spec, alg)) + return spec + + +def build_all_reduce_device_prefixes(job_name, num_tasks): + """Build list of device prefix names for all_reduce. + + Args: + job_name: 'worker', 'ps' or 'localhost'. + num_tasks: number of jobs across which device names should be generated. + + Returns: + A list of device name prefix strings. Each element spells out the full + host name without adding the device. + e.g. '/job:worker/task:0' + """ + if job_name != 'localhost': + return ['/job:%s/task:%d' % (job_name, d) for d in range(0, num_tasks)] + else: + assert num_tasks == 1 + return ['/job:%s' % job_name] + + +def group_device_names(devices, group_size): + """Group device names into groups of group_size. + + Args: + devices: list of strings naming devices. + group_size: int >= 1 + + Returns: + list of lists of devices, where each inner list is group_size long, + and each device appears at least once in an inner list. If + len(devices) % group_size = 0 then each device will appear + exactly once. + + Raises: + ValueError: group_size > len(devices) + """ + num_devices = len(devices) + if group_size > num_devices: + raise ValueError('only %d devices, but group_size=%d' % (num_devices, + group_size)) + num_groups = ( + num_devices // group_size + (1 if (num_devices % group_size != 0) else 0)) + groups = [[] for i in range(num_groups)] + for i in range(0, num_groups * group_size): + groups[i % num_groups].append(devices[i % num_devices]) + return groups + + +def split_grads_by_size(threshold_size, device_grads): + """Break gradients into two sets according to tensor size. + + Args: + threshold_size: int size cutoff for small vs large tensor. + device_grads: List of lists of (gradient, variable) tuples. The outer + list is over devices. The inner list is over individual gradients. + + Returns: + small_grads: Subset of device_grads where shape is <= theshold_size + elements. + large_grads: Subset of device_grads where shape is > threshold_size + elements. + """ + small_grads = [] + large_grads = [] + for dl in device_grads: + small_dl = [] + large_dl = [] + for (g, v) in dl: + tensor_size = g.get_shape().num_elements() + if tensor_size <= threshold_size: + small_dl.append([g, v]) + else: + large_dl.append([g, v]) + if small_dl: + small_grads.append(small_dl) + if large_dl: + large_grads.append(large_dl) + return small_grads, large_grads + + +_instance_key = 1 + + +def new_collective_instance_key(): + """Returns a new instance key for use in defining a collective op.""" + global _instance_key + v = _instance_key + _instance_key += 1 + return v + + +_group_key = 1 +_group_key_table = dict() + + +def collective_group_key(devices): + """Returns a group key for the set of devices. + + Args: + devices: list of strings naming devices in a collective group. + + Returns: + int key uniquely identifying the set of device names. + """ + global _group_key + global _group_key_table + parsed = [pydev.DeviceSpec.from_string(d) for d in devices] + names = sorted(['%s:%d' % (d.device_type, d.device_index) for d in parsed]) + concat = ','.join(names) + if concat not in _group_key_table.keys(): + new_key = _group_key + _group_key += 1 + _group_key_table[concat] = new_key + rv = _group_key_table[concat] + return rv + + +def build_collective_reduce(input_tensors, num_workers, num_shards, + red_op='Add', un_op='Id'): + """Build a subgraph that does one full all-reduce, using the collective Op. + + Args: + input_tensors: tensors within a single worker graph that are to be reduced + together; must be one per device. + num_workers: total number of workers with identical independent graphs that + will be doing this same reduction. The reduction will actually include + the corresponding tensors at all these workers. + num_shards: number of shards into which to divide each per-tick chunk, + normally 1 but could be higher on multi-data-path architectures. + red_op: string naming the reduction op + un_op: string naming the unary final op + + Returns: + An array of final tensors, one per device, computed by the full reduction. + + Raises: + ValueError: There must be at least two tensors over all the workers. + """ + group_size = len(input_tensors) * num_workers + if group_size < 2: + raise ValueError('num_workers * len(input_tensors) must be 2 or greater') + devices = [t.device for t in input_tensors] + num_devices = len(devices) + group_key = collective_group_key(devices) + instance_key = new_collective_instance_key() + out_tensors = [] + if num_shards == 1: + subdiv_offsets = [0] + elif num_shards == 2: + if num_devices > 1: + subdiv_offsets = [0, -(num_devices // 2)] + else: + subdiv_offsets = [0] + else: + raise ValueError('Unsupported num_shards %d' % num_shards) + for d in range(num_devices): + with ops.device(devices[d]): + reduce_op = collective_ops.all_reduce(input_tensors[d], + group_size, group_key, instance_key, + red_op, un_op, + subdiv_offsets) + out_tensors.append(reduce_op) + return out_tensors + + +def broadcast_send(t, shape, dtype, group_size, group_key, instance_key): + return collective_ops.broadcast_send(t, shape, dtype, group_size, group_key, + instance_key) + + +def broadcast_recv(shape, dtype, group_size, group_key, instance_key): + return collective_ops.broadcast_recv(shape, dtype, group_size, group_key, + instance_key) + + +def sum_grad_and_var_all_reduce(single_session, + grad_and_vars, + num_workers, + alg, + gpu_indices, + aux_devices=None, + num_shards=1): + """Apply all-reduce algorithm over specified gradient tensors.""" + scaled_grads = [g for g, _ in grad_and_vars] + if alg == 'collective': + assert not single_session + summed_grads = build_collective_reduce( + scaled_grads, num_workers, num_shards, 'Add', 'Id') + else: + with tf.name_scope('allreduce'): + # Note that each grad_and_vars looks like the following: + # ((grad0_gpu0, var0_gpu0), ... , (grad0_gpuN, var0_gpuN)) + if alg == 'nccl': + summed_grads = all_reduce.build_nccl_all_reduce(scaled_grads, tf.add) + elif alg == 'xring': + summed_grads = all_reduce.build_ring_all_reduce( + scaled_grads, num_workers, num_shards, gpu_indices, tf.add) + elif alg == 'nccl/xring': + summed_grads = all_reduce.build_nccl_then_ring(scaled_grads, num_shards, + tf.add) + elif alg == 'nccl/rechd': + summed_grads = all_reduce.build_nccl_then_recursive_hd( + scaled_grads, tf.add) + elif alg == 'nccl/pscpu': + summed_grads = all_reduce.build_nccl_then_shuffle( + scaled_grads, aux_devices, tf.add, tf.add_n) + elif alg == 'pscpu/pscpu': + summed_grads = all_reduce.build_shuffle_then_shuffle( + scaled_grads, + aux_devices, + # TODO(tucker): devise a way of better specifying the device set + # for the second level. + [aux_devices[0]], + tf.add_n) + elif alg in ['pscpu', 'psgpu']: + summed_grads = all_reduce.build_shuffle_all_reduce( + scaled_grads, aux_devices, tf.add_n) + else: + raise ValueError('unsupported all_reduce alg: ', alg) + + result = [] + for (_, v), g in zip(grad_and_vars, summed_grads): + result.append([g, v]) + return result + + +def contains_any(haystack, needles): + """Tests if any needle is a substring of haystack. + + Args: + haystack: a string + needles: list of strings + + Returns: + True if any element of needles is a substring of haystack, + False otherwise. + """ + for n in needles: + if n in haystack: + return True + return False + + +def sum_gradients_all_reduce(single_session, + dev_prefixes, + tower_grads, + num_workers, + alg, + num_shards, + gpu_indices, + agg_small_grads_max_bytes=0, + agg_small_grads_max_group=10, + allreduce_merge_scope=1): + """Apply all-reduce algorithm over specified gradient tensors. + + Args: + single_session: true if reduction is applied to one graph across + all workers, false if ths application is to a single-worker graph only. + dev_prefixes: list of prefix strings to use to generate PS device names. + tower_grads: the gradients to reduce. + num_workers: number of worker processes across entire job. + alg: the all-reduce algorithm to apply. + num_shards: alg-specific sharding factor. + gpu_indices: indices of local GPUs in order usable for ring-reduce. + agg_small_grads_max_bytes: largest tensor eligible for aggregation, + in number of bytes. + agg_small_grads_max_group: largest permitted aggregation of small + tensors. + allreduce_merge_scope: size of groups into which to partition consecutive + gradients grouped under a common 'allreduce' name scope for application + of ScopedAllocator optimization. + + Returns: + list of reduced tensors + """ + alg_contains_shuffle = contains_any(alg, ['pscpu', 'psgpu']) + is_hierarchical = '/' in alg + if 'pscpu' in alg: + aux_devices = [prefix + '/cpu:0' for prefix in dev_prefixes] + elif 'psgpu' in alg: + aux_devices = [ + prefix + '/gpu:%d' % i + for i in range(len(gpu_indices)) + for prefix in dev_prefixes + ] + else: + aux_devices = ['/job:localhost/cpu:0'] + aux_device_groups = group_device_names( + aux_devices, + num_shards if (alg != 'collective' and alg_contains_shuffle) else 1) + group_index = 0 + if agg_small_grads_max_bytes > 0 and agg_small_grads_max_group > 0: + tower_grads, packing = pack_small_tensors( + tower_grads, + max_bytes=agg_small_grads_max_bytes, + max_group=agg_small_grads_max_group) + else: + packing = None + reduced_gv_list = [] + gv = list(zip(*tower_grads)) + merge_scope = allreduce_merge_scope if allreduce_merge_scope > 0 else 1 + chunked_gv = [gv[x:x + merge_scope] + for x in xrange(0, len(gv), merge_scope)] + for chunk in chunked_gv: + with tf.name_scope('allreduce'): + for grad_and_vars in chunk: + reduced_gv_list.append(sum_grad_and_var_all_reduce( + single_session, + grad_and_vars, num_workers, alg, gpu_indices, + (aux_devices if is_hierarchical + else aux_device_groups[group_index]), + num_shards)) + group_index = (group_index + 1) % len(aux_device_groups) + new_tower_grads = [list(x) for x in zip(*reduced_gv_list)] + if packing: + new_tower_grads = unpack_small_tensors(new_tower_grads, packing) + return new_tower_grads + + +def extract_ranges(index_list, range_size_limit=32): + """Extract consecutive ranges and singles from index_list. + + Args: + index_list: List of monotone increasing non-negative integers. + range_size_limit: Largest size range to return. If a larger + consecutive range exists it will be returned as multiple + ranges. + + Returns: + ranges, singles where ranges is a list of [first, last] pairs of + consecutive elements in index_list, and singles is all of the + other elements, in original order. + """ + if not index_list: + return [], [] + first = index_list[0] + last = first + ranges = [] + singles = [] + for i in index_list[1:]: + if i == last + 1 and (last - first) <= range_size_limit: + last = i + else: + if last > first: + ranges.append([first, last]) + else: + singles.append(first) + first = i + last = i + if last > first: + ranges.append([first, last]) + else: + singles.append(first) + return ranges, singles + + +GradPackTuple = pycoll.namedtuple('GradPackTuple', 'indices vars shapes') + + +def pack_range(key, packing, grad_vars, rng): + """Form the concatenation of a specified range of gradient tensors. + + Args: + key: Value under which to store meta-data in packing that will be used + later to restore the grad_var list structure. + packing: Dict holding data describing packed ranges of small tensors. + grad_vars: List of (grad, var) pairs for one tower. + rng: A pair of integers giving the first, last indices of a consecutive + range of tensors to be packed. + + Returns: + A tensor that is the concatenation of all the specified small tensors. + """ + to_pack = grad_vars[rng[0]:rng[1] + 1] + members = [] + variables = [] + restore_shapes = [] + with tf.name_scope('pack'): + for g, v in to_pack: + variables.append(v) + restore_shapes.append(g.shape) + with tf.device(g.device): + members.append(tf.reshape(g, [-1])) + packing[key] = GradPackTuple( + indices=range(rng[0], rng[1] + 1), + vars=variables, + shapes=restore_shapes) + with tf.device(members[0].device): + return tf.concat(members, 0) + + +def unpack_grad_tuple(gv, gpt): + """Unpack a previously packed collection of gradient tensors. + + Args: + gv: A (grad, var) pair to be unpacked. + gpt: A GradPackTuple describing the packing operation that produced gv. + + Returns: + A list of (grad, var) pairs corresponding to the values that were + originally packed into gv, maybe following subsequent operations like + reduction. + """ + elt_widths = [x.num_elements() for x in gpt.shapes] + with tf.device(gv[0][0].device): + with tf.name_scope('unpack'): + splits = tf.split(gv[0], elt_widths) + unpacked_gv = [] + for idx, s in enumerate(splits): + unpacked_gv.append((tf.reshape(s, gpt.shapes[idx]), gpt.vars[idx])) + return unpacked_gv + + +def pack_small_tensors(tower_grads, max_bytes=0, max_group=0): + """Concatenate small gradient tensors together for reduction. + + Args: + tower_grads: List of lists of (gradient, variable) tuples. + max_bytes: Int giving max number of bytes in a tensor that + may be considered small. + max_group: Int giving max number of small tensors that may be + concatenated into one new tensor. + + Returns: + new_tower_grads, packing where new_tower_grads is identical to + tower_grads except that all feasible small_tensors have been removed + from their places and concatenated into larger tensors that are + now in the front of the list for each tower, and packing contains + the data necessary to restore the tower_grads structure. + + Look through the first tower for gradients of the same type (float), + and small size, that are all sequential. For each such group, + replace by a new tensor that is a flattened concatenation. Note + that the corresponding variable will be absent, which doesn't matter + because it isn't used during all-reduce. + + Requires: + Every gv_list in towers must have isomorphic structure including identical + tensor sizes and types. + """ + small_indices = [] + large_indices = [] + for idx, (g, _) in enumerate(tower_grads[0]): + if g.dtype == tf.float32 and (4 * g.shape.num_elements()) <= max_bytes: + small_indices.append(idx) + else: + large_indices.append(idx) + small_ranges, small_singles = extract_ranges( + small_indices, range_size_limit=max_group) + large_indices = sorted(large_indices + small_singles) + num_gv = len(tower_grads[0]) + packing = {} + if small_ranges: + new_tower_grads = [] + for dev_idx, gv_list in enumerate(tower_grads): + assert len(gv_list) == num_gv + new_gv_list = [] + for r in small_ranges: + key = '%d:%d' % (dev_idx, len(new_gv_list)) + new_gv_list.append((pack_range(key, packing, gv_list, r), + 'packing_var_placeholder')) + for i in large_indices: + new_gv_list.append(gv_list[i]) + new_tower_grads.append(new_gv_list) + return new_tower_grads, packing + else: + return tower_grads, None + + +def unpack_small_tensors(tower_grads, packing): + """Undo the structure alterations to tower_grads done by pack_small_tensors. + + Args: + tower_grads: List of List of (grad, var) tuples. + packing: A dict generated by pack_small_tensors describing the changes + it made to tower_grads. + + Returns: + new_tower_grads: identical to tower_grads except that concatentations + of small tensors have been split apart and returned to their original + positions, paired with their original variables. + """ + if not packing: + return tower_grads + new_tower_grads = [] + num_devices = len(tower_grads) + num_packed = len(packing.keys()) // num_devices + for dev_idx, gv_list in enumerate(tower_grads): + new_gv_list = gv_list[num_packed:] + for i in xrange(0, num_packed): + k = '%d:%d' % (dev_idx, i) + gpt = packing[k] + gv = unpack_grad_tuple(gv_list[i], gpt) + for gi, idx in enumerate(gpt.indices): + assert idx == gpt.indices[gi] + new_gv_list.insert(idx, gv[gi]) + new_tower_grads.append(new_gv_list) + return new_tower_grads diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/allreduce_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/allreduce_test.py new file mode 100644 index 00000000..c414e48f --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/allreduce_test.py @@ -0,0 +1,448 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 tf_cnn_benchmark.allreduce.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import collections as pycoll + +import numpy as np +import tensorflow.compat.v1 as tf +from tensorflow.python.framework import ops +from tensorflow.python.framework import test_util +from tensorflow.python.ops import variables +import allreduce + + +class AllReduceTest(tf.test.TestCase): + + def testGroupKey(self): + d0 = ['/job:worker/replica:0/task:0/device:GPU:1', + '/job:worker/replica:0/task:0/device:GPU:0', + '/job:worker/replica:0/task:0/device:GPU:3',] + d1 = ['/job:worker/replica:0/task:1/device:GPU:1', + '/job:worker/replica:0/task:1/device:GPU:0', + '/job:worker/replica:0/task:1/device:GPU:3',] + d2 = ['/job:worker/replica:0/task:1/device:GPU:1', + '/job:worker/replica:0/task:1/device:GPU:3', + '/job:worker/replica:0/task:1/device:GPU:0',] + d3 = ['/job:worker/replica:0/task:1/device:GPU:1', + '/job:worker/replica:0/task:1/device:GPU:3', + '/job:worker/replica:0/task:1/device:GPU:2',] + d4 = ['/job:worker/task:0/device:GPU:1', + '/job:worker/task:0/device:GPU:2', + '/job:worker/task:0/device:GPU:3',] + d5 = ['/job:worker/task:0/device:CPU:1', + '/job:worker/task:0/device:CPU:2'] + d6 = ['/job:worker/task:0/device:CPU:2', + '/job:worker/task:0/device:CPU:1'] + g0 = allreduce.collective_group_key(d0) + g1 = allreduce.collective_group_key(d1) + g2 = allreduce.collective_group_key(d2) + g3 = allreduce.collective_group_key(d3) + g4 = allreduce.collective_group_key(d4) + g5 = allreduce.collective_group_key(d5) + g6 = allreduce.collective_group_key(d6) + self.assertEqual(g0, g1) + self.assertEqual(g0, g2) + self.assertNotEqual(g0, g3) + self.assertEqual(g3, g4) + self.assertEqual(g5, g6) + self.assertNotEqual(g4, g5) + + def testExtractRanges(self): + x = [] + expected_ranges = [] + expected_singles = [] + ranges, singles = allreduce.extract_ranges(x) + self.assertEqual(expected_ranges, ranges) + self.assertEqual(expected_singles, singles) + x = [1, 3, 4, 6, 7, 8, 9] + expected_ranges = [[3, 4], [6, 9]] + expected_singles = [1] + ranges, singles = allreduce.extract_ranges(x) + self.assertEqual(expected_ranges, ranges) + self.assertEqual(expected_singles, singles) + x = [1, 2, 3, 4, 6, 7, 8, 9] + expected_ranges = [[1, 4], [6, 9]] + expected_singles = [] + ranges, singles = allreduce.extract_ranges(x) + self.assertEqual(expected_ranges, ranges) + self.assertEqual(expected_singles, singles) + x = [1, 3, 4, 6, 7, 9] + expected_ranges = [[3, 4], [6, 7]] + expected_singles = [1, 9] + ranges, singles = allreduce.extract_ranges(x) + self.assertEqual(expected_ranges, ranges) + self.assertEqual(expected_singles, singles) + x = [1, 3, 6, 9] + expected_ranges = [] + expected_singles = [1, 3, 6, 9] + ranges, singles = allreduce.extract_ranges(x) + self.assertEqual(expected_ranges, ranges) + self.assertEqual(expected_singles, singles) + + def testPackRange(self): + packing = {} + t0 = tf.constant([0, 1, 2, 3], dtype=tf.float32) + t1 = tf.constant([4, 5, 6, 7], dtype=tf.float32) + + gv = [(t0, 'v0'), (t1, 'v1')] + new_t = allreduce.pack_range('0:0', packing, gv, [0, 1]) + self.assertEqual(1, new_t.shape.ndims) + self.assertEqual(8, new_t.shape.dims[0]) + self.assertEqual( + packing, { + '0:0': + allreduce.GradPackTuple( + indices=range(2), + vars=['v0', 'v1'], + shapes=[tf.TensorShape([4]), + tf.TensorShape([4])]) + }) + + t2 = tf.constant([[0, 1, 2], [3, 4, 5], [6, 7, 8]], dtype=tf.float32) + t3 = tf.constant([[0, 1, 2], [3, 4, 5], [6, 7, 8]], dtype=tf.float32) + gv = [(t0, 'v0'), (t1, 'v1'), (t2, 'v2'), (t3, 'v3')] + packing = {} + new_t = allreduce.pack_range('1:0', packing, gv, [0, 3]) + self.assertEqual(1, new_t.shape.ndims) + self.assertEqual(26, new_t.shape.dims[0]) + self.assertEqual( + packing, { + '1:0': + allreduce.GradPackTuple( + indices=range(4), + vars=['v0', 'v1', 'v2', 'v3'], + shapes=[ + tf.TensorShape([4]), + tf.TensorShape([4]), + tf.TensorShape([3, 3]), + tf.TensorShape([3, 3]) + ]) + }) + + def testUnpackGradTuple(self): + packing = { + '0:0': + allreduce.GradPackTuple( + indices=range(4), + vars=['v0', 'v1', 'v2', 'v3'], + shapes=[ + tf.TensorShape([4]), + tf.TensorShape([4]), + tf.TensorShape([3, 3]), + tf.TensorShape([3, 3]) + ]) + } + tc = tf.constant([0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8], dtype=tf.float32) + packed_gv = [tc, 'packing_var_placeholder'] + gv = allreduce.unpack_grad_tuple(packed_gv, packing['0:0']) + self.assertLen(gv, 4) + self.assertEqual('v0', gv[0][1]) + self.assertEqual('v1', gv[1][1]) + self.assertEqual('v2', gv[2][1]) + self.assertEqual('v3', gv[3][1]) + self.assertEqual(1, gv[0][0].shape.ndims) + self.assertEqual(4, gv[0][0].shape.dims[0]) + self.assertEqual(1, gv[1][0].shape.ndims) + self.assertEqual(4, gv[1][0].shape.dims[0]) + self.assertEqual(2, gv[2][0].shape.ndims) + self.assertEqual(3, gv[2][0].shape.dims[0]) + self.assertEqual(3, gv[2][0].shape.dims[1]) + + def testPackSmallTensors(self): + t0 = tf.constant([0, 1, 2, 3], dtype=tf.float32) + t1 = tf.constant([4, 5, 6, 7], dtype=tf.float32) + t2 = tf.constant([[0, 1, 2], [3, 4, 5], [6, 7, 8]], dtype=tf.float32) + t3 = tf.constant([[0, 1, 2], [3, 4, 5], [6, 7, 8]], dtype=tf.float32) + tower_grads = [] + for d in range(0, 3): + gv = [(t0, 'v_%d_0' % d), (t1, 'v_%d_1' %d), (t2, 'v_%d_2' %d), + (t3, 'v_%d_3' % d)] + tower_grads.append(gv) + + # 1) Set the size limit so small that nothing gets concatenated. + new_tower_grads, packing = allreduce.pack_small_tensors( + tower_grads, max_bytes=12, + max_group=10) + self.assertEqual(tower_grads, new_tower_grads) + self.assertIs(packing, None) + + # 2) Set the size limit so only the first two tensors get concatenated + new_tower_grads, packing = allreduce.pack_small_tensors( + tower_grads, max_bytes=16, # 16 bytes == 4 elements + max_group=10) + self.assertLen(new_tower_grads, 3) + self.assertLen(tower_grads[0], 4) + first_tower = new_tower_grads[0] + self.assertLen(first_tower, 3) + self.assertEqual(1, first_tower[0][0].shape.ndims) + self.assertEqual(8, first_tower[0][0].shape.dims[0]) + self.assertEqual(packing, + {'0:0': allreduce.GradPackTuple( + indices=range(2), + vars=['v_0_0', 'v_0_1'], + shapes=[tf.TensorShape([4]), + tf.TensorShape([4])]), + '1:0': allreduce.GradPackTuple( + indices=range(2), + vars=['v_1_0', 'v_1_1'], + shapes=[tf.TensorShape([4]), + tf.TensorShape([4])]), + '2:0': allreduce.GradPackTuple( + indices=range(2), + vars=['v_2_0', 'v_2_1'], + shapes=[tf.TensorShape([4]), + tf.TensorShape([4])])}) + + # 3) Set the size limit so all tensors get concatenated + new_tower_grads, packing = allreduce.pack_small_tensors( + tower_grads, max_bytes=256, # bytes = 64 elements + max_group=10) + self.assertLen(new_tower_grads, 3) + self.assertLen(tower_grads[0], 4) + self.assertLen(new_tower_grads[0], 1) + first_tower = new_tower_grads[0] + self.assertEqual(1, first_tower[0][0].shape.ndims) + self.assertEqual(26, first_tower[0][0].shape.dims[0]) + self.assertEqual(packing, + {'0:0': allreduce.GradPackTuple( + indices=range(4), + vars=['v_0_0', 'v_0_1', 'v_0_2', 'v_0_3'], + shapes=[tf.TensorShape([4]), + tf.TensorShape([4]), + tf.TensorShape([3, 3,]), + tf.TensorShape([3, 3,])]), + '1:0': allreduce.GradPackTuple( + indices=range(4), + vars=['v_1_0', 'v_1_1', 'v_1_2', 'v_1_3'], + shapes=[tf.TensorShape([4]), + tf.TensorShape([4]), + tf.TensorShape([3, 3,]), + tf.TensorShape([3, 3,])]), + '2:0': allreduce.GradPackTuple( + indices=range(4), + vars=['v_2_0', 'v_2_1', 'v_2_2', 'v_2_3'], + shapes=[tf.TensorShape([4]), + tf.TensorShape([4]), + tf.TensorShape([3, 3,]), + tf.TensorShape([3, 3,])])}) + + def testUnpackSmallTensors(self): + packing = {'0:0': allreduce.GradPackTuple(indices=range(2), + vars=['v_0_0', 'v_0_1'], + shapes=[tf.TensorShape([4]), + tf.TensorShape([4])]), + '0:1': allreduce.GradPackTuple(indices=range(3, 5), + vars=['v_0_3', 'v_0_4'], + shapes=[tf.TensorShape([3, 3,]), + tf.TensorShape([3, 3,])]), + '1:0': allreduce.GradPackTuple(indices=range(2), + vars=['v_1_0', 'v_1_1'], + shapes=[tf.TensorShape([4]), + tf.TensorShape([4])]), + '1:1': allreduce.GradPackTuple(indices=range(3, 5), + vars=['v_1_3', 'v_1_4'], + shapes=[tf.TensorShape([3, 3,]), + tf.TensorShape([3, 3,])])} + t0 = tf.constant([0, 1, 2, 3, 4, 5, 6, 7], dtype=tf.float32) + t1 = tf.constant([17, 17], dtype=tf.float32) + t2 = tf.constant([0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8], dtype=tf.float32) + t3 = tf.constant([0], dtype=tf.float32) + tower_grads = [] + for d in range(0, 2): + one_tower = [(t0, 'packing_var_placeholder'), + (t2, 'packing_var_placeholder'), + (t1, 'v_%d_2' % d), (t3, 'v_%d_5' %d)] + tower_grads.append(one_tower) + new_tower_grads = allreduce.unpack_small_tensors(tower_grads, packing) + self.assertLen(new_tower_grads, 2) + for d, tg in enumerate(new_tower_grads): + self.assertLen(tg, 6) + self.assertEqual('v_%d_0' % d, tg[0][1]) + self.assertEqual('v_%d_1' % d, tg[1][1]) + self.assertEqual('v_%d_2' % d, tg[2][1]) + self.assertEqual('v_%d_3' % d, tg[3][1]) + self.assertEqual('v_%d_4' % d, tg[4][1]) + self.assertEqual('v_%d_5' % d, tg[5][1]) + self.assertEqual(1, tg[0][0].shape.ndims) + self.assertEqual(4, tg[0][0].shape.dims[0]) + self.assertEqual(1, tg[1][0].shape.ndims) + self.assertEqual(4, tg[1][0].shape.dims[0]) + self.assertEqual(1, tg[2][0].shape.ndims) + self.assertEqual(2, tg[2][0].shape.dims[0]) + self.assertEqual(2, tg[3][0].shape.ndims) + self.assertEqual(3, tg[3][0].shape.dims[0]) + self.assertEqual(3, tg[3][0].shape.dims[1]) + self.assertEqual(2, tg[4][0].shape.ndims) + self.assertEqual(3, tg[4][0].shape.dims[0]) + self.assertEqual(3, tg[4][0].shape.dims[1]) + self.assertEqual(1, tg[5][0].shape.ndims) + self.assertEqual(1, tg[5][0].shape.dims[0]) + + +class DynamicPackingTest(test_util.TensorFlowTestCase): + """Packing/Unpacking tests that require executing a TensorFlow session.""" + + def _init_tensors(self, num_towers, tensor_shapes): + """Construct a collection of tensors across multiple devices.""" + num_tensors = len(tensor_shapes) + consts = [] + tensors = [] + vrbls = [] + tower_grads = [] + tf.Variable([-1], dtype=tf.int32, name='packing_var_placeholder') + for dev_idx in range(0, num_towers): + devname = '/job:localhost/device:GPU:%d' % dev_idx + consts.append([]) + tensors.append([]) + vrbls.append([]) + with tf.device(devname): + base_value = 0 + gv_tuples = [] + for t_idx in range(0, num_tensors): + shape = tensor_shapes[t_idx] + num_elts = 0 + for d in shape: + num_elts = (num_elts or 1) * d + c = np.fromiter(range(base_value, base_value + num_elts), + dtype=np.float32).reshape(shape) + base_value += num_elts + consts[dev_idx].append(c) + tensors[dev_idx].append(tf.constant(c)) + vrbls[dev_idx].append( + tf.Variable(c, name='v_d%d_t%d' % (dev_idx, t_idx))) + gv_tuples.append((tensors[dev_idx][-1], vrbls[dev_idx][-1])) + tower_grads.append(gv_tuples) + return tower_grads, consts, tensors, vrbls + + _test_tuple = pycoll.namedtuple('_test_tuple', + 'num_devices, in_shapes out_shapes out_i') + + def _do_pack_unpack_test(self, tt): + """Do a single pack-unpack test. + + Args: + tt: A _test_tuple defining the parameters of the test to do. + + This test executes a graph that performs a pack of tower_grads + followed by an unpack and verifies that the shapes and values + of gradient tensors are unchanged, along with paired variables. + """ + with ops.Graph().as_default(): + tower_grads, consts, _, vrbls = self._init_tensors( + tt.num_devices, tt.in_shapes) + packed_tg, packing = allreduce.pack_small_tensors( + tower_grads, max_bytes=40, max_group=10) + unpacked_tg = allreduce.unpack_small_tensors(packed_tg, packing) + with self.test_session() as sess: + sess.run(variables.global_variables_initializer()) + packed = sess.run(packed_tg) + for d in range(0, tt.num_devices): + for t in range(0, len(tt.out_shapes)): + num_elts = 0 + for dim in tt.out_shapes[t]: + num_elts = (num_elts or 1) * dim + self.assertTrue(np.array_equal( + np.array(range(tt.out_i[t], tt.out_i[t] + num_elts), + dtype=np.float32).reshape(tt.out_shapes[t]), + packed[d][t][0])) + unpacked = sess.run(unpacked_tg) + for d in range(0, tt.num_devices): + for t in range(0, len(tt.in_shapes)): + self.assertTrue(np.array_equal(consts[d][t], unpacked[d][t][0])) + self.assertEqual(vrbls[d][t], unpacked_tg[d][t][1]) + + def testPackUnpack0(self): + self._do_pack_unpack_test( + self._test_tuple(num_devices=3, + in_shapes=[[8], [3, 3], [12], [5, 5, 5]], + out_shapes=[[17], [12], [5, 5, 5]], + out_i=[0, 17, 29])) + + def testPackUnpack1(self): + self._do_pack_unpack_test( + self._test_tuple(num_devices=4, + in_shapes=[[5, 5, 5], [2, 3], [5]], + out_shapes=[[11], [5, 5, 5]], + out_i=[125, 0])) + + def testPackUnpack2(self): + self._do_pack_unpack_test( + self._test_tuple(num_devices=2, + in_shapes=[[5, 5, 5], [2, 3], [1, 5], [7], [100]], + out_shapes=[[18], [5, 5, 5], [100]], + out_i=[125, 0, 143])) + + def _do_all_reduce_pack_test(self, tt): + """Test that all-reduce results are the same with or without packing.""" + with ops.Graph().as_default(): + tower_grads, consts, _, _ = self._init_tensors( + tt.num_devices, tt.in_shapes) + dev_prefixes = ['/job:localhost'] + num_workers = 1 + alg = 'xring' + shards = 1 + single_session = True + gpu_indices = range(0, tt.num_devices) + assert len(gpu_indices) == len(tower_grads) + no_pack_all_reduce = allreduce.sum_gradients_all_reduce( + single_session, + dev_prefixes, tower_grads, num_workers, alg, shards, + gpu_indices, + agg_small_grads_max_bytes=0, agg_small_grads_max_group=1) + packed_tg, packing = allreduce.pack_small_tensors(tower_grads, 100, 100) + packed_all_reduce = allreduce.sum_gradients_all_reduce( + single_session, + dev_prefixes, packed_tg, num_workers, alg, shards, + gpu_indices, + agg_small_grads_max_bytes=0, agg_small_grads_max_group=1) + unpacked_tg = allreduce.unpack_small_tensors(packed_all_reduce, packing) + with self.test_session() as sess: + sess.run(variables.global_variables_initializer()) + no_pack_values = sess.run(no_pack_all_reduce) + pack_unpack_values = sess.run(unpacked_tg) + for d in range(1, tt.num_devices): + for t in range(0, len(tt.in_shapes)): + self.assertTrue(np.allclose(no_pack_values[d][t][0], + tt.num_devices * consts[0][t])) + self.assertTrue(np.array_equal(no_pack_values[d][t][0], + pack_unpack_values[d][t][0])) + + def testAllReducePacked0(self): + self._do_all_reduce_pack_test( + self._test_tuple(num_devices=3, + in_shapes=[[8], [3, 3], [12], [5, 5, 5]], + out_shapes=[[17], [12], [5, 5, 5]], + out_i=[0, 17, 29])) + + def testAllReducePacked1(self): + self._do_all_reduce_pack_test( + self._test_tuple(num_devices=2, + in_shapes=[[8], [3, 3], [12], [5, 5, 5], [3], [4]], + out_shapes=[[17], [7], [12], [5, 5, 5]], + out_i=[0, 17, 29, 154, 157])) + + +if __name__ == '__main__': + tf.disable_v2_behavior() + tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/batch_allreduce.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/batch_allreduce.py new file mode 100644 index 00000000..a2cbe68d --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/batch_allreduce.py @@ -0,0 +1,628 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Contains classes and functions for doing a single-machine batch all-reduce. + +An all-reduce is taking the reduction (typically a sum) of a list of tensors, +each on a different device. The result must end up back on each device, which is +where the word "all" comes from. In summary, each device starts with a single +tensor, and ends up with the reduction of all tensors. + +A batch all-reduce is doing several independent all-reduces. When doing a batch +all-reduce, care is taken to evenly distribute the reduction computations +across devices and inter-device tensor transfers across device links. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +# TODO(reedwm): Support distributed all-reduces in this file. +# TODO(reedwm): Merge this code with allreduce.py, which contains some batch +# all-reduce code that this file calls. allreduce.py also supports distributed +# batch-reduce while this file only supports single-machine all-reduce. + +import abc + +import six +import tensorflow.compat.v1 as tf + +from tensorflow.python.ops import data_flow_ops +import allreduce +import constants + + +def _all_reduce_using_copy(tensors_across_devices, use_mean): + """Does an all-reduce of a list of tensors by copying to the current device. + + The tensors are copied to the current device and then reduced. + + Args: + tensors_across_devices: A list of tensors, each on a different device. + use_mean: Whether to take the mean of the tensors instead of a sum: + Returns: + A reduced tensor on the current device. + """ + reduced_tensor = tf.add_n(tensors_across_devices) + if use_mean: + reduced_tensor *= 1 / len(tensors_across_devices) + return reduced_tensor + + +@six.add_metaclass(abc.ABCMeta) +class BatchAllReduceAlgorithm(object): + """Represents an algorithm for performing a batch all-reduce operation.""" + + def batch_all_reduce(self, + all_device_tensors, + num_splits, + compact_tensors, + defer_tensors, + xla_compile=False): + """Performs a batch all-reduce. + + The reduction done is a sum. + + `all_device_tensors` is a list of list of tensors that will be batch + all-reduced. All tensors within a single inner list must be on the same + device. The nth element in each list, for any n, will be reduced together. + The return value is in the same form as `all_device_tensors`, except that + each tensor is reduced. + + For example, if `all_device_tensors` is: + [[ A, B ], # A and B are on GPU 0 + [ C, D ]] # C and D are on GPU 1 + + Then the return value will be: + [[ A+C, B+D ], # These two tensors are on GPU 0 + [ A+C, B+D ]] # These two tensors are on GPU 1 + + Args: + all_device_tensors: A list of list of tensors. `all_device_tensors[i][j]` + is a tensor where `i` is the device index and `j` is the tensor index. + num_splits: If not None, tensors will be concatenated and split into this + many pieces during the all-reduce, then split back into their original + shapes afterwards. Has no impact on correctness and can improve + performance. Requires all tensors to be the same type. + compact_tensors: If True, tensors are casted to fp16 before being all- + reduced. Improves performance, but hurts numerical stability. + defer_tensors: If True, every time the return value + `reduced_all_device_tensors` is evaluated, the result will be the + reduced tensors values of `all_device_tensors` from the previous session + run instead of the current session run, or zero on the first session + run. This can improve performance. When training neural networks, + deferring gradients often does not harm training, so this can be used to + improve performance. + xla_compile: If True, use XLA to compile gradients packing and unpacking + ops. + + Returns: + reduced_all_device_tensors: A list in the same form as + `all_device_tensors`, except each tensor has been reduced. + warmup_ops: A list of ops needed to be run once before the all-reduce can + occur. + """ + + # Before all-reducing tensors, we do several preprocessing functions that + # can speed up the all-reduce. We undo these functions after all-reducing + # the tensors. + + # all_device_packed_tensors is a 2-d list of tensors indexed by + # [device_id][tensor_id], holding packed tensors from all devices involved + # in all-reduce. + all_device_packed_tensors = [] + + # all_device_warmup_ops is a 2-d list of ops indexed by + # [device_id][tensor_id], holding warmup_ops that need to be run once before + # all-reduce can occur. + all_device_warmup_ops = [] + + # all_device_put_ops is a 2-d list of ops indexed by + # [device_id][tensor_id], holding put ops for deferred tensors. They will be + # called in each all-reduce step automatically due to control dependency. + all_device_put_ops = [] + + # packers is a list of _TensorPacker, one for each device involved in + # all-reduce. + packers = [ + _TensorPacker(num_splits, compact_tensors) for _ in all_device_tensors + ] + + for packer, device_tensors in zip(packers, all_device_tensors): + + def pack_single_device_tensors(packer=packer, + device_tensors=device_tensors): + """Pack gradient tensors of a device.""" + packed_tensors = packer.maybe_concat_tensors(device_tensors) + packed_tensors = packer.maybe_compact_tensors(packed_tensors) + # When xla_compile=False, defer tensors after concat for better + # performance. + if defer_tensors and not xla_compile: + packed_tensors, put_ops, warmup_ops = defer_single_device_tensors( + packed_tensors) + all_device_put_ops.append(put_ops) + all_device_warmup_ops.append(warmup_ops) + packed_tensors = packer.maybe_split_tensors(packed_tensors) + return packed_tensors + + with tf.device(device_tensors[0].device): + if xla_compile: + packed_tensors = tf.xla.experimental.compile( + pack_single_device_tensors) + # When xla_compile=True, intermediate tensors in packing process are + # not materialized. Thus, we defer tensors after packing process is + # completed instead of in the middle of it. + if defer_tensors: + packed_tensors, put_ops, warmup_ops = defer_single_device_tensors( + packed_tensors) + all_device_put_ops.append(put_ops) + all_device_warmup_ops.append(warmup_ops) + else: + packed_tensors = pack_single_device_tensors() + + all_device_packed_tensors.append(packed_tensors) + + # Perform all-reduce on packed tensors. + all_device_tensors = self._do_batch_all_reduce(all_device_packed_tensors) + + all_device_unpacked_tensors = [] + for packer, device_tensors in zip(packers, all_device_tensors): + + def unpack_single_device_tensors(packer=packer, + device_tensors=device_tensors): + """Unpack gradient tensors of a device.""" + unpacked_tensors = packer.undo_maybe_split_tensors(device_tensors) + unpacked_tensors = packer.undo_maybe_compact_tensors(unpacked_tensors) + unpacked_tensors = packer.undo_maybe_concat_tensors(unpacked_tensors) + return unpacked_tensors + + with tf.device(device_tensors[0].device): + if xla_compile: + unpacked_device_tensor = tf.xla.experimental.compile( + unpack_single_device_tensors) + else: + unpacked_device_tensor = unpack_single_device_tensors() + + all_device_unpacked_tensors.append(unpacked_device_tensor) + + # Note: There is no undo operation for deferring tensors. But we do need to + # call _add_put_op_control_deps at the end if we deferred the tensors. + if defer_tensors: + all_device_unpacked_tensors = _add_put_op_control_deps( + all_device_unpacked_tensors, num_splits, all_device_put_ops) + + return all_device_unpacked_tensors, all_device_warmup_ops + + @abc.abstractmethod + def _do_batch_all_reduce(self, all_device_tensors): + """Performs a batch all-reduce. + + Unlike `self.batch_all_reduce`, this does not do any preprocessing of the + tensors. + + Args: + all_device_tensors: A list of list of tensors. `all_device_tensors[i][j]` + is a tensor where `i` is the device index and `j` is the tensor index. + Returns: + reduced_all_device_tensors: A list in the same form as + `all_device_tensors`, except each tensor has been reduced. + """ + pass + + +class CopyToDeviceAlgorithm(BatchAllReduceAlgorithm): + """An algorithm that copies tensors to be reduced to a specific device.""" + + def __init__(self, devices_to_reduce_on, use_mean=False): + self._devices = devices_to_reduce_on + self._use_mean = use_mean + + def _do_batch_all_reduce(self, all_device_tensors): + reduced_tensors = [] + for i, tensors_across_devices in enumerate(zip(*all_device_tensors)): + with tf.device(self._devices[i % len(self._devices)]): + reduced_tensor = _all_reduce_using_copy(tensors_across_devices, + self._use_mean) + reduced_tensors.append(reduced_tensor) + # The tensors will be brought back to each device once they are used. + return [reduced_tensors] * len(all_device_tensors) + + +class HierarchicalCopyAlgorithm(BatchAllReduceAlgorithm): + """An algorithm that uses hierarchical copies. This is only optimized for + eight devices connected in NetworkTopology.DGX1 or NetworkTopology.GCP_V100 + topology. + """ + + def __init__(self, network_topology): + """Initializer for HierarchicalCopyAlgorithm. + + Args: + network_topology: An instance of Enum class constants.NetworkTopology. + """ + self._network_topology = network_topology + + def _do_batch_all_reduce(self, all_device_tensors): + avail_devices = [device_tensors[0].device + for device_tensors in all_device_tensors] + reduced_tensors = [] + num_devices = len(avail_devices) + group_size = num_devices // 2 + for i, tensors_across_devices in enumerate(zip(*all_device_tensors)): + group_0_main_device, group_1_main_device = self.__get_main_devices( + i, num_devices) + if group_0_main_device < group_size: + group_0_begin = 0 + group_1_begin = group_size + else: + group_0_begin = group_size + group_1_begin = 0 + + # Reduce the first group. + group_0_tensors = tensors_across_devices[group_0_begin: + group_0_begin + group_size] + with tf.device(avail_devices[group_0_main_device]): + group_0_reduced_tensor = _all_reduce_using_copy(group_0_tensors, False) + + # Reduce the second group. + group_1_tensors = tensors_across_devices[group_1_begin: + group_1_begin + group_size] + with tf.device(avail_devices[group_1_main_device]): + group_1_reduced_tensor = _all_reduce_using_copy(group_1_tensors, False) + + # Reduce between the groups. + with tf.device(avail_devices[group_0_main_device]): + total_reduced_tensor = _all_reduce_using_copy( + [group_0_reduced_tensor, group_1_reduced_tensor], False) + + # Broadcast the result back into the root of each group. + with tf.device(avail_devices[group_0_main_device]): + group_0_reduced_tensor_bcast = tf.identity(total_reduced_tensor) + with tf.device(avail_devices[group_1_main_device]): + group_1_reduced_tensor_bcast = tf.identity(total_reduced_tensor) + + reduced_tensors_bcast = [] + for j in range(len(tensors_across_devices)): + with tf.device(avail_devices[j]): + # Broadcast the result back to each member in the group from the root. + if (group_0_main_device < group_size) == (j < group_size): + src_device_tensor = group_0_reduced_tensor_bcast + else: + src_device_tensor = group_1_reduced_tensor_bcast + reduced_tensors_bcast.append(tf.identity(src_device_tensor)) + + reduced_tensors.append(reduced_tensors_bcast) + + reduced_tensors = list(zip(*reduced_tensors)) + return reduced_tensors + + def __get_main_devices(self, tensor_index, num_devices): + """Returns the pair of main devices to use for initial reduction. + + Args: + tensor_index: Index of the current tensor in the list of tensors to copy. + num_devices: Total number of devices. + + Returns: + A tuple containing pair of main device indices for the initial + reduction. Then, the first element of the tuple should be used for the + final reduction. + + Raises: + ValueError: Invalid input arguments. + """ + if self._network_topology == constants.NetworkTopology.DGX1: + return tensor_index % num_devices, (tensor_index + + (num_devices // 2)) % num_devices + elif self._network_topology == constants.NetworkTopology.GCP_V100: + if num_devices != 8: + raise ValueError('HierarchicalCopy only supports eight devices in %s.' % + self._network_topology) + # TODO(hinsu): Generalize main device indices to handle any other + # isomorphic connection graph that connects two cliques using connections + # other than 0-5 and 2-7. + main_device_pairs = [(0, 5), (2, 7), (5, 0), (7, 2)] + return main_device_pairs[tensor_index % len(main_device_pairs)] + else: + # TODO(reedwm): make this logic more general for arbitrary topology. + raise ValueError( + 'HierarchicalCopy is not supported for %s network topology.' % + self._network_topology) + + +class AllReduceSpecAlgorithm(BatchAllReduceAlgorithm): + """An algorithm that uses an all reduce spec.""" + + def __init__(self, all_reduce_spec, gpu_indices, agg_small_grads_max_bytes, + agg_small_grads_max_group): + spec = allreduce.parse_all_reduce_spec(all_reduce_spec) + if len(spec) != 1: + raise ValueError( + 'Replicated mode does not support hybrid all-reduce strategies') + self._all_reduce_spec = spec[0] + self._gpu_indices = gpu_indices + self._agg_small_grads_max_bytes = agg_small_grads_max_bytes + self._agg_small_grads_max_group = agg_small_grads_max_group + + def _do_batch_all_reduce(self, all_device_tensors): + # TODO(reedwm): Merge allreduce.sum_gradients_all_reduce with the other + # gradient aggregation code, since gradient aggregation is doing an all + # reduce. Currently, we do gradient repacking in two different places. + # TODO(reedwm): Change the allreduce code to reduce tensors instead of + # tower_grads. + tower_grads = [[(t, None) for t in device_tensors] + for device_tensors in all_device_tensors] + aggregated_device_grads = allreduce.sum_gradients_all_reduce( + False, # single_session + ['/job:localhost'], + tower_grads, + 1, + self._all_reduce_spec.alg, + self._all_reduce_spec.shards, + self._gpu_indices, + agg_small_grads_max_bytes=self._agg_small_grads_max_bytes, + agg_small_grads_max_group=self._agg_small_grads_max_group) + return [[t for t, _ in grad_vars] for grad_vars in aggregated_device_grads] + + +def algorithm_from_params(params): + """Returns a BatchAllReduceAlgorithm from a Params tuple.""" + if params.all_reduce_spec: + if params.gpu_indices: + gpu_indices = [int(x) for x in params.gpu_indices.split(',')] + else: + gpu_indices = [x for x in range(params.num_gpus)] + return AllReduceSpecAlgorithm(params.all_reduce_spec, gpu_indices, + params.agg_small_grads_max_bytes, + params.agg_small_grads_max_group) + elif params.hierarchical_copy: + return HierarchicalCopyAlgorithm(params.network_topology) + else: + if params.local_parameter_device == 'gpu': + devices_to_reduce_on = ['/gpu:%d' % i for i in range(params.num_gpus)] + else: + devices_to_reduce_on = ['/cpu:0'] + return CopyToDeviceAlgorithm(devices_to_reduce_on) + + +def _apply_to_all_device_tensors(all_device_tensors, apply_func, colocate=True): + """Applies a function to each tensor in `all_device_tensors`. + + A new list of lists of tensors is returned, where every tensor in + `all_device_tensors` has had `apply_func` called on it. `all_device_tensors` + is not modified. + + Args: + all_device_tensors: A list of list of tensors. `all_device_tensors[i][j]` is + a tensor where `i` is the device index and `j` is the tensor index. + apply_func: A function taking in three arguments: tensor, device_index, + tensor_index, and returning a modified tensor. + `tensor` is `all_device_tensors[device_index][tensor_index]`. + colocate: If True, apply_func will be run under context manager colocated + with it's input tensor. + Returns: + A list in the same form as `all_device_tensors`, except each tensor has had + `apply_func` called on it. + """ + new_all_device_tensors = [] + for device_index, device_tensors in enumerate(all_device_tensors): + new_device_tensors = [] + for tensor_index, t in enumerate(device_tensors): + if colocate: + with tf.colocate_with(t): + new_t = apply_func(t, device_index, tensor_index) + else: + new_t = apply_func(t, device_index, tensor_index) + new_device_tensors.append(new_t) + new_all_device_tensors.append(new_device_tensors) + return new_all_device_tensors + + +def _defer_tensor(tensor): + """Defers the retrieval of a tensor. + + The tensor is put into a StagingArea, and the return value is the + retrieval of the tensor from the StagingArea. The effect is that the + tensor returned from this function is the tensor that was put in the + StagingArea for the previous Session.run() call. + + Args: + tensor: The tensor to defer for one step. + + Returns: + deferred_tensor: The tensor deferred for one step. + put_op: An op to put `tensor` in the StagingArea. Must be run every step + that `deferred_tensor` is run. + warmup_op: A warmup op that should be called before the first step. Puts + a zero tensor into the StagingArea. + """ + tensor_stage = data_flow_ops.StagingArea([tensor.dtype], [tensor.shape]) + put_op = tensor_stage.put([tensor]) + warmup_op = tensor_stage.put([tf.zeros(tensor.shape, dtype=tensor.dtype)]) + + # Fetch the next tensor to use. + (tensor,) = tensor_stage.get() + return tensor, put_op, warmup_op + + +def defer_single_device_tensors(device_tensors): + """Defer tensors (gradients in this case) from a single device. + + Args: + device_tensors: A list of gradients tensors from a single device to defer. + + Returns: + deferred_tensors: A list of tensors deferred for one step. + put_ops: A list of ops that put `tensors` in the StagingAreas. Must be run + every step that `deferred_tensors` is run. + warmup_ops: Warmup ops that should be called before the first step. Puts + zero tensors into the StagingArea. + """ + put_ops = [] + warmup_ops = [] + deferred_tensors = [] + + for tensor in device_tensors: + deferred_tensor, put_op, warmup_op = _defer_tensor(tensor) + deferred_tensors.append(deferred_tensor) + put_ops.append(put_op) + warmup_ops.append(warmup_op) + + return deferred_tensors, put_ops, warmup_ops + + +def _add_put_op_control_deps(all_device_tensors, num_splits, put_ops): + """Add control dependencies from `put_ops` to `all_device_tensors`. + + This should only be called when deferred tensors are being used. + + The control dependencies are added so that the put ops are run whenever + `all_device_tensors` is run. That way, the caller does not have to explicitly + run the put ops. + + Args: + all_device_tensors: A list of list of tensors. `all_device_tensors[i][j]` is + a tensor where `i` is the device index and `j` is the tensor index. + num_splits: The number of splits that were used for the all-reduce. + put_ops: A list of put ops from deferring the tensors. + Returns: + A list in the same form as `all_device_tensors`, except each tensor has a + control dependency on an op in `put_ops`. + + """ + def apply_func(tensor, device_index, tensor_index): + if num_splits == 0: + deps = [put_ops[device_index][tensor_index]] + else: + deps = put_ops[device_index] + assert len(deps) == 1 + with tf.control_dependencies(deps): + return tf.identity(tensor, name='control_dependency') + return _apply_to_all_device_tensors(all_device_tensors, apply_func) + + +class _TensorPacker(object): + """Packs and unpacks tensors into groups. + + This class first concatenates a set of tensors, then split the concatenated + tensor into a small number of chunks. This is useful for all-reducing tensors, + as doing a small number of all-reduces on large tensors can be faster than + doing a large number of all-reduces on small tensors. + + It also provides option to compact tensors by casting them to fp16, for better + all-reduce performance. + + This class maintains states of processed tensors like shapes and types. So + each packer can only be used to pack and unpack one list of tensors. If you + need to pack multiple lists of tensors (say from multiple devices), then you + need multiple _TensorPacker object, one for each device. + """ + + def __init__(self, num_splits, compact): + """Initializes the _TensorPacker. + + Args: + num_splits: The number of tensors to split the concatenated tensor into. + The batch all-reduce will consist of `num_splits` all-reduces. if None + or zero, tensors are not split or concatenated. + compact: If True, tensors are casted to fp16 during packing and casted + back to their original dtypes during unpacking. + """ + self._num_splits = num_splits + self._compact = compact + self._before_compact_dtypes = [] + + def maybe_concat_tensors(self, device_tensors): + """Concatenate tensors into a single tensor.""" + if not self._num_splits: + return device_tensors + + flat_tensors = [tf.reshape(t, [-1]) for t in device_tensors] + self._orig_shapes = [t.shape for t in device_tensors] + self._orig_sizes = [s.num_elements() for s in self._orig_shapes] + # All shapes must be fully defined. + assert None not in self._orig_sizes + concatenated_grad = tf.concat(flat_tensors, 0) + return [concatenated_grad] + + def maybe_split_tensors(self, concatenated_tensor): + """Split concatenated tensor into `num_splits` pieces.""" + if not self._num_splits: + return concatenated_tensor + + if len(concatenated_tensor) != 1: + raise RuntimeError('tensors must be concatenated via ' + 'maybe_concat_tensors() before splitting') + + concatenated_tensor = concatenated_tensor[0] + total_tensor_size = concatenated_tensor.shape.num_elements() + split_size = total_tensor_size // self._num_splits + split_size_last = total_tensor_size - split_size * (self._num_splits - 1) + split_sizes = [split_size] * (self._num_splits - 1) + [split_size_last] + tensor_packs = tf.split(concatenated_tensor, split_sizes) + return tensor_packs + + def undo_maybe_split_tensors(self, tensor_packs): + """Undo maybe_split_tensors().""" + if not self._num_splits: + return tensor_packs + + return [tf.concat(tensor_packs, 0)] + + def undo_maybe_concat_tensors(self, concatenated_tensor): + """Undo maybe_concat_tensors().""" + if not self._num_splits: + return concatenated_tensor + + if len(concatenated_tensor) != 1: + raise RuntimeError( + 'undo_maybe_split_tensors() must be called before ' + 'undo_maybe_concat_tensors when num_splits is greater than 1') + concatenated_tensor = concatenated_tensor[0] + + tensors_with_sizes = tf.split(concatenated_tensor, + self._orig_sizes) + tensors_with_shapes = [ + tf.reshape(grad, shape) for grad, shape in zip( + tensors_with_sizes, self._orig_shapes) + ] + return tensors_with_shapes + + def maybe_compact_tensors(self, device_tensors): + """Cast tensors to fp16 and store their original types.""" + if not self._compact: + return device_tensors + + if self._before_compact_dtypes: + raise RuntimeError('maybe_compact_tensors can only be called once.') + + self._before_compact_dtypes = [t.dtype for t in device_tensors] + compact_tensors = [tf.cast(t, tf.float16) for t in device_tensors] + + return compact_tensors + + def undo_maybe_compact_tensors(self, compact_tensors): + """Undo maybe_compact_tensors().""" + if not self._compact: + return compact_tensors + + if not self._before_compact_dtypes: + raise RuntimeError('maybe_compact_tensors() must be called before ' + 'undo_maybe_compact_tensors()') + + device_tensors = [ + tf.cast(t, dtype) + for t, dtype in zip(compact_tensors, self._before_compact_dtypes) + ] + return device_tensors diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/benchmark_cnn.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/benchmark_cnn.py new file mode 100644 index 00000000..6cec0a3b --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/benchmark_cnn.py @@ -0,0 +1,3548 @@ +# 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. +# ============================================================================== +"""TensorFlow benchmark library. + +See the README for more information. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import argparse +from collections import namedtuple +import contextlib +import math +import multiprocessing +import os +import re +import threading +import time +import traceback + +from absl import flags as absl_flags +import numpy as np + +import six +from six.moves import xrange # pylint: disable=redefined-builtin +import tensorflow.compat.v1 as tf + +# pylint: disable=g-direct-tensorflow-import +import cnn_util +import constants +import datasets +import flags +import mlperf +import variable_mgr +import variable_mgr_util +from cnn_util import log_fn +from models import model_config +from platforms import util as platforms_util +from google.protobuf import text_format +from tensorflow.core.protobuf import rewriter_config_pb2 +from tensorflow.python import debug as tf_debug +from tensorflow.python.client import timeline +from tensorflow.python.framework import graph_util +from tensorflow.python.framework import graph_util_impl +from tensorflow.python.framework import importer +from tensorflow.python.ops import data_flow_ops +from tensorflow.python.platform import gfile +from tensorflow.python.util import nest + + +_DEFAULT_NUM_BATCHES = 100 + + +# GraphInfo encapsulates the tensors/ops that we care about after building a +# graph. We use them to benchmark the graph. +GraphInfo = namedtuple( # pylint: disable=invalid-name + 'GraphInfo', + [ + # Ops that produce the input batches (before preprocessing). + 'input_producer_op', + # Ops that adds the preprocessed images to the staging areas + 'enqueue_ops', + # Fetches of sess.run() + 'fetches', + # Op that performs synchronization in distributed mode + 'execution_barrier', + # The global step variable + 'global_step', + # Group of ops that perform per-device initialization work + 'local_var_init_op_group', + # Op to produce summaries + 'summary_op' + ]) + + +# InputProcessingInfo contains various sources of inputs which will be later fed +# into the model. If synthetic data is used, all three fields are None. +InputProcessingInfo = namedtuple( + 'InputProcessingInfo', + [ + # The first two fields are non-None iff datasets prefetching is not + # used. + + # Ops that produce the input batches. + 'input_producer_op', + # A list of StagingArea for each device. + 'input_producer_stages', + + # Input produced using multi device iterator. Non-None iff datasets + # prefetching is used + 'multi_device_iterator_input' + ]) + + +# TODO(reedwm): add upper_bound and lower_bound to appropriate integer and +# float flags, and change certain string flags to enum flags. + +flags.DEFINE_string('model', 'trivial', + 'Name of the model to run, the list of supported models ' + 'are defined in models/model.py') +# The code will first check if it's running under benchmarking mode +# or evaluation mode, depending on 'eval': +# Under the evaluation mode, this script will read a saved model, +# and compute the accuracy of the model against a validation dataset. +# Additional ops for accuracy and top_k predictors are only used under +# this mode. +# Under the benchmarking mode, user can specify whether nor not to use +# the forward-only option, which will only compute the loss function. +# forward-only cannot be enabled with eval at the same time. +flags.DEFINE_boolean('eval', False, 'whether use eval or benchmarking') +flags.DEFINE_integer('eval_interval_secs', 0, + 'How often to run eval on saved checkpoints. Usually the ' + 'same as save_model_secs from the corresponding training ' + 'run. Pass 0 to eval only once.') +flags.DEFINE_integer('eval_during_training_every_n_steps', None, + 'Every n steps during training, pause training, run ' + 'evaluation, then resume training. Must not be used with ' + '--eval, as unlike --eval, this option causes both ' + 'training and eval to be done. This may take slightly ' + 'more GPU memory than running just training or evaluation ' + 'alone. It also may slightly slow down training, even ' + 'when not taking into account the additional time to ' + 'evaluate.', lower_bound=1) +flags.DEFINE_float('eval_during_training_every_n_epochs', None, + 'After every n training epochs, pause training, run ' + 'evaluation, then resume training. See ' + '--eval_during_training_every_n_steps for more information.') +flags.DEFINE_list('eval_during_training_at_specified_steps', [], + 'Specify a list of training steps, pause training at each of ' + 'these steps, run evaluation, then resume training. See ' + '--eval_during_training_every_n_steps for more information.') +flags.DEFINE_list('eval_during_training_at_specified_epochs', [], + 'Specify a list of training epochs, pause training after ' + 'each of these epochs, run evaluation, then resume training. ' + 'See --eval_during_training_every_n_steps for more ' + 'information.') +flags.DEFINE_boolean('forward_only', False, + 'whether use forward-only or training for benchmarking') +flags.DEFINE_boolean('freeze_when_forward_only', False, + 'whether to freeze the graph when in forward-only mode.') +flags.DEFINE_boolean('print_training_accuracy', False, + 'whether to calculate and print training accuracy during ' + 'training') +flags.DEFINE_integer('batch_size', 0, 'batch size per compute device') +flags.DEFINE_integer('eval_batch_size', 0, 'eval batch size per compute device') +flags.DEFINE_integer('batch_group_size', 1, + 'number of groups of batches processed in the image ' + 'producer.') +flags.DEFINE_integer('num_batches', None, 'number of batches to run, excluding ' + 'warmup. Defaults to %d' % _DEFAULT_NUM_BATCHES) +flags.DEFINE_integer('num_eval_batches', None, + 'number of eval batches to run, excluding warmup. ' + 'Defaults to --num_batches') +flags.DEFINE_float('num_epochs', None, + 'number of epochs to run, excluding warmup. ' + 'This and --num_batches cannot both be specified.') +flags.DEFINE_float('num_eval_epochs', None, + 'number of eval epochs to run, excluding warmup. ' + 'Defaults to --num_epochs') +flags.DEFINE_float('stop_at_top_1_accuracy', None, + 'If set, stops training after the evaluation accuracy hits ' + 'this number. Can only be used with one of the ' + '--eval_during_training_* flags.') +flags.DEFINE_boolean('collect_eval_results_async', False, + 'If True, start a separate process to postprocess eval ' + 'results asynchronously. This currently only works with ' + 'the SSD model.') +flags.DEFINE_integer('num_warmup_batches', None, + 'number of batches to run before timing') +flags.DEFINE_integer('autotune_threshold', None, + 'The autotune threshold for the models') +# TODO(tucker): change num_gpus to num_devices +flags.DEFINE_integer('num_gpus', 1, 'the number of GPUs to run on') +flags.DEFINE_string('gpu_indices', '', 'indices of worker GPUs in ring order') +flags.DEFINE_integer('display_every', 10, + 'Number of local steps after which progress is printed ' + 'out') +flags.DEFINE_float('display_perf_ewma', None, + 'If set, display numbers of images/sec using exponentially ' + 'weighted moving avearge with the specified weight, which ' + 'defines how much current value contributes to the reported ' + 'average. Increasing weight makes the reported performance ' + 'number reflect more about the real-time speed instead of ' + 'the entire history', lower_bound=0, upper_bound=1) +flags.DEFINE_string('data_dir', None, + 'Path to dataset in TFRecord format (aka Example ' + 'protobufs). If not specified, synthetic data will be ' + 'used.') +flags.DEFINE_string('data_name', None, + 'Name of dataset: imagenet or cifar10. If not specified, ' + 'it is automatically guessed based on data_dir.') +flags.DEFINE_string('resize_method', 'bilinear', + 'Method for resizing input images: crop, nearest, ' + 'bilinear, bicubic, area, or round_robin. The `crop` mode ' + 'requires source images to be at least as large as the ' + 'network input size. The `round_robin` mode applies ' + 'different resize methods based on position in a batch in ' + 'a round-robin fashion. Other modes support any sizes and ' + 'apply random bbox distortions before resizing (even with ' + 'distortions=False).') +flags.DEFINE_boolean('distortions', False, + 'Enable/disable distortions during image preprocessing. ' + 'These include bbox and color distortions.') +flags.DEFINE_boolean('use_datasets', True, + 'Enable use of datasets for input pipeline') +flags.DEFINE_string('input_preprocessor', 'default', + 'Name of input preprocessor. The list of supported input ' + 'preprocessors are defined in preprocessing.py.') +flags.DEFINE_string('gpu_thread_mode', 'gpu_private', + 'Methods to assign GPU host work to threads. ' + 'global: all GPUs and CPUs share the same global threads; ' + 'gpu_private: a private threadpool for each GPU; ' + 'gpu_shared: all GPUs share the same threadpool.') +flags.DEFINE_integer('per_gpu_thread_count', 0, + 'The number of threads to use for GPU. Only valid when ' + 'gpu_thread_mode is not global.') +flags.DEFINE_boolean('hierarchical_copy', False, + 'Use hierarchical copies. Currently only optimized for ' + 'use on a DGX-1 with 8 GPUs and may perform poorly on ' + 'other hardware. Requires --num_gpus > 1, and only ' + 'recommended when --num_gpus=8') +# TODO(hinsu): Support auto-detection of the network topology while still +# retaining the ability to specify a particular topology for debugging. +flags.DEFINE_enum( + 'network_topology', constants.NetworkTopology.DGX1, + (constants.NetworkTopology.DGX1, constants.NetworkTopology.GCP_V100), + 'Network topology specifies the topology used to connect multiple devices. ' + 'Network topology is used to decide the hierarchy to use for the ' + 'hierarchical_copy.') +flags.DEFINE_integer('gradient_repacking', 0, 'Use gradient repacking. It ' + 'currently only works with replicated mode. At the end of ' + 'each step, it repacks the gradients for more efficient ' + 'cross-device transportation. A non-zero value specifies ' + 'the number of split packs that will be formed.', + lower_bound=0) +flags.DEFINE_boolean('compact_gradient_transfer', True, 'Compact gradient' + 'as much as possible for cross-device transfer and ' + 'aggregation.') +flags.DEFINE_enum('variable_consistency', 'strong', ('strong', 'relaxed'), + 'The data consistency for trainable variables. With strong ' + 'consistency, the variable always have the updates from ' + 'previous step. With relaxed consistency, all the updates ' + 'will eventually show up in the variables. Likely one step ' + 'behind.') +flags.DEFINE_boolean('datasets_repeat_cached_sample', False, + 'Enable use of a special datasets pipeline that reads a ' + 'single TFRecord into memory and repeats it infinitely ' + 'many times. The purpose of this flag is to make it ' + 'possible to write regression tests that are not ' + 'bottlenecked by CNS throughput. ' + 'Use datasets_use_caching to cache input data.') +flags.DEFINE_enum('local_parameter_device', 'gpu', ('cpu', 'gpu', 'CPU', 'GPU'), + 'Device to use as parameter server: cpu or gpu. For ' + 'distributed training, it can affect where caching of ' + 'variables happens.') +flags.DEFINE_enum('device', 'gpu', ('cpu', 'gpu', 'CPU', 'GPU'), + 'Device to use for computation: cpu or gpu') +flags.DEFINE_enum('data_format', 'NCHW', ('NHWC', 'NCHW'), + 'Data layout to use: NHWC (TF native) or NCHW (cuDNN ' + 'native, requires GPU).') +flags.DEFINE_integer('num_intra_threads', None, + 'Number of threads to use for intra-op parallelism. If ' + 'set to 0, the system will pick an appropriate number. ' + 'None is the same as 0 except that it disables intra-op ' + 'parallelism on a GPU.') +flags.DEFINE_integer('num_inter_threads', 0, + 'Number of threads to use for inter-op parallelism. If ' + 'set to 0, the system will pick an appropriate number.') +flags.DEFINE_boolean('use_numa_affinity', False, + 'Whether to turn on NUMA affinity for CPU devices. ' + 'This is probably only useful when --device=cpu.') +flags.DEFINE_string('trace_file', '', + 'Enable TensorFlow tracing and write trace to this file.') +flags.DEFINE_boolean('use_chrome_trace_format', True, + 'If True, the trace_file, if specified, will be in a ' + 'Chrome trace format. If False, then it will be a ' + 'StepStats raw proto.') +_NUM_STEPS_TO_PROFILE = 10 +_NUM_OPS_TO_PRINT = 20 +flags.DEFINE_string('tfprof_file', None, + 'If specified, write a tfprof ProfileProto to this file. ' + 'The performance and other aspects of the model can then ' + 'be analyzed with tfprof. See ' + 'https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/profiler/g3doc/command_line.md ' # pylint: disable=line-too-long + 'for more info on how to do this. The first %d steps ' + 'are profiled. Additionally, the top %d most time ' + 'consuming ops will be printed.\n' + 'Note: profiling with tfprof is very slow, but most of the ' + 'overhead is spent between steps. So, profiling results ' + 'are more accurate than the slowdown would suggest.' % + (_NUM_STEPS_TO_PROFILE, _NUM_OPS_TO_PRINT)) +flags.DEFINE_string('graph_file', None, + 'Write the model\'s graph definition to this file. ' + 'Defaults to binary format unless filename ends in "txt".') +flags.DEFINE_string('partitioned_graph_file_prefix', None, + 'If specified, after the graph has been partitioned and ' + 'optimized, write out each partitioned graph to a file ' + 'with the given prefix.') +flags.DEFINE_enum('optimizer', 'sgd', ('momentum', 'sgd', 'rmsprop', 'adam'), + 'Optimizer to use') +flags.DEFINE_float('init_learning_rate', None, + 'Initial learning rate for training.') +flags.DEFINE_string('piecewise_learning_rate_schedule', None, + 'Specifies a piecewise learning rate schedule based on the ' + 'number of epochs. This is the form LR0;E1;LR1;...;En;LRn, ' + 'where each LRi is a learning rate and each Ei is an epoch ' + 'indexed from 0. The learning rate is LRi if the ' + 'E(i-1) <= current_epoch < Ei. For example, if this ' + 'paramater is 0.3;10;0.2;25;0.1, the learning rate is 0.3 ' + 'for the first 10 epochs, then is 0.2 for the next 15 ' + 'epochs, then is 0.1 until training ends.') +flags.DEFINE_float('num_epochs_per_decay', 0, + 'Steps after which learning rate decays. If 0, the learning ' + 'rate does not decay.') +flags.DEFINE_float('learning_rate_decay_factor', 0, + 'Learning rate decay factor. Decay by this factor every ' + '`num_epochs_per_decay` epochs. If 0, learning rate does ' + 'not decay.') +flags.DEFINE_float('num_learning_rate_warmup_epochs', 0, + 'Slowly increase to the initial learning rate in the first ' + 'num_learning_rate_warmup_epochs linearly.') +flags.DEFINE_float('minimum_learning_rate', 0, + 'The minimum learning rate. The learning rate will ' + 'never decay past this value. Requires `learning_rate`, ' + '`num_epochs_per_decay` and `learning_rate_decay_factor` to ' + 'be set.') +flags.DEFINE_float('resnet_base_lr', None, "Base learning rate at bs=256. Only " + "relevant when training ResNet and utilizing the model's " + "learning rate heuristic (get_learning_rate).") +flags.DEFINE_float('momentum', 0.9, 'Momentum for training.') +flags.DEFINE_float('rmsprop_decay', 0.9, 'Decay term for RMSProp.') +flags.DEFINE_float('rmsprop_momentum', 0.9, 'Momentum in RMSProp.') +flags.DEFINE_float('rmsprop_epsilon', 1.0, 'Epsilon term for RMSProp.') +flags.DEFINE_float('adam_beta1', 0.9, 'Beta2 term for the Adam optimizer') +flags.DEFINE_float('adam_beta2', 0.999, 'Beta2 term for the Adam optimizer') +flags.DEFINE_float('adam_epsilon', 1e-8, 'Epsilon term for the Adam optimizer') +flags.DEFINE_float('gradient_clip', None, + 'Gradient clipping magnitude. Disabled by default.') +flags.DEFINE_float('weight_decay', 0.00004, + 'Weight decay factor for training.') +flags.DEFINE_float('gpu_memory_frac_for_testing', 0, + 'If non-zero, the fraction of GPU memory that will be used. ' + 'Useful for testing the benchmark script, as this allows ' + 'distributed mode to be run on a single machine. For ' + 'example, if there are two tasks, each can be allocated ' + '~40 percent of the memory on a single machine. This is ' + 'also useful for using unified memory, as this can be set ' + 'above 1 to oversubscribe the GPU using unified memory.', + lower_bound=0.) +flags.DEFINE_boolean('use_unified_memory', None, + 'If True, allocate unified memory enabling larger models ' + 'to fit in available device RAM.') +flags.DEFINE_boolean('timestamped_allocator', False, + 'If True marks free BFCAllocator::Chunks with time ' + 'at which they are freed which can allow more efficient ' + 'memory allocation in cases like RDMA networking.') +flags.DEFINE_integer('gpu_kt_max_interval', 0, + 'If > 0, the maximum number of GPU Ops that may be queued ' + 'in a row without also queuing a tracking event.') +flags.DEFINE_integer('gpu_kt_max_bytes', 0, + 'If > 0, the maximum number of bytes ' + 'of GPU memory that may be allocated by sequential ' + 'GPU Ops without queuing a tracking event.') +flags.DEFINE_integer('gpu_kt_max_pending', 0, + 'If > 0 no more than this many GPU tracking events may be ' + 'outstanding at any time. When this limit is reached ' + 'launch of additional kernels will stall until an ' + 'outstanding event completes.') +flags.DEFINE_boolean('use_tf_layers', True, + 'If True, use tf.layers for neural network layers. This ' + 'should not affect performance or accuracy in any way.') +flags.DEFINE_integer('tf_random_seed', 1234, + 'The TensorFlow random seed. Useful for debugging NaNs, ' + 'as this can be set to various values to see if the NaNs ' + 'depend on the seed.') +flags.DEFINE_string('debugger', None, + 'If set, use the TensorFlow debugger. If set to "cli", use ' + 'the local CLI debugger. Otherwise, this must be in the ' + 'form hostname:port (e.g., localhost:7007) in which case ' + 'the experimental TensorBoard debugger will be used') +flags.DEFINE_boolean('use_python32_barrier', False, + 'When on, use threading.Barrier at Python 3.2.') + +flags.DEFINE_boolean('ml_perf', False, + 'When True, change how the Imagenet input pipeline works ' + 'slightly to meet the MLPerf compliance rules. This slows ' + 'down the input pipeline. Without this option, at the end ' + 'of the input pipeline, the image is divided by 127.5, ' + 'then 1.0 is subtracted from it, bringing the image ' + 'values from [0, 255] to [-1.0, 1.0]. With this option, ' + 'each of the three channels (red, green, blue) have the ' + 'average channel value among all image subtracted from ' + 'it, and no division is done.') + +flags.DEFINE_boolean('datasets_use_prefetch', True, + 'Enable use of prefetched datasets for input pipeline. ' + 'This option is meaningless if use_datasets=False.') +flags.DEFINE_integer('datasets_prefetch_buffer_size', 1, + 'Prefetching op buffer size per compute device.') +flags.DEFINE_integer('datasets_num_private_threads', None, + 'Number of threads for a private threadpool created for ' + 'all datasets computation. By default, we pick an ' + 'appropriate number. If set to 0, we use the default ' + 'tf-Compute threads for dataset operations.') +flags.DEFINE_boolean('datasets_use_caching', False, + 'Cache the compressed input data in memory. This improves ' + 'the data input performance, at the cost of additional ' + 'memory.') +flags.DEFINE_integer('datasets_parallel_interleave_cycle_length', None, + 'Number of parallel file readers interleaving input data.') +flags.DEFINE_boolean('datasets_sloppy_parallel_interleave', False, + 'Allow parallel interleave to depart from deterministic ' + 'ordering, by temporarily skipping over files whose ' + 'elements are not readily available. This can increase ' + 'througput in particular in the presence of stragglers.') +flags.DEFINE_integer('datasets_parallel_interleave_prefetch', None, + 'The number of input elements to fetch before they are ' + 'needed for interleaving.') + +flags.DEFINE_integer( + 'multi_device_iterator_max_buffer_size', 1, + 'Configuration parameter for the MultiDeviceIterator that ' + ' specifies the host side buffer size for each device.') + +# Performance tuning parameters. +flags.DEFINE_boolean('winograd_nonfused', True, + 'Enable/disable using the Winograd non-fused algorithms.') +flags.DEFINE_boolean( + 'batchnorm_persistent', True, + 'Enable/disable using the CUDNN_BATCHNORM_SPATIAL_PERSISTENT ' + 'mode for batchnorm.') +flags.DEFINE_boolean('sync_on_finish', False, + 'Enable/disable whether the devices are synced after each ' + 'step.') +flags.DEFINE_boolean('staged_vars', False, + 'whether the variables are staged from the main ' + 'computation') +flags.DEFINE_boolean('force_gpu_compatible', False, + 'whether to enable force_gpu_compatible in GPU_Options') +flags.DEFINE_boolean('allow_growth', None, + 'whether to enable allow_growth in GPU_Options') +flags.DEFINE_boolean('xla', False, 'whether to enable XLA auto-jit compilation') +flags.DEFINE_boolean('xla_compile', False, + 'Enable xla to compile the graph. Uncompilable ops will ' + 'result in fatal errors.') +flags.DEFINE_boolean('fuse_decode_and_crop', True, + 'Fuse decode_and_crop for image preprocessing.') +flags.DEFINE_boolean('distort_color_in_yiq', True, + 'Distort color of input images in YIQ space.') +flags.DEFINE_boolean('enable_optimizations', True, + 'Whether to enable grappler and other optimizations.') +flags.DEFINE_string('rewriter_config', None, + 'Config for graph optimizers, described as a ' + 'RewriterConfig proto buffer.') +flags.DEFINE_enum('loss_type_to_report', 'total_loss', + ('base_loss', 'total_loss'), + 'Which type of loss to output and to write summaries for. ' + 'The total loss includes L2 loss while the base loss does ' + 'not. Note that the total loss is always used while ' + 'computing gradients during training if weight_decay > 0, ' + 'but explicitly computing the total loss, instead of just ' + 'computing its gradients, can have a performance impact.') +flags.DEFINE_boolean('single_l2_loss_op', False, + 'If True, instead of using an L2 loss op per variable, ' + 'concatenate the variables into a single tensor and do a ' + 'single L2 loss on the concatenated tensor.') +flags.DEFINE_boolean('use_resource_vars', False, + 'Use resource variables instead of normal variables. ' + 'Resource variables are slower, but this option is useful ' + 'for debugging their performance.') +flags.DEFINE_boolean('compute_lr_on_cpu', False, + 'If True, do computations related to learning rate on the ' + 'CPU instead of the GPU. This will significantly improve ' + 'XLA performance in some cases.') +flags.DEFINE_boolean('sparse_to_dense_grads', False, + 'If True, convert all sparse gradients to dense gradients ' + 'before passing them to the optimizer to update ' + 'variables. Only affects models with sparse gradients, ' + 'which currently is only the NCF model.') +# Performance tuning specific to MKL. +flags.DEFINE_boolean('mkl', False, 'If true, set MKL environment variables.') +flags.DEFINE_integer('kmp_blocktime', 0, + 'The time, in milliseconds, that a thread should wait, ' + 'after completing the execution of a parallel region, ' + 'before sleeping') +flags.DEFINE_string('kmp_affinity', 'granularity=fine,verbose,compact,1,0', + 'Restricts execution of certain threads (virtual execution ' + 'units) to a subset of the physical processing units in a ' + 'multiprocessor computer.') +flags.DEFINE_integer('kmp_settings', 1, + 'If set to 1, MKL settings will be printed.') + +# fp16 parameters. If use_fp16=False, no other fp16 parameters apply. +flags.DEFINE_boolean('use_fp16', False, + 'Use 16-bit floats for certain tensors instead of 32-bit ' + 'floats. This is currently experimental.') +# TODO(reedwm): The default loss scale of 128 causes most models to diverge +# on the second step with synthetic data. Changing the tf.set_random_seed +# call to tf.set_random_seed(1235) or most other seed values causes the +# issue not to occur. +flags.DEFINE_float('fp16_loss_scale', None, + 'If fp16 is enabled, the loss is multiplied by this amount ' + 'right before gradients are computed, then each gradient ' + 'is divided by this amount. Mathematically, this has no ' + 'effect, but it helps avoid fp16 underflow. Set to 1 to ' + 'effectively disable. Ignored during eval.') +flags.DEFINE_boolean('fp16_vars', False, + 'If fp16 is enabled, also use fp16 for variables. If ' + 'False, the variables are stored in fp32 and casted to ' + 'fp16 when retrieved. Recommended to leave as False.') +flags.DEFINE_boolean('fp16_enable_auto_loss_scale', False, + 'If True and use_fp16 is True, automatically adjust the ' + 'loss scale during training.') +flags.DEFINE_integer('fp16_inc_loss_scale_every_n', 1000, + 'If fp16 is enabled and fp16_enable_auto_loss_scale is ' + 'True, increase the loss scale every n steps.') + +# The method for managing variables: +# parameter_server: variables are stored on a parameter server that holds +# the master copy of the variable. In local execution, a local device +# acts as the parameter server for each variable; in distributed +# execution, the parameter servers are separate processes in the +# cluster. +# For each step, each tower gets a copy of the variables from the +# parameter server, and sends its gradients to the param server. +# replicated: each GPU has its own copy of the variables. To apply +# gradients, an all_reduce algorithm or or regular cross-device +# aggregation is used to replicate the combined gradients to all +# towers (depending on all_reduce_spec parameter setting). +# independent: each GPU has its own copy of the variables, and gradients +# are not shared between towers. This can be used to check performance +# when no data is moved between GPUs. +# distributed_replicated: Distributed training only. Each GPU has a copy +# of the variables, and updates its copy after the parameter servers +# are all updated with the gradients from all servers. Only works with +# cross_replica_sync=true. Unlike 'replicated', currently never uses +# nccl all-reduce for replicating within a server. +# distributed_all_reduce: Distributed training where all replicas run +# in a single session, using all-reduce to mutally reduce the +# gradients. Uses no parameter servers. When there is only one +# worker, this is the same as replicated. +# collective_all_reduce: Distributed training where all replicas run +# independepently except for variable initialization and for +# gradient reduction which is done via collective all-reduce. +# NOTE: collective_all_reduce in conjunction with use_fp16 can +# lead to NaNs in some models (resnet50). TODO(tucker): fix it. +# horovod: Distributed training using Horovod library. Runs workers using +# an MPI framework (e.g. Open MPI). Each worker runs training on +# single GPU, and averages gradients using NCCL or MPI all-reduce. +# See https://github.com/uber/horovod for more details. +flags.DEFINE_enum('variable_update', 'parameter_server', + ('parameter_server', 'replicated', 'distributed_replicated', + 'independent', 'distributed_all_reduce', + 'collective_all_reduce', 'horovod'), + 'The method for managing variables: parameter_server, ' + 'replicated, distributed_replicated, independent, ' + 'distributed_all_reduce, collective_all_reduce, horovod') +flags.DEFINE_string('all_reduce_spec', None, + 'A specification of the all_reduce algorithm to be used ' + 'for reducing gradients. For more details, see ' + 'parse_all_reduce_spec in variable_mgr.py. An ' + 'all_reduce_spec has BNF form:\n' + 'int ::= positive whole number\n' + 'g_int ::= int[KkMGT]?\n' + 'alg_spec ::= alg | alg#int\n' + 'range_spec ::= alg_spec | alg_spec/alg_spec\n' + 'spec ::= range_spec | range_spec:g_int:range_spec\n' + 'NOTE: not all syntactically correct constructs are ' + 'supported.\n\n' + 'Examples:\n ' + '"xring" == use one global ring reduction for all ' + 'tensors\n' + '"pscpu" == use CPU at worker 0 to reduce all tensors\n' + '"nccl" == use NCCL to locally reduce all tensors. ' + 'Limited to 1 worker.\n' + '"nccl/xring" == locally (to one worker) reduce values ' + 'using NCCL then ring reduce across workers.\n' + '"pscpu:32k:xring" == use pscpu algorithm for tensors of ' + 'size up to 32kB, then xring for larger tensors.') + +# If variable_update==distributed_all_reduce then it may be advantageous +# to aggregate small tensors into one prior to reduction. These parameters +# control that aggregation. +flags.DEFINE_integer('agg_small_grads_max_bytes', 0, + 'If > 0, try to aggregate tensors of less than this ' + 'number of bytes prior to all-reduce.') +flags.DEFINE_integer('agg_small_grads_max_group', 10, + 'When aggregating small tensors for all-reduce do not ' + 'aggregate more than this many into one new tensor.') +flags.DEFINE_integer('allreduce_merge_scope', 1, + 'Establish a name scope around this many ' + 'gradients prior to creating the all-reduce operations. ' + 'It may affect the ability of the backend to merge ' + 'parallel ops.') + +# Distributed training parameters. +flags.DEFINE_enum('job_name', '', ('ps', 'worker', 'controller', ''), + 'One of "ps", "worker", "controller", "". Empty for local ' + 'training') +flags.DEFINE_string('ps_hosts', '', 'Comma-separated list of target hosts') +flags.DEFINE_string('worker_hosts', '', 'Comma-separated list of target hosts') +flags.DEFINE_string('controller_host', None, 'optional controller host') +flags.DEFINE_integer('task_index', 0, 'Index of task within the job') +flags.DEFINE_string('server_protocol', 'grpc', 'protocol for servers') +flags.DEFINE_boolean('cross_replica_sync', True, '') +flags.DEFINE_string('horovod_device', '', 'Device to do Horovod all-reduce on: ' + 'empty (default), cpu or gpu. Default with utilize GPU if ' + 'Horovod was compiled with the HOROVOD_GPU_ALLREDUCE ' + 'option, and CPU otherwise.') + +# Summary and Save & load checkpoints. +flags.DEFINE_integer('summary_verbosity', 0, 'Verbosity level for summary ops. ' + 'level 0: disable any summary.\n' + 'level 1: small and fast ops, e.g.: learning_rate, ' + 'total_loss.\n' + 'level 2: medium-cost ops, e.g. histogram of all ' + 'gradients.\n' + 'level 3: expensive ops: images and histogram of each ' + 'gradient.\n') +flags.DEFINE_integer('save_summaries_steps', 0, + 'How often to save summaries for trained models. Pass 0 ' + 'to disable summaries.') +flags.DEFINE_integer('save_model_secs', 0, + 'How often to save trained models. Pass 0 to disable ' + 'saving checkpoints every N seconds. A checkpoint is ' + 'saved after training completes regardless of this ' + 'option.') +flags.DEFINE_integer('save_model_steps', None, + 'How often to save trained models. If specified, ' + 'save_model_secs must not be specified.') +flags.DEFINE_integer('max_ckpts_to_keep', 5, + 'Max number of checkpoints to keep.') +flags.DEFINE_string('train_dir', None, + 'Path to session checkpoints. Pass None to disable saving ' + 'checkpoint at the end.') +flags.DEFINE_string('eval_dir', '/tmp/tf_cnn_benchmarks/eval', + 'Directory where to write eval event logs.') +flags.DEFINE_string('backbone_model_path', None, + 'Path to pretrained backbone model checkpoint. Pass None ' + 'if not using a backbone model.') +flags.DEFINE_enum('trt_mode', '', ['', 'FP32', 'FP16', 'INT8'], + 'If this is specified in forward_only mode and ' + 'freeze_when_forward_only is set to True, use TensorRT to ' + 'optimize the graph before execution.') +flags.DEFINE_integer('trt_max_workspace_size_bytes', 4 << 30, + 'Max workspace size bytes used by the TensorRT optimizer.') + +# Benchmark logging for model garden metric +flags.DEFINE_string('benchmark_log_dir', None, + 'The directory to place the log files containing the ' + 'results of benchmark. The logs are created by ' + 'BenchmarkFileLogger. Requires the root of the Tensorflow ' + 'models repository to be in $PYTHTONPATH.') +flags.DEFINE_string('benchmark_test_id', None, + 'The unique test ID of the benchmark run. It could be the ' + 'combination of key parameters. It is hardware independent ' + 'and could be used compare the performance between ' + 'different test runs. This flag is designed for human ' + 'consumption, and does not have any impact within the ' + 'system.') + +platforms_util.define_platform_params() + + +class GlobalStepWatcher(threading.Thread): + """A helper class for global_step. + + Polls for changes in the global_step of the model, and finishes when the + number of steps for the global run are done. + """ + + def __init__(self, sess, global_step_op, start_at_global_step, + end_at_global_step): + threading.Thread.__init__(self) + self.sess = sess + self.global_step_op = global_step_op + self.start_at_global_step = start_at_global_step + self.end_at_global_step = end_at_global_step + + self.start_time = 0 + self.start_step = 0 + self.finish_time = 0 + self.finish_step = 0 + + def run(self): + while self.finish_time == 0: + time.sleep(.25) + global_step_val, = self.sess.run([self.global_step_op]) + if self.start_time == 0 and global_step_val >= self.start_at_global_step: + # Use tf.logging.info instead of log_fn, since print (which is log_fn) + # is not thread safe and may interleave the outputs from two parallel + # calls to print, which can break tests. + tf.logging.info('Starting real work at step %s at time %s' % + (global_step_val, time.ctime())) + self.start_time = time.perf_counter() + self.start_step = global_step_val + if self.finish_time == 0 and global_step_val >= self.end_at_global_step: + tf.logging.info('Finishing real work at step %s at time %s' % + (global_step_val, time.ctime())) + self.finish_time = time.perf_counter() + self.finish_step = global_step_val + + def done(self): + return self.finish_time > 0 + + def num_steps(self): + return self.finish_step - self.start_step + + def elapsed_time(self): + return self.finish_time - self.start_time + + +class CheckpointNotFoundException(Exception): + pass + + +def create_config_proto(params): + """Returns session config proto. + + Args: + params: Params tuple, typically created by make_params or + make_params_from_flags. + """ + config = tf.ConfigProto() + config.allow_soft_placement = True + if params.num_intra_threads is None: + if params.device == 'gpu': + config.intra_op_parallelism_threads = 1 + else: + config.intra_op_parallelism_threads = params.num_intra_threads + config.inter_op_parallelism_threads = params.num_inter_threads + config.experimental.collective_group_leader = '/job:worker/replica:0/task:0' + config.gpu_options.experimental.collective_ring_order = params.gpu_indices + config.gpu_options.force_gpu_compatible = params.force_gpu_compatible + config.experimental.use_numa_affinity = params.use_numa_affinity + if params.device == 'cpu': + # TODO(tucker): change num_gpus to num_devices + config.device_count['CPU'] = params.num_gpus + if params.allow_growth is not None: + config.gpu_options.allow_growth = params.allow_growth + if params.gpu_memory_frac_for_testing > 0: + config.gpu_options.per_process_gpu_memory_fraction = ( + params.gpu_memory_frac_for_testing) + if params.use_unified_memory: + config.gpu_options.experimental.use_unified_memory = ( + params.use_unified_memory) + if params.timestamped_allocator: + config.gpu_options.experimental.timestamped_allocator = ( + params.timestamped_allocator) + if params.gpu_kt_max_interval > 0: + config.gpu_options.experimental.kernel_tracker_max_interval = ( + params.gpu_kt_max_interval) + if params.gpu_kt_max_bytes > 0: + config.gpu_options.experimental.kernel_tracker_max_bytes = ( + params.gpu_kt_max_bytes) + if params.gpu_kt_max_pending > 0: + config.gpu_options.experimental.kernel_tracker_max_pending = ( + params.gpu_kt_max_pending) + if params.xla: + config.graph_options.optimizer_options.global_jit_level = ( + tf.OptimizerOptions.ON_1) + if params.rewriter_config: + rewriter_config = rewriter_config_pb2.RewriterConfig() + text_format.Merge(params.rewriter_config, rewriter_config) + config.graph_options.rewrite_options.CopyFrom(rewriter_config) + elif not params.enable_optimizations: + config.graph_options.optimizer_options.opt_level = tf.OptimizerOptions.L0 + config.graph_options.rewrite_options.disable_meta_optimizer = True + elif params.variable_update == 'collective_all_reduce': + rewrite_options = config.graph_options.rewrite_options + rewrite_options.scoped_allocator_optimization = ( + rewriter_config_pb2.RewriterConfig.ON) + rewrite_options.scoped_allocator_opts.enable_op.append('CollectiveReduce') + if params.variable_update == 'horovod': + import horovod.tensorflow as hvd # pylint: disable=g-import-not-at-top + config.gpu_options.visible_device_list = str(hvd.local_rank()) + # For collective_all_reduce, ignore all devices except current worker. + if params.variable_update == 'collective_all_reduce': + del config.device_filters[:] + config.device_filters.append( + '/job:%s/replica:0/task:%d' % (params.job_name, params.task_index)) + + # TODO(b/117324590): Re-enable PinToHostOptimizer when b/117324590 is fixed. + # Currently we have to disable PinToHostOptimizer w/ XLA since it causes + # OOM/perf cliffs. + config.graph_options.rewrite_options.pin_to_host_optimization = ( + rewriter_config_pb2.RewriterConfig.OFF) + return config + + +def get_mode_from_params(params): + """Returns the mode in which this script is running. + + Args: + params: Params tuple, typically created by make_params or + make_params_from_flags. + Raises: + ValueError: Unsupported params settings. + """ + if params.forward_only and params.eval: + raise ValueError('Only one of forward_only and eval parameters is true') + + if params.eval: + return constants.BenchmarkMode.EVAL + elif params.forward_only: + return constants.BenchmarkMode.FORWARD_ONLY + elif (params.eval_during_training_every_n_steps or + params.eval_during_training_every_n_epochs or + params.eval_during_training_at_specified_steps or + params.eval_during_training_at_specified_epochs): + return constants.BenchmarkMode.TRAIN_AND_EVAL + else: + return constants.BenchmarkMode.TRAIN + + +# How many digits to show for the loss and accuracies during training. +LOSS_AND_ACCURACY_DIGITS_TO_SHOW = 3 + + +def benchmark_one_step(sess, + fetches, + step, + batch_size, + step_train_times, + trace_filename, + partitioned_graph_file_prefix, + profiler, + image_producer, + params, + summary_op=None, + show_images_per_sec=True, + benchmark_logger=None, + collective_graph_key=0, + should_output_files=True): + """Advance one step of benchmarking.""" + should_profile = profiler and 0 <= step < _NUM_STEPS_TO_PROFILE + need_options_and_metadata = ( + should_profile or collective_graph_key > 0 or + ((trace_filename or partitioned_graph_file_prefix) and step == -2) + ) + if need_options_and_metadata: + run_options = tf.RunOptions() + if (trace_filename and step == -2) or should_profile: + run_options.trace_level = tf.RunOptions.FULL_TRACE + if partitioned_graph_file_prefix and step == -2: + run_options.output_partition_graphs = True + if collective_graph_key > 0: + run_options.experimental.collective_graph_key = collective_graph_key + run_metadata = tf.RunMetadata() + else: + run_options = None + run_metadata = None + summary_str = None + start_time = time.perf_counter() + if summary_op is None: + results = sess.run(fetches, options=run_options, run_metadata=run_metadata) + else: + (results, summary_str) = sess.run( + [fetches, summary_op], options=run_options, run_metadata=run_metadata) + + if not params.forward_only: + lossval = results['average_loss'] + else: + lossval = 0. + if image_producer is not None: + image_producer.notify_image_consumption() + train_time = time.perf_counter() - start_time + step_train_times.append(train_time) + if (show_images_per_sec and step >= 0 and + (step == 0 or (step + 1) % params.display_every == 0)): + speed_mean, speed_uncertainty, speed_jitter = get_perf_timing( + batch_size, step_train_times, params.display_perf_ewma) + log_str = '%i\t%s\t%.*f' % ( + step + 1, + get_perf_timing_str(speed_mean, speed_uncertainty, speed_jitter), + LOSS_AND_ACCURACY_DIGITS_TO_SHOW, lossval) + if 'top_1_accuracy' in results: + log_str += '\t%.*f\t%.*f' % ( + LOSS_AND_ACCURACY_DIGITS_TO_SHOW, results['top_1_accuracy'], + LOSS_AND_ACCURACY_DIGITS_TO_SHOW, results['top_5_accuracy']) + log_fn(log_str) + if benchmark_logger: + benchmark_logger.log_metric( + 'current_examples_per_sec', speed_mean, global_step=step + 1) + if 'top_1_accuracy' in results: + benchmark_logger.log_metric( + 'top_1_accuracy', results['top_1_accuracy'], global_step=step + 1) + benchmark_logger.log_metric( + 'top_5_accuracy', results['top_5_accuracy'], global_step=step + 1) + if need_options_and_metadata: + if should_profile: + profiler.add_step(step, run_metadata) + if trace_filename and step == -2 and should_output_files: + log_fn('Dumping trace to %s' % trace_filename) + trace_dir = os.path.dirname(trace_filename) + if not gfile.Exists(trace_dir): + gfile.MakeDirs(trace_dir) + with gfile.Open(trace_filename, 'w') as trace_file: + if params.use_chrome_trace_format: + trace = timeline.Timeline(step_stats=run_metadata.step_stats) + trace_file.write(trace.generate_chrome_trace_format(show_memory=True)) + else: + trace_file.write(str(run_metadata.step_stats)) + if partitioned_graph_file_prefix and step == -2 and should_output_files: + path, filename = os.path.split(partitioned_graph_file_prefix) + if '.' in filename: + base_filename, ext = filename.rsplit('.', 1) + ext = '.' + ext + else: + base_filename, ext = filename, '' + as_text = filename.endswith('txt') + for graph_def in run_metadata.partition_graphs: + device = graph_def.node[0].device.replace('/', '_').replace(':', '_') + graph_filename = '%s%s%s' % (base_filename, device, ext) + log_fn('Writing partitioned GraphDef as %s to %s' % ( + 'text' if as_text else 'binary', + os.path.join(path, graph_filename))) + tf.train.write_graph(graph_def, path, graph_filename, as_text) + return (summary_str, lossval) + + +def get_perf_timing_str(speed_mean, speed_uncertainty, speed_jitter, scale=1): + if scale == 1: + # TODO(laigd): rename 'images' to maybe 'inputs', same below. + return ('images/sec: %.1f +/- %.1f (jitter = %.1f)' % + (speed_mean, speed_uncertainty, speed_jitter)) + else: + return 'images/sec: %.1f' % speed_mean + + +def get_perf_timing(batch_size, step_train_times, ewma_alpha=None, scale=1): + """Calculate benchmark processing speed.""" + times = np.array(step_train_times) + speeds = batch_size / times + if ewma_alpha: + weights = np.logspace(len(times)-1, 0, len(times), base=1-ewma_alpha) + time_mean = np.average(times, weights=weights) + else: + time_mean = np.mean(times) + speed_mean = scale * batch_size / time_mean + speed_uncertainty = np.std(speeds) / np.sqrt(float(len(speeds))) + speed_jitter = 1.4826 * np.median(np.abs(speeds - np.median(speeds))) + return speed_mean, speed_uncertainty, speed_jitter + + +def load_checkpoint(saver, sess, ckpt_dir): + """Loads checkpoint from provided directory or full path. + + Args: + saver: Saver used to restore the checkpoint. + sess: TensorFlow session. + ckpt_dir: Path to a folder of checkpoints or full path to a checkpoint. + + Returns: + Global step. + """ + model_checkpoint_path = _get_checkpoint_to_load(ckpt_dir) + global_step = model_checkpoint_path.split('/')[-1].split('-')[-1] + if not global_step.isdigit(): + global_step = 0 + else: + global_step = int(global_step) + saver.restore(sess, model_checkpoint_path) + log_fn('Successfully loaded model from %s.' % model_checkpoint_path) + return global_step + + +def _get_checkpoint_to_load(ckpt_dir): + """Returns which checkpoint to load. + + Args: + ckpt_dir: Path to a folder of checkpoints or full path to a checkpoint. + + Returns: + Full path to checkpoint to load. + + Raises: + CheckpointNotFoundException: If checkpoint is not found. + """ + p = re.compile(r'ckpt-\d+$') + if p.search(ckpt_dir): + model_checkpoint_path = ckpt_dir + else: + # Finds latest checkpoint in directory provided + ckpt = tf.train.get_checkpoint_state(ckpt_dir) + if ckpt and ckpt.model_checkpoint_path: + model_checkpoint_path = ckpt.model_checkpoint_path + else: + raise CheckpointNotFoundException('No checkpoint file found in dir:{}'. + format(ckpt_dir)) + return model_checkpoint_path + + +# Params are passed to BenchmarkCNN's constructor. Params is a map from name +# to value, with one field per key in flags.param_specs. +# +# Call make_params() or make_params_from_flags() below to construct a Params +# tuple with default values from flags.param_specs, rather than constructing +# Params directly. +Params = namedtuple('Params', flags.param_specs.keys()) # pylint: disable=invalid-name + + +def validate_params(params): + """Validates that the Params tuple had valid values. + + When command-line flags are defined for each ParamSpec by calling + flags.define_flags(), calling this function is unnecessary because absl + already does flag validation. Otherwise, this function should be called. + + Args: + params: A Params tuple. + Raises: + ValueError: An element of params had an invalid value. + """ + for name, value in params._asdict().items(): + param_spec = flags.param_specs[name] + if param_spec.flag_type in ('integer', 'float'): + if (value is not None and param_spec.kwargs['lower_bound'] is not None and + value < param_spec.kwargs['lower_bound']): + raise ValueError('Param %s value of %s is lower than the lower bound ' + 'of %s' % + (name, value, param_spec.kwargs['lower_bound'])) + if (value is not None and param_spec.kwargs['upper_bound'] is not None and + param_spec.kwargs['upper_bound'] < value): + raise ValueError('Param %s value of %s is higher than the upper bound ' + 'of %s' % + (name, value, param_spec.kwargs['upper_bound'])) + elif (value is not None and param_spec.flag_type == 'enum' and + value not in param_spec.kwargs['enum_values']): + raise ValueError('Param %s of value %s is not in %s'% + (name, value, param_spec.kwargs['enum_values'])) + + +def make_params(**kwargs): + """Create a Params tuple for BenchmarkCNN from kwargs. + + Default values are filled in from flags.param_specs. + + Args: + **kwargs: kwarg values will override the default values. + Returns: + Params namedtuple for constructing BenchmarkCNN. + """ + # Create a (name: default_value) map from flags.param_specs. + default_kwargs = { + name: flags.param_specs[name].default_value + for name in flags.param_specs + } + params = Params(**default_kwargs)._replace(**kwargs) + validate_params(params) + return params + + +def make_params_from_flags(): + """Create a Params tuple for BenchmarkCNN from absl_flags.FLAGS. + + Returns: + Params namedtuple for constructing BenchmarkCNN. + """ + # Collect (name: value) pairs for absl_flags.FLAGS with matching names in + # flags.param_specs. + flag_values = {name: getattr(absl_flags.FLAGS, name) + for name in flags.param_specs.keys()} + return Params(**flag_values) + + +def remove_param_fields(params, fields_to_remove): + """Remove fields from a Params namedtuple.""" + params_dict = params._asdict() + for field in fields_to_remove: + assert field in params_dict, 'Invalid Params field: ' + field + params_dict = {k: v for k, v in params_dict.items() + if k not in fields_to_remove} + new_params_type = namedtuple('Params', params_dict.keys()) + return new_params_type(**params_dict) + + +def get_num_batches_and_epochs(params, batch_size, num_examples_per_epoch): + """Returns the number of batches and epochs to run for. + + Args: + params: Params tuple, typically created by make_params or + make_params_from_flags. + batch_size: The number of images per step. + num_examples_per_epoch: The number of images in a single epoch. + + Returns: + num_batches: The number of batches to run for. + num_epochs: The number of epochs to run for. This might be slightly + smaller than params.num_epochs if specified, because the number of batches + must be an integer. + + Raises: + ValueError: Invalid or unsupported params. + """ + if params.num_batches and params.num_epochs: + raise ValueError('At most one of --num_batches and --num_epochs may be ' + 'specified.') + if params.num_epochs: + num_batches = int(params.num_epochs * num_examples_per_epoch + + batch_size - 1) // batch_size + else: + num_batches = params.num_batches or _DEFAULT_NUM_BATCHES + num_epochs = num_batches * batch_size / num_examples_per_epoch + return (num_batches, num_epochs) + + +def get_piecewise_learning_rate(piecewise_learning_rate_schedule, + global_step, num_batches_per_epoch): + """Returns a piecewise learning rate tensor. + + Args: + piecewise_learning_rate_schedule: The --piecewise_learning_rate_schedule + parameter + global_step: Scalar tensor representing the global step. + num_batches_per_epoch: float indicating the number of batches per epoch. + + Returns: + A scalar float tensor, representing the learning rate. + + Raises: + ValueError: piecewise_learning_rate_schedule is not formatted correctly. + """ + pieces = piecewise_learning_rate_schedule.split(';') + if len(pieces) % 2 == 0: + raise ValueError('--piecewise_learning_rate_schedule must have an odd ' + 'number of components') + values = [] + boundaries = [] + for i, piece in enumerate(pieces): + if i % 2 == 0: + try: + values.append(float(piece)) + except ValueError: + raise ValueError('Invalid learning rate: ' + piece) + else: + try: + boundaries.append(int(int(piece) * num_batches_per_epoch) - 1) + except ValueError: + raise ValueError('Invalid epoch: ' + piece) + return tf.train.piecewise_constant(global_step, boundaries, values, + name='piecewise_learning_rate') + + +def get_learning_rate(params, global_step, num_examples_per_epoch, model, + batch_size): + """Returns a learning rate tensor based on global_step. + + Args: + params: Params tuple, typically created by make_params or + make_params_from_flags. + global_step: Scalar tensor representing the global step. + num_examples_per_epoch: The number of examples per epoch. + model: The model.Model object to obtain the default learning rate from if no + learning rate is specified. + batch_size: Number of examples per step + + Returns: + A scalar float tensor, representing the learning rate. When evaluated, the + learning rate depends on the current value of global_step. + + Raises: + ValueError: Invalid or unsupported params. + """ + with tf.name_scope('learning_rate'): + num_batches_per_epoch = num_examples_per_epoch / batch_size + + if params.piecewise_learning_rate_schedule: + if (params.init_learning_rate is not None or + params.learning_rate_decay_factor or + params.minimum_learning_rate or params.num_epochs_per_decay): + raise ValueError('No other learning rate-related flags can be ' + 'specified if --piecewise_learning_rate_schedule is ' + 'specified') + learning_rate = get_piecewise_learning_rate( + params.piecewise_learning_rate_schedule, + global_step, num_batches_per_epoch) + elif params.init_learning_rate is not None: + learning_rate = params.init_learning_rate + if (params.num_epochs_per_decay > 0 and + params.learning_rate_decay_factor > 0): + decay_steps = int(num_batches_per_epoch * params.num_epochs_per_decay) + + # Decay the learning rate exponentially based on the number of steps. + learning_rate = tf.train.exponential_decay( + params.init_learning_rate, + global_step, + decay_steps, + params.learning_rate_decay_factor, + staircase=True) + + if params.minimum_learning_rate != 0.: + learning_rate = tf.maximum(learning_rate, + params.minimum_learning_rate) + else: + learning_rate = model.get_learning_rate(global_step, batch_size) + if params.num_learning_rate_warmup_epochs > 0 and ( + params.init_learning_rate is not None or + params.piecewise_learning_rate_schedule): + warmup_steps = int(num_batches_per_epoch * + params.num_learning_rate_warmup_epochs) + init_lr = params.init_learning_rate + if init_lr is None: + init_lr = float(params.piecewise_learning_rate_schedule.split(';')[0]) + warmup_lr = init_lr * tf.cast(global_step, tf.float32) / tf.cast( + warmup_steps, tf.float32) + learning_rate = tf.cond(global_step < warmup_steps, + lambda: warmup_lr, lambda: learning_rate) + + learning_rate = mlperf.logger.log_deferred_tensor_value( + mlperf.tags.OPT_LR, learning_rate, global_step, every_n=100) + return learning_rate + + +def get_optimizer(params, learning_rate): + """Returns the optimizer that should be used based on params.""" + if params.optimizer == 'momentum': + mlperf.logger.log(key=mlperf.tags.OPT_NAME, + value=mlperf.tags.SGD_WITH_MOMENTUM) + mlperf.logger.log(key=mlperf.tags.OPT_MOMENTUM, value=params.momentum) + opt = tf.train.MomentumOptimizer( + learning_rate, params.momentum, use_nesterov=True) + elif params.optimizer == 'sgd': + mlperf.logger.log(key=mlperf.tags.OPT_NAME, value=mlperf.tags.SGD) + opt = tf.train.GradientDescentOptimizer(learning_rate) + elif params.optimizer == 'rmsprop': + opt = tf.train.RMSPropOptimizer( + learning_rate, + params.rmsprop_decay, + momentum=params.rmsprop_momentum, + epsilon=params.rmsprop_epsilon) + elif params.optimizer == 'adam': + opt = tf.train.AdamOptimizer(learning_rate, params.adam_beta1, + params.adam_beta2, params.adam_epsilon) + else: + raise ValueError('Optimizer "{}" was not recognized'. + format(params.optimizer)) + return opt + + +def generate_tfprof_profile(profiler, tfprof_file): + """Generates a tfprof profile, writing it to a file and printing top ops. + + Args: + profiler: A tf.profiler.Profiler. `profiler.add_step` must have already been + called. + tfprof_file: The filename to write the ProfileProto to. + """ + profile_proto = profiler.serialize_to_string() + log_fn('Dumping ProfileProto to %s' % tfprof_file) + with gfile.Open(tfprof_file, 'wb') as f: + f.write(profile_proto) + + # Print out the execution times of the top operations. Note this + # information can also be obtained with the dumped ProfileProto, but + # printing it means tfprof doesn't have to be used if all the user wants + # is the top ops. + options = tf.profiler.ProfileOptionBuilder.time_and_memory() + options['max_depth'] = _NUM_OPS_TO_PRINT + options['order_by'] = 'accelerator_micros' + profiler.profile_operations(options) + + +class BenchmarkCNN(object): + """Class for benchmarking a cnn network.""" + + def __init__(self, params, dataset=None, model=None): + """Initialize BenchmarkCNN. + + Args: + params: Params tuple, typically created by make_params or + make_params_from_flags. + dataset: If not None, the dataset to use. Otherwise, params is used to + obtain the dataset. + model: If not None, the model to use. Otherwise, params is used to obtain + the model. + Raises: + ValueError: Unsupported params settings. + """ + mlperf.logger.log(key=mlperf.tags.RUN_START) + self.params = params + if params.eval: + self._doing_eval = True + else: + # Note self._doing_eval can later switch to True in self._do_eval() if + # self.params.eval_during_training_* is specified. + self._doing_eval = False + self.dataset = dataset or datasets.create_dataset(self.params.data_dir, + self.params.data_name) + self.model = model or model_config.get_model_config( + self.params.model, self.dataset, self.params) + self.trace_filename = self.params.trace_file + self.rewriter_config = self.params.rewriter_config + autotune_threshold = self.params.autotune_threshold if ( + self.params.autotune_threshold) else 1 + min_autotune_warmup = 5 * autotune_threshold * autotune_threshold + self.num_warmup_batches = self.params.num_warmup_batches if ( + self.params.num_warmup_batches is not None) else max( + 10, min_autotune_warmup) + self.graph_file = self.params.graph_file + self.resize_method = self.params.resize_method + self.sync_queue_counter = 0 + self.num_gpus = self.params.num_gpus + if self.params.gpu_indices: + self.gpu_indices = [int(x) for x in self.params.gpu_indices.split(',')] + else: + self.gpu_indices = [x for x in range(self.num_gpus)] + + if (self.params.device == 'cpu' and self.params.data_format == 'NCHW' and + not self.params.mkl): + raise ValueError('device=cpu requires that data_format=NHWC') + + if ((self.params.num_epochs_per_decay or + self.params.learning_rate_decay_factor) and + not (self.params.init_learning_rate is not None and + self.params.num_epochs_per_decay + and self.params.learning_rate_decay_factor)): + raise ValueError('If one of num_epochs_per_decay or ' + 'learning_rate_decay_factor is set, both must be set' + 'and learning_rate must be set') + if (self.params.minimum_learning_rate and + not (self.params.init_learning_rate is not None and + self.params.num_epochs_per_decay and + self.params.learning_rate_decay_factor)): + raise ValueError('minimum_learning_rate requires learning_rate,' + 'num_epochs_per_decay, and ' + 'learning_rate_decay_factor to be set') + + if (self.params.use_fp16 and self.params.fp16_vars and + 'replicated' in self.params.variable_update and + self.params.all_reduce_spec and 'nccl' in self.params.all_reduce_spec): + raise ValueError('fp16 variables are not supported with NCCL') + if (self.params.use_fp16 and self.params.fp16_vars and + self.params.gradient_repacking): + raise ValueError('--fp16_vars cannot be used with --gradient_repacking') + + if self.params.variable_update == 'horovod' and self.params.num_gpus > 1: + raise ValueError('Horovod benchmarks require num_gpus=1 on each worker') + + if self.params.variable_update == 'horovod' and self.params.job_name: + raise ValueError('job_name should not be specified for Horovod.') + + if self.params.use_fp16 and self.params.fp16_enable_auto_loss_scale: + if self.params.all_reduce_spec and 'nccl' in self.params.all_reduce_spec: + raise ValueError('Automatic loss scaling is not supported with NCCL.') + if self.params.variable_update not in ('parameter_server', 'replicated', + 'independent'): + raise ValueError('Automatic loss scaling is not supported with ' + 'variable_update=%s.' % self.params.variable_update) + if self.params.staged_vars: + raise ValueError('Automatic loss scaling is not supported with' + 'staged_vars.') + + if (self.params.debugger is not None and self.params.debugger != 'cli' and + ':' not in self.params.debugger): + raise ValueError('--debugger must be "cli" or in the form ' + 'host:port') + + if self.params.hierarchical_copy and self.params.num_gpus <= 1: + raise ValueError('--hierarchical_copy requires --num_gpus to be greater ' + 'than 1') + + if params.save_model_secs and params.save_model_steps: + raise ValueError('At most one of --save_model_secs and ' + '--save_model_steps can be specified') + + eval_during_training_flags = list(map(bool, [ + params.eval_during_training_every_n_steps, + params.eval_during_training_every_n_epochs, + params.eval_during_training_at_specified_steps, + params.eval_during_training_at_specified_epochs, + ])) + + if eval_during_training_flags.count(True) > 1: + raise ValueError('At most one flag with --eval_during_training_* prefix ' + 'must be specified.') + + eval_during_training_enabled = any(eval_during_training_flags) + + if eval_during_training_enabled: + if params.eval: + raise ValueError('At most one of --eval and --eval_during_training_* ' + 'must be specified') + if params.forward_only: + raise ValueError('At most one of --forward_only and ' + '--eval_during_training_* must be specified') + if params.job_name: + raise ValueError('--eval_during_training_* is not yet supported in ' + 'distributed mode.') + if params.staged_vars: + raise ValueError('--eval_during_training_* is not currently compatible ' + 'with --staged_vars') + + if params.stop_at_top_1_accuracy and not eval_during_training_enabled: + raise ValueError('--stop_at_top_1_accuracy is only supported with ' + '--eval_during_training_*') + if params.collect_eval_results_async and params.model != 'ssd300': + raise ValueError('--collect_eval_results_async only works with ssd300 ' + 'model currently.') + if self.params.forward_only and self.params.freeze_when_forward_only: + if self.params.train_dir is not None: + raise ValueError('In forward_only mode, when --freeze_when_forward_only' + ' is True, --train_dir should not be specified') + if self.params.data_dir and not self.params.datasets_use_prefetch: + raise ValueError('In forward_only mode, when --freeze_when_forward_only' + ' is True and --data_dir is set, ' + '--datasets_use_prefetch should be set to True') + if self.params.job_name: + raise ValueError('In forward_only mode, when --freeze_when_forward_only' + ' is True, --job_name should not be specified and ' + 'distributed running is not supported') + self.forward_only_and_freeze = True + else: + self.forward_only_and_freeze = False + if self.params.trt_mode: + raise ValueError('--trt_mode should not be specified if one of ' + '--forward_only and --freeze_when_forward_only is set ' + 'to False') + + self.mode = get_mode_from_params(self.params) + + # Use the batch size from the command line if specified, otherwise use the + # model's default batch size. Scale the benchmark's batch size by the + # number of GPUs. + if self.params.batch_size > 0: + self.model.set_batch_size(self.params.batch_size) + self.batch_size = self.model.get_batch_size() * self.num_gpus + if self.mode in (constants.BenchmarkMode.TRAIN, + constants.BenchmarkMode.TRAIN_AND_EVAL): + self.train_batch_size = self.batch_size + else: + self.train_batch_size = None + if self.mode in (constants.BenchmarkMode.EVAL, + constants.BenchmarkMode.TRAIN_AND_EVAL): + if self.params.eval_batch_size > 0: + self.eval_batch_size = self.params.eval_batch_size * self.num_gpus + else: + self.eval_batch_size = self.batch_size + else: + self.eval_batch_size = None + self.batch_group_size = self.params.batch_group_size + self.enable_auto_loss_scale = ( + self.params.use_fp16 and self.params.fp16_enable_auto_loss_scale) + self.loss_scale = None + self.loss_scale_normal_steps = None + + self.job_name = self.params.job_name # "" for local training + + # PS server is used for distributed jobs not using all-reduce. + use_ps_server = self.job_name and (self.params.variable_update != + 'distributed_all_reduce' and + self.params.variable_update != + 'collective_all_reduce') + # controller is used for distributed_all_reduce with > 1 worker. + use_controller = ( + self.params.variable_update == 'distributed_all_reduce' and + self.job_name) + if use_controller and not params.controller_host: + raise ValueError('When variable_update==distributed_all_reduce ' + 'controller_host must also be specified.') + self.single_session = ( + self.params.variable_update == 'distributed_all_reduce') + # collective_all_reduce doesn't need a controller or ps + self.distributed_collective = ( + self.params.variable_update == 'collective_all_reduce' and + self.job_name) + + self.local_parameter_device_flag = self.params.local_parameter_device + if self.job_name: + self.task_index = self.params.task_index + self.cluster_manager = platforms_util.get_cluster_manager( + params, create_config_proto(params)) + assert isinstance(self.cluster_manager, cnn_util.BaseClusterManager) + + worker_prefix = '/job:worker/replica:0/task:%s' % self.task_index + if use_ps_server: + self.param_server_device = tf.train.replica_device_setter( + worker_device=worker_prefix + '/cpu:0', + cluster=self.cluster_manager.get_cluster_spec()) + # This device on which the queues for managing synchronization between + # servers should be stored. + self.sync_queue_devices = [ + '/job:ps/replica:0/task:%s/cpu:0' % i + for i in range(self.cluster_manager.num_ps()) + ] + else: + self.sync_queue_devices = ['/job:worker/replica:0/task:0/cpu:0'] + else: + self.task_index = 0 + self.cluster_manager = None + worker_prefix = '' + self.param_server_device = '/%s:0' % self.params.local_parameter_device + self.sync_queue_devices = [self.param_server_device] + + if self.cluster_manager: + self.num_workers = self.cluster_manager.num_workers() + elif self.params.variable_update == 'horovod': + import horovod.tensorflow as hvd # pylint: disable=g-import-not-at-top + self.num_workers = hvd.size() + else: + self.num_workers = 1 + self.num_ps = self.cluster_manager.num_ps() if self.cluster_manager else 0 + + if self.num_workers > 1 and self.params.all_reduce_spec == 'nccl': + raise ValueError('--all_reduce_spec=nccl is invalid in a ' + 'multi-worker job') + + # Device to use for ops that need to always run on the local worker's CPU. + self.cpu_device = '%s/cpu:0' % worker_prefix + + # Device to use for ops that need to always run on the local worker's + # compute device, and never on a parameter server device. + self.raw_devices = [ + '%s/%s:%i' % (worker_prefix, self.params.device, i) + for i in xrange(self.num_gpus) + ] + + subset = 'validation' if params.eval else 'train' + self.num_batches, self.num_epochs = get_num_batches_and_epochs( + params, self.batch_size * self.num_workers, + self.dataset.num_examples_per_epoch(subset)) + if self.mode in (constants.BenchmarkMode.EVAL, + constants.BenchmarkMode.TRAIN_AND_EVAL): + # TODO(reedwm): Currently we do extra eval logic for num_eval_batches and + # the preprocessor. We should encapsulate this logic into a shared + # function or class. + if params.num_eval_batches is None and params.num_eval_epochs is None: + eval_params = self.params + else: + eval_params = self.params._replace( + num_batches=self.params.num_eval_batches, + num_epochs=self.params.num_eval_epochs) + self.num_eval_batches, self.num_eval_epochs = get_num_batches_and_epochs( + eval_params, self.eval_batch_size * self.num_workers, + self.dataset.num_examples_per_epoch('validation')) + else: + self.num_eval_batches, self.num_eval_epochs = None, None + + num_train_examples_per_epoch = self.dataset.num_examples_per_epoch('train') + if self.params.eval_during_training_every_n_epochs: + n_epochs = self.params.eval_during_training_every_n_epochs + self.eval_during_training_at_specified_steps = { + (int(e * num_train_examples_per_epoch + self.batch_size - 1) // + self.batch_size) + for e in np.arange(n_epochs, self.num_epochs, n_epochs)} + + if self.params.eval_during_training_at_specified_steps: + try: + self.eval_during_training_at_specified_steps = set(map( + int, self.params.eval_during_training_at_specified_steps)) + except ValueError: + raise ValueError('Param eval_during_training_at_specified_steps value ' + 'of %s cannot be converted to a list of integers.' % + (self.params.eval_during_training_at_specified_steps)) + + if self.params.eval_during_training_at_specified_epochs: + try: + n_epochs = list(map( + float, self.params.eval_during_training_at_specified_epochs)) + offset = n_epochs[0] - 1 + if offset.is_integer(): + offset = int(offset) + mlperf.logger.log(key=mlperf.tags.EVAL_EPOCH_OFFSET, value=offset) + self.eval_during_training_at_specified_steps = { + (int(e * num_train_examples_per_epoch + self.batch_size - 1) // + self.batch_size) + for e in n_epochs} + except ValueError: + raise ValueError('Param eval_during_training_at_specified_epochs value ' + 'of %s cannot be converted to a list of floats.' % + (self.params.eval_during_training_at_specified_epochs)) + + if params.eval_during_training_every_n_epochs: + offset = params.eval_during_training_every_n_epochs - 1 + if offset.is_integer(): + offset = int(offset) + mlperf.logger.log(key=mlperf.tags.EVAL_EPOCH_OFFSET, value=offset) + + if (self.params.staged_vars and + self.params.variable_update != 'parameter_server'): + raise ValueError('staged_vars for now is only supported with ' + 'variable_update=parameter_server') + + if self.params.variable_update == 'parameter_server': + if self.job_name: + if not self.params.staged_vars: + self.variable_mgr = variable_mgr.VariableMgrDistributedFetchFromPS( + self) + else: + self.variable_mgr = ( + variable_mgr.VariableMgrDistributedFetchFromStagedPS(self)) + else: + if not self.params.staged_vars: + self.variable_mgr = variable_mgr.VariableMgrLocalFetchFromPS(self) + else: + self.variable_mgr = variable_mgr.VariableMgrLocalFetchFromStagedPS( + self) + elif self.params.variable_update == 'replicated': + if self.job_name: + raise ValueError('Invalid variable_update in distributed mode: %s' % + self.params.variable_update) + self.variable_mgr = variable_mgr.VariableMgrLocalReplicated( + self, self.params.all_reduce_spec, + self.params.agg_small_grads_max_bytes, + self.params.agg_small_grads_max_group, + self.params.allreduce_merge_scope) + elif self.params.variable_update == 'distributed_all_reduce': + assert self.params.cross_replica_sync + self.variable_mgr = variable_mgr.VariableMgrDistributedAllReduce( + self, self.params.all_reduce_spec, + ('worker' if self.num_workers > 1 else 'localhost'), + self.num_workers, self.params.agg_small_grads_max_bytes, + self.params.agg_small_grads_max_group, + self.params.allreduce_merge_scope) + elif self.params.variable_update == 'collective_all_reduce': + assert self.params.cross_replica_sync + self.variable_mgr = variable_mgr.VariableMgrCollectiveAllReduce( + self, self.params.all_reduce_spec, + self.num_workers, self.num_gpus, self.task_index, + self.params.allreduce_merge_scope) + elif self.params.variable_update == 'distributed_replicated': + assert self.params.cross_replica_sync + if not self.job_name: + raise ValueError('Invalid variable_update in local mode: %s' % + self.params.variable_update) + self.variable_mgr = variable_mgr.VariableMgrDistributedReplicated(self) + elif self.params.variable_update in ('independent', 'horovod'): + if self.job_name: + raise ValueError('Invalid variable_update in distributed mode: %s' % + self.params.variable_update) + self.variable_mgr = variable_mgr.VariableMgrIndependent(self) + else: + raise ValueError( + 'Invalid variable_update: %s' % self.params.variable_update) + + # Device to use for running on the local worker's compute device, but + # with variables assigned to parameter server devices. + self.devices = self.variable_mgr.get_devices() + if self.job_name: + if use_ps_server: + self.global_step_device = self.param_server_device + elif self.params.variable_update == 'collective_all_reduce': + self.global_step_device = self.cpu_device + else: + self.global_step_device = '/job:worker/replica:0/task:0/cpu:0' + else: + self.global_step_device = self.cpu_device + + self.input_preprocessor = None + self.eval_input_preprocessor = None + if not self.dataset.use_synthetic_gpu_inputs(): + if not self.params.eval: + self.input_preprocessor = self.get_input_preprocessor() + if self.mode in (constants.BenchmarkMode.EVAL, + constants.BenchmarkMode.TRAIN_AND_EVAL): + with self._do_eval(): + self.eval_input_preprocessor = self.get_input_preprocessor() + self.datasets_use_prefetch = ( + self.params.datasets_use_prefetch and + # TODO(rohanj): Figure out why --datasets_use_prefetch freezes on the + # CPU. + self.params.device.lower() != 'cpu' and + self.input_preprocessor and + self.input_preprocessor.supports_datasets()) + self.init_global_step = 0 + + self._config_benchmark_logger() + + if self.mode == constants.BenchmarkMode.TRAIN_AND_EVAL: + # Remove "eval" from params so it is not accidentally used. Since eval can + # still occur despite params.eval being False, params.eval should never + # be used. We cannot yet remove this unconditionally, because the SSD + # model still uses params.eval, and hence does not work properly with + # --eval_during_training_*. + # TODO(b/116627045): We should also remove fields that have an eval + # equivalent, like num_batches and num_eval_batches. + self.params = remove_param_fields(self.params, {'eval'}) + + @contextlib.contextmanager + def _do_eval(self): + """Context manager to switches BenchmarkCNN to eval mode. + + Any evaluation code should be put under this context manager. This context + manager switches self._doing_eval to True. It also switches certain + attributes, like self.num_batches and self.num_epochs, to be the number of + batches and epochs for evaluation respectively + + Yields: + Nothing. + """ + # TODO(b/116627045): Find a more general way of switching attributes to the + # eval equivalents. + old_doing_eval = self._doing_eval + old_num_batches = self.num_batches + old_num_epochs = self.num_epochs + old_batch_size = self.batch_size + try: + self._doing_eval = True + self.num_batches = self.num_eval_batches + self.num_epochs = self.num_eval_epochs + self.batch_size = self.eval_batch_size + self.model.set_batch_size(self.eval_batch_size // self.num_gpus) + yield + finally: + self._doing_eval = old_doing_eval + self.num_batches = old_num_batches + self.num_epochs = old_num_epochs + self.batch_size = old_batch_size + self.model.set_batch_size(old_batch_size // self.num_gpus) + + def _config_benchmark_logger(self): + """Config the model garden benchmark logger.""" + model_benchmark_logger = None + if self.params.benchmark_log_dir is not None: + try: + from official.r1.utils.logs import logger as models_logger # pylint: disable=g-import-not-at-top + except ImportError: + tf.logging.fatal('Please include tensorflow/models to the PYTHONPATH ' + 'in order to use BenchmarkLogger. Configured ' + 'benchmark_log_dir: %s' + % self.params.benchmark_log_dir) + raise + model_benchmark_logger = models_logger.BenchmarkFileLogger( + self.params.benchmark_log_dir) + self.benchmark_logger = model_benchmark_logger + + # TODO(laigd): this changes the global device list which is used everywhere, + # consider refactoring it. + def reset_devices_for_task(self, task_num, is_local=False): + """Used to imitate another task when building a distributed graph.""" + worker_prefix = ('/job:localhost' if is_local else + '/job:worker/replica:0/task:%s' % task_num) + self.cpu_device = '%s/cpu:0' % worker_prefix + self.raw_devices = [ + '%s/%s:%i' % (worker_prefix, self.params.device, i) + for i in xrange(self.num_gpus) + ] + self.devices = self.variable_mgr.get_devices() + + def raw_devices_across_tasks(self, is_local=False): + """Returns list of raw device names across all tasks.""" + if is_local: + assert self.num_workers == 1 + return self.raw_devices + else: + return [ + 'job:worker/replica:0/task%s/%s:%i' % (t, self.params.device, i) + for t in xrange(self.num_workers) + for i in xrange(self.num_gpus) + ] + + def print_info(self): + """Print basic information.""" + benchmark_info = self._get_params_info() + log_fn('Model: %s' % self.model.get_model_name()) + log_fn('Dataset: %s' % benchmark_info['dataset_name']) + log_fn('Mode: %s' % self.mode) + log_fn('SingleSess: %s' % benchmark_info['single_session']) + log_fn('Batch size: %s global' % (self.batch_size * self.num_workers)) + log_fn(' %s per device' % (self.batch_size // + len(self.raw_devices))) + if self.batch_group_size > 1: + log_fn(' %d batches per prepocessing group' % + self.batch_group_size) + log_fn('Num batches: %d' % self.num_batches) + log_fn('Num epochs: %.2f' % self.num_epochs) + log_fn('Devices: %s' % benchmark_info['device_list']) + log_fn('NUMA bind: %s' % self.params.use_numa_affinity) + log_fn('Data format: %s' % self.params.data_format) + if self.rewriter_config: + log_fn('RewriterConfig: %s' % self.rewriter_config) + log_fn('Optimizer: %s' % self.params.optimizer) + log_fn('Variables: %s' % self.params.variable_update) + if (self.params.variable_update == 'replicated' or + self.params.variable_update == 'distributed_all_reduce' + or self.params.variable_update == 'collective_all_reduce'): + log_fn('AllReduce: %s' % self.params.all_reduce_spec) + if self.job_name: + log_fn('Sync: %s' % self.params.cross_replica_sync) + if self.params.staged_vars: + log_fn('Staged vars: %s' % self.params.staged_vars) + if self.params.variable_update == 'horovod' and self.params.horovod_device: + log_fn('Horovod on: %s' % self.params.horovod_device) + log_fn('==========') + + def _get_params_info(self): + """Get the common parameters info for the benchmark run. + + Returns: + A dict of processed parameters. + """ + dataset_name = self.dataset.name + if self.dataset.use_synthetic_gpu_inputs(): + dataset_name += ' (synthetic)' + single_session = self.params.variable_update == 'distributed_all_reduce' + if single_session: + device_list = self.raw_devices_across_tasks() + elif self.params.variable_update == 'horovod': + device_list = ['horovod/%s:%d' % (self.params.device, idx) + for idx in range(self.num_workers)] + else: + device_list = self.raw_devices + return { + 'dataset_name': dataset_name, + 'single_session': single_session, + 'device_list': device_list,} + + def _log_benchmark_run(self): + """Log the benchmark info to the logger. + + The info logged here should be similar to print_info(), but in a structured + JSON format. + """ + if self.benchmark_logger: + benchmark_info = self._get_params_info() + + run_param = { + 'model': self.model.get_model_name(), + 'dataset': benchmark_info['dataset_name'], + 'mode': self.mode, + 'single_sess': benchmark_info['single_session'], + 'devices': benchmark_info['device_list'], + 'batch_size': self.batch_size, + 'batch_size_per_device': self.batch_size // len(self.raw_devices), + 'num_batches': self.num_batches, + 'num_epochs': self.num_epochs, + 'data_format': self.params.data_format, + 'rewrite_config': self.rewriter_config, + 'optimizer': self.params.optimizer, + 'session_config': create_config_proto(self.params), + } + # TODO(scottzhu): tf_cnn_benchmark might execute several times with + # different param setting on the same box. This will cause the run file to + # only contain the latest info. The benchmark_log_dir should be updated + # for every new run. + self.benchmark_logger.log_run_info( + self.model.get_model_name(), benchmark_info['dataset_name'], + run_param, test_id=self.params.benchmark_test_id) + + def run(self): + """Run the benchmark task assigned to this process. + + Returns: + Dictionary of statistics for training or eval. + Raises: + ValueError: unrecognized job name. + """ + if self.params.job_name == 'ps': + log_fn('Running parameter server %s' % self.task_index) + self.cluster_manager.join_server() + return {} + + # For distributed_all_reduce with multiple workers, drive + # from a separate controller process. + if self.params.variable_update == 'distributed_all_reduce': + if self.params.job_name == 'worker': + log_fn('Starting worker %s' % self.task_index) + self.cluster_manager.join_server() + return + elif self.params.job_name and self.params.job_name != 'controller': + raise ValueError('unrecognized job name: %s' % self.params.job_name) + + self._log_benchmark_run() + if self._doing_eval: + with tf.Graph().as_default(): + # TODO(laigd): freeze the graph in eval mode. + return self._run_eval() + else: + return self._benchmark_train() + + def _run_eval(self): + """Evaluate a model every self.params.eval_interval_secs. + + Returns: + Dictionary containing eval statistics. Currently returns an empty + dictionary. + + Raises: + ValueError: If self.params.train_dir is unspecified. + """ + if self.params.train_dir is None: + raise ValueError('Trained model directory not specified') + graph_info = self._build_eval_graph() + saver = tf.train.Saver(self.variable_mgr.savable_variables()) + summary_writer = tf.summary.FileWriter(self.params.eval_dir, + tf.get_default_graph()) + target = '' + # TODO(huangyp): Check if checkpoints haven't updated for hours and abort. + while True: + with tf.Session( + target=target, config=create_config_proto(self.params)) as sess: + image_producer = None + try: + global_step = load_checkpoint(saver, sess, self.params.train_dir) + image_producer = self._initialize_eval_graph( + graph_info.enqueue_ops, graph_info.input_producer_op, + graph_info.local_var_init_op_group, sess) + except CheckpointNotFoundException: + log_fn('Checkpoint not found in %s' % self.params.train_dir) + else: # Only executes if an exception was not thrown + self._eval_once(sess, summary_writer, graph_info.fetches, + graph_info.summary_op, image_producer, global_step) + if image_producer is not None: + image_producer.done() + if self.params.eval_interval_secs <= 0: + break + time.sleep(self.params.eval_interval_secs) + return {} + + def _build_eval_graph(self, scope_name=None): + """Build the evaluation graph. + + Args: + scope_name: String to filter what summaries are collected. Only summary + ops whose name contains `scope_name` will be added, which is useful for + only including evaluation ops. + + Returns: + A GraphInfo named_tuple containing various useful ops and tensors of the + evaluation grpah. + """ + with self._do_eval(): + input_producer_op, enqueue_ops, fetches = self._build_model() + local_var_init_op = tf.local_variables_initializer() + table_init_ops = tf.tables_initializer() + variable_mgr_init_ops = [local_var_init_op] + if table_init_ops: + variable_mgr_init_ops.extend([table_init_ops]) + with tf.control_dependencies([local_var_init_op]): + variable_mgr_init_ops.extend(self.variable_mgr.get_post_init_ops()) + local_var_init_op_group = tf.group(*variable_mgr_init_ops) + + summary_op = tf.summary.merge_all(scope=scope_name) + # The eval graph has no execution barrier because it doesn't run in + # distributed mode. + execution_barrier = None + # We do not use the global step during evaluation. + global_step = None + return GraphInfo(input_producer_op, enqueue_ops, fetches, + execution_barrier, global_step, local_var_init_op_group, + summary_op) + + # TODO(reedwm): For consistency, we should have a similar + # "_initialize_train_graph" function. They can likely be the same function. + def _initialize_eval_graph(self, enqueue_ops, input_producer_op, + local_var_init_op_group, sess): + """Initializes the evaluation graph. + + Args: + enqueue_ops: Ops that adds the preprocessed images to the staging areas. + input_producer_op: Op that produce the input batches (before + preprocessing). + local_var_init_op_group: Group of ops that perform per-device + initialization work. + sess: The session to initialize the eval graph with. + + Returns: + An ImageProducer, or None if an ImageProducer isn't being used. + """ + with self._do_eval(): + if local_var_init_op_group is not None: + # We might reinitialize local variables if they were already initialized + # during training. This is OK. + sess.run(local_var_init_op_group) + if self.dataset.queue_runner_required(): + tf.train.start_queue_runners(sess=sess) + image_producer = None + if input_producer_op is not None: + image_producer = cnn_util.ImageProducer( + sess, input_producer_op, self.batch_group_size, + self.params.use_python32_barrier) + image_producer.start() + if enqueue_ops: + for i in xrange(len(enqueue_ops)): + sess.run(enqueue_ops[:(i + 1)]) + if image_producer is not None: + image_producer.notify_image_consumption() + return image_producer + + def _eval_once(self, sess, summary_writer, fetches, summary_op, + image_producer, global_step): + """Evaluate the model using the validation dataset.""" + with self._do_eval(): + mlperf.logger.log_eval_epoch( + mlperf.tags.EVAL_START, global_step, self.batch_size) + loop_start_time = start_time = time.perf_counter() + # TODO(laigd): refactor the part to compute/report the accuracy. Currently + # it only works for image models. + top_1_accuracy_sum = 0.0 + top_5_accuracy_sum = 0.0 + total_eval_count = self.num_batches * self.batch_size + for step in xrange(self.num_batches): + if (summary_writer and self.params.save_summaries_steps > 0 and + (step + 1) % self.params.save_summaries_steps == 0): + results, summary_str = sess.run([fetches, summary_op]) + summary_writer.add_summary(summary_str) + else: + results = sess.run(fetches) + # Make global_step available in results for postprocessing. + results['global_step'] = global_step + results = self.model.postprocess(results) + top_1_accuracy_sum += results['top_1_accuracy'] + top_5_accuracy_sum += results['top_5_accuracy'] + if (step + 1) % self.params.display_every == 0: + duration = time.perf_counter() - start_time + examples_per_sec = ( + self.batch_size * self.params.display_every / duration) + log_fn('%i\t%.1f examples/sec' % (step + 1, examples_per_sec)) + start_time = time.perf_counter() + if image_producer is not None: + image_producer.notify_image_consumption() + loop_end_time = time.perf_counter() + accuracy_at_1 = top_1_accuracy_sum / self.num_batches + accuracy_at_5 = top_5_accuracy_sum / self.num_batches + summary = tf.Summary() + summary.value.add(tag='eval/Accuracy@1', simple_value=accuracy_at_1) + summary.value.add(tag='eval/Accuracy@5', simple_value=accuracy_at_5) + for result_key, result_value in results.items(): + if result_key.startswith(constants.SIMPLE_VALUE_RESULT_PREFIX): + prefix_len = len(constants.SIMPLE_VALUE_RESULT_PREFIX) + summary.value.add(tag='eval/' + result_key[prefix_len:], + simple_value=result_value) + if summary_writer: + summary_writer.add_summary(summary, global_step) + log_fn('Accuracy @ 1 = %.4f Accuracy @ 5 = %.4f [%d examples]' % + (accuracy_at_1, accuracy_at_5, total_eval_count)) + elapsed_time = loop_end_time - loop_start_time + images_per_sec = (self.num_batches * self.batch_size / elapsed_time) + if self.mode != constants.BenchmarkMode.TRAIN_AND_EVAL: + # Note that we compute the top 1 accuracy and top 5 accuracy for each + # batch, which will have a slight performance impact. + log_fn('-' * 64) + log_fn('total images/sec: %.2f' % images_per_sec) + log_fn('-' * 64) + if self.benchmark_logger: + eval_result = { + 'eval_top_1_accuracy', accuracy_at_1, + 'eval_top_5_accuracy', accuracy_at_5, + 'eval_average_examples_per_sec', images_per_sec, + tf.GraphKeys.GLOBAL_STEP, global_step, + } + self.benchmark_logger.log_evaluation_result(eval_result) + mlperf.logger.log_eval_epoch( + mlperf.tags.EVAL_STOP, global_step, self.batch_size) + mlperf.logger.log(key=mlperf.tags.EVAL_SIZE, + value=self.num_batches * self.batch_size) + if self.params.model != 'ssd300': # ssd300 logs eval accuracy elsewhere. + mlperf.logger.log_eval_accuracy( + accuracy_at_1, global_step, self.train_batch_size, + examples_per_epoch=self.dataset.num_examples_per_epoch('train')) + if self.params.stop_at_top_1_accuracy: + mlperf.logger.log(key=mlperf.tags.EVAL_TARGET, + value=self.params.stop_at_top_1_accuracy) + return accuracy_at_1, accuracy_at_5 + + def _benchmark_train(self): + """Run cnn in benchmark mode. Skip the backward pass if forward_only is on. + + Returns: + Dictionary containing training statistics (num_workers, num_steps, + average_wall_time, images_per_sec). + """ + graph = tf.Graph() + with graph.as_default(): + build_result = self._build_graph() + if self.mode == constants.BenchmarkMode.TRAIN_AND_EVAL: + with self.variable_mgr.reuse_variables(): + with tf.name_scope('Evaluation') as ns: + eval_build_results = self._build_eval_graph(ns) + else: + eval_build_results = None + (graph, result_to_benchmark) = self._preprocess_graph(graph, build_result) + with graph.as_default(): + return self._benchmark_graph(result_to_benchmark, eval_build_results) + + GPU_CACHED_INPUT_VARIABLE_NAME = 'gpu_cached_inputs' + + def _unfreezable_local_variables(self, graph): + """Get the local variables that we don't want to freeze.""" + return graph.get_collection( + tf.GraphKeys.LOCAL_VARIABLES, + # We don't freeze the gpu_cached_images local variable so it won't get + # constant folded with ops which process the input. + scope='.*' + BenchmarkCNN.GPU_CACHED_INPUT_VARIABLE_NAME) + + def _build_graph(self): + """Build the graph. + + Returns: + A namedtuple containing the ops/tensors that required by + _benchmark_graph(). + """ + if self.single_session: + (input_producer_op, enqueue_ops, fetches) = ( + self._build_model_single_session()) + else: + (input_producer_op, enqueue_ops, fetches) = self._build_model() + fetches_list = nest.flatten(list(fetches.values())) + main_fetch_group = tf.group(*fetches_list, name='main_fetch_group') + execution_barrier = None + if (not self.single_session and self.job_name and + not self.params.cross_replica_sync): + execution_barrier = self.add_sync_queues_and_barrier( + 'execution_barrier_', []) + + global_step = tf.train.get_global_step() + with tf.device(self.global_step_device), tf.name_scope('inc_global_step'): + with tf.control_dependencies([main_fetch_group]): + fetches['inc_global_step'] = global_step.assign_add(1) + + if ((not self.single_session) and (not self.distributed_collective) and + self.job_name and self.params.cross_replica_sync): + # Block all replicas until all replicas are ready for next step. + fetches['sync_queues'] = self.add_sync_queues_and_barrier( + 'sync_queues_step_end_', [main_fetch_group]) + + # Skips the init ops for freezable local variables in forward_only mode so + # we can remove all the assign ops when converting variables to constants. + with tf.name_scope('local_variable_initialization'): + if self.forward_only_and_freeze: + local_var_init_op = tf.variables_initializer( + self._unfreezable_local_variables(tf.get_default_graph())) + else: + local_var_init_op = tf.local_variables_initializer() + table_init_ops = tf.tables_initializer() + + variable_manager_init_ops = [local_var_init_op] + if table_init_ops: + variable_manager_init_ops.extend([table_init_ops]) + if not self.forward_only_and_freeze: + with tf.control_dependencies([local_var_init_op]): + variable_manager_init_ops.extend(self.variable_mgr.get_post_init_ops()) + if ((not self.single_session) and (not self.distributed_collective) and + self.job_name and self.params.cross_replica_sync): + # Ensure all workers execute variable_manager_init_ops before they start + # executing the model. + variable_manager_init_ops.append( + self.add_sync_queues_and_barrier('init_ops_end_', + variable_manager_init_ops)) + local_var_init_op_group = tf.group(*variable_manager_init_ops, + name='local_var_init_op_group') + summary_op = tf.summary.merge_all() + + return GraphInfo( + input_producer_op=input_producer_op, + enqueue_ops=enqueue_ops, + fetches=fetches, + execution_barrier=execution_barrier, + global_step=global_step, + local_var_init_op_group=local_var_init_op_group, + summary_op=summary_op) + + def _benchmark_graph(self, graph_info, eval_graph_info): + """Benchmark the training graph. + + Args: + graph_info: the namedtuple returned by _build_graph() which + contains all necessary information to benchmark the graph, including + named tensors/ops list, fetches, etc. + eval_graph_info: Similar to graph_info but for the eval graph if + --eval_during_training_* is used. Otherwise, None. + Returns: + Dictionary containing training statistics (num_workers, num_steps, + average_wall_time, images_per_sec). + """ + log_fn('Initializing graph') + if self.params.variable_update == 'horovod': + import horovod.tensorflow as hvd # pylint: disable=g-import-not-at-top + # First worker will be 'chief' - it will write summaries and + # save checkpoints. + is_chief = hvd.rank() == 0 + else: + is_chief = (not self.job_name or self.task_index == 0) + + summary_writer = None + if (is_chief and self.params.summary_verbosity and self.params.train_dir and + self.params.save_summaries_steps > 0): + summary_writer = tf.summary.FileWriter(self.params.train_dir, + tf.get_default_graph()) + + # We want to start the benchmark timer right after a image_producer barrier + # and avoids undesired waiting times on barriers. + if ((self.num_warmup_batches + len(graph_info.enqueue_ops) - 1) % + self.batch_group_size) != 0: + self.num_warmup_batches = int( + math.ceil( + (self.num_warmup_batches + len(graph_info.enqueue_ops) - 1.0) / + (self.batch_group_size)) * self.batch_group_size - + len(graph_info.enqueue_ops) + 1) + log_fn('Round up warm up steps to %d to match batch_group_size' % + self.num_warmup_batches) + assert ((self.num_warmup_batches + len(graph_info.enqueue_ops) - 1) % + self.batch_group_size) == 0 + # We run the summaries in the same thread as the training operations by + # passing in None for summary_op to avoid a summary_thread being started. + # Running summaries and training operations in parallel could run out of + # GPU memory. + if is_chief and not self.forward_only_and_freeze: + saver = tf.train.Saver( + self.variable_mgr.savable_variables(), + save_relative_paths=True, + max_to_keep=self.params.max_ckpts_to_keep) + else: + saver = None + ready_for_local_init_op = None + if self.job_name and not (self.single_session or + self.distributed_collective): + # In distributed mode, we don't want to run local_var_init_op_group until + # the global variables are initialized, because local_var_init_op_group + # may use global variables (such as in distributed replicated mode). We + # don't set this in non-distributed mode, because in non-distributed mode, + # local_var_init_op_group may itself initialize global variables (such as + # in replicated mode). + ready_for_local_init_op = tf.report_uninitialized_variables( + tf.global_variables()) + if self.params.variable_update == 'horovod': + import horovod.tensorflow as hvd # pylint: disable=g-import-not-at-top + bcast_global_variables_op = hvd.broadcast_global_variables(0) + else: + bcast_global_variables_op = None + + if self.params.variable_update == 'collective_all_reduce': + # It doesn't matter what this collective_graph_key value is, + # so long as it's > 0 and the same at every worker. + init_run_options = tf.RunOptions() + init_run_options.experimental.collective_graph_key = 6 + else: + init_run_options = tf.RunOptions() + local_var_init_ops = [graph_info.local_var_init_op_group] + if eval_graph_info: + # `eval_graph_info.local_var_init_op_group` also includes some of the + # training initializer ops, since it's difficult to filter them out. + # Rerunning the training initializer ops is OK, but we add a control + # dependency since running two sets of training initializer ops at the + # same time can cause race conditions. + with tf.control_dependencies(local_var_init_ops): + local_var_init_ops.append(eval_graph_info.local_var_init_op_group) + sv = tf.train.Supervisor( + # For the purpose of Supervisor, all Horovod workers are 'chiefs', + # since we want session to be initialized symmetrically on all the + # workers. + is_chief=is_chief or (self.params.variable_update == 'horovod' + or self.distributed_collective), + # Log dir should be unset on non-chief workers to prevent Horovod + # workers from corrupting each other's checkpoints. + logdir=self.params.train_dir if is_chief else None, + ready_for_local_init_op=ready_for_local_init_op, + local_init_op=local_var_init_ops, + saver=saver, + global_step=graph_info.global_step, + summary_op=None, + save_model_secs=self.params.save_model_secs, + summary_writer=summary_writer, + local_init_run_options=init_run_options) + + profiler = tf.profiler.Profiler() if self.params.tfprof_file else None + if self.graph_file is not None: + path, filename = os.path.split(self.graph_file) + as_text = filename.endswith('txt') + log_fn('Writing GraphDef as %s to %s' % ( # pyformat break + 'text' if as_text else 'binary', self.graph_file)) + tf.train.write_graph(tf.get_default_graph().as_graph_def(add_shapes=True), + path, filename, as_text) + + start_standard_services = ( + self.params.train_dir or + self.dataset.queue_runner_required()) + target = self.cluster_manager.get_target() if self.cluster_manager else '' + with sv.managed_session( + master=target, + config=create_config_proto(self.params), + start_standard_services=start_standard_services) as sess: + # Anything that can potentially raise an OutOfRangeError with 'sess' MUST + # be under this try block. The managed_session() context manager silently + # ignores OutOfRangeError, so we must catch them and wrap them with + # a different exception type so that they can be propagated up to the + # caller. + try: + stats = self.benchmark_with_session( + sess, sv, graph_info, eval_graph_info, bcast_global_variables_op, + is_chief, summary_writer, profiler) + except tf.errors.OutOfRangeError: + raise RuntimeError( + 'Received OutOfRangeError. Wrapping in Runtime error to avoid ' + 'Supervisor from suppressing the error. Original OutOfRangeError ' + 'with traceback:\n' + traceback.format_exc()) + + sv.stop() + if profiler: + generate_tfprof_profile(profiler, self.params.tfprof_file) + return stats + + def benchmark_with_session(self, sess, supervisor, graph_info, + eval_graph_info, bcast_global_variables_op, + is_chief, summary_writer, profiler): + """Benchmarks the graph with the given session. + + Args: + sess: The session to benchmark the graph with + supervisor: The Supervisor that created the session. + graph_info: the namedtuple returned by _build_graph() which + contains all necessary information to benchmark the graph, including + named tensors/ops list, fetches, etc. + eval_graph_info: Similar to graph_info but for the eval graph if + --eval_during_training_every_n_steps is used. Otherwise, None. + bcast_global_variables_op: If Horovod is used, the op to broadcast the + global variables to all the processes. None if Horovod is not used. + is_chief: True if this is the chief process. + summary_writer: The SummaryWriter used to write summaries, or None if + summaries are not used. + profiler: The tf.profiler.Profiler, or None if tfprof is not used. + + Returns: + Dictionary containing training statistics (num_workers, num_steps, + average_wall_time, images_per_sec). + """ + if self.params.backbone_model_path is not None: + self.model.load_backbone_model(sess, self.params.backbone_model_path) + if bcast_global_variables_op: + sess.run(bcast_global_variables_op) + image_producer = None + if graph_info.input_producer_op is not None: + image_producer = cnn_util.ImageProducer( + sess, graph_info.input_producer_op, self.batch_group_size, + self.params.use_python32_barrier) + image_producer.start() + if graph_info.enqueue_ops: + for i in xrange(len(graph_info.enqueue_ops)): + sess.run(graph_info.enqueue_ops[:(i + 1)]) + if image_producer is not None: + image_producer.notify_image_consumption() + self.init_global_step, = sess.run([graph_info.global_step]) + if self.job_name and not self.params.cross_replica_sync: + # TODO(zhengxq): Do we need to use a global step watcher at all? + global_step_watcher = GlobalStepWatcher( + sess, graph_info.global_step, + self.num_workers * self.num_warmup_batches + + self.init_global_step, + self.num_workers * (self.num_warmup_batches + self.num_batches) - 1) + global_step_watcher.start() + else: + global_step_watcher = None + eval_image_producer = None + if eval_graph_info: + # We pass local_var_init_op_group=None because the Supervisor already + # initialized local variables above. We need to have the Supervisor + # initialize the local variables, because otherwise it throws an error + # complaining that not all variables were initialized. + eval_image_producer = self._initialize_eval_graph( + eval_graph_info.enqueue_ops, eval_graph_info.input_producer_op, + local_var_init_op_group=None, sess=sess) + step_train_times = [] + log_fn('Running warm up') + local_step = -1 * self.num_warmup_batches + if self.single_session: + # In single session mode, each step, the global_step is incremented by + # 1. In non-single session mode, each step, the global_step is + # incremented once per worker. This means we need to divide + # init_global_step by num_workers only in non-single session mode. + end_local_step = self.num_batches - self.init_global_step + else: + end_local_step = self.num_batches - (self.init_global_step // + self.num_workers) + if not global_step_watcher: + # In cross-replica sync mode, all workers must run the same number of + # local steps, or else the workers running the extra step will block. + done_fn = lambda: local_step >= end_local_step + else: + done_fn = global_step_watcher.done + if self.params.debugger is not None: + if self.params.debugger == 'cli': + log_fn('The CLI TensorFlow debugger will be used.') + sess = tf_debug.LocalCLIDebugWrapperSession(sess) + else: + log_fn('The TensorBoard debugger plugin will be used.') + sess = tf_debug.TensorBoardDebugWrapperSession(sess, + self.params.debugger) + mlperf.logger.log(key=mlperf.tags.TRAIN_LOOP) + skip_final_eval = False + accuracy_at_1 = None + accuracy_at_5 = None + last_eval_step = local_step + loop_start_time = time.perf_counter() + last_average_loss = None + while not done_fn(): + if local_step == 0: + log_fn('Done warm up') + if graph_info.execution_barrier: + log_fn('Waiting for other replicas to finish warm up') + sess.run([graph_info.execution_barrier]) + + # TODO(laigd): rename 'Img' to maybe 'Input'. + header_str = ('Step\tImg/sec\t' + + self.params.loss_type_to_report.replace('/', ' ')) + if self.params.print_training_accuracy or self.params.forward_only: + # TODO(laigd): use the actual accuracy op names of the model. + header_str += '\ttop_1_accuracy\ttop_5_accuracy' + log_fn(header_str) + assert len(step_train_times) == self.num_warmup_batches + # reset times to ignore warm up batch + step_train_times = [] + loop_start_time = time.perf_counter() + if (summary_writer and + (local_step + 1) % self.params.save_summaries_steps == 0): + fetch_summary = graph_info.summary_op + else: + fetch_summary = None + collective_graph_key = 7 if ( + self.params.variable_update == 'collective_all_reduce') else 0 + (summary_str, last_average_loss) = benchmark_one_step( + sess, graph_info.fetches, local_step, + self.batch_size * (self.num_workers + if self.single_session else 1), step_train_times, + self.trace_filename, self.params.partitioned_graph_file_prefix, + profiler, image_producer, self.params, fetch_summary, + benchmark_logger=self.benchmark_logger, + collective_graph_key=collective_graph_key, + should_output_files=(self.params.variable_update != 'horovod' or + is_chief)) + if summary_str is not None and is_chief: + supervisor.summary_computed(sess, summary_str) + local_step += 1 + if (self.params.save_model_steps and + local_step % self.params.save_model_steps == 0 and + local_step > 0 and + is_chief): + supervisor.saver.save(sess, supervisor.save_path, + supervisor.global_step) + if (eval_graph_info and local_step > 0 and not done_fn() and + self._should_eval_during_training(local_step)): + python_global_step = sess.run(graph_info.global_step) + num_steps_since_last_eval = local_step - last_eval_step + # The INPUT_SIZE tag value might not match the + # PREPROC_NUM_TRAIN_EXAMPLES tag value, because the number of examples + # run, which is INPUT_SIZE, is rounded up to the nearest multiple of + # self.batch_size. + mlperf.logger.log( + key=mlperf.tags.INPUT_SIZE, + value=num_steps_since_last_eval * self.batch_size) + log_fn('Running evaluation at global_step {}'.format( + python_global_step)) + accuracy_at_1, accuracy_at_5 = self._eval_once( + sess, summary_writer, eval_graph_info.fetches, + eval_graph_info.summary_op, eval_image_producer, + python_global_step) + last_eval_step = local_step + if (self.params.stop_at_top_1_accuracy and + accuracy_at_1 >= self.params.stop_at_top_1_accuracy): + log_fn('Stopping, as eval accuracy at least %s was reached' % + self.params.stop_at_top_1_accuracy) + skip_final_eval = True + break + else: + log_fn('Resuming training') + if eval_graph_info and self.model.reached_target(): + log_fn('Stopping, as the model indicates its custom goal was reached') + skip_final_eval = True + break + loop_end_time = time.perf_counter() + # Waits for the global step to be done, regardless of done_fn. + if global_step_watcher: + while not global_step_watcher.done(): + time.sleep(.25) + if not global_step_watcher: + elapsed_time = loop_end_time - loop_start_time + average_wall_time = elapsed_time / local_step if local_step > 0 else 0 + images_per_sec = (self.num_workers * local_step * self.batch_size / + elapsed_time) + num_steps = local_step * self.num_workers + else: + # NOTE: Each worker independently increases the global step. So, + # num_steps will be the sum of the local_steps from each worker. + num_steps = global_step_watcher.num_steps() + elapsed_time = global_step_watcher.elapsed_time() + average_wall_time = (elapsed_time * self.num_workers / num_steps + if num_steps > 0 else 0) + images_per_sec = num_steps * self.batch_size / elapsed_time + + # We skip printing images/sec if --eval_during_training_* is specified, + # because we are both processing training and evaluation images, so a + # singular "images/sec" value is meaningless. + if self.mode != constants.BenchmarkMode.TRAIN_AND_EVAL: + log_fn('-' * 64) + # TODO(laigd): rename 'images' to maybe 'inputs'. + log_fn('total images/sec: %.2f' % images_per_sec) + log_fn('-' * 64) + else: + log_fn('Done with training') + num_steps_since_last_eval = local_step - last_eval_step + mlperf.logger.log( + key=mlperf.tags.INPUT_SIZE, + value=num_steps_since_last_eval * self.batch_size) + python_global_step = sess.run(graph_info.global_step) + if eval_graph_info and not skip_final_eval: + log_fn('Running final evaluation at global_step {}'.format( + python_global_step)) + accuracy_at_1, accuracy_at_5 = self._eval_once( + sess, summary_writer, eval_graph_info.fetches, + eval_graph_info.summary_op, eval_image_producer, python_global_step) + num_epochs_ran = (python_global_step * self.batch_size / + self.dataset.num_examples_per_epoch('train')) + mlperf.logger.log_train_epochs(num_epochs_ran) + if image_producer is not None: + image_producer.done() + if eval_image_producer is not None: + eval_image_producer.done() + if is_chief: + if self.benchmark_logger: + self.benchmark_logger.log_metric( + 'average_examples_per_sec', images_per_sec, global_step=num_steps) + + # Save the model checkpoint. + if self.params.train_dir is not None and is_chief: + checkpoint_path = os.path.join(self.params.train_dir, 'model.ckpt') + if not gfile.Exists(self.params.train_dir): + gfile.MakeDirs(self.params.train_dir) + supervisor.saver.save(sess, checkpoint_path, graph_info.global_step) + if graph_info.execution_barrier: + # Wait for other workers to reach the end, so this worker doesn't + # go away underneath them. + sess.run([graph_info.execution_barrier]) + stats = { + 'num_workers': self.num_workers, + 'num_steps': num_steps, + 'average_wall_time': average_wall_time, + 'images_per_sec': images_per_sec + } + if last_average_loss is not None: + stats['last_average_loss'] = last_average_loss + if accuracy_at_1 is not None: + stats['top_1_accuracy'] = accuracy_at_1 + if accuracy_at_5 is not None: + stats['top_5_accuracy'] = accuracy_at_5 + + success = bool(self.model.reached_target() or + (accuracy_at_1 and self.params.stop_at_top_1_accuracy and + accuracy_at_1 >= self.params.stop_at_top_1_accuracy)) + mlperf.logger.log(key=mlperf.tags.RUN_STOP, value={'success': success}) + mlperf.logger.log(key=mlperf.tags.RUN_FINAL) + return stats + + def _should_eval_during_training(self, step): + """Return True iff should run eval during training at current step.""" + + assert self.mode == constants.BenchmarkMode.TRAIN_AND_EVAL + + if self.params.eval_during_training_every_n_steps: + return step % self.params.eval_during_training_every_n_steps == 0 + + # All other --eval_during_training_* flags are converted to step numbers + # at which the model should run evaluation during training. + return step in self.eval_during_training_at_specified_steps + + def _preprocess_graph(self, graph, graph_info): + """Preprocess the graph before executing. + + Depending on the params, it runs various preprocessing on the graph, + including freezing, TensorRT conversion, etc. + + Args: + graph: the graph to preprocess. + graph_info: the namedtuple returned by _build_graph() which + contains all necessary information to benchmark the graph, including + named tensors/ops list, fetches, etc. + + Returns: + The updated graph and graph_info with the ops/tensors/fetches updated + according to the imported graph. + """ + assert isinstance(graph_info.fetches, dict) + assert isinstance(graph_info.global_step, tf.Variable) + if not self.forward_only_and_freeze: + return (graph, graph_info) + + # Get the names of the ops that need to keep during conversion. + flattened_op_names = list( + set([ + v.name.split(':')[0] + for v in nest.flatten(graph_info) + if v is not None + ])) + # Get variables that we don't want to freeze. + # Only keep unfreezable variables in forward_only_and_freeze mode. + # TODO(laigd): consider making global_step a constant. + variables_to_keep = {graph_info.global_step: tf.GraphKeys.GLOBAL_VARIABLES} + variables_to_keep.update({ + local_variable: tf.GraphKeys.LOCAL_VARIABLES + for local_variable in self._unfreezable_local_variables(graph) + }) + + variable_initializers = [ + variable.initializer.name for variable in variables_to_keep] + output_node_names = ( + flattened_op_names + + # Add variable initializer and read ops to the output list, so + # convert_variables_to_constants() will keep them. + variable_initializers + + [variable.value().op.name for variable in variables_to_keep]) + graphdef = graph.as_graph_def(add_shapes=True) + + # Freeze the graph. + with graph.as_default(): + with tf.Session(config=create_config_proto(self.params)) as sess: + sess.run(tf.global_variables_initializer()) + sess.run(tf.local_variables_initializer()) + graphdef = graph_util.convert_variables_to_constants( + sess, + graphdef, + output_node_names, + variable_names_blacklist=[ + variable.op.name for variable in variables_to_keep + ]) + + # Run TensorRT conversion. + if self.params.trt_mode: + # Import here instead of at top, because this will crash if TensorRT is + # not installed + from tensorflow.python.compiler.tensorrt import trt_convert # pylint: disable=g-import-not-at-top + # Avoid TF-TRT bridge from touching all variable initializer ops and their + # dependencies, since they can directly be fetched by sess.run()s that + # initialize the variables. + # pylint: disable=protected-access + name_to_input_name, _, _ = graph_util_impl._extract_graph_summary( + graphdef) + initializer_subgraph_ops = graph_util_impl._bfs_for_reachable_nodes( + variable_initializers, name_to_input_name) + # pylint: enable=protected-access + + graphdef = trt_convert.create_inference_graph( + graphdef, + outputs=output_node_names + list(initializer_subgraph_ops), + max_batch_size=self.model.get_batch_size(), + max_workspace_size_bytes=self.params.trt_max_workspace_size_bytes, + precision_mode=self.params.trt_mode) + + # Creates a new graph as the default and import the converted graph back. + updated_graph = tf.Graph() + + def _get_tensors_or_ops(inputs): + """Gets the updated tensors or ops from 'updated_graph'.""" + + def _get_fn(element): + if element is None: + return None + if ':' in element.name: + return updated_graph.get_tensor_by_name(element.name) + return updated_graph.get_operation_by_name(element.name) + + if isinstance(inputs, (list, dict, tuple)): + return nest.map_structure(_get_fn, inputs) + else: + return _get_fn(inputs) + + with updated_graph.as_default(): + importer.import_graph_def(graph_def=graphdef, name='') + + # Update the variables + for variable in variables_to_keep: + updated_variable = tf.Variable.from_proto(variable.to_proto()) + tf.add_to_collection(variables_to_keep[variable], updated_variable) + if variable is graph_info.global_step: + updated_global_step = updated_variable + + updated_graph_info = GraphInfo( + input_producer_op=_get_tensors_or_ops(graph_info.input_producer_op), + enqueue_ops=_get_tensors_or_ops(graph_info.enqueue_ops), + execution_barrier=_get_tensors_or_ops(graph_info.execution_barrier), + local_var_init_op_group=_get_tensors_or_ops( + graph_info.local_var_init_op_group), + fetches=_get_tensors_or_ops(graph_info.fetches), + global_step=updated_global_step, + summary_op=None) + return (updated_graph, updated_graph_info) + + def _build_input_processing(self, shift_ratio=0): + """"Build the image (pre)processing portion of the model graph. + + Args: + shift_ratio: shift_ratio for data_flow_ops.RecordInput. + + Returns: + An InputProcessingInfo containing all the input sources to the model. + """ + input_processing_info = InputProcessingInfo( + input_producer_op=None, + input_producer_stages=None, + multi_device_iterator_input=None) + + mlperf.logger.log(key=mlperf.tags.INPUT_ORDER) + if not self._doing_eval: + mlperf.logger.log(key=mlperf.tags.INPUT_BATCH_SIZE, value=self.batch_size) + + # If using synthetic gpu inputs, do nothing on the cpu side. + if self.dataset.use_synthetic_gpu_inputs(): + assert not self.datasets_use_prefetch + return input_processing_info + + if self._doing_eval: + input_preprocessor = self.eval_input_preprocessor + mlperf.logger.log(key=mlperf.tags.PREPROC_NUM_EVAL_EXAMPLES, + value=self.dataset.num_examples_per_epoch('validation')) + else: + input_preprocessor = self.input_preprocessor + mlperf.logger.log(key=mlperf.tags.PREPROC_NUM_TRAIN_EXAMPLES, + value=self.dataset.num_examples_per_epoch('train')) + + # Use prefetching mechanism provided by dataset input pipeline. + if self.datasets_use_prefetch: + multi_device_iterator = ( + input_preprocessor.build_multi_device_iterator( + self.batch_size, len(self.devices), self.cpu_device, self.params, + self.raw_devices, self.dataset, self._doing_eval)) + return input_processing_info._replace( + multi_device_iterator_input=multi_device_iterator.get_next()) + + # Not using dataset prefetching. Use a staging area to mimic the prefetching + # behavior instead. + with tf.device(self.cpu_device): + if self._doing_eval: + subset = 'validation' + else: + subset = 'train' + input_list = input_preprocessor.minibatch( + self.dataset, + subset=subset, + params=self.params, + shift_ratio=shift_ratio) + + input_producer_op = [] + input_producer_stages = [] + for device_num in range(len(self.devices)): + staging_area = data_flow_ops.StagingArea( + [parts[0].dtype for parts in input_list], + shapes=[parts[0].get_shape() for parts in input_list], + shared_name='input_producer_staging_area_%d_eval_%s' % + (device_num, self._doing_eval)) + input_producer_stages.append(staging_area) + for group_index in xrange(self.batch_group_size): + batch_index = group_index + device_num * self.batch_group_size + put_op = staging_area.put( + [parts[batch_index] for parts in input_list]) + input_producer_op.append(put_op) + assert input_producer_op + + return input_processing_info._replace( + input_producer_op=input_producer_op, + input_producer_stages=input_producer_stages) + + def _maybe_initialize_fp16(self): + """Initialize fp16 settings.""" + if self.params.use_fp16 and not self._doing_eval: + init_loss_scale_val = float(self.params.fp16_loss_scale or + self.model.get_fp16_loss_scale()) + self.loss_scale = None + self.loss_scale_normal_steps = None + if self.enable_auto_loss_scale or init_loss_scale_val != 1: + self.loss_scale = tf.get_variable( + name='loss_scale', + initializer=init_loss_scale_val, + dtype=tf.float32, + trainable=False) + if self.enable_auto_loss_scale: + self.loss_scale_normal_steps = tf.get_variable( + name='loss_scale_normal_steps', initializer=0, trainable=False) + + def _build_model(self): + """Build the TensorFlow graph.""" + if self.datasets_use_prefetch: + assert not self.params.staged_vars + assert not self.variable_mgr.supports_staged_vars() + + # Adjust seed so different workers start read different input files. + if self.params.variable_update == 'horovod': + import horovod.tensorflow as hvd # pylint: disable=g-import-not-at-top + seed_adjustment = hvd.rank() + else: + seed_adjustment = 0 + mlperf.logger.log(key=mlperf.tags.RUN_SET_RANDOM_SEED, + value=self.params.tf_random_seed + seed_adjustment) + tf.set_random_seed(self.params.tf_random_seed + seed_adjustment) + mlperf.logger.log(key=mlperf.tags.RUN_SET_RANDOM_SEED, + value=4321 + seed_adjustment) + np.random.seed(4321 + seed_adjustment) + phase_train = not (self._doing_eval or self.params.forward_only) + + if self._doing_eval: + mode_string = 'evaluation' + else: + mode_string = 'training' + + log_fn('Generating {} model'.format(mode_string)) + losses = [] + device_grads = [] + all_logits = [] + all_accuracy_ops = {} + gpu_compute_stage_ops = [] + gpu_grad_stage_ops = [] + + with tf.device(self.global_step_device): + global_step = tf.train.get_or_create_global_step() + self._maybe_initialize_fp16() + + # Build the processing and model for the worker. + input_producer_op = None + with tf.name_scope('input_processing'): + input_processing_info = self._build_input_processing(shift_ratio=0) + if input_processing_info.input_producer_op is not None: + input_producer_op = tf.group(*input_processing_info.input_producer_op) + update_ops = None + staging_delta_ops = [] + + for device_num in range(len(self.devices)): + with tf.name_scope('tower_%i' % device_num) as name_scope, ( + self.variable_mgr.create_outer_variable_scope(device_num)): + results = self.add_forward_pass_and_gradients( + phase_train, device_num, device_num, input_processing_info, + gpu_compute_stage_ops, gpu_grad_stage_ops) + + if self.params.backbone_model_path: + self.model.add_backbone_saver() + + if phase_train: + losses.append(results['loss']) + device_grads.append(results['gradvars']) + else: + all_logits.append(results['logits']) + if not phase_train or self.params.print_training_accuracy: + for name, op in results.items(): + if name.startswith('accuracy:'): + key = name[9:] + if key not in all_accuracy_ops: + all_accuracy_ops[key] = [] + all_accuracy_ops[key].append(op) + + if device_num == 0: + # Retain the Batch Normalization updates operations only from the + # first tower. These operations update the moving mean and moving + # variance variables, which are updated (but not used) during + # training, and used during evaluation. The moving mean and variance + # approximate the true mean and variance across all images in the + # dataset. Therefore, in replicated mode, these moving averages would + # be almost identical for each tower, and so we only update and save + # the moving averages for one tower. In parameter server mode, all + # towers share a copy of the variables so we also only need to update + # and save the moving averages once. + update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS, name_scope) + if self.datasets_use_prefetch: + assert not self.variable_mgr.staging_delta_ops + else: + staging_delta_ops = list(self.variable_mgr.staging_delta_ops) + + enqueue_ops = [] + if not self.datasets_use_prefetch: + if self.variable_mgr.supports_staged_vars(): + for staging_ops in self.variable_mgr.staging_vars_on_devices: + gpu_compute_stage_ops.extend( + [put_op for _, (put_op, _) in six.iteritems(staging_ops)]) + enqueue_ops.append(tf.group(*gpu_compute_stage_ops, + name='gpu_compute_stage_ops_group')) + if gpu_grad_stage_ops: + staging_delta_ops += gpu_grad_stage_ops + if staging_delta_ops: + enqueue_ops.append(tf.group(*(staging_delta_ops))) + + if (self.mode == constants.BenchmarkMode.TRAIN_AND_EVAL and + self.params.variable_update == 'replicated'): + # We need to get all the update ops instead of only those for the first + # tower. This is because during evaluation, each tower will read from its + # own tower's moving averages instead of the first tower's moving + # averages. + # TODO(reedwm): Have each tower read from the first tower's moving + # averages for a slight performance gain. + update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) + mlperf.logger.log(key=mlperf.tags.INPUT_BN_SPAN, + value=self.batch_size // len(self.raw_devices)) + + fetches = self._build_fetches(global_step, all_logits, losses, device_grads, + enqueue_ops, update_ops, all_accuracy_ops, + phase_train) + return (input_producer_op, enqueue_ops, fetches) + + def _build_fetches(self, global_step, all_logits, losses, device_grads, + enqueue_ops, update_ops, all_accuracy_ops, phase_train): + """Complete construction of model graph, populating the fetches map.""" + fetches = {} + if enqueue_ops: + fetches['enqueue_ops'] = enqueue_ops + for name, ops in all_accuracy_ops.items(): + # For fetches that starts with 'tensor:', keep dimension and skip reducing + # them to scalars. + if name.startswith(constants.UNREDUCED_ACCURACY_OP_PREFIX): + key = name[len(constants.UNREDUCED_ACCURACY_OP_PREFIX):] + fetches[key] = tf.concat(ops, 0) + else: + fetches[name] = ( + tf.reduce_sum(ops) / + (self.batch_size * + (self.num_workers if self.single_session else 1))) + if self.task_index == 0 and self.params.summary_verbosity >= 1: + tf.summary.scalar(name, fetches[name]) + + if not phase_train: + if self.params.forward_only: + fetches['all_logits'] = tf.concat(all_logits, 0) + return fetches + apply_gradient_devices, gradient_state = ( + self.variable_mgr.preprocess_device_grads(device_grads)) + + # TODO(reedwm): Greatly simplify the learning rate code. + if (self.params.variable_update == 'horovod' or + self.params.variable_update == 'collective_all_reduce'): + # Each worker independently increments global_step. + examples_per_step = self.batch_size * self.num_workers + else: + # global_step is shared by all workers, and so every iteration + # global_step is incremented by num_workers. + examples_per_step = self.batch_size + if self.params.compute_lr_on_cpu: + with tf.device(self.cpu_device): + learning_rate = get_learning_rate(self.params, global_step, + self.dataset.num_examples_per_epoch(), + self.model, examples_per_step) + + training_ops = [] + for d, device in enumerate(apply_gradient_devices): + with tf.device(device): + with tf.name_scope('average_loss'): + average_loss = tf.reduce_mean(losses) + with tf.name_scope('get_gradients_to_apply'): + avg_grads = self.variable_mgr.get_gradients_to_apply(d, + gradient_state) + + if not self.params.compute_lr_on_cpu: + # We compute the learning rate once for each device in + # `apply_gradient_devices`. + learning_rate = get_learning_rate( + self.params, global_step, self.dataset.num_examples_per_epoch(), + self.model, examples_per_step) + gradient_clip = self.params.gradient_clip + if gradient_clip is not None: + with tf.name_scope('clip_gradients'): + clipped_grads = [(tf.clip_by_value(grad, -gradient_clip, + +gradient_clip), var) + for grad, var in avg_grads] + else: + clipped_grads = avg_grads + + learning_rate = tf.identity(learning_rate, name='learning_rate_tensor') + opt = get_optimizer(self.params, learning_rate) + loss_scale_params = variable_mgr_util.AutoLossScaleParams( + enable_auto_loss_scale=self.enable_auto_loss_scale, + loss_scale=self.loss_scale, + loss_scale_normal_steps=self.loss_scale_normal_steps, + inc_loss_scale_every_n=self.params.fp16_inc_loss_scale_every_n, + is_chief=not self.job_name or self.task_index == 0) + + with tf.name_scope('append_apply_gradient_ops'): + self.variable_mgr.append_apply_gradients_ops( + gradient_state, opt, clipped_grads, training_ops, + loss_scale_params) + train_op = tf.group(*(training_ops + update_ops), name='train_ops_group') + + with tf.device(self.cpu_device): + if self.task_index == 0 and self.params.summary_verbosity >= 1: + tf.summary.scalar('learning_rate', learning_rate) + tf.summary.scalar(self.params.loss_type_to_report, average_loss) + if self.loss_scale is not None: + tf.summary.scalar('loss_scale', self.loss_scale) + if self.loss_scale_normal_steps: + tf.summary.scalar('loss_scale_normal_steps', + self.loss_scale_normal_steps) + + if self.params.summary_verbosity >= 2: + self.gradient_histogram_summary(avg_grads) + + if self.params.summary_verbosity >= 3: + for grad, var in avg_grads: + if grad is not None: + tf.summary.histogram(var.op.name + '/gradients', grad) + for var in tf.trainable_variables(): + tf.summary.histogram(var.op.name, var) + + fetches['train_op'] = train_op + fetches['average_loss'] = average_loss + return fetches + + def gradient_histogram_summary(self, avg_grads): + """Create histogram of log values of all non-zero gradients.""" + with tf.name_scope('log_gradients_summary'): + all_grads = [] + for grad, _ in avg_grads: + all_grads.append(tf.reshape(grad, [-1])) + grads = tf.abs(tf.concat(all_grads, 0)) + # exclude grads with zero values. + indices_for_non_zero_grads = tf.where(tf.not_equal(grads, 0)) + log_grads = tf.reshape( + tf.log(tf.gather(grads, indices_for_non_zero_grads)), [-1]) + tf.summary.histogram('log_gradients', log_grads) + + def _build_model_single_session(self): + """Build the TensorFlow graph for multiple replicas in a single_session. + + Returns: + input_producer_op: + enqueue_ops: + fetches: + + Raises: + ValueError: optimizer not recognized. + + Single session runs multiple model replicas as part of one large + distributed graph, whose global execution is always step-synchronized. + """ + # verify assumptions + assert self.params.task_index == 0 + assert not self._doing_eval + assert not self.params.forward_only + assert not self.params.staged_vars + + tf.set_random_seed(self.params.tf_random_seed) + np.random.seed(4321) + phase_train = True + + log_fn('Generating training model') + losses = [] + device_grads = [] + all_logits = [] + all_accuracy_ops = {} + gpu_compute_stage_ops = [] + gpu_grad_stage_ops = [] + + with tf.device(self.global_step_device): + global_step = tf.train.get_or_create_global_step() + + update_ops = [] + global_input_producer_op = [] + + is_local = not self.job_name + if is_local: + assert self.num_workers == 1 + for task_num in range(self.num_workers): + # Reset the devices that self.variable_mgr knows about to those + # belonging to the next worker (task). + self.reset_devices_for_task(task_num, is_local) + # Build the per-worker image processing + with tf.name_scope('input_processing'): + input_processing_info = self._build_input_processing( + shift_ratio=(task_num / self.num_workers)) + if input_processing_info.input_producer_op is not None: + global_input_producer_op.extend(input_processing_info.input_producer_op) + # Build the per-worker model replica. + for rel_device_num in range(len(self.devices)): + abs_device_num = task_num * len(self.devices) + rel_device_num + with self.variable_mgr.create_outer_variable_scope( + abs_device_num), tf.name_scope( + 'task_%i_tower_%i' % (task_num, rel_device_num)) as name_scope: + task_results = self.add_forward_pass_and_gradients( + phase_train, rel_device_num, abs_device_num, + input_processing_info, gpu_compute_stage_ops, gpu_grad_stage_ops) + + if self.params.backbone_model_path: + self.model.add_backbone_saver() + + if phase_train: + losses.append(task_results['loss']) + device_grads.append(task_results['gradvars']) + else: + all_logits.append(task_results['logits']) + if not phase_train or self.params.print_training_accuracy: + for name, op in task_results.items(): + if name.startswith('accuracy:'): + key = name[9:] + if key not in all_accuracy_ops: + all_accuracy_ops[key] = [] + all_accuracy_ops[key].append(op) + + if rel_device_num == 0: + # Retain the Batch Normalization updates operations only + # from the first tower. These operations update the moving + # mean and moving variance variables, which are updated + # (but not used) during training, and used during + # evaluation. The moving mean and variance approximate the + # true mean and variance across all images in the + # dataset. Therefore, in replicated mode, these moving + # averages would be almost identical for each tower, and + # so we only update and save the moving averages for one + # tower. In parameter server mode, all towers share a copy + # of the variables so we also only need to update and save + # the moving averages once. + update_ops.extend( + tf.get_collection(tf.GraphKeys.UPDATE_OPS, name_scope)) + assert not self.variable_mgr.staging_delta_ops + + enqueue_ops = [] + if gpu_compute_stage_ops: + enqueue_ops.append(tf.group(*gpu_compute_stage_ops, + name='gpu_compute_stage_ops')) + assert not self.variable_mgr.supports_staged_vars() + assert not gpu_grad_stage_ops + + fetches = self._build_fetches(global_step, all_logits, losses, device_grads, + enqueue_ops, update_ops, all_accuracy_ops, + phase_train) + if global_input_producer_op: + global_input_producer_op = tf.group(*global_input_producer_op) + else: + global_input_producer_op = None + return (global_input_producer_op, enqueue_ops, fetches) + + def add_forward_pass_and_gradients(self, + phase_train, + rel_device_num, + abs_device_num, + input_processing_info, + gpu_compute_stage_ops, + gpu_grad_stage_ops): + """Add ops for forward-pass and gradient computations.""" + nclass = self.dataset.num_classes + if self.datasets_use_prefetch: + assert input_processing_info.multi_device_iterator_input, ( + 'multi_device_iterator_input cannot be None if ' + 'datasets_use_prefetch=True') + input_list = ( + input_processing_info.multi_device_iterator_input[rel_device_num]) + else: + if not self.dataset.use_synthetic_gpu_inputs(): + input_producer_stage = input_processing_info.input_producer_stages[ + rel_device_num] + with tf.device(self.cpu_device): + host_input_list = input_producer_stage.get() + with tf.device(self.raw_devices[rel_device_num]): + gpu_compute_stage = data_flow_ops.StagingArea( + [inp.dtype for inp in host_input_list], + shapes=[inp.get_shape() for inp in host_input_list]) + # The CPU-to-GPU copy is triggered here. + gpu_compute_stage_op = gpu_compute_stage.put(host_input_list) + input_list = gpu_compute_stage.get() + gpu_compute_stage_ops.append(gpu_compute_stage_op) + else: + with tf.device(self.raw_devices[rel_device_num]): + # Minor hack to avoid H2D copy when using synthetic data + input_list = self.model.get_synthetic_inputs( + BenchmarkCNN.GPU_CACHED_INPUT_VARIABLE_NAME, nclass) + + # Labels reshaping happens all on gpu:0. Reshaping synthetic labels on + # multiple devices slows down XLA computation for an unknown reason. + # TODO(b/116875203): Find/address root cause of XLA slow down. + labels_device_placement_hack = ( + self.dataset.use_synthetic_gpu_inputs() and self.params.xla_compile) + + def device_aware_reshape(tensor, shape): + device = self.devices[rel_device_num] + # Labels are int32, place reshapes on gpu:0 (no device placement) when the + # hack is enabled. + if labels_device_placement_hack and tensor.dtype == tf.int32: + device = '' + with tf.device(device): + return tf.reshape(tensor, shape=shape) + + subset = 'validation' if self._doing_eval else 'train' + input_shapes = self.model.get_input_shapes(subset) + input_list = [ + device_aware_reshape(input_list[i], shape=input_shapes[i]) + for i in range(len(input_list)) + ] + + def forward_pass_and_gradients(): + """Builds forward pass and gradient computation network. + + When phase_train=True and print_training_accuracy=False: + return [loss] + grads + + When phase_train=True and print_training_accuracy=True: + return [logits, loss] + grads + + When phase_train=False, + return [logits] + + Its output can always be unpacked by + + ``` + outputs = forward_pass_and_gradients() + logits, loss, grads = unpack_forward_pass_and_gradients_output(outputs) + ``` + + Returns: + outputs: A list of tensors depending on different modes. + """ + + build_network_result = self.model.build_network( + input_list, phase_train, nclass) + logits = build_network_result.logits + + if not phase_train: + return [logits] + + base_loss = self.model.loss_function(input_list, build_network_result) + params = self.variable_mgr.trainable_variables_on_device( + rel_device_num, abs_device_num) + l2_loss = None + total_loss = base_loss + with tf.name_scope('l2_loss'): + fp32_params = params + if self.model.data_type == tf.float16 and self.params.fp16_vars: + # fp16 reductions are very slow on GPUs, so cast to fp32 before + # calling tf.nn.l2_loss and tf.add_n. + # TODO(b/36217816): Once the bug is fixed, investigate if we should do + # this reduction in fp16. + fp32_params = (tf.cast(p, tf.float32) for p in params) + filtered_params = self.model.filter_l2_loss_vars(fp32_params) + if rel_device_num == len(self.devices) - 1: + # We compute the L2 loss for only one device instead of all of them, + # because the L2 loss for each device is the same. To adjust for this, + # we multiply the L2 loss by the number of devices. We choose the + # last device because for some reason, on a Volta DGX1, the first four + # GPUs take slightly longer to complete a step than the last four. + # TODO(reedwm): Shard the L2 loss computations across GPUs. + if self.params.single_l2_loss_op: + # TODO(reedwm): If faster, create a fused op that does the L2 loss + # on multiple tensors, and use that instead of concatenating + # tensors. + reshaped_params = [tf.reshape(p, (-1,)) for p in filtered_params] + l2_loss = tf.nn.l2_loss(tf.concat(reshaped_params, axis=0)) + else: + l2_loss = tf.add_n([tf.nn.l2_loss(v) for v in filtered_params]) + weight_decay = self.params.weight_decay + mlperf.logger.log(key=mlperf.tags.OPT_WEIGHT_DECAY, value=weight_decay) + if (weight_decay is not None and weight_decay != 0. and + l2_loss is not None): + mlperf.logger.log(key=mlperf.tags.MODEL_L2_REGULARIZATION, + value=weight_decay) + total_loss += len(self.devices) * weight_decay * l2_loss + + aggmeth = tf.AggregationMethod.DEFAULT + scaled_loss = (total_loss if self.loss_scale is None + else total_loss * self.loss_scale) + grads = tf.gradients(scaled_loss, params, aggregation_method=aggmeth) + if self.params.sparse_to_dense_grads: + # Passing a sparse gradient to convert_to_tensor turns it into a dense + # gradient. A sparse gradient is an instance of tf.IndexedSlices. + # convert_to_tensor does not modify dense tensors. + grads = [tf.convert_to_tensor(g) for g in grads] + if self.loss_scale is not None: + # TODO(reedwm): If automatic loss scaling is not used, we could avoid + # these multiplications by directly modifying the learning rate instead. + # If this is done, care must be taken to ensure that this scaling method + # is correct, as some optimizers square gradients and do other + # operations which might not be compatible with modifying both the + # gradients and the learning rate. + + grads = [ + grad * tf.cast(1. / self.loss_scale, grad.dtype) for grad in grads + ] + + if self.params.variable_update == 'horovod': + import horovod.tensorflow as hvd # pylint: disable=g-import-not-at-top + if self.params.horovod_device: + horovod_device = '/%s:0' % self.params.horovod_device + else: + horovod_device = '' + # All-reduce gradients using Horovod. + grads = [hvd.allreduce(grad, average=False, device_dense=horovod_device) + for grad in grads] + + if self.params.staged_vars: + grad_dtypes = [grad.dtype for grad in grads] + grad_shapes = [grad.shape for grad in grads] + grad_stage = data_flow_ops.StagingArea(grad_dtypes, grad_shapes) + grad_stage_op = grad_stage.put(grads) + # In general, this decouples the computation of the gradients and + # the updates of the weights. + # During the pipeline warm up, this runs enough training to produce + # the first set of gradients. + gpu_grad_stage_ops.append(grad_stage_op) + grads = grad_stage.get() + + if self.params.loss_type_to_report == 'total_loss': + loss = total_loss + else: + loss = base_loss + + if self.params.print_training_accuracy: + return [logits, loss] + grads + else: + return [loss] + grads + + def unpack_forward_pass_and_gradients_output(forward_pass_and_grad_outputs): + """Unpacks outputs from forward_pass_and_gradients. + + Args: + forward_pass_and_grad_outputs: Output from forward_pass_and_gradients. + + Returns: + logits: Unscaled probability distribution from forward pass. + If unavailable, None is returned. + loss: Loss function result from logits. + If unavailable, None is returned. + grads: Gradients for all trainable variables. + If unavailable, None is returned. + """ + logits = None + # logits is only fetched in non-train mode or when + # print_training_accuracy is set. + if not phase_train or self.params.print_training_accuracy: + logits = forward_pass_and_grad_outputs.pop(0) + + loss = ( + forward_pass_and_grad_outputs[0] + if forward_pass_and_grad_outputs else None) + grads = ( + forward_pass_and_grad_outputs[1:] + if forward_pass_and_grad_outputs else None) + + return logits, loss, grads + + def make_results(logits, loss, grads): + """Generate results based on logits, loss and grads.""" + results = {} # The return value + + if logits is not None: + results['logits'] = logits + accuracy_ops = self.model.accuracy_function(input_list, logits) + for name, op in accuracy_ops.items(): + results['accuracy:' + name] = op + + if loss is not None: + results['loss'] = loss + + if grads is not None: + param_refs = self.variable_mgr.trainable_variables_on_device( + rel_device_num, abs_device_num, writable=True) + results['gradvars'] = list(zip(grads, param_refs)) + + return results + + with tf.device(self.devices[rel_device_num]): + outputs = maybe_compile(forward_pass_and_gradients, self.params) + logits, loss, grads = unpack_forward_pass_and_gradients_output(outputs) + return make_results(logits, loss, grads) + + def get_input_preprocessor(self): + """Returns the image preprocessor to used, based on the model. + + Returns: + The image preprocessor, or None if synthetic data should be used. + """ + shift_ratio = 0 + if self.job_name: + # shift_ratio prevents multiple workers from processing the same batch + # during a step + shift_ratio = self.task_index / self.num_workers + + processor_class = self.dataset.get_input_preprocessor( + self.params.input_preprocessor) + assert processor_class + subset = 'validation' if self._doing_eval else 'train' + return processor_class( + self.batch_size * self.batch_group_size, + self.model.get_input_shapes(subset), + len(self.devices) * self.batch_group_size, + dtype=self.model.data_type, + train=(not self._doing_eval), + # TODO(laigd): refactor away image model specific parameters. + distortions=self.params.distortions, + resize_method=self.resize_method, + shift_ratio=shift_ratio, + summary_verbosity=self.params.summary_verbosity, + distort_color_in_yiq=self.params.distort_color_in_yiq, + fuse_decode_and_crop=self.params.fuse_decode_and_crop, + match_mlperf=self.params.ml_perf) + + def add_sync_queues_and_barrier(self, name_prefix, enqueue_after_list): + """Adds ops to enqueue on all worker queues. + + Args: + name_prefix: prefixed for the shared_name of ops. + enqueue_after_list: control dependency from ops. + + Returns: + An op that should be used as control dependency before starting next step. + """ + self.sync_queue_counter += 1 + with tf.device(self.sync_queue_devices[( + self.sync_queue_counter % len(self.sync_queue_devices))]): + sync_queues = [ + tf.FIFOQueue(self.num_workers, [tf.bool], shapes=[[]], + shared_name='%s%s' % (name_prefix, i)) + for i in range(self.num_workers)] + queue_ops = [] + # For each other worker, add an entry in a queue, signaling that it can + # finish this step. + token = tf.constant(False) + with tf.control_dependencies(enqueue_after_list): + for i, q in enumerate(sync_queues): + if i == self.task_index: + queue_ops.append(tf.no_op()) + else: + queue_ops.append(q.enqueue(token)) + + # Drain tokens off queue for this worker, one for each other worker. + queue_ops.append( + sync_queues[self.task_index].dequeue_many(len(sync_queues) - 1)) + + return tf.group(*queue_ops) + + +def _is_mkl_flag_absent(mkl_flag): + return not (absl_flags.FLAGS.is_parsed() and mkl_flag in absl_flags.FLAGS + and absl_flags.FLAGS[mkl_flag].present) + + +def _print_os_env_ignored_warning(mkl_flag, flag_default_val, os_env_var): + tf.logging.warn( + ('OS ENV variable %s=%s is ignored and script default: ' + '%s is used. Use --%s to override.') % + (os_env_var, os.environ[os_env_var], flag_default_val, mkl_flag)) + + +def set_default_param_values_and_env_vars(params): + """Sets up the default param values and environment variables .""" + if params.batchnorm_persistent: + os.environ['TF_USE_CUDNN_BATCHNORM_SPATIAL_PERSISTENT'] = '1' + else: + os.environ.pop('TF_USE_CUDNN_BATCHNORM_SPATIAL_PERSISTENT', None) + if params.winograd_nonfused: + os.environ['TF_ENABLE_WINOGRAD_NONFUSED'] = '1' + else: + os.environ.pop('TF_ENABLE_WINOGRAD_NONFUSED', None) + if params.autotune_threshold: + os.environ['TF_AUTOTUNE_THRESHOLD'] = str(params.autotune_threshold) + os.environ['TF_SYNC_ON_FINISH'] = str(int(params.sync_on_finish)) + argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + # Sets environment variables for MKL + # If OS ENV vars are overridden by script defaults, a warning msg is printed. + if params.mkl: + mkl_flags = ['kmp_blocktime', 'kmp_settings', 'kmp_affinity', + 'num_intra_threads'] + for mkl_flag in mkl_flags: + os_env_var = mkl_flag.upper() + if mkl_flag == 'num_intra_threads': + os_env_var = 'OMP_NUM_THREADS' + flag_val = str(getattr(params, mkl_flag)) + if _is_mkl_flag_absent(mkl_flag) and os_env_var in os.environ: + _print_os_env_ignored_warning(mkl_flag, flag_val, os_env_var) + os.environ[os_env_var] = flag_val + if mkl_flag == 'num_intra_threads' and not params.num_intra_threads: + os.environ.pop(os_env_var, None) + + # Sets GPU thread settings + if params.device.lower() == 'gpu': + params = params._replace(gpu_thread_mode=params.gpu_thread_mode.lower()) + if params.gpu_thread_mode not in ['global', 'gpu_shared', 'gpu_private']: + raise ValueError('Invalid gpu_thread_mode: %s' % params.gpu_thread_mode) + os.environ['TF_GPU_THREAD_MODE'] = params.gpu_thread_mode + + if params.per_gpu_thread_count and params.gpu_thread_mode == 'global': + raise ValueError( + 'Invalid per_gpu_thread_count with gpu_thread_mode=global: %s' % + params.per_gpu_thread_count) + # Default to two threads. One for the device compute and the other for + # memory copies. + per_gpu_thread_count = params.per_gpu_thread_count or 2 + total_gpu_thread_count = per_gpu_thread_count * params.num_gpus + + if params.gpu_thread_mode == 'gpu_private': + os.environ['TF_GPU_THREAD_COUNT'] = str(per_gpu_thread_count) + elif params.gpu_thread_mode == 'gpu_shared': + os.environ['TF_GPU_THREAD_COUNT'] = str(total_gpu_thread_count) + + cpu_count = multiprocessing.cpu_count() + if not params.num_inter_threads and params.gpu_thread_mode in [ + 'gpu_private', 'gpu_shared' + ]: + main_thread_count = max(cpu_count - total_gpu_thread_count, 1) + params = params._replace(num_inter_threads=main_thread_count) + + if (params.datasets_use_prefetch and + params.datasets_num_private_threads is None): + # From the total cpu thread count, subtract the total_gpu_thread_count, + # and then 2 threads per GPU device for event monitoring and sending / + # receiving tensors + num_monitoring_threads = 2 * params.num_gpus + num_private_threads = max( + cpu_count - total_gpu_thread_count - num_monitoring_threads, 1) + params = params._replace(datasets_num_private_threads=num_private_threads) + return params + + +def setup(params): + """Sets up the environment that BenchmarkCNN should run in. + + Args: + params: Params tuple, typically created by make_params or + make_params_from_flags. + + Returns: + A potentially modified params. + Raises: + ValueError: invalid parames combinations. + """ + # Set up environment variables before doing any other global initialization to + # make sure it uses the appropriate environment variables. + params = set_default_param_values_and_env_vars(params) + + # horovod needs to be initialized before create_config_proto() call since + # it will be used in config generation if enabled. + if params.variable_update == 'horovod': + import horovod.tensorflow as hvd # pylint: disable=g-import-not-at-top + hvd.init() + + platforms_util.initialize(params, create_config_proto(params)) + + if not params.job_name: + # Create a dummy session to initialize TF global variables using the input + # params. Otherwise, ListDevices function may create global devices using + # the default config instead of using the user provided config. + # + # TODO(hinsu): Find a way to achieve the same for distributed benchmark. It + # is not legal to create distributed session after local session. It is also + # not possible to create distributed session here as that results in + # multiple creation of ClusterManager and Server. + with tf.Session(config=create_config_proto(params)) as sess: + del sess + + return params + + +def maybe_compile(computation, params): + if params and params.xla_compile: + return tf.xla.experimental.compile(computation) + else: + return computation() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/benchmark_cnn_distributed_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/benchmark_cnn_distributed_test.py new file mode 100644 index 00000000..43dac487 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/benchmark_cnn_distributed_test.py @@ -0,0 +1,493 @@ +# 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. +# ============================================================================== +"""Tests running benchmark_cnn in distributed mode. + +This is done by spawning one process per task. Each process runs +benchmark_cnn_distributed_test_runner.py. + +The output for each process is written to disk and can be viewed to debug tests. +See get_test_output_dir() in platforms/default/util.py for more info. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from collections import namedtuple +import os +import subprocess +import time +import unittest + +from absl import flags as absl_flags +import portpicker +import six +import tensorflow.compat.v1 as tf +import flags +import test_util +from platforms import util as platforms_util + +FLAGS = absl_flags.FLAGS + + +def _convert_params_to_flags_list(params): + """Converts Params to a list of flags. Skips default-valued parameters. + + E.g., converts + benchmark_cnn.make_params(batch_size=32, model='resnet50') + to + ['--batch_size=32', '--model=resnet50'] + + Args: + params: Params for BenchmarkCNN. + Returns: + A list of flags. + """ + return [ + '--%s=%s' % (k, str(v)) for k, v in six.iteritems(params._asdict()) + if v != flags.param_specs[k].default_value + ] + + +# When outputting a process's output in the log, maximum number of characters +# to output. The log system does not allow us to output more than this in a +# single log message, but this limit is also useful to avoid the logs from +# becoming too large (the full process output is written to disk). +MAX_OUTPUT_CHARS = 15000 + + +# A process. name is a string identifying the process in logs. stdout and +# stderr are file objects of the process's stdout and stderr, respectively. +_ProcessInfo = namedtuple('_ProcessInfo', ['name', 'popen', 'stdout', 'stderr']) + + +def _create_task_process(job_name, task_index, args, env, output_dir): + """Creates a process for a single task for benchmark_cnn. + + Args: + job_name: 'worker' or 'ps' or ''. Empty string used for non-distributed + mode. + task_index: The index of the task within the cluster. + args: A list of arguments to pass to the task. This function additionally + sets --task_index and --job_name + env: The environment to use for the task. + output_dir: Where to place the output files, storing the task's stdout and + stderr. + Returns: + A _ProcessInfo namedtuple of the running process. The stdout and stderr + fields of this tuple must be closed by the caller once the process ends. + """ + args = args[:] + args += ['--task_index=%s' % task_index, '--job_name=%s' % job_name] + name_prefix = job_name or 'local' + process_name = '%s_%s' % (name_prefix, task_index) + tf.logging.info('Spawning %s process: %s' % (process_name, ' '.join(args))) + stdout_filename = os.path.join(output_dir, '%s_stdout.txt' % process_name) + stderr_filename = os.path.join(output_dir, '%s_stderr.txt' % process_name) + stdout_file = open(stdout_filename, 'w+') + stderr_file = open(stderr_filename, 'w+') + popen = subprocess.Popen( + args, stdout=stdout_file, stderr=stderr_file, env=env) + return _ProcessInfo(process_name, popen, stdout_file, stderr_file) + + +def _wait_for_processes(wait_processes, kill_processes): + """Waits until all `wait_processes` finish, then kills `kill_processes`. + + Fails an assert if a process in `wait_processes` finishes unsuccessfully. + The processes in `kill_processes` are assumed to never finish so they are + killed. + + Args: + wait_processes: A list of _ProcessInfo tuples. This function will wait + for each to finish. + kill_processes: A list of _ProcessInfo tuples. Each will be killed once + every process in `wait_processes` is finished. + Returns: + A list of strings, each which is a string of the stdout of a wait process. + """ + wait_process_stdouts = [None] * len(wait_processes) + finished_wait_processes = set() + while len(finished_wait_processes) < len(wait_processes): + for i, wait_process in enumerate(wait_processes): + if i in finished_wait_processes: + continue + ret_code = wait_process.popen.poll() + if ret_code is None: + continue + tf.logging.info('{} finished'.format(wait_process.name)) + wait_process.stdout.seek(0) + wait_process_stdouts[i] = wait_process.stdout.read() + tf.logging.info('stdout for {} (last {} chars): {}\n'.format( + wait_process.name, MAX_OUTPUT_CHARS, + wait_process_stdouts[i][-MAX_OUTPUT_CHARS:])) + wait_process.stderr.seek(0) + tf.logging.info('stderr for {} (last {} chars): {}\n'.format( + wait_process.name, MAX_OUTPUT_CHARS, + wait_process.stderr.read()[-MAX_OUTPUT_CHARS:])) + assert ret_code == 0, 'Process failed with return code %d' % ret_code + finished_wait_processes.add(i) + for kill_process in kill_processes: + ret_code = kill_process.popen.poll() + # kill processes should not end until we kill them. + assert ret_code is None, 'Process returned early with code %d' % ret_code + time.sleep(0.25) + tf.logging.info('All wait processes finished') + for i, kill_process in enumerate(kill_processes): + # Kill each kill process. + kill_process.popen.kill() + kill_process.popen.wait() + kill_process.stdout.seek(0) + tf.logging.info('stdout for {} (last {} chars): {}\n'.format( + kill_process.name, MAX_OUTPUT_CHARS, + kill_process.stdout.read()[-MAX_OUTPUT_CHARS:])) + kill_process.stderr.seek(0) + tf.logging.info('stderr for {} (last {} chars): {}\n'.format( + kill_process.name, MAX_OUTPUT_CHARS, + kill_process.stderr.read()[-MAX_OUTPUT_CHARS:])) + return wait_process_stdouts + + +def _spawn_benchmark_processes(output_dir_path, num_workers, num_ps, + num_controllers, params): + """Run training or evaluation in spawned processes. + + Runs locally if num_workers == 1, num_ps == 0, and num_controllers == 0, + otherwise runs in distributed mode. In either case, one process is spawned + per worker and ps. Waits for training/evaluation to finish before returning. + + Args: + output_dir_path: Relative path where stdout and stderr files will be + placed. + num_workers: Number of workers to spawn. + num_ps: Number of ps processes to spawn. + num_controllers: Number of controller processes to spawn (must be 0 or 1). + params: Params for BenchmarkCNN in each subprocess. + Returns: + A list output_list of outputs from all processes that output the + images/sec and accuracy. This process is the controller host in + distributed_all_reduce, and the workers otherwise. output_list[i] is a + list of lines from the ith worker's stdout. + """ + run_distributed = num_workers != 1 or num_ps != 0 or num_controllers != 0 + if params.variable_update == 'distributed_all_reduce': + assert num_controllers == 1 or not run_distributed + assert num_ps == 0 + else: + assert num_controllers == 0 + output_base_dir = platforms_util.get_test_output_dir() + output_dir = os.path.join(output_base_dir, output_dir_path) + os.makedirs(output_dir) + tf.logging.info('Outputs of processes will be outputted to: %s' % output_dir) + + args = platforms_util.get_command_to_run_python_module( + 'benchmark_cnn_distributed_test_runner') + args += _convert_params_to_flags_list(params) + if run_distributed: + worker_ports = [portpicker.pick_unused_port() for _ in range(num_workers)] + ps_ports = [portpicker.pick_unused_port() for _ in range(num_ps)] + controller_ports = [portpicker.pick_unused_port() + for _ in range(num_controllers)] + # The numerator is 0.7 instead of 1 to leave some memory for the Cuda + # runtime, etc. + gpu_memory_frac = 0.7 / num_workers + args += [ + '--gpu_memory_frac_for_testing=%f' % gpu_memory_frac, + '--worker_hosts=' + ','.join('localhost:%d' % p for p in worker_ports) + ] + if num_ps > 0: + ps_hosts_str = ','.join('localhost:%d' % p for p in ps_ports) + args.append('--ps_hosts=' + ps_hosts_str) + else: + controller_host_str = ','.join('localhost:%d' % p + for p in controller_ports) + args.append('--controller_host=' + controller_host_str) + env = os.environ.copy() + # Allow stdout to be viewed before the process ends. + env['PYTHONUNBUFFERED'] = '1' + + worker_processes = [] + ps_processes = [] + controller_processes = [] + try: + for i in range(num_workers): + job_name = 'worker' if run_distributed else '' + process = _create_task_process(job_name, i, args, env, output_dir) + worker_processes.append(process) + # Don't let ps or controller processes use the gpu. + env['CUDA_VISIBLE_DEVICES'] = '' + + for i in range(num_ps): + process = _create_task_process('ps', i, args, env, output_dir) + ps_processes.append(process) + for i in range(num_controllers): + process = _create_task_process('controller', i, args, env, output_dir) + controller_processes.append(process) + # If all distributed all reduce mode is being used, the controller process + # finishes and the worker processes block forever. Otherwise, the worker + # processes finish and the ps processes block forever. We set + # wait_processes and kill_processes accordingly. + if controller_processes: + wait_processes = controller_processes + kill_processes = worker_processes + else: + wait_processes = worker_processes + kill_processes = ps_processes + outputs = _wait_for_processes(wait_processes, kill_processes) + finally: + for process in worker_processes + ps_processes + controller_processes: + try: + process.popen.kill() + except OSError: + pass # It's OK (and expected) if the process already exited. + process.stdout.close() + process.stderr.close() + return [output.splitlines() for output in outputs] + + +# When this test class is run, a method will fail about 0.3% of the time with a +# gRPC error. It is not clear why this occurs. +# TODO(reedwm): Fix this test class. +class TfCnnBenchmarksDistributedTest(tf.test.TestCase): + """Tests running benchmark_cnn in distributed mode.""" + + # We cannot check for a GPU via tf.test.is_gpu_available() before the tests in + # this class because it allocates all the GPU memory which would cause the + # spawned processes to run out of GPU memory. + + def _test_distributed(self, + test_name, + num_workers, + num_ps, + params, + num_controllers=0, + check_output_values=False, + skip=None): + # TODO(reedwm): check_output_values should default to True and be enabled + # on every test. See the TODO in benchmark_cnn_test.py. + def run_fn(run_type, inner_params): + output_dir_path = os.path.join(test_name, run_type) + if run_type == 'Evaluation': + # Distributed evaluation is not supported, so we use a single process. + # We still must spawn another process, because if we evaluate in the + # current process, it would allocate the GPU memory causing future test + # methods to fail. + if inner_params.variable_update == 'distributed_replicated': + inner_params = inner_params._replace(variable_update='replicated') + return _spawn_benchmark_processes( + output_dir_path, num_workers=1, num_ps=0, num_controllers=0, + params=inner_params) + else: + return _spawn_benchmark_processes(output_dir_path, num_workers, num_ps, + num_controllers, inner_params) + + return test_util.train_and_eval(self, run_fn, params, + check_output_values=check_output_values, + skip=skip) + + def testParameterServer(self): + test_name = 'testParameterServer' + params = test_util.get_params(test_name) + self._test_distributed(test_name, 2, 2, params) + + def testParameterServerStaged(self): + test_name = 'testParameterServerStaged' + params = test_util.get_params(test_name)._replace(staged_vars=True) + self._test_distributed(test_name, 2, 2, params) + + def testReplicated(self): + test_name = 'testReplicated' + params = test_util.get_params(test_name)._replace( + variable_update='distributed_replicated') + self._test_distributed(test_name, 2, 2, params) + + def testAllReducePsgpu(self): + test_name = 'testAllReducePsgpu' + flags_dict = test_util.get_params(test_name)._replace( + variable_update='distributed_all_reduce', + all_reduce_spec='psgpu#4') + self._test_distributed(test_name, 2, 0, flags_dict, num_controllers=1) + + def testAllReducePscpuXring(self): + test_name = 'testAllReducePscpuXring' + flags_dict = test_util.get_params(test_name)._replace( + variable_update='distributed_all_reduce', + all_reduce_spec='pscpu:2k:xring') + self._test_distributed(test_name, 2, 0, flags_dict, num_controllers=1) + + def testForwardOnly(self): + test_name = 'testForwardOnly' + params = test_util.get_params(test_name)._replace(forward_only=True) + # Evaluation is not supported with --forward_only, so we set skip='eval'. + self._test_distributed(test_name, 2, 2, params, skip='eval') + + def testSingleWorkerAndPs(self): + test_name = 'testSingleWorkerAndPs' + params = test_util.get_params(test_name) + self._test_distributed(test_name, 1, 1, params) + + def testThreeWorkersAndPses(self): + test_name = 'testThreeWorkersAndPses' + params = test_util.get_params(test_name) + self._test_distributed(test_name, 3, 3, params) + + def testOneWorkerThreePses(self): + test_name = 'testOneWorkerThreePses' + params = test_util.get_params(test_name) + self._test_distributed(test_name, 1, 3, params) + + def testThreeWorkersOnePs(self): + test_name = 'testThreeWorkersOnePs' + params = test_util.get_params(test_name) + self._test_distributed(test_name, 3, 1, params) + + def testNoPrintTrainingAccuracy(self): + test_name = 'testNoPrintTrainingAccuracy' + params = test_util.get_params(test_name)._replace( + print_training_accuracy=False) + self._test_distributed(test_name, 2, 2, params) + + def testRmspropParameterServer(self): + test_name = 'testRmspropParameterServer' + params = test_util.get_params(test_name)._replace(optimizer='rmsprop') + self._test_distributed(test_name, 2, 2, params) + + def testMomentumReplicated(self): + test_name = 'testMomentumReplicated' + params = test_util.get_params(test_name)._replace( + optimizer='momentum', variable_update='distributed_replicated') + self._test_distributed(test_name, 2, 2, params) + + def testNoCrossReplicaSyncParameterServerStaged(self): + test_name = 'testNoCrossReplicaSyncParameterServerStaged' + params = test_util.get_params(test_name)._replace( + staged_vars=True, cross_replica_sync=False) + self._test_distributed(test_name, 2, 2, params) + + def testSingleGpu(self): + test_name = 'testSingleGpu' + params = test_util.get_params(test_name)._replace(num_gpus=1) + self._test_distributed(test_name, 2, 2, params) + + def testBatchGroupSize(self): + test_name = 'testBatchGroupSize' + params = test_util.get_params(test_name)._replace( + batch_group_size=4, num_batches=100, num_warmup_batches=5) + self._test_distributed(test_name, 2, 2, params) + + def testFp16WithFp32Vars(self): + test_name = 'testFp16WithFp32Vars' + params = test_util.get_params(test_name)._replace( + use_fp16=True, fp16_vars=False) + self._test_distributed(test_name, 2, 2, params) + + def testFp16WithFp16Vars(self): + test_name = 'testFp16WithFp16Vars' + params = test_util.get_params(test_name)._replace( + use_fp16=True, fp16_vars=True, fp16_loss_scale=1.) + self._test_distributed(test_name, 2, 2, params) + + def testFp16Replicated(self): + test_name = 'testFp16Replicated' + params = test_util.get_params(test_name)._replace( + use_fp16=True, variable_update='distributed_replicated') + self._test_distributed(test_name, 2, 2, params) + + @unittest.skip('b/147310862: Fails for unknown reason') + def testReplicatedRealData(self): + test_name = 'testReplicatedRealData' + imagenet_dir = os.path.join(platforms_util.get_test_data_dir(), + 'fake_tf_record_data') + params = test_util.get_params(test_name)._replace( + variable_update='distributed_replicated', + data_dir=imagenet_dir, + data_name='imagenet') + self._test_distributed(test_name, 2, 2, params) + + +class DistributedVariableUpdateTest(tf.test.TestCase): + """Tests that variables are updated correctly in distributed mode.""" + + def _test_variable_update(self, + test_name, + num_workers, + num_ps, + params, + num_controllers=0): + """Tests variables are updated correctly when the given params are used.""" + output_dir_path = os.path.join(test_name, 'variable_update') + logs = _spawn_benchmark_processes(output_dir_path, num_workers, num_ps, + num_controllers, params) + actual_losses = [] + for worker_logs in logs: + outputs = test_util.get_training_outputs_from_logs( + worker_logs, params.print_training_accuracy) + actual_losses.append([x.loss for x in outputs]) + + inputs = test_util.get_fake_var_update_inputs() + expected_losses = test_util.TestCNNModel().manually_compute_losses( + inputs, num_workers, params) + if params.variable_update == 'distributed_all_reduce': + # In distributed all reduce, each step, the controller outputs the average + # of the loss from each worker. So we modify expected losses accordingly. + # E.g, we change [[1, 2], [4, 5]] to [[2.5, 3.5]] + expected_losses = [[sum(losses) / num_workers + for losses in zip(*expected_losses)]] + rtol = 3e-2 if params.use_fp16 else 1e-5 + for worker_actual_losses, worker_expected_losses in zip(actual_losses, + expected_losses): + self.assertAllClose(worker_actual_losses[:len(worker_expected_losses)], + worker_expected_losses, rtol=rtol, atol=0.) + + def _test_variable_updates(self, test_name, params): + """Tests variables are updated correctly with various variable updates.""" + + # Unfortunately, distributed parameter server is non-deterministic with + # multiple workers, because one worker may write to a variable before + # another worker reads it. This probably does not harm training, but it + # does mean we cannot easily test that case. So, we use one worker. + self._test_variable_update( + test_name + '_ps', num_workers=1, num_ps=2, num_controllers=0, + params=params._replace(variable_update='parameter_server')) + + self._test_variable_update( + test_name + '_rep', num_workers=2, num_ps=1, num_controllers=0, + params=params._replace(variable_update='distributed_replicated')) + + self._test_variable_update( + test_name + '_allreduce', num_workers=2, num_ps=0, num_controllers=1, + params=params._replace(variable_update='distributed_all_reduce', + all_reduce_spec='psgpu#%d' % params.num_gpus)) + + def testVarUpdateDefault(self): + params = test_util.get_var_update_params() + self._test_variable_updates('testVarUpdateDefault', params) + + def testVarUpdateCpuAsLocalParamDevice(self): + params = test_util.get_var_update_params()._replace( + local_parameter_device='cpu') + self._test_variable_updates('testVarUpdateCpuAsLocalParamDevice', params) + + def testVarUpdateFp16(self): + params = test_util.get_var_update_params()._replace(use_fp16=True) + self._test_variable_updates('testVarUpdateFp16', params) + + def testVarUpdateResourceVars(self): + params = test_util.get_var_update_params()._replace(use_resource_vars=True) + self._test_variable_updates('testVarUpdateResourceVars', params) + + +if __name__ == '__main__': + tf.disable_v2_behavior() + tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/benchmark_cnn_distributed_test_runner.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/benchmark_cnn_distributed_test_runner.py new file mode 100644 index 00000000..9291a801 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/benchmark_cnn_distributed_test_runner.py @@ -0,0 +1,122 @@ +# 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. +# ============================================================================== + +"""Used to run benchmark_cnn for distributed tests. + +In distributed tests, we spawn processes to run tf_cnn_benchmark tasks. We could +directly spawn tf_cnn_benchmark processes, but we want some added functionality, +such as being able to inject custom images during training. So instead, this +file is spawned as a Python process, which supports the added functionality. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from absl import flags as absl_flags +import numpy as np +import tensorflow.compat.v1 as tf +import benchmark_cnn +import flags +import preprocessing +import test_util + + +absl_flags.DEFINE_string('fake_input', 'none', + """What fake input to inject into benchmark_cnn. This + is ignored if --model=test_model. + Options are: + none: Do not inject any fake input. + zeros_and_ones: Half the images will be all 0s with + a label of 0. Half the images will be all 1s with a + label of 1.""") + +flags.define_flags() +FLAGS = flags.FLAGS + + +def get_test_image_preprocessor(batch_size, params): + """Returns the preprocessing.TestImagePreprocessor that should be injected. + + Returns None if no preprocessor should be injected. + + Args: + batch_size: The batch size across all GPUs. + params: BenchmarkCNN's parameters. + Returns: + Returns the preprocessing.TestImagePreprocessor that should be injected. + Raises: + ValueError: Flag --fake_input is an invalid value. + """ + if FLAGS.fake_input == 'none': + return None + elif FLAGS.fake_input == 'zeros_and_ones': + half_batch_size = batch_size // 2 + images = np.zeros((batch_size, 227, 227, 3), dtype=np.float32) + images[half_batch_size:, :, :, :] = 1 + labels = np.array([0] * half_batch_size + [1] * half_batch_size, + dtype=np.int32) + preprocessor = preprocessing.TestImagePreprocessor( + batch_size, [227, 227, 3], params.num_gpus, + benchmark_cnn.get_data_type(params)) + preprocessor.set_fake_data(images, labels) + preprocessor.expected_subset = 'validation' if params.eval else 'train' + return preprocessor + else: + raise ValueError('Invalid --fake_input: %s' % FLAGS.fake_input) + + +def run_with_real_model(params): + """Runs tf_cnn_benchmarks with a real model.""" + bench = benchmark_cnn.BenchmarkCNN(params) + bench.print_info() + preprocessor = get_test_image_preprocessor(bench.batch_size, params) + if preprocessor is not None: + # The test image preprocessor requires queue runners. Since this file is + # used for testing, it is OK to access protected members. + # pylint: disable=protected-access + bench.dataset._queue_runner_required = True + # pylint: enable=protected-access + bench.input_preprocessor = preprocessor + bench.run() + + +def run_with_test_model(params): + """Runs tf_cnn_benchmarks with a test model.""" + model = test_util.TestCNNModel() + inputs = test_util.get_fake_var_update_inputs() + with test_util.monkey_patch(benchmark_cnn, + LOSS_AND_ACCURACY_DIGITS_TO_SHOW=15): + bench = benchmark_cnn.BenchmarkCNN(params, dataset=test_util.TestDataSet(), + model=model) + # The test model does not use labels when computing loss, so the label + # values do not matter as long as it's the right shape. + labels = np.array([1] * inputs.shape[0]) + bench.input_preprocessor.set_fake_data(inputs, labels) + bench.run() + + +def main(_): + params = benchmark_cnn.make_params_from_flags() + params = benchmark_cnn.setup(params) + if params.model == 'test_model': + run_with_test_model(params) + else: + run_with_real_model(params) + + +if __name__ == '__main__': + tf.disable_v2_behavior() + tf.app.run() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/benchmark_cnn_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/benchmark_cnn_test.py new file mode 100644 index 00000000..9e849739 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/benchmark_cnn_test.py @@ -0,0 +1,1493 @@ +# 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. +# ============================================================================== + +"""Tests for benchmark_cnn.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +import glob +import os +import re +import unittest + +import mock +import numpy as np +import tensorflow.compat.v1 as tf +from google.protobuf import text_format +from tensorflow.core.framework import step_stats_pb2 +from tensorflow.core.profiler import tfprof_log_pb2 +from tensorflow.python.platform import test +import benchmark_cnn +import datasets +import flags +import preprocessing +import test_util +import variable_mgr_util +from platforms import util as platforms_util + + +def _check_has_gpu(): + if not test.is_gpu_available(cuda_only=True): + raise ValueError( + """You have asked to run part or all of this on GPU, but it appears + that no GPU is available. If your machine has GPUs it is possible you + do not have a version of TensorFlow with GPU support. To build with GPU + support, add --config=cuda to the build flags.\n """) + + +class TfCnnBenchmarksModelTest(tf.test.TestCase): + """Tests which are run with multiple models.""" + + def setUp(self): + super(TfCnnBenchmarksModelTest, self).setUp() + benchmark_cnn.setup(benchmark_cnn.make_params()) + + def get_model_name(self): + return None + + # Return true to run tests that don't need to be run on every model. + # This should be done for one or two cheap models. + def extended_tests(self): + return False + + # Return false to suppress actually running the model; this is useful + # for tests that are large. + def model_execution_test(self): + return False + + # Return false to suppress actually saving and loading the model. + def model_save_load_test(self): + return False + + def testSaveLoadModel(self): + _check_has_gpu() + if not self.get_model_name() or not self.model_save_load_test(): + return + + params = benchmark_cnn.make_params( + model=self.get_model_name(), + num_batches=1, + num_intra_threads=0, + num_inter_threads=0, + distortions=False, + batch_size=2, + variable_update='replicated', + num_warmup_batches=0, + num_gpus=2, + train_dir=test_util.get_temp_dir('testSaveLoadModel_' + + self.get_model_name())) + + # Run one batch and save the model. + # Note that this uses a non-test session. + bench = benchmark_cnn.BenchmarkCNN(params) + bench.run() + self.assertEqual(bench.init_global_step, 0) + # Clear the default graph. + tf.reset_default_graph() + # Test if checkpoint had been saved. + ckpt = tf.train.get_checkpoint_state(params.train_dir) + match = re.match(os.path.join(params.train_dir, r'model.ckpt-(\d+).index'), + ckpt.model_checkpoint_path + '.index') + self.assertTrue(match) + self.assertGreaterEqual(int(match.group(1)), params.num_batches) + params = params._replace(num_batches=2) + # Reload the model + bench = benchmark_cnn.BenchmarkCNN(params) + bench.run() + # Check if global step has been restored. + self.assertNotEqual(bench.init_global_step, 0) + ckpt = tf.train.get_checkpoint_state(params.train_dir) + match = re.match(os.path.join(params.train_dir, r'model.ckpt-(\d+).index'), + ckpt.model_checkpoint_path + '.index') + self.assertTrue(match) + self.assertGreaterEqual(int(match.group(1)), params.num_batches) + # Check that the batch norm moving averages are restored from checkpoints + with tf.Graph().as_default(): + bench = benchmark_cnn.BenchmarkCNN(params) + bench._build_model() + saver = tf.train.Saver(bench.variable_mgr.savable_variables()) + with tf.Session(config=benchmark_cnn.create_config_proto(params)) as sess: + benchmark_cnn.load_checkpoint(saver, sess, params.train_dir) + sess.run(bench.variable_mgr.get_post_init_ops()) + bn_moving_vars = [ + v for v in tf.global_variables() + if '/batchnorm' in v.name and '/moving' in v.name + ] + self.assertGreater(len(bn_moving_vars), 0) + for moving_var in bn_moving_vars: + moving_var_value = sess.run(moving_var) + # Check that the moving means and moving variances have been restored + # by asserting they are not their default values of 0 and 1, + # respectively + if '/moving_mean' in moving_var.name: + self.assertFalse(np.array_equal(moving_var_value, + np.zeros(moving_var_value.shape, + moving_var_value.dtype))) + else: + self.assertIn('/moving_variance', moving_var.name) + self.assertFalse(np.array_equal(moving_var_value, + np.ones(moving_var_value.shape, + moving_var_value.dtype))) + + def testModel(self): + _check_has_gpu() + if not self.get_model_name() or not self.model_execution_test(): + return + + params = benchmark_cnn.make_params( + model=self.get_model_name(), + num_batches=1, + num_intra_threads=1, + num_inter_threads=12, + batch_size=2, + distortions=False) + + # Run this one; note that this uses a non-test session. + bench = benchmark_cnn.BenchmarkCNN(params) + bench.run() + + def testSendRecvVariables(self): + self._testVariables('parameter_server') + if self.extended_tests(): + self._testVariables('parameter_server', local_parameter_device='CPU') + self._testVariables('parameter_server', optimizer='sgd') + + def testReplicatedVariables(self): + self._testVariables('replicated') + if self.extended_tests(): + self._testVariables('replicated', all_reduce_spec=None) + self._testVariables('replicated', use_fp16=True, fp16_vars=False) + self._testVariables( + 'replicated', + all_reduce_spec=None, + use_fp16=True, + fp16_vars=False, + fp16_enable_auto_loss_scale=True, + fp16_inc_loss_scale_every_n=4) + + def testIndependentVariables(self): + self._testVariables('independent') + self._testVariables( + 'independent', + all_reduce_spec=None, + use_fp16=True, + fp16_vars=False, + fp16_enable_auto_loss_scale=True, + fp16_inc_loss_scale_every_n=4) + + def testSummaryVerbosity(self): + self._testVariables('parameter_server', summary_verbosity=1) + if self.extended_tests(): + self._testVariables('parameter_server', summary_verbosity=2) + self._testVariables('parameter_server', summary_verbosity=3) + + def testStagedVariables(self): + self._testVariables('parameter_server', staged_vars=True) + if self.extended_tests(): + self._testVariables('parameter_server', staged_vars=True, + local_parameter_device='CPU') + self._testVariables('parameter_server', staged_vars=True, use_fp16=True, + fp16_vars=True) + + def _assert_correct_var_type(self, var, params): + if 'gpu_cached_inputs' not in var.name: + if params.use_fp16 and params.fp16_vars and 'batchnorm' not in var.name: + expected_type = tf.float16 + else: + expected_type = tf.float32 + self.assertEqual(var.dtype.base_dtype, expected_type) + + def _testVariables(self, + variable_update, + summary_verbosity=0, + local_parameter_device='GPU', + staged_vars=False, + optimizer='momentum', + # TODO(b/80125832): Enable nccl in tests + # all_reduce_spec='nccl', + all_reduce_spec='', + use_fp16=False, + fp16_vars=False, + fp16_enable_auto_loss_scale=False, + fp16_inc_loss_scale_every_n=10): + if not self.get_model_name(): + return + _check_has_gpu() + + params = benchmark_cnn.make_params( + model=self.get_model_name(), + num_batches=1, + num_intra_threads=1, + num_inter_threads=12, + distortions=False, + variable_update=variable_update, + local_parameter_device=local_parameter_device, + num_gpus=2, + summary_verbosity=summary_verbosity, + staged_vars=staged_vars, + optimizer=optimizer, + all_reduce_spec=all_reduce_spec, + compact_gradient_transfer=False if all_reduce_spec == 'nccl' else True, + use_fp16=use_fp16, + fp16_loss_scale=2., + fp16_vars=fp16_vars, + fp16_enable_auto_loss_scale=fp16_enable_auto_loss_scale, + fp16_inc_loss_scale_every_n=fp16_inc_loss_scale_every_n, + ) + + # Test building models using multiple GPUs, but don't + # run them. + with self.test_session(graph=tf.Graph()): + bench = benchmark_cnn.BenchmarkCNN(params) + bench._build_model() + + # Rough validation of variable type and placement, depending on mode. + all_vars = tf.global_variables() + tf.local_variables() + if params.variable_update == 'parameter_server': + for v in all_vars: + tf.logging.debug('var: %s' % v.name) + match = re.match(r'tower_(\d+)/v/gpu_cached_inputs:0', v.name) + if match: + self.assertEqual(v.device, '/device:GPU:%s' % match.group(1)) + elif v.name.startswith('v/'): + self.assertEqual(v.device, '/device:%s:0' % local_parameter_device) + self._assert_correct_var_type(v, params) + elif v.name in ('input_processing/images:0', + 'input_processing/labels:0', 'init_learning_rate:0', + 'global_step:0', 'loss_scale:0', + 'loss_scale_normal_steps:0'): + self.assertEqual(v.device, '/device:CPU:0') + else: + raise ValueError('Unexpected variable %s' % v.name) + else: + v0_count = 0 + v1_count = 0 + for v in all_vars: + if v.name.startswith('tower_0/v0/'): + self.assertEqual(v.name, 'tower_0/v0/gpu_cached_inputs:0') + self.assertEqual(v.device, '/device:GPU:0') + elif v.name.startswith('tower_1/v1/'): + self.assertEqual(v.name, 'tower_1/v1/gpu_cached_inputs:0') + self.assertEqual(v.device, '/device:GPU:1') + elif v.name.startswith('v0/'): + v0_count += 1 + self.assertEqual(v.device, '/device:GPU:0') + self._assert_correct_var_type(v, params) + elif v.name.startswith('v1/'): + v1_count += 1 + self.assertEqual(v.device, '/device:GPU:1') + self._assert_correct_var_type(v, params) + elif v.name in ('input_processing/images:0', + 'input_processing/labels:0', 'init_learning_rate:0', + 'global_step:0', 'loss_scale:0', + 'loss_scale_normal_steps:0'): + self.assertEqual(v.device, '/device:CPU:0') + else: + raise ValueError('Unexpected variable %s' % v.name) + self.assertEqual(v0_count, v1_count) + + # Validate summary ops in the model depending on verbosity level + summary_ops = tf.get_collection(tf.GraphKeys.SUMMARIES) + num_summary_ops = len(summary_ops) + self.assertEqual(num_summary_ops > 0, summary_verbosity > 0) + if summary_verbosity > 0: + has_affine_histogram = False + has_gradient_histogram = False + has_log_gradients_histogram = False + for op in summary_ops: + if '/gradients' in op.name: + has_gradient_histogram = True + elif '/affine' in op.name: + has_affine_histogram = True + elif 'log_gradients' in op.name: + has_log_gradients_histogram = True + self.assertEqual(summary_verbosity >= 3, has_affine_histogram) + self.assertEqual(summary_verbosity >= 3, has_gradient_histogram) + self.assertEqual(summary_verbosity >= 2, has_log_gradients_histogram) + if summary_verbosity == 1: + self.assertLess(num_summary_ops, 10) + + +class TrivialModelTest(TfCnnBenchmarksModelTest): + + def get_model_name(self): + return 'trivial' + + +class TestVgg1Model(TfCnnBenchmarksModelTest): + + def get_model_name(self): + return 'vgg11' + + +class TestVgg19Model(TfCnnBenchmarksModelTest): + + def get_model_name(self): + return 'vgg19' + + +class TestLenet5Model(TfCnnBenchmarksModelTest): + + def get_model_name(self): + return 'lenet' + + +class TestGooglenetModel(TfCnnBenchmarksModelTest): + + def get_model_name(self): + return 'googlenet' + + +class TestOverfeatModel(TfCnnBenchmarksModelTest): + + def get_model_name(self): + return 'overfeat' + + +class TestAlexnetModel(TfCnnBenchmarksModelTest): + + def get_model_name(self): + return 'alexnet' + + def extended_tests(self): + return True + + +class TestTrivialModel(TfCnnBenchmarksModelTest): + + def get_model_name(self): + return 'trivial' + + +class TestInceptionv3Model(TfCnnBenchmarksModelTest): + + def get_model_name(self): + return 'inception3' + + def extended_tests(self): + return True + + +class TestInceptionv4Model(TfCnnBenchmarksModelTest): + + def get_model_name(self): + return 'inception4' + + +class TestResnet50Model(TfCnnBenchmarksModelTest): + + def get_model_name(self): + return 'resnet50' + + def model_save_load_test(self): + return True + + +class TestResnet101Model(TfCnnBenchmarksModelTest): + + def get_model_name(self): + return 'resnet101' + + +class TestResnet152Model(TfCnnBenchmarksModelTest): + + def get_model_name(self): + return 'resnet152' + + +class TestResnet50V2Model(TfCnnBenchmarksModelTest): + + def get_model_name(self): + return 'resnet50_v2' + + +class TestResnet101V2Model(TfCnnBenchmarksModelTest): + + def get_model_name(self): + return 'resnet101_v2' + + +class TestResnet152V2Model(TfCnnBenchmarksModelTest): + + def get_model_name(self): + return 'resnet152_v2' + + +class TfCnnBenchmarksTest(tf.test.TestCase): + """Tests that benchmark_cnn runs correctly.""" + + def setUp(self): + super(TfCnnBenchmarksTest, self).setUp() + _check_has_gpu() + benchmark_cnn.setup(benchmark_cnn.make_params()) + + def _run_benchmark_cnn(self, params): + logs = [] + benchmark_cnn.log_fn = test_util.print_and_add_to_list(logs) + benchmark_cnn.BenchmarkCNN(params).run() + return logs + + def _run_benchmark_cnn_with_fake_images(self, params, images, labels): + logs = [] + benchmark_cnn.log_fn = test_util.print_and_add_to_list(logs) + bench = benchmark_cnn.BenchmarkCNN(params) + bench.input_preprocessor = preprocessing.TestImagePreprocessor( + params.batch_size * params.num_gpus, + [[params.batch_size, 227, 227, 3], [params.batch_size]], + params.num_gpus, + bench.model.data_type) + bench.dataset._queue_runner_required = True + bench.input_preprocessor.set_fake_data(images, labels) + bench.input_preprocessor.expected_subset = ('validation' + if params.eval else 'train') + bench.run() + return logs + + def _run_benchmark_cnn_with_black_and_white_images(self, params): + """Runs BenchmarkCNN with black and white images. + + A BenchmarkCNN is created and run with black and white images as input. Half + the images are black (i.e., filled with 0s) and half are white (i.e., filled + with 255s). + + Args: + params: Params for BenchmarkCNN. + + Returns: + A list of lines from the output of BenchmarkCNN. + """ + # TODO(reedwm): Instead of generating images here, use black and white + # tfrecords by calling test_util.create_black_and_white_images(). + effective_batch_size = params.batch_size * params.num_gpus + half_batch_size = effective_batch_size // 2 + images = np.zeros((effective_batch_size, 227, 227, 3), dtype=np.float32) + images[half_batch_size:, :, :, :] = 255 + labels = np.array([0] * half_batch_size + [1] * half_batch_size, + dtype=np.int32) + return self._run_benchmark_cnn_with_fake_images(params, images, labels) + + def _train_and_eval_local(self, + params, + check_output_values=False, + max_final_loss=10., + skip=None, + use_test_preprocessor=True): + # TODO(reedwm): check_output_values should default to True and be enabled + # on every test. Currently, if check_output_values=True and the calls to + # tf.set_random_seed(...) and np.seed(...) are passed certain seed values in + # benchmark_cnn.py, then most tests will fail. This indicates the tests + # are brittle and could fail with small changes when + # check_output_values=True, so check_output_values defaults to False for + # now. + + def run_fn(run_type, inner_params): + del run_type + if use_test_preprocessor: + return [ + self._run_benchmark_cnn_with_black_and_white_images(inner_params) + ] + else: + return [self._run_benchmark_cnn(inner_params)] + + return test_util.train_and_eval(self, run_fn, params, + check_output_values=check_output_values, + max_final_loss=max_final_loss, + skip=skip) + + def testAlexnet(self): + params = test_util.get_params('testAlexnet')._replace( + num_batches=30, init_learning_rate=0.01, model='alexnet') + self._train_and_eval_local(params) + + def testNoPrintAccuracy(self): + params = test_util.get_params('testNoPrintAccuracy')._replace( + print_training_accuracy=False) + self._train_and_eval_local(params) + + def testLowAccuracy(self): + params = test_util.get_params('testLowAccuracy')._replace( + print_training_accuracy=True, batch_size=5, num_batches=10) + # We force low accuracy by having each batch containing 10 identical images, + # each with a different label. This guarantees a top-1 accuracy of exactly + # 0.1 and a top-5 accuracy of exactly 0.5. + images = np.zeros((10, 227, 227, 3), dtype=np.float32) + labels = np.arange(10, dtype=np.int32) + logs = self._run_benchmark_cnn_with_fake_images(params, images, labels) + training_outputs = test_util.get_training_outputs_from_logs( + logs, params.print_training_accuracy) + last_output = training_outputs[-1] + # TODO(reedwm): These should be assertEqual but for some reason, + # occasionally the accuracies are lower (Running this test 500 times, these + # asserts failed twice). Investigate this problem. + self.assertLessEqual(last_output.top_1_accuracy, 0.1) + self.assertLessEqual(last_output.top_5_accuracy, 0.5) + + def testParameterServer(self): + params = test_util.get_params('testParameterServer') + self._train_and_eval_local(params) + + def testParameterServerStaged(self): + params = test_util.get_params('testParameterServerStaged')._replace( + staged_vars=True) + self._train_and_eval_local(params) + + def testReplicated(self): + params = test_util.get_params('testReplicated')._replace( + variable_update='replicated') + self._train_and_eval_local(params) + + def testIndependent(self): + params = test_util.get_params('testIndependent')._replace( + variable_update='independent') + self._train_and_eval_local(params) + + def testForwardOnly(self): + params = test_util.get_params('testForwardOnly')._replace(forward_only=True) + # Evaluation is not supported with --forward_only, so we set skip='eval'. + self._train_and_eval_local(params, skip='eval') + + def testForwardOnlyAndFreeze(self): + params = test_util.get_params('testForwardOnlyAndFreeze')._replace( + forward_only=True, freeze_when_forward_only=True, train_dir=None) + # Training is not supported with --freeze_when_forward_only. + self._train_and_eval_local(params, skip='eval_and_train_from_checkpoint') + + def testNoDistortions(self): + params = test_util.get_params('testNoDistortions')._replace( + distortions=False) + self._train_and_eval_local(params) + + def testCpuAsLocalParamDevice(self): + params = test_util.get_params('testCpuAsLocalParamDevice')._replace( + local_parameter_device='cpu') + self._train_and_eval_local(params) + + def testNHWC(self): + params = test_util.get_params('testNHWC')._replace(data_format='NHWC') + self._train_and_eval_local(params) + + def testCpuAsDevice(self): + params = test_util.get_params('testCpuAsDevice')._replace( + device='cpu', data_format='NHWC') # NHWC required when --device=cpu + self._train_and_eval_local(params) + + def testMomentumParameterServer(self): + params = test_util.get_params('testMomentumParameterServer')._replace( + optimizer='momentum', momentum=0.8) + self._train_and_eval_local(params) + + def testRmspropReplicated(self): + params = test_util.get_params('testRmspropReplicated')._replace( + variable_update='replicated', + optimizer='rmsprop', + rmsprop_decay=0.8, + rmsprop_momentum=0.6, + rmsprop_epsilon=0.7, + init_learning_rate=0.01) + self._train_and_eval_local(params) + + def testBatchGroupSize(self): + params = test_util.get_params('testBatchGroupSize')._replace( + batch_group_size=4, num_batches=100, num_warmup_batches=5) + self._train_and_eval_local(params) + + def testGradientClip(self): + params = test_util.get_params('testGradientClip')._replace( + gradient_clip=100.0) + self._train_and_eval_local(params) + + def testWeightDecay(self): + params = test_util.get_params('testWeightDecay')._replace( + weight_decay=0.0001) + self._train_and_eval_local(params) + + def testNoLayers(self): + params = test_util.get_params('testNoLayers')._replace(use_tf_layers=False) + self._train_and_eval_local(params) + + def testSaveModelSteps(self): + params = test_util.get_params('testSaveModelSteps')._replace( + save_model_steps=2, num_warmup_batches=0, num_batches=10, + max_ckpts_to_keep=3) + self._train_and_eval_local(params) + for i in range(1, 20 + 1): + # We train for 20 steps, since self._train_and_eval_local() does two + # training runs of 10 steps each. We save a checkpoint every 2 steps and + # keep the last 3 checkpoints, so at the end, we should have checkpoints + # for steps 16, 18, and 20. + matches = glob.glob(os.path.join(params.train_dir, + 'model.ckpt-{}.*'.format(i))) + if i in (16, 18, 20): + self.assertTrue(matches) + else: + self.assertFalse(matches) + + def testFp16WithFp32Vars(self): + params = test_util.get_params('testFp16WithFp32Vars')._replace( + use_fp16=True, fp16_vars=False, fp16_loss_scale=1.) + self._train_and_eval_local(params) + + def testFp16WithFp16Vars(self): + params = test_util.get_params('testFp16WithFp16Vars')._replace( + use_fp16=True, fp16_vars=True) + self._train_and_eval_local(params) + + def testXlaCompile(self): + params = test_util.get_params('testXlaCompile')._replace(xla_compile=True) + self._train_and_eval_local(params) + + @unittest.skip('Fails for unknown reason') + def testXlaCompileWithFp16(self): + params = test_util.get_params('testXlaCompileWithFp16')._replace( + use_fp16=True, xla_compile=True) + self._train_and_eval_local(params) + + def testGradientRepacking(self): + params = test_util.get_params('testGradientRepacking1')._replace( + gradient_repacking=2) + self._train_and_eval_local(params, skip='eval_and_train_from_checkpoint') + params = test_util.get_params('testGradientRepacking2')._replace( + gradient_repacking=2, use_fp16=True) + self._train_and_eval_local(params, skip='eval_and_train_from_checkpoint') + + def testTraceFileChromeTraceFormat(self): + trace_file = os.path.join(self.get_temp_dir(), + 'testTraceFileChromeTraceFormat_tracefile') + params = test_util.get_params('testTraceFileChromeTraceFormat')._replace( + trace_file=trace_file, use_chrome_trace_format=True) + self._train_and_eval_local(params) + self.assertGreater(os.stat(trace_file).st_size, 0) + + def testTraceFileStepStatsProto(self): + trace_file = os.path.join(self.get_temp_dir(), + 'testTraceFileStepStatsProto_tracefile') + params = test_util.get_params('testTraceFileStepStatsProto')._replace( + trace_file=trace_file, use_chrome_trace_format=False) + self._train_and_eval_local(params) + self.assertGreater(os.stat(trace_file).st_size, 0) + with open(trace_file) as f: + step_stats = step_stats_pb2.StepStats() + # The following statement should not raise an exception. + contents = f.read() + text_format.Merge(contents, step_stats) + + def testTfprofFile(self): + tfprof_file = os.path.join(self.get_temp_dir(), 'testTfprofFile_tfproffile') + params = test_util.get_params('testTfprofFile')._replace( + tfprof_file=tfprof_file) + self._train_and_eval_local(params, skip='eval_and_train_from_checkpoint') + self.assertGreater(os.stat(tfprof_file).st_size, 0) + with open(tfprof_file, 'rb') as f: + profile_proto = tfprof_log_pb2.ProfileProto() + # The following statement should not raise an exception. + profile_proto.ParseFromString(f.read()) + + @unittest.skip('Fails for unknown reason') + def testMoveTrainDir(self): + params = test_util.get_params('testMoveTrainDir') + self._train_and_eval_local(params) + new_train_dir = params.train_dir + '_moved' + os.rename(params.train_dir, new_train_dir) + params = params._replace(train_dir=new_train_dir, eval=True) + self._run_benchmark_cnn_with_black_and_white_images(params) + + @mock.patch('tensorflow.compat.v1.train.Saver') + @mock.patch('benchmark_cnn._get_checkpoint_to_load') + def testLoadCheckpoint(self, mock_checkpoint_to_load, mock_saver): + """Tests load checkpoint with full path to checkpoint.""" + expected_checkpoint = '/path/to/checkpoints/model.ckpt-1243' + mock_checkpoint_to_load.return_value = expected_checkpoint + + global_batch = benchmark_cnn.load_checkpoint(mock_saver, + None, + expected_checkpoint) + self.assertEqual(global_batch, 1243) + + def testGetCheckpointToLoadFullPath(self): + """Tests passing full path.""" + ckpt_path = '/foo/bar/model.ckpt-189' + full_path = benchmark_cnn._get_checkpoint_to_load(ckpt_path) + self.assertEqual(full_path, ckpt_path) + + def testGetCheckpointToLoadException(self): + """Tests exception for directory without a checkpoint.""" + ckpt_path = '/foo/bar/checkpoints' + self.assertRaises(benchmark_cnn.CheckpointNotFoundException, + benchmark_cnn._get_checkpoint_to_load, ckpt_path) + + @mock.patch('tensorflow.compat.v1.train.get_checkpoint_state') + def testGetCheckpointToLoad(self, mock_checkpoint_state): + """Tests passing path to checkpoint folder.""" + expected_checkpoint = '/path/to/checkpoints/model.ckpt-1243' + mock_checkpoint_state.return_value = mock.Mock( + model_checkpoint_path=expected_checkpoint) + ckpt_path = '/path/to/checkpoints/' + full_path = benchmark_cnn._get_checkpoint_to_load(ckpt_path) + self.assertEqual(full_path, expected_checkpoint) + + def testImagenetPreprocessor(self): + imagenet_dir = os.path.join(platforms_util.get_test_data_dir(), + 'fake_tf_record_data') + params = test_util.get_params('testImagenetPreprocessor')._replace( + data_dir=imagenet_dir, data_name='imagenet') + self._train_and_eval_local(params, use_test_preprocessor=False) + + def testImagenetPreprocessorNoDistortions(self): + imagenet_dir = os.path.join(platforms_util.get_test_data_dir(), + 'fake_tf_record_data') + params = test_util.get_params( + 'testImagenetPreprocessorNoDistortions')._replace( + data_dir=imagenet_dir, data_name='imagenet', distortions=False) + self._train_and_eval_local(params, use_test_preprocessor=False) + + def testImagenetPreprocessorVerboseSummary(self): + imagenet_dir = os.path.join(platforms_util.get_test_data_dir(), + 'fake_tf_record_data') + params = test_util.get_params( + 'testImagenetPreprocessorVerboseSummary')._replace( + data_dir=imagenet_dir, data_name='imagenet', distortions=False, + summary_verbosity=2) + self._train_and_eval_local(params, use_test_preprocessor=False) + + def testCifar10SyntheticData(self): + params = test_util.get_params('testCifar10SyntheticData')._replace( + data_name='cifar10') + self._train_and_eval_local(params) + + def testShiftRatio(self): + test_util.monkey_patch_base_cluster_manager() + params = benchmark_cnn.make_params( + data_name='imagenet', + data_dir=os.path.join(platforms_util.get_test_data_dir(), + 'fake_tf_record_data'), + job_name='worker', + worker_hosts='w1,w2,w3,w4', + ps_hosts='p1', + task_index=0) + self.assertEqual( + benchmark_cnn.BenchmarkCNN(params).input_preprocessor.shift_ratio, 0.0) + params = params._replace(task_index=3) + self.assertEqual( + benchmark_cnn.BenchmarkCNN(params).input_preprocessor.shift_ratio, 0.75) + + def testDistributedReplicatedSavableVars(self): + test_util.monkey_patch_base_cluster_manager() + params = benchmark_cnn.make_params( + variable_update='distributed_replicated', + model='inception4', + data_name='imagenet', + data_dir=os.path.join(platforms_util.get_test_data_dir(), + 'fake_tf_record_data'), + job_name='worker', + worker_hosts='w1,w2,w3,w4', + ps_hosts='p1', + datasets_use_prefetch=False) + + bench = benchmark_cnn.BenchmarkCNN(params) + with tf.Graph().as_default(): + bench._build_model() + savable_vars = bench.variable_mgr.savable_variables() + # Assert all global variables are in savable_vars + for v in tf.global_variables(): + if not v.name.startswith( + variable_mgr_util.PS_SHADOW_VAR_PREFIX + '/v0'): + self.assertEqual(v.name, 'global_step:0') + name = bench.variable_mgr._strip_port(v.name) + if name.startswith(variable_mgr_util.PS_SHADOW_VAR_PREFIX): + name = name[len(variable_mgr_util.PS_SHADOW_VAR_PREFIX + '/'):] + self.assertIn(name, savable_vars) + self.assertIn(savable_vars[name], tf.global_variables()) + # Assert all local variables on the first tower are in savable_vars + for v in tf.local_variables(): + if v.name.startswith('v0/'): + name = bench.variable_mgr._strip_port(v.name) + self.assertIn(name, savable_vars) + + def _test_preprocessing_eval(self, image_height, image_width, output_height, + output_width): + image = tf.fill((image_height, image_width, 3), + tf.constant(128, dtype=tf.uint8)) + params = benchmark_cnn.make_params() + new_image = preprocessing.eval_image(image, output_height, output_width, 0, + 'bilinear', params.summary_verbosity) + with self.test_session() as sess: + new_image_value = sess.run(new_image) + self.assertAllEqual(new_image_value, + np.full((output_height, output_width, 3), 128, + dtype=np.uint8)) + + def testPreprocessingEval(self): + self._test_preprocessing_eval(10, 10, 4, 4) + self._test_preprocessing_eval(4, 4, 10, 10) + self._test_preprocessing_eval(1, 100, 100, 1) + self._test_preprocessing_eval(100, 1, 1, 100) + self._test_preprocessing_eval(1, 100, 1, 100) + + def _test_preprocessing_traing(self, image_buf, image_color, + output_height, output_width, bbox, + batch_position, resize_method, distortions, + summary_verbosity, fuse_decode_and_crop): + new_image = preprocessing.train_image( + image_buf, + output_height, + output_width, + bbox, + batch_position, + resize_method, + distortions, + summary_verbosity=summary_verbosity, + fuse_decode_and_crop=fuse_decode_and_crop) + self.assertEqual(new_image.shape, [output_height, output_width, 3]) + with self.test_session(use_gpu=True) as sess: + new_image_value = sess.run(new_image) + self.assertAllClose( + new_image_value, + np.full( + [output_height, output_width, 3], + image_color, + dtype=np.float32), + atol=50., + rtol=0.) + + def testPreprocessingTrain(self): + test_data_dir = os.path.join(platforms_util.get_test_data_dir(), 'images') + black_file = os.path.join(test_data_dir, 'black_image.jpg') + with open(black_file, 'rb') as f: + black_jpg_buffer = f.read() + white_file = os.path.join(test_data_dir, 'white_image.jpg') + with open(white_file, 'rb') as f: + white_jpg_buffer = f.read() + bbox = tf.zeros((1, 0, 4), dtype=tf.float32) + batch_position = 0 + # Each size config is (output_height, output_width, resize_method) + size_configs = [(100, 100, 'round_robin'), (150, 10, 'bilinear'), + (10, 150, 'nearest')] + # Each image config is (image_buf, image_color) + image_configs = [(white_jpg_buffer, 255), (black_jpg_buffer, 0)] + for (image_buf, image_color) in image_configs: + for output_height, output_width, resize_method in size_configs: + for distortions in [True, False]: + for summary_verbosity in [0, 2]: + for fuse_decode_and_crop in [True, False]: + self._test_preprocessing_traing( + image_buf, image_color, output_height, output_width, bbox, + batch_position, resize_method, distortions, summary_verbosity, + fuse_decode_and_crop) + + def _test_learning_rate(self, params, global_step_to_expected_learning_rate): + self.longMessage = True # pylint: disable=invalid-name + bench = benchmark_cnn.BenchmarkCNN(params) + with tf.Graph().as_default() as graph: + bench._build_model() + global_step = graph.get_tensor_by_name('global_step:0') + learning_rate = graph.get_tensor_by_name('learning_rate_tensor:0') + with self.test_session(graph=graph, use_gpu=True) as sess: + items = global_step_to_expected_learning_rate.items() + for global_step_val, expected_learning_rate in items: + self.assertAlmostEqual(sess.run(learning_rate, + {global_step: global_step_val}), + expected_learning_rate, + msg='at global_step:{}'. + format(global_step_val)) + + def testLearningRateModelSpecificResNet(self): + params = benchmark_cnn.make_params(model='resnet50', + batch_size=256, + variable_update='parameter_server', + num_gpus=1) + self._test_learning_rate(params, { + 0: 0, + 150136: 0.128, + 150137: 0.0128, + 300273: 0.0128, + 300274: 0.00128, + 10000000: 0.0000128 + }) + + def testLearningRateUserProvidedInitLr(self): + params = benchmark_cnn.make_params(model='resnet50', + batch_size=256, + variable_update='replicated', + init_learning_rate=1.) + self._test_learning_rate(params, { + 0: 1., + 10000000: 1. + }) + + def testLearningRateUserProvidedInitLrAndWarmup(self): + params = benchmark_cnn.make_params(model='resnet50', + batch_size=256, + variable_update='replicated', + init_learning_rate=1., + num_learning_rate_warmup_epochs=5) + self._test_learning_rate(params, { + 0: 0., + 12511: 0.5, + 25022: 1., + 10000000: 1. + }) + + def testLearningRateUserProvidedDecayInfo(self): + params = benchmark_cnn.make_params(model='resnet50', + init_learning_rate=1., + learning_rate_decay_factor=0.5, + num_epochs_per_decay=2, + minimum_learning_rate=0.3750, + batch_size=32) + self._test_learning_rate(params, { + 0: 1., + 80071: 1., + 80072: 0.5, + 160143: 0.5, + 160144: 0.375, + 10000000: 0.375 + }) + + def testLearningRateUserProvidedZeroDecay(self): + params = benchmark_cnn.make_params(model='resnet50', + num_learning_rate_warmup_epochs=0, + learning_rate_decay_factor=0.5, + num_epochs_per_decay=0, + minimum_learning_rate=0.3750, + batch_size=32) + with self.assertRaises(ValueError): + with tf.Graph().as_default(): + # This will fail because params.learning_rate_decay_factor cannot be + # nonzero if params.num_epochs_per_decay is zero. + benchmark_cnn.BenchmarkCNN(params)._build_model() + + def testLearningRateUserProvidedSchedule(self): + params = benchmark_cnn.make_params( + model='trivial', + batch_size=32, + piecewise_learning_rate_schedule='1;3;.1;5;.01') + self._test_learning_rate(params, { + 0: 1., + 120108: 1., + 120109: 0.1, + 200181: 0.1, + 200182: 0.01, + 100000000: 0.01 + }) + + def testNumBatchesAndEpochs(self): + params = benchmark_cnn.make_params() + batches, epochs = benchmark_cnn.get_num_batches_and_epochs(params, 10, 100) + self.assertEqual(batches, benchmark_cnn._DEFAULT_NUM_BATCHES) + self.assertAlmostEqual(epochs, + float(benchmark_cnn._DEFAULT_NUM_BATCHES) / 10) + + params = benchmark_cnn.make_params(num_batches=21) + batches, epochs = benchmark_cnn.get_num_batches_and_epochs(params, 25, 50) + self.assertEqual(batches, 21) + self.assertAlmostEqual(epochs, 10.5) + + params = benchmark_cnn.make_params(num_epochs=3) + batches, epochs = benchmark_cnn.get_num_batches_and_epochs(params, 2, 3) + self.assertEqual(batches, 5) + self.assertAlmostEqual(epochs, 10./3.) + + params = benchmark_cnn.make_params(num_epochs=4) + batches, epochs = benchmark_cnn.get_num_batches_and_epochs(params, 2, 3) + self.assertEqual(batches, 6) + self.assertAlmostEqual(epochs, 4) + + with self.assertRaises(ValueError): + params = benchmark_cnn.make_params(num_batches=100, num_epochs=100) + benchmark_cnn.get_num_batches_and_epochs(params, 1, 1) + + def _testEvalDuringTraining(self, params, expected_num_eval_batches_found): + # The idea of this test is that all train images are black and all eval + # images are white. We pass the images through the TestModel, and ensure + # the outputs are as expected. + + batch_size = params.batch_size + eval_batch_size = params.eval_batch_size or params.batch_size + + class TestModel(test_util.TestCNNModel): + + def __init__(self): + super(TestModel, self).__init__() + self.depth = 3 + + def add_inference(self, cnn): + if cnn.phase_train: + # This will allow us to test that 100 is only added during training + # and not during eval. + cnn.top_layer += 100 + assert cnn.top_layer.shape[0] == batch_size + else: + assert cnn.top_layer.shape[0] == eval_batch_size + + # Reduce the image to a single number. The number should be (-1 + 100) + # during training and 1 during testing. + cnn.top_layer = tf.reshape(cnn.top_layer, (cnn.top_layer.shape[0], -1)) + cnn.top_layer = tf.reduce_mean(cnn.top_layer, axis=1) + cnn.top_layer = tf.reshape(cnn.top_layer, + (cnn.top_layer.shape[0], 1, 1, 1)) + cnn.top_size = 1 + trainable_vars = tf.trainable_variables() + + # The super method will compute image*A*B, where A=1 and B=2. + super(TestModel, self).add_inference(cnn) + + if not cnn.phase_train: + # Assert no new variables were added, since they should be reused from + # training. + assert len(trainable_vars) == len(tf.trainable_variables()) + + model = TestModel() + dataset = datasets.ImagenetDataset(params.data_dir) + logs = [] + bench_cnn = benchmark_cnn.BenchmarkCNN(params, model=model, dataset=dataset) + with test_util.monkey_patch(benchmark_cnn, + log_fn=test_util.print_and_add_to_list(logs)): + bench_cnn.run() + training_outputs = test_util.get_training_outputs_from_logs( + logs, print_training_accuracy=False) + self.assertEqual(len(training_outputs), params.num_batches) + expected_training_output = (-1 + 100) * 1 * 2 + for training_output in training_outputs: + self.assertEqual(training_output.loss, expected_training_output) + eval_outputs = test_util.get_evaluation_outputs_from_logs(logs) + self.assertTrue(eval_outputs) + expected_eval_output = 1 * 1 * 2 + for eval_output in eval_outputs: + self.assertEqual(eval_output.top_1_accuracy, expected_eval_output) + self.assertEqual(eval_output.top_5_accuracy, expected_eval_output) + + num_eval_batches_found = 0 + eval_batch_regex = re.compile(r'^\d+\t[0-9.]+ examples/sec$') + for log in logs: + if eval_batch_regex.match(log): + num_eval_batches_found += 1 + self.assertEqual(num_eval_batches_found, expected_num_eval_batches_found) + + def testEvalDuringTraining(self): + data_dir = test_util.create_black_and_white_images() + base_params = test_util.get_params('testEvalDuringTraining') + train_dir = base_params.train_dir + base_params = base_params._replace( + train_dir=None, print_training_accuracy=False, num_warmup_batches=0, + num_batches=7, num_eval_batches=2, display_every=1, + init_learning_rate=0, weight_decay=0, + distortions=False, data_dir=data_dir) + expected_num_eval_batches_found = ( + base_params.num_eval_batches * (base_params.num_batches // 2 + 1)) + + # Test --eval_during_training_every_n_steps + self._testEvalDuringTraining( + base_params._replace(eval_during_training_every_n_steps=2, + variable_update='parameter_server'), + expected_num_eval_batches_found) + self._testEvalDuringTraining( + base_params._replace(eval_during_training_every_n_steps=2, + variable_update='replicated'), + expected_num_eval_batches_found) + self._testEvalDuringTraining( + base_params._replace(eval_during_training_every_n_steps=2, + variable_update='replicated', + summary_verbosity=2, + save_summaries_steps=2, + datasets_use_prefetch=False), + expected_num_eval_batches_found) + self._testEvalDuringTraining( + base_params._replace(eval_during_training_every_n_steps=2, + variable_update='replicated', + use_fp16=True, train_dir=train_dir, + eval_batch_size=base_params.batch_size + 2), + expected_num_eval_batches_found) + + # Test --eval_during_training_every_n_epochs + every_n_epochs = (2 * base_params.batch_size * base_params.num_gpus / + datasets.IMAGENET_NUM_TRAIN_IMAGES) + self._testEvalDuringTraining( + base_params._replace(eval_during_training_every_n_epochs=every_n_epochs, + variable_update='replicated'), + expected_num_eval_batches_found) + + # Test --eval_during_training_at_specified_steps + list_steps = [2, 3, 5, 7, 1000] + num_eval_steps = 1 + sum(1 for step in list_steps + if step < base_params.num_batches) + expected_num_eval_batches_found = ( + base_params.num_eval_batches * num_eval_steps) + + self._testEvalDuringTraining( + base_params._replace(eval_during_training_at_specified_steps=list_steps, + variable_update='replicated'), + expected_num_eval_batches_found) + + # Test --eval_during_training_at_specified_epochs + list_epochs = [(step * base_params.batch_size * base_params.num_gpus / + datasets.IMAGENET_NUM_TRAIN_IMAGES) + for step in list_steps] + self._testEvalDuringTraining( + base_params._replace( + eval_during_training_at_specified_epochs=list_epochs, + variable_update='replicated'), + expected_num_eval_batches_found) + + # Test --eval_during_training_every_n_steps runs with synthetic data. + params = base_params._replace( + variable_update='replicated', data_dir=None, + eval_during_training_every_n_steps=2, num_batches=2) + benchmark_cnn.BenchmarkCNN(params).run() + + def testEvalDuringTrainingNumEpochs(self): + params = benchmark_cnn.make_params( + batch_size=1, eval_batch_size=2, eval_during_training_every_n_steps=1, + num_batches=30, num_eval_epochs=100 / datasets.IMAGENET_NUM_VAL_IMAGES) + bench_cnn = benchmark_cnn.BenchmarkCNN(params) + self.assertEqual(bench_cnn.num_batches, 30) + self.assertAlmostEqual(bench_cnn.num_epochs, + 30 / datasets.IMAGENET_NUM_TRAIN_IMAGES) + self.assertAlmostEqual(bench_cnn.num_eval_batches, 50) + self.assertAlmostEqual(bench_cnn.num_eval_epochs, + 100 / datasets.IMAGENET_NUM_VAL_IMAGES) + + def testEarlyStopping(self): + params = benchmark_cnn.make_params( + batch_size=2, + display_every=1, + num_batches=100, + eval_during_training_every_n_steps=2, + stop_at_top_1_accuracy=0.4, + ) + with mock.patch.object(benchmark_cnn.BenchmarkCNN, '_eval_once', + side_effect=[(0.1, 0.1), (0.5, 0.5), (0.2, 0.2)] + ) as mock_eval_once: + logs = [] + bench_cnn = benchmark_cnn.BenchmarkCNN(params) + with test_util.monkey_patch(benchmark_cnn, + log_fn=test_util.print_and_add_to_list(logs)): + bench_cnn.run() + training_outputs = test_util.get_training_outputs_from_logs( + logs, print_training_accuracy=False) + # We should stop after the second evaluation, and we evaluate every 2 + # steps. So there should be 2 * 2 = 4 training outputs. + self.assertEqual(len(training_outputs), 4) + self.assertEqual(mock_eval_once.call_count, 2) + + def testOutOfRangeErrorsAreNotIgnored(self): + error_msg = 'Fake OutOfRangeError error message' + with mock.patch.object(benchmark_cnn.BenchmarkCNN, 'benchmark_with_session', + side_effect=tf.errors.OutOfRangeError(None, None, + error_msg)): + with self.assertRaisesRegex(RuntimeError, error_msg): + benchmark_cnn.BenchmarkCNN(benchmark_cnn.make_params()).run() + + def testInvalidFlags(self): + params = benchmark_cnn.make_params(device='cpu', data_format='NCHW') + with self.assertRaises(ValueError): + benchmark_cnn.BenchmarkCNN(params) + + params = benchmark_cnn.make_params(use_fp16=True, fp16_vars=True, + variable_update='replicated', + all_reduce_spec='nccl') + with self.assertRaises(ValueError): + benchmark_cnn.BenchmarkCNN(params) + + # Automatic loss scaling is only supported for 'replicated', 'ps', + # and 'independent' variable_updates. + invalid_variable_updates = [ + 'distributed_replicated', 'distributed_all_reduce' + ] + for variable_update in invalid_variable_updates: + params = benchmark_cnn.make_params( + use_fp16=True, + fp16_vars=True, + fp16_enable_auto_loss_scale=True, + variable_update=variable_update) + with self.assertRaises(ValueError): + benchmark_cnn.BenchmarkCNN(params) + + # Automatic loss scaling is not supported for 'nccl'. + params = benchmark_cnn.make_params( + use_fp16=True, + fp16_vars=True, + fp16_enable_auto_loss_scale=True, + all_reduce_spec='nccl') + with self.assertRaises(ValueError): + benchmark_cnn.BenchmarkCNN(params) + + # Automatic loss scaling is not supported for 'staged_vars'. + params = benchmark_cnn.make_params( + use_fp16=True, + fp16_vars=True, + fp16_enable_auto_loss_scale=True, + staged_vars=True) + with self.assertRaises(ValueError): + benchmark_cnn.BenchmarkCNN(params) + + def testMakeParams(self): + default_params = benchmark_cnn.make_params() + self.assertEqual(default_params.model, + flags.param_specs['model'].default_value) + params = benchmark_cnn.make_params(model='foo') + self.assertEqual(params.model, 'foo') + with self.assertRaises(ValueError): + benchmark_cnn.make_params(job_name='foo') + with self.assertRaises(ValueError): + benchmark_cnn.make_params(gpu_memory_frac_for_testing=-1.) + + +class VariableUpdateTest(tf.test.TestCase): + """Tests that variables are updated correctly. + + These tests use a very simple deterministic model. For example, some tests use + the model + + loss = image * A * B + + where image is a 1x1 images (with a single scalar value), and A and B are + scalar variables. Tests will run tf_cnn_benchmarks with such a model, on a + sequence of scalar images, and assert that the losses are the correct value. + Since the losses depend on the variables, this indirectly tests variables are + updated correctly. + """ + + def setUp(self): + super(VariableUpdateTest, self).setUp() + _check_has_gpu() + benchmark_cnn.setup(benchmark_cnn.make_params()) + + def _get_benchmark_cnn_losses(self, inputs, params): + """Returns the losses of BenchmarkCNN on the given inputs and params.""" + logs = [] + model = test_util.TestCNNModel() + with test_util.monkey_patch(benchmark_cnn, + log_fn=test_util.print_and_add_to_list(logs), + LOSS_AND_ACCURACY_DIGITS_TO_SHOW=15): + bench = benchmark_cnn.BenchmarkCNN( + params, dataset=test_util.TestDataSet(), model=model) + # The test model does not use labels when computing loss, so the label + # values do not matter as long as it's the right shape. + labels = np.array([1] * inputs.shape[0]) + bench.input_preprocessor.set_fake_data(inputs, labels) + if bench.eval_input_preprocessor: + bench.eval_input_preprocessor.set_fake_data(inputs, labels) + bench.run() + + outputs = test_util.get_training_outputs_from_logs( + logs, params.print_training_accuracy) + return [x.loss for x in outputs] + + def _test_variable_update(self, params): + """Tests variables are updated correctly when the given params are used. + + A BenchmarkCNN is created with a TestCNNModel, and is run with some scalar + images. The losses are then compared with the losses obtained with + TestCNNModel().manually_compute_losses() + + Args: + params: a Params tuple used to create BenchmarkCNN. + """ + inputs = test_util.get_fake_var_update_inputs() + actual_losses = self._get_benchmark_cnn_losses(inputs, params) + expected_losses, = test_util.TestCNNModel().manually_compute_losses( + inputs, 1, params) + rtol = 3e-2 if params.use_fp16 else 1e-5 + self.assertAllClose(actual_losses[:len(expected_losses)], expected_losses, + rtol=rtol, atol=0.) + + def _test_variable_updates(self, params, + var_updates=('parameter_server', 'replicated')): + for var_update in var_updates: + self._test_variable_update(params._replace(variable_update=var_update)) + + def testDefault(self): + params = test_util.get_var_update_params() + self._test_variable_updates(params) + + # For some reason, this test doesn't always pass + + # def testCpuAsDevice(self): + # params = test_util.get_var_update_params()._replace( + # device='cpu', + # data_format='NHWC') # NHWC required when --device=cpu + # self._test_variable_updates(params) + + def testCpuAsLocalParamDevice(self): + params = test_util.get_var_update_params()._replace( + local_parameter_device='cpu') + self._test_variable_updates(params) + + def testFp16(self): + params = test_util.get_var_update_params()._replace(use_fp16=True) + self._test_variable_updates(params) + + def testMomentum(self): + params = test_util.get_var_update_params()._replace(optimizer='momentum') + self._test_variable_updates(params) + + def testRmsprop(self): + params = test_util.get_var_update_params()._replace(optimizer='rmsprop') + self._test_variable_updates(params) + + def testNoLayers(self): + params = test_util.get_var_update_params()._replace(use_tf_layers=False) + self._test_variable_updates(params) + + def testVariousAllReduceSpecs(self): + # We do not test xring, because it requires all Variables to have at least + # two elements. + params = test_util.get_var_update_params()._replace(all_reduce_spec='pscpu') + self._test_variable_updates(params, var_updates=('replicated',)) + params = params._replace(all_reduce_spec='psgpu') + self._test_variable_updates(params, var_updates=('replicated',)) + # TODO(b/80125832): Enable nccl in tests + # params = params._replace(all_reduce_spec='nccl', + # compact_gradient_transfer=False) + # self._test_variable_updates(params, var_updates=('replicated',)) + + def testPrintBaseLoss(self): + params = test_util.get_var_update_params()._replace( + loss_type_to_report='base_loss') + self._test_variable_updates(params) + + def testSingleL2LossOp(self): + params = test_util.get_var_update_params()._replace( + single_l2_loss_op=True) + self._test_variable_updates(params) + + def testResourceVars(self): + params = test_util.get_var_update_params()._replace( + use_resource_vars=True) + self._test_variable_updates(params) + + def testEvalDuringTrainingEveryNSteps(self): + # TODO(reedwm): Test that the eval results are correct. This only tests that + # training results are correct. + params = test_util.get_var_update_params()._replace( + eval_during_training_every_n_steps=1) + self._test_variable_updates(params, var_updates=('replicated',)) + + +class VariableMgrLocalReplicatedTest(tf.test.TestCase): + + def _test_grad_aggregation_with_var_mgr(self, variable_mgr, num_towers, + num_vars, deferred_grads): + tower_devices = ['/gpu:%d' % i for i in range(num_towers)] + tower_grads = [] + expected_sums = [0.] * num_vars + for i, tower_device in enumerate(tower_devices): + with tf.device(tower_device): + grad_vars = [] + for j in range(num_vars): + n = num_towers * i + j + grad_vars.append((tf.constant(n, dtype=tf.float32), + tf.Variable(n, dtype=tf.float32))) + expected_sums[j] += n + tower_grads.append(grad_vars) + + _, agg_device_grads = variable_mgr.preprocess_device_grads( + tower_grads) + expected_device_grads = [] + for i in range(num_towers): + expected_grad_vars = [] + for j in range(num_vars): + expected_grad_and_var = [expected_sums[j], num_towers * i + j] + if isinstance(agg_device_grads[i][j], tuple): + # agg_device_grads[i][j] can be a list or tuple. + expected_grad_and_var = tuple(expected_grad_and_var) + expected_grad_vars.append(expected_grad_and_var) + if isinstance(agg_device_grads[i], tuple): + # agg_device_grads[i] can be a list or tuple. + expected_grad_vars = tuple(expected_grad_vars) + expected_device_grads.append(expected_grad_vars) + config = tf.ConfigProto(allow_soft_placement=True) + with tf.Session(config=config) as sess: + sess.run(tf.initialize_all_variables()) + sess.run(variable_mgr._warmup_ops) + if deferred_grads: + # With deferred grads, the result of a session run is always the summed + # gradients from the previous session run. + sess.run(agg_device_grads) + feed_dict = {g: 0 for grad_vars in tower_grads for g, _ in grad_vars} + agg_device_grads_ = sess.run(agg_device_grads, feed_dict) + else: + agg_device_grads_ = sess.run(agg_device_grads) + self.assertEqual(agg_device_grads_, expected_device_grads) + + def _test_grad_aggregation(self, params, num_vars): + bench = benchmark_cnn.BenchmarkCNN(params) + deferred_grads = (params.variable_consistency == 'relaxed') + self._test_grad_aggregation_with_var_mgr(bench.variable_mgr, bench.num_gpus, + num_vars, deferred_grads) + + def test_grad_aggregation(self): + base_params = benchmark_cnn.make_params(num_gpus=10, + variable_update='replicated', + use_fp16=True) + params = base_params + self._test_grad_aggregation(params, 10) + params = base_params._replace(gradient_repacking=3) + self._test_grad_aggregation(params, 10) + params = base_params._replace(variable_consistency='relaxed') + self._test_grad_aggregation(params, 10) + params = base_params._replace(compact_gradient_transfer=False) + self._test_grad_aggregation(params, 10) + params = base_params._replace(gradient_repacking=3, + variable_consistency='relaxed') + self._test_grad_aggregation(params, 10) + params = base_params._replace(gradient_repacking=3, + compact_gradient_transfer=False) + self._test_grad_aggregation(params, 10) + params = base_params._replace(variable_consistency='relaxed', + compact_gradient_transfer=False) + self._test_grad_aggregation(params, 10) + params = base_params._replace(gradient_repacking=3, + variable_consistency='relaxed', + compact_gradient_transfer=False) + self._test_grad_aggregation(params, 10) + params = base_params._replace(num_gpus=8, hierarchical_copy=True) + self._test_grad_aggregation(params, 10) + # TODO(b/80125832): Enable nccl in tests + # params = base_params._replace(all_reduce_spec='nccl', + # compact_gradient_transfer=False, + # # For some reason, this test freezes when + # # num_gpus=10 + # num_gpus=8) + # self._test_grad_aggregation(params, 10) + params = base_params._replace(all_reduce_spec='pscpu') + self._test_grad_aggregation(params, 10) + + params = base_params._replace(num_gpus=8, + gradient_repacking=3, + variable_consistency='relaxed', + hierarchical_copy=True) + self._test_grad_aggregation(params, 10) + # TODO(b/80125832): Enable nccl in tests + # params = base_params._replace(num_gpus=8, + # gradient_repacking=3, + # variable_consistency='relaxed', + # all_reduce_spec='nccl', + # compact_gradient_transfer=False) + # self._test_grad_aggregation(params, 10) + params = base_params._replace(gradient_repacking=3, + variable_consistency='relaxed', + all_reduce_spec='pscpu') + self._test_grad_aggregation(params, 10) + params = base_params._replace(gradient_repacking=3, + variable_consistency='relaxed', + all_reduce_spec='xring') + self._test_grad_aggregation(params, 10) + + +if __name__ == '__main__': + tf.disable_v2_behavior() + tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/cnn_util.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/cnn_util.py new file mode 100644 index 00000000..2cde5104 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/cnn_util.py @@ -0,0 +1,253 @@ +# 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. +# ============================================================================== + +"""Utilities for CNN benchmarks.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import sys +import threading + +import numpy as np +import tensorflow.compat.v1 as tf + + +def tensorflow_version_tuple(): + v = tf.__version__ + major, minor, patch = v.split('.') + return (int(major), int(minor), patch) + + +def tensorflow_version(): + vt = tensorflow_version_tuple() + return vt[0] * 1000 + vt[1] + + +def log_fn(log): + print(log, flush=True) + + +def roll_numpy_batches(array, batch_size, shift_ratio): + """Moves a proportion of batches from start to the end of the array. + + This function moves a proportion of batches, specified by `shift_ratio`, from + the starts of the array to the end. The number of batches moved is rounded + down to the nearest integer. For example, + + ``` + roll_numpy_batches([1, 2, 3, 4, 5, 6], 2, 0.34) == [3, 4, 5, 6, 1, 2] + ``` + + Args: + array: A Numpy array whose first dimension is the batch dimension. + batch_size: The batch size. + shift_ratio: Proportion of batches to move from the start of the array to + the end of the array. + Returns: + A new Numpy array, with a proportion of the batches at the start of `array` + moved to the end. + """ + num_items = array.shape[0] + assert num_items % batch_size == 0 + num_batches = num_items // batch_size + starting_batch = int(num_batches * shift_ratio) + starting_item = starting_batch * batch_size + return np.roll(array, -starting_item, axis=0) + + +# For Python 2.7 compatibility, we do not use threading.Barrier. +class Barrier(object): + """Implements a lightweight Barrier. + + Useful for synchronizing a fixed number of threads at known synchronization + points. Threads block on 'wait()' and simultaneously return once they have + all made that call. + + # Implementation adopted from boost/thread/barrier.hpp + """ + + def __init__(self, parties): + """Create a barrier, initialised to 'parties' threads.""" + self.cond = threading.Condition(threading.Lock()) + self.parties = parties + # Indicates the number of waiting parties. + self.waiting = 0 + # generation is needed to deal with spurious wakeups. If self.cond.wait() + # wakes up for other reasons, generation will force it go back to wait(). + self.generation = 0 + self.broken = False + + def wait(self): + """Wait for the barrier.""" + with self.cond: + # Check if the barrier has been disabled or not. + if self.broken: + return + gen = self.generation + self.waiting += 1 + if self.waiting == self.parties: + self.waiting = 0 + self.generation += 1 + self.cond.notify_all() + # loop because of spurious wakeups + while gen == self.generation: + self.cond.wait() + + # TODO(huangyp): Remove this method once we find a way to know which step + # is the last barrier. + def abort(self): + """Clear existing barrier and disable this barrier.""" + with self.cond: + if self.waiting > 0: + self.generation += 1 + self.cond.notify_all() + self.broken = True + + +class ImageProducer(object): + """An image producer that puts images into a staging area periodically. + + This class is useful for periodically running a set of ops, `put_ops` on a + different thread every `batch_group_size` steps. + + The notify_image_consumption() method is used to increment an internal counter + so that every `batch_group_size` times it is called, `put_ops` is executed. A + barrier is placed so that notify_image_consumption() will block until + the previous call to `put_ops` has been executed. + + The start() method is used to start the thread that runs `put_ops`. + + The done() method waits until the last put_ops is executed and stops the + thread. + + The purpose of this class is to fill an image input pipeline every + `batch_group_size` steps. Suppose `put_ops` supplies `batch_group_size` images + to the input pipeline when run, and that every step, 1 batch of images is + consumed. Then, by calling notify_image_consumption() every step, images are + supplied to the input pipeline at the same amount they are consumed. + + Example usage: + ``` + put_ops = ... # Enqueues `batch_group_size` batches to a StagingArea + get_op = ... # Dequeues 1 batch, and does some operations on it + batch_group_size = 4 + with tf.Session() as sess: + image_producer = cnn_util.ImageProducer(sess, put_op, batch_group_size) + image_producer.start() + for _ in range(100): + sess.run(get_op) + image_producer.notify_image_consumption() + ``` + """ + + def __init__(self, sess, put_ops, batch_group_size, use_python32_barrier): + self.sess = sess + self.num_gets = 0 + self.put_ops = put_ops + self.batch_group_size = batch_group_size + self.done_event = threading.Event() + if (use_python32_barrier and + sys.version_info[0] == 3 and sys.version_info[1] >= 2): + self.put_barrier = threading.Barrier(2) + else: + self.put_barrier = Barrier(2) + + def _should_put(self): + return (self.num_gets + 1) % self.batch_group_size == 0 + + def done(self): + """Stop the image producer.""" + self.done_event.set() + self.put_barrier.abort() + self.thread.join() + + def start(self): + """Start the image producer.""" + self.sess.run([self.put_ops]) + self.thread = threading.Thread(target=self._loop_producer) + # Set daemon to true to allow Ctrl + C to terminate all threads. + self.thread.daemon = True + self.thread.start() + + def notify_image_consumption(self): + """Increment the counter of image_producer by 1. + + This should only be called by the main thread that consumes images and runs + the model computation. One batch of images should be consumed between + calling start() and the first call to this method. Then, one batch of images + should be consumed between any two successive calls to this method. + """ + if self._should_put(): + self.put_barrier.wait() + self.num_gets += 1 + + def _loop_producer(self): + while not self.done_event.isSet(): + self.sess.run([self.put_ops]) + self.put_barrier.wait() + + +class BaseClusterManager(object): + """The manager for the cluster of servers running the benchmark.""" + + def __init__(self, params): + worker_hosts = params.worker_hosts.split(',') + ps_hosts = params.ps_hosts.split(',') if params.ps_hosts else [] + cluster = {'worker': worker_hosts} + if ps_hosts: + cluster['ps'] = ps_hosts + self._cluster_spec = tf.train.ClusterSpec(cluster) + + def get_target(self): + """Returns a target to be passed to tf.Session().""" + raise NotImplementedError('get_target must be implemented by subclass') + + def join_server(self): + raise NotImplementedError('join must be implemented by subclass') + + def get_cluster_spec(self): + return self._cluster_spec + + def num_workers(self): + return len(self._cluster_spec.job_tasks('worker')) + + def num_ps(self): + if 'ps' in self._cluster_spec.jobs: + return len(self._cluster_spec.job_tasks('ps')) + else: + return 0 + + +class GrpcClusterManager(BaseClusterManager): + """A cluster manager for a cluster networked with gRPC.""" + + def __init__(self, params, config_proto): + super(GrpcClusterManager, self).__init__(params) + if params.job_name == 'controller': + self._target = 'grpc://%s' % self._cluster_spec.job_tasks('worker')[0] + else: + self._server = tf.train.Server(self._cluster_spec, + job_name=params.job_name, + task_index=params.task_index, + config=config_proto, + protocol=params.server_protocol) + self._target = self._server.target + + def get_target(self): + return self._target + + def join_server(self): + return self._server.join() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/cnn_util_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/cnn_util_test.py new file mode 100644 index 00000000..7c245afb --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/cnn_util_test.py @@ -0,0 +1,129 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 tf_cnn_benchmarks.cnn_util.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import threading +import time + +import tensorflow.compat.v1 as tf + +import cnn_util + + +class CnnUtilBarrierTest(tf.test.TestCase): + + def testBarrier(self): + num_tasks = 20 + num_waits = 4 + barrier = cnn_util.Barrier(num_tasks) + threads = [] + sync_matrix = [] + for i in range(num_tasks): + sync_times = [0] * num_waits + thread = threading.Thread( + target=self._run_task, args=(barrier, sync_times)) + thread.start() + threads.append(thread) + sync_matrix.append(sync_times) + for thread in threads: + thread.join() + for wait_index in range(num_waits - 1): + # Max of times at iteration i < min of times at iteration i + 1 + self.assertLessEqual( + max([sync_matrix[i][wait_index] for i in range(num_tasks)]), + min([sync_matrix[i][wait_index + 1] for i in range(num_tasks)])) + + def _run_task(self, barrier, sync_times): + for wait_index in range(len(sync_times)): + sync_times[wait_index] = time.time() + barrier.wait() + + def testBarrierAbort(self): + num_tasks = 2 + num_waits = 1 + sync_times = [0] * num_waits + barrier = cnn_util.Barrier(num_tasks) + thread = threading.Thread( + target=self._run_task, args=(barrier, sync_times)) + thread.start() + barrier.abort() + # thread won't be blocked by done barrier. + thread.join() + + +class ImageProducerTest(tf.test.TestCase): + + def _slow_tensorflow_op(self): + """Returns a TensorFlow op that takes approximately 0.1s to complete.""" + def slow_func(v): + time.sleep(0.1) + return v + return tf.py_func(slow_func, [tf.constant(0.)], tf.float32).op + + def _test_image_producer(self, batch_group_size, put_slower_than_get): + # We use the variable x to simulate a staging area of images. x represents + # the number of batches in the staging area. + x = tf.Variable(0, dtype=tf.int32) + if put_slower_than_get: + put_dep = self._slow_tensorflow_op() + get_dep = tf.no_op() + else: + put_dep = tf.no_op() + get_dep = self._slow_tensorflow_op() + with tf.control_dependencies([put_dep]): + put_op = x.assign_add(batch_group_size, use_locking=True) + with tf.control_dependencies([get_dep]): + get_op = x.assign_sub(1, use_locking=True) + with self.test_session() as sess: + sess.run(tf.variables_initializer([x])) + image_producer = cnn_util.ImageProducer(sess, put_op, batch_group_size, + use_python32_barrier=False) + image_producer.start() + for _ in range(5 * batch_group_size): + sess.run(get_op) + # We assert x is nonnegative, to ensure image_producer never causes + # an unstage op to block. We assert x is at most 2 * batch_group_size, + # to ensure it doesn't use too much memory by storing too many batches + # in the staging area. + self.assertGreaterEqual(sess.run(x), 0) + self.assertLessEqual(sess.run(x), 2 * batch_group_size) + image_producer.notify_image_consumption() + self.assertGreaterEqual(sess.run(x), 0) + self.assertLessEqual(sess.run(x), 2 * batch_group_size) + + image_producer.done() + time.sleep(0.1) + self.assertGreaterEqual(sess.run(x), 0) + self.assertLessEqual(sess.run(x), 2 * batch_group_size) + + def test_image_producer(self): + self._test_image_producer(1, False) + self._test_image_producer(1, True) + self._test_image_producer(2, False) + self._test_image_producer(2, True) + self._test_image_producer(3, False) + self._test_image_producer(3, True) + self._test_image_producer(8, False) + self._test_image_producer(8, True) + + +if __name__ == '__main__': + tf.disable_v2_behavior() + tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/coco_metric.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/coco_metric.py new file mode 100644 index 00000000..d8ba67da --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/coco_metric.py @@ -0,0 +1,198 @@ +# Copyright 2018 Google. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""COCO-style evaluation metrics. + +Forked from reference model implementation. + +COCO API: github.com/cocodataset/cocoapi/ +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import atexit +import tempfile + +from absl import flags + +import numpy as np +from pycocotools.coco import COCO +from pycocotools.cocoeval import COCOeval +import six + +import tensorflow.compat.v1 as tf + +import mlperf +import ssd_constants + +FLAGS = flags.FLAGS + + +# https://github.com/cocodataset/cocoapi/issues/49 +if six.PY3: + import pycocotools.coco + pycocotools.coco.unicode = str + + +def async_eval_runner(queue_predictions, queue_results, val_json_file): + """Load intermediate eval results and get COCO metrics.""" + while True: + message = queue_predictions.get() + if message == 'STOP': # poison pill + break + step, predictions = message + results = compute_map(predictions, val_json_file) + queue_results.put((step, results)) + + +def compute_map(predictions, val_json_file): + """Use model predictions to compute mAP. + + Args: + predictions: a list of tuples returned by decoded_predictions function, + each containing the following elements: + image source_id, box coordinates in XYWH order, probability score, label + val_json_file: path to COCO annotation file + Returns: + A dictionary that maps all COCO metrics (keys) to their values + """ + + if val_json_file.startswith("gs://"): + _, local_val_json = tempfile.mkstemp(suffix=".json") + tf.gfile.Remove(local_val_json) + + tf.gfile.Copy(val_json_file, local_val_json) + atexit.register(tf.gfile.Remove, local_val_json) + else: + local_val_json = val_json_file + + cocoGt = COCO(local_val_json) + cocoDt = cocoGt.loadRes(np.array(predictions)) + E = COCOeval(cocoGt, cocoDt, iouType='bbox') + E.evaluate() + E.accumulate() + E.summarize() + print("Current AP: {:.5f}".format(E.stats[0])) + metric_names = ['AP', 'AP50', 'AP75', 'APs', 'APm', 'APl', 'ARmax1', + 'ARmax10', 'ARmax100', 'ARs', 'ARm', 'ARl'] + + # Prefix with "COCO" to group in TensorBoard. + return {"COCO/" + key: value for key, value in zip(metric_names, E.stats)} + + +def calc_iou(target, candidates): + target_tiled = np.tile(target[np.newaxis, :], (candidates.shape[0], 1)) + # Left Top & Right Bottom + lt = np.maximum(target_tiled[:,:2], candidates[:,:2]) + + rb = np.minimum(target_tiled[:,2:], candidates[:,2:]) + + delta = np.maximum(rb - lt, 0) + + intersect = delta[:,0] * delta[:,1] + + delta1 = target_tiled[:,2:] - candidates[:,:2] + area1 = delta1[:,0] * delta1[:,1] + delta2 = target_tiled[:,2:] - candidates[:,:2] + area2 = delta2[:,0] * delta2[:,1] + + iou = intersect/(area1 + area2 - intersect) + return iou + + +# TODO(haoyuzhang): Rewrite this NumPy based implementation to TensorFlow based +# implementation under ssd_model.py accuracy_function. +def decode_predictions(labels_and_predictions): + """Decode predictions and remove unused boxes and labels.""" + predictions = [] + for example in labels_and_predictions: + source_id = int(example[ssd_constants.SOURCE_ID]) + pred_box = example[ssd_constants.PRED_BOXES] + pred_scores = example[ssd_constants.PRED_SCORES] + + locs, labels, probs = decode_single( + pred_box, pred_scores, ssd_constants.OVERLAP_CRITERIA, + ssd_constants.MAX_NUM_EVAL_BOXES, ssd_constants.MAX_NUM_EVAL_BOXES) + + raw_height, raw_width, _ = example[ssd_constants.RAW_SHAPE] + for loc, label, prob in zip(locs, labels, probs): + # Ordering convention differs, hence [1], [0] rather than [0], [1] + x, y = loc[1] * raw_width, loc[0] * raw_height + w, h = (loc[3] - loc[1]) * raw_width, (loc[2] - loc[0]) * raw_height + predictions.append( + [source_id, x, y, w, h, prob, ssd_constants.CLASS_INV_MAP[label]]) + mlperf.logger.log(key=mlperf.tags.NMS_THRESHOLD, + value=ssd_constants.OVERLAP_CRITERIA) + mlperf.logger.log(key=mlperf.tags.NMS_MAX_DETECTIONS, + value=ssd_constants.MAX_NUM_EVAL_BOXES) + return predictions + + +def decode_single(bboxes_in, scores_in, criteria, max_output, max_num=200): + # Reference to https://github.com/amdegroot/ssd.pytorch + + bboxes_out = [] + scores_out = [] + labels_out = [] + + for i, score in enumerate(np.split(scores_in, scores_in.shape[1], 1)): + score = np.squeeze(score, 1) + + # skip background + if i == 0: + continue + + mask = score > ssd_constants.MIN_SCORE + if not np.any(mask): + continue + + bboxes, score = bboxes_in[mask, :], score[mask] + + score_idx_sorted = np.argsort(score) + score_sorted = score[score_idx_sorted] + + score_idx_sorted = score_idx_sorted[-max_num:] + candidates = [] + + # perform non-maximum suppression + while len(score_idx_sorted): + idx = score_idx_sorted[-1] + bboxes_sorted = bboxes[score_idx_sorted, :] + bboxes_idx = bboxes[idx, :] + iou = calc_iou(bboxes_idx, bboxes_sorted) + + score_idx_sorted = score_idx_sorted[iou < criteria] + candidates.append(idx) + + bboxes_out.append(bboxes[candidates, :]) + scores_out.append(score[candidates]) + labels_out.extend([i]*len(candidates)) + + if len(scores_out) == 0: + tf.logging.info("No objects detected. Returning dummy values.") + return ( + np.zeros(shape=(1, 4), dtype=np.float32), + np.zeros(shape=(1,), dtype=np.int32), + np.ones(shape=(1,), dtype=np.float32) * ssd_constants.DUMMY_SCORE, + ) + + bboxes_out = np.concatenate(bboxes_out, axis=0) + scores_out = np.concatenate(scores_out, axis=0) + labels_out = np.array(labels_out) + + max_ids = np.argsort(scores_out)[-max_output:] + + return bboxes_out[max_ids, :], labels_out[max_ids], scores_out[max_ids] diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/constants.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/constants.py new file mode 100644 index 00000000..dbb32271 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/constants.py @@ -0,0 +1,67 @@ +# 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. +# ============================================================================== +"""Constants used in tf_cnn_benchmarks.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from enum import Enum + +# Results fetched with this prefix will not be reduced. Instead, they will be +# passed as matrices to model's postprocess function. +UNREDUCED_ACCURACY_OP_PREFIX = "tensor:" + +# Eval result values with this name prefix will be included in summary. +SIMPLE_VALUE_RESULT_PREFIX = "simple_value:" + + +class BenchmarkMode(object): + """Benchmark running mode.""" + TRAIN = "training" + EVAL = "evaluation" + TRAIN_AND_EVAL = "training + evaluation" + FORWARD_ONLY = "forward only" + + +class NetworkTopology(str, Enum): + """Network topology describes how multiple GPUs are inter-connected. + """ + # DGX-1 uses hybrid cube mesh topology with the following device peer to peer + # matrix: + # DMA: 0 1 2 3 4 5 6 7 + # 0: Y Y Y Y Y N N N + # 1: Y Y Y Y N Y N N + # 2: Y Y Y Y N N Y N + # 3: Y Y Y Y N N N Y + # 4: Y N N N Y Y Y Y + # 5: N Y N N Y Y Y Y + # 6: N N Y N Y Y Y Y + # 7: N N N Y Y Y Y Y + DGX1 = "dgx1" + + # V100 in GCP are connected with the following device peer to peer matrix. + # In this topology, bandwidth of the connection depends on if it uses NVLink + # or PCIe link. + # DMA: 0 1 2 3 4 5 6 7 + # 0: Y Y Y Y N Y N N + # 1: Y Y Y Y N N N N + # 2: Y Y Y Y N N N Y + # 3: Y Y Y Y N N N N + # 4: N N N N Y Y Y Y + # 5: Y N N N Y Y Y Y + # 6: N N N N Y Y Y Y + # 7: N N Y N Y Y Y Y + GCP_V100 = "gcp_v100" diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/convnet_builder.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/convnet_builder.py new file mode 100644 index 00000000..9903de92 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/convnet_builder.py @@ -0,0 +1,498 @@ +# 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. +# ============================================================================== +"""CNN builder.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from collections import defaultdict +import contextlib + +import numpy as np + +import tensorflow.compat.v1 as tf + +# pylint: disable=g-direct-tensorflow-import +import mlperf +from tensorflow.python.layers import convolutional as conv_layers +from tensorflow.python.layers import core as core_layers +from tensorflow.python.layers import normalization as normalization_layers +from tensorflow.python.layers import pooling as pooling_layers +from tensorflow.python.training import moving_averages + + +_data_format_to_channel_axis = {'NCHW': 1, 'NHWC': 3} + + +class ConvNetBuilder(object): + """Builder of cnn net.""" + + def __init__(self, + input_op, + input_nchan, + phase_train, + use_tf_layers, + data_format='NCHW', + dtype=tf.float32, + variable_dtype=tf.float32): + self.top_layer = input_op + self.top_size = input_nchan + self.phase_train = phase_train + self.use_tf_layers = use_tf_layers + self.data_format = data_format + self.dtype = dtype + self.variable_dtype = variable_dtype + self.counts = defaultdict(lambda: 0) + self.use_batch_norm = False + self.batch_norm_config = {} # 'decay': 0.997, 'scale': True} + self.channel_pos = ('channels_last' + if data_format == 'NHWC' else 'channels_first') + self.aux_top_layer = None + self.aux_top_size = 0 + + def get_custom_getter(self): + """Returns a custom getter that this class's methods must be called under. + + All methods of this class must be called under a variable scope that was + passed this custom getter. Example: + + ```python + network = ConvNetBuilder(...) + with tf.variable_scope('cg', custom_getter=network.get_custom_getter()): + network.conv(...) + # Call more methods of network here + ``` + + Currently, this custom getter only does anything if self.use_tf_layers is + True. In that case, it causes variables to be stored as dtype + self.variable_type, then casted to the requested dtype, instead of directly + storing the variable as the requested dtype. + """ + def inner_custom_getter(getter, *args, **kwargs): + """Custom getter that forces variables to have type self.variable_type.""" + if not self.use_tf_layers: + return getter(*args, **kwargs) + requested_dtype = kwargs['dtype'] + if not (requested_dtype == tf.float32 and + self.variable_dtype == tf.float16): + # Only change the variable dtype if doing so does not decrease variable + # precision. + kwargs['dtype'] = self.variable_dtype + var = getter(*args, **kwargs) + # This if statement is needed to guard the cast, because batch norm + # assigns directly to the return value of this custom getter. The cast + # makes the return value not a variable so it cannot be assigned. Batch + # norm variables are always in fp32 so this if statement is never + # triggered for them. + if var.dtype.base_dtype != requested_dtype: + var = tf.cast(var, requested_dtype) + return var + return inner_custom_getter + + @contextlib.contextmanager + def switch_to_aux_top_layer(self): + """Context that construct cnn in the auxiliary arm.""" + if self.aux_top_layer is None: + raise RuntimeError('Empty auxiliary top layer in the network.') + saved_top_layer = self.top_layer + saved_top_size = self.top_size + self.top_layer = self.aux_top_layer + self.top_size = self.aux_top_size + yield + self.aux_top_layer = self.top_layer + self.aux_top_size = self.top_size + self.top_layer = saved_top_layer + self.top_size = saved_top_size + + def get_variable(self, name, shape, dtype, cast_dtype, *args, **kwargs): + # TODO(reedwm): Currently variables and gradients are transferred to other + # devices and machines as type `dtype`, not `cast_dtype`. In particular, + # this means in fp16 mode, variables are transferred as fp32 values, not + # fp16 values, which uses extra bandwidth. + var = tf.get_variable(name, shape, dtype, *args, **kwargs) + return tf.cast(var, cast_dtype) + + def _conv2d_impl(self, input_layer, num_channels_in, filters, kernel_size, + strides, padding, kernel_initializer): + if self.use_tf_layers: + return conv_layers.conv2d(input_layer, filters, kernel_size, strides, + padding, self.channel_pos, + kernel_initializer=kernel_initializer, + use_bias=False) + else: + weights_shape = [kernel_size[0], kernel_size[1], num_channels_in, filters] + # We use the name 'conv2d/kernel' so the variable has the same name as its + # tf.layers equivalent. This way, if a checkpoint is written when + # self.use_tf_layers == True, it can be loaded when + # self.use_tf_layers == False, and vice versa. + weights = self.get_variable('conv2d/kernel', weights_shape, + self.variable_dtype, self.dtype, + initializer=kernel_initializer) + if self.data_format == 'NHWC': + strides = [1] + strides + [1] + else: + strides = [1, 1] + strides + return tf.nn.conv2d(input_layer, weights, strides, padding, + data_format=self.data_format) + + def conv(self, + num_out_channels, + k_height, + k_width, + d_height=1, + d_width=1, + mode='SAME', + input_layer=None, + num_channels_in=None, + use_batch_norm=None, + stddev=None, + activation='relu', + bias=0.0, + kernel_initializer=None): + """Construct a conv2d layer on top of cnn.""" + if input_layer is None: + input_layer = self.top_layer + if num_channels_in is None: + num_channels_in = self.top_size + if stddev is not None and kernel_initializer is None: + kernel_initializer = tf.truncated_normal_initializer(stddev=stddev) + if kernel_initializer is None: + kernel_initializer = tf.variance_scaling_initializer() + name = 'conv' + str(self.counts['conv']) + self.counts['conv'] += 1 + with tf.variable_scope(name): + strides = [1, d_height, d_width, 1] + if self.data_format == 'NCHW': + strides = [strides[0], strides[3], strides[1], strides[2]] + if mode != 'SAME_RESNET': + conv = self._conv2d_impl(input_layer, num_channels_in, num_out_channels, + kernel_size=[k_height, k_width], + strides=[d_height, d_width], padding=mode, + kernel_initializer=kernel_initializer) + else: # Special padding mode for ResNet models + if d_height == 1 and d_width == 1: + conv = self._conv2d_impl(input_layer, num_channels_in, + num_out_channels, + kernel_size=[k_height, k_width], + strides=[d_height, d_width], padding='SAME', + kernel_initializer=kernel_initializer) + else: + rate = 1 # Unused (for 'a trous' convolutions) + kernel_height_effective = k_height + (k_height - 1) * (rate - 1) + pad_h_beg = (kernel_height_effective - 1) // 2 + pad_h_end = kernel_height_effective - 1 - pad_h_beg + kernel_width_effective = k_width + (k_width - 1) * (rate - 1) + pad_w_beg = (kernel_width_effective - 1) // 2 + pad_w_end = kernel_width_effective - 1 - pad_w_beg + padding = [[0, 0], [pad_h_beg, pad_h_end], + [pad_w_beg, pad_w_end], [0, 0]] + if self.data_format == 'NCHW': + padding = [padding[0], padding[3], padding[1], padding[2]] + padded_input_layer = tf.pad(input_layer, padding) + conv = self._conv2d_impl(padded_input_layer, num_channels_in, + num_out_channels, + kernel_size=[k_height, k_width], + strides=[d_height, d_width], padding='VALID', + kernel_initializer=kernel_initializer) + if use_batch_norm is None: + use_batch_norm = self.use_batch_norm + mlperf.logger.log_conv2d(input_tensor=input_layer, output_tensor=conv, + stride_height=d_height, stride_width=d_width, + filters=num_out_channels, + initializer=kernel_initializer, + use_bias=not use_batch_norm and bias is not None) + if not use_batch_norm: + if bias is not None: + biases = self.get_variable('biases', [num_out_channels], + self.variable_dtype, self.dtype, + initializer=tf.constant_initializer(bias)) + biased = tf.reshape( + tf.nn.bias_add(conv, biases, data_format=self.data_format), + conv.get_shape()) + else: + biased = conv + else: + self.top_layer = conv + self.top_size = num_out_channels + biased = self.batch_norm(**self.batch_norm_config) + if activation == 'relu': + mlperf.logger.log(key=mlperf.tags.MODEL_HP_RELU) + conv1 = tf.nn.relu(biased) + elif activation == 'linear' or activation is None: + conv1 = biased + elif activation == 'tanh': + conv1 = tf.nn.tanh(biased) + else: + raise KeyError('Invalid activation type \'%s\'' % activation) + self.top_layer = conv1 + self.top_size = num_out_channels + return conv1 + + def _pool(self, + pool_name, + pool_function, + k_height, + k_width, + d_height, + d_width, + mode, + input_layer, + num_channels_in): + """Construct a pooling layer.""" + if input_layer is None: + input_layer = self.top_layer + else: + self.top_size = num_channels_in + name = pool_name + str(self.counts[pool_name]) + self.counts[pool_name] += 1 + if self.use_tf_layers: + pool = pool_function( + input_layer, [k_height, k_width], [d_height, d_width], + padding=mode, + data_format=self.channel_pos, + name=name) + else: + if self.data_format == 'NHWC': + ksize = [1, k_height, k_width, 1] + strides = [1, d_height, d_width, 1] + else: + ksize = [1, 1, k_height, k_width] + strides = [1, 1, d_height, d_width] + pool = tf.nn.max_pool(input_layer, ksize, strides, padding=mode, + data_format=self.data_format, name=name) + if pool_name == 'mpool': + mlperf.logger.log_max_pool(input_tensor=input_layer, + output_tensor=pool) + self.top_layer = pool + return pool + + def mpool(self, + k_height, + k_width, + d_height=2, + d_width=2, + mode='VALID', + input_layer=None, + num_channels_in=None): + """Construct a max pooling layer.""" + return self._pool('mpool', pooling_layers.max_pooling2d, k_height, k_width, + d_height, d_width, mode, input_layer, num_channels_in) + + def apool(self, + k_height, + k_width, + d_height=2, + d_width=2, + mode='VALID', + input_layer=None, + num_channels_in=None): + """Construct an average pooling layer.""" + return self._pool('apool', pooling_layers.average_pooling2d, k_height, + k_width, d_height, d_width, mode, input_layer, + num_channels_in) + + def reshape(self, shape, input_layer=None): + if input_layer is None: + input_layer = self.top_layer + self.top_layer = tf.reshape(input_layer, shape) + self.top_size = shape[-1] # HACK This may not always work + return self.top_layer + + def affine(self, + num_out_channels, + input_layer=None, + num_channels_in=None, + bias=0.0, + stddev=None, + activation='relu'): + if input_layer is None: + input_layer = self.top_layer + if num_channels_in is None: + num_channels_in = self.top_size + name = 'affine' + str(self.counts['affine']) + self.counts['affine'] += 1 + with tf.variable_scope(name): + init_factor = 2. if activation == 'relu' else 1. + stddev = stddev or np.sqrt(init_factor / num_channels_in) + kernel = self.get_variable( + 'weights', [num_channels_in, num_out_channels], + self.variable_dtype, self.dtype, + initializer=tf.truncated_normal_initializer(stddev=stddev)) + biases = self.get_variable('biases', [num_out_channels], + self.variable_dtype, self.dtype, + initializer=tf.constant_initializer(bias)) + mlperf.logger.log(key=mlperf.tags.MODEL_HP_DENSE, + value=num_out_channels) + logits = tf.nn.xw_plus_b(input_layer, kernel, biases) + if activation == 'relu': + mlperf.logger.log(key=mlperf.tags.MODEL_HP_RELU) + affine1 = tf.nn.relu(logits, name=name) + elif activation == 'linear' or activation is None: + affine1 = logits + else: + raise KeyError('Invalid activation type \'%s\'' % activation) + self.top_layer = affine1 + self.top_size = num_out_channels + return affine1 + + def inception_module(self, name, cols, input_layer=None, in_size=None): + if input_layer is None: + input_layer = self.top_layer + if in_size is None: + in_size = self.top_size + name += str(self.counts[name]) + self.counts[name] += 1 + with tf.variable_scope(name): + col_layers = [] + col_layer_sizes = [] + for c, col in enumerate(cols): + col_layers.append([]) + col_layer_sizes.append([]) + for l, layer in enumerate(col): + ltype, args = layer[0], layer[1:] + kwargs = { + 'input_layer': input_layer, + 'num_channels_in': in_size + } if l == 0 else {} + if ltype == 'conv': + self.conv(*args, **kwargs) + elif ltype == 'mpool': + self.mpool(*args, **kwargs) + elif ltype == 'apool': + self.apool(*args, **kwargs) + elif ltype == 'share': # Share matching layer from previous column + self.top_layer = col_layers[c - 1][l] + self.top_size = col_layer_sizes[c - 1][l] + else: + raise KeyError( + 'Invalid layer type for inception module: \'%s\'' % ltype) + col_layers[c].append(self.top_layer) + col_layer_sizes[c].append(self.top_size) + catdim = 3 if self.data_format == 'NHWC' else 1 + self.top_layer = tf.concat([layers[-1] for layers in col_layers], catdim) + self.top_size = sum([sizes[-1] for sizes in col_layer_sizes]) + return self.top_layer + + def spatial_mean(self, keep_dims=False): + name = 'spatial_mean' + str(self.counts['spatial_mean']) + self.counts['spatial_mean'] += 1 + axes = [1, 2] if self.data_format == 'NHWC' else [2, 3] + self.top_layer = tf.reduce_mean( + self.top_layer, axes, keepdims=keep_dims, name=name) + return self.top_layer + + def dropout(self, keep_prob=0.5, input_layer=None): + if input_layer is None: + input_layer = self.top_layer + else: + self.top_size = None + name = 'dropout' + str(self.counts['dropout']) + with tf.variable_scope(name): + if not self.phase_train: + keep_prob = 1.0 + if self.use_tf_layers: + dropout = core_layers.dropout(input_layer, 1. - keep_prob, + training=self.phase_train) + else: + dropout = tf.nn.dropout(input_layer, keep_prob) + self.top_layer = dropout + return dropout + + def _batch_norm_without_layers(self, input_layer, decay, use_scale, epsilon): + """Batch normalization on `input_layer` without tf.layers.""" + # We make this function as similar as possible to the + # tf.contrib.layers.batch_norm, to minimize the differences between using + # layers and not using layers. + shape = input_layer.shape + num_channels = shape[3] if self.data_format == 'NHWC' else shape[1] + beta = self.get_variable('beta', [num_channels], tf.float32, tf.float32, + initializer=tf.zeros_initializer()) + if use_scale: + gamma = self.get_variable('gamma', [num_channels], tf.float32, + tf.float32, initializer=tf.ones_initializer()) + else: + gamma = tf.constant(1.0, tf.float32, [num_channels]) + # For moving variables, we use tf.get_variable instead of self.get_variable, + # since self.get_variable returns the result of tf.cast which we cannot + # assign to. + moving_mean = tf.get_variable('moving_mean', [num_channels], + tf.float32, + initializer=tf.zeros_initializer(), + trainable=False) + moving_variance = tf.get_variable('moving_variance', [num_channels], + tf.float32, + initializer=tf.ones_initializer(), + trainable=False) + if self.phase_train: + bn, batch_mean, batch_variance = tf.nn.fused_batch_norm( + input_layer, gamma, beta, epsilon=epsilon, + data_format=self.data_format, is_training=True) + mean_update = moving_averages.assign_moving_average( + moving_mean, batch_mean, decay=decay, zero_debias=False) + variance_update = moving_averages.assign_moving_average( + moving_variance, batch_variance, decay=decay, zero_debias=False) + tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, mean_update) + tf.add_to_collection(tf.GraphKeys.UPDATE_OPS, variance_update) + else: + bn, _, _ = tf.nn.fused_batch_norm( + input_layer, gamma, beta, mean=moving_mean, + variance=moving_variance, epsilon=epsilon, + data_format=self.data_format, is_training=False) + return bn + + def batch_norm(self, input_layer=None, decay=0.999, scale=False, + epsilon=0.001): + """Adds a Batch Normalization layer.""" + if input_layer is None: + input_layer = self.top_layer + else: + self.top_size = None + name = 'batchnorm' + str(self.counts['batchnorm']) + self.counts['batchnorm'] += 1 + + center = True + with tf.variable_scope(name) as scope: + if self.use_tf_layers: + layer_obj = normalization_layers.BatchNormalization( + momentum=decay, + scale=scale, + epsilon=epsilon, + fused=True, + axis=_data_format_to_channel_axis[self.data_format], + # We pass this 'scope' argument for compatibility with checkpoints + # created with the contrib version of batch norm. tf_cnn_benchmarks + # used to use the contrib version. + _scope=scope, + center=center, + name=scope.name) + bn = layer_obj.apply(input_layer, training=self.phase_train) + else: + bn = self._batch_norm_without_layers(input_layer, decay, scale, epsilon) + self.top_layer = bn + self.top_size = bn.shape[3] if self.data_format == 'NHWC' else bn.shape[1] + self.top_size = int(self.top_size) + mlperf.logger.log_batch_norm( + input_tensor=input_layer, output_tensor=bn, momentum=decay, + epsilon=epsilon, center=center, scale=scale, training=self.phase_train) + return bn + + def lrn(self, depth_radius, bias, alpha, beta): + """Adds a local response normalization layer.""" + name = 'lrn' + str(self.counts['lrn']) + self.counts['lrn'] += 1 + self.top_layer = tf.nn.lrn( + self.top_layer, depth_radius, bias, alpha, beta, name=name) + return self.top_layer diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/datasets.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/datasets.py new file mode 100644 index 00000000..875e1123 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/datasets.py @@ -0,0 +1,251 @@ +# 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. +# ============================================================================== +"""Benchmark dataset utilities. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from abc import abstractmethod +import os + +import numpy as np +import six +from six.moves import cPickle +from six.moves import xrange # pylint: disable=redefined-builtin +import tensorflow.compat.v1 as tf + +from tensorflow.python.platform import gfile +import preprocessing + +IMAGENET_NUM_TRAIN_IMAGES = 1281167 +IMAGENET_NUM_VAL_IMAGES = 50000 + +COCO_NUM_TRAIN_IMAGES = 118287 +COCO_NUM_VAL_IMAGES = 4952 + + +class Dataset(object): + """Abstract class for cnn benchmarks dataset.""" + + def __init__(self, + name, + data_dir=None, + queue_runner_required=False, + num_classes=None): + self.name = name + self.data_dir = data_dir + self._queue_runner_required = queue_runner_required + self._num_classes = num_classes + + def tf_record_pattern(self, subset): + return os.path.join(self.data_dir, '%s-*-of-*' % subset) + + def reader(self): + return tf.TFRecordReader() + + @property + def num_classes(self): + return self._num_classes + + @num_classes.setter + def num_classes(self, val): + self._num_classes = val + + @abstractmethod + def num_examples_per_epoch(self, subset): + pass + + def __str__(self): + return self.name + + def get_input_preprocessor(self, input_preprocessor='default'): + assert not self.use_synthetic_gpu_inputs() + return _SUPPORTED_INPUT_PREPROCESSORS[self.name][input_preprocessor] + + def queue_runner_required(self): + return self._queue_runner_required + + def use_synthetic_gpu_inputs(self): + return not self.data_dir + + +class LibrispeechDataset(Dataset): + """Configuration for LibriSpeech dataset.""" + + def __init__(self, data_dir=None): + super(LibrispeechDataset, self).__init__( + 'librispeech', data_dir, num_classes=29) + + def tf_record_pattern(self, subset): + if subset == 'train': + return os.path.join(self.data_dir, 'train-clean-*.tfrecords') + elif subset == 'validation': + return os.path.join(self.data_dir, 'test-clean.tfrecords') + else: + return '' + + def num_examples_per_epoch(self, subset='train'): + del subset + return 2 # TODO(laigd): currently this is an arbitrary number. + + +class ImageDataset(Dataset): + """Abstract class for image datasets.""" + + def __init__(self, + name, + height, + width, + depth=None, + data_dir=None, + queue_runner_required=False, + num_classes=1001): + super(ImageDataset, self).__init__(name, data_dir, queue_runner_required, + num_classes) + self.height = height + self.width = width + self.depth = depth or 3 + + +class ImagenetDataset(ImageDataset): + """Configuration for Imagenet dataset.""" + + def __init__(self, data_dir=None): + super(ImagenetDataset, self).__init__( + 'imagenet', 300, 300, data_dir=data_dir) + + def num_examples_per_epoch(self, subset='train'): + if subset == 'train': + return IMAGENET_NUM_TRAIN_IMAGES + elif subset == 'validation': + return IMAGENET_NUM_VAL_IMAGES + else: + raise ValueError('Invalid data subset "%s"' % subset) + + +class Cifar10Dataset(ImageDataset): + """Configuration for cifar 10 dataset. + + It will mount all the input images to memory. + """ + + def __init__(self, data_dir=None): + super(Cifar10Dataset, self).__init__( + 'cifar10', + 32, + 32, + data_dir=data_dir, + queue_runner_required=True, + num_classes=11) + + def read_data_files(self, subset='train'): + """Reads from data file and returns images and labels in a numpy array.""" + assert self.data_dir, ('Cannot call `read_data_files` when using synthetic ' + 'data') + if subset == 'train': + filenames = [ + os.path.join(self.data_dir, 'data_batch_%d' % i) + for i in xrange(1, 6) + ] + elif subset == 'validation': + filenames = [os.path.join(self.data_dir, 'test_batch')] + else: + raise ValueError('Invalid data subset "%s"' % subset) + + inputs = [] + for filename in filenames: + with gfile.Open(filename, 'rb') as f: + # python2 does not have the encoding parameter + encoding = {} if six.PY2 else {'encoding': 'bytes'} + inputs.append(cPickle.load(f, **encoding)) + # See http://www.cs.toronto.edu/~kriz/cifar.html for a description of the + # input format. + all_images = np.concatenate( + [each_input[b'data'] for each_input in inputs]).astype(np.float32) + all_labels = np.concatenate( + [each_input[b'labels'] for each_input in inputs]) + return all_images, all_labels + + def num_examples_per_epoch(self, subset='train'): + if subset == 'train': + return 50000 + elif subset == 'validation': + return 10000 + else: + raise ValueError('Invalid data subset "%s"' % subset) + + +class COCODataset(ImageDataset): + """COnfiguration for COCO dataset.""" + + def __init__(self, data_dir=None, image_size=300): + super(COCODataset, self).__init__( + 'coco', image_size, image_size, data_dir=data_dir, num_classes=81) + + def num_examples_per_epoch(self, subset='train'): + if subset == 'train': + return COCO_NUM_TRAIN_IMAGES + elif subset == 'validation': + return COCO_NUM_VAL_IMAGES + else: + raise ValueError('Invalid data subset "%s"' % subset) + + +_SUPPORTED_DATASETS = { + 'imagenet': ImagenetDataset, + 'cifar10': Cifar10Dataset, + 'librispeech': LibrispeechDataset, + 'coco': COCODataset, +} + +_SUPPORTED_INPUT_PREPROCESSORS = { + 'imagenet': { + 'default': preprocessing.RecordInputImagePreprocessor, + 'official_models_imagenet': preprocessing.ImagenetPreprocessor, + }, + 'cifar10': { + 'default': preprocessing.Cifar10ImagePreprocessor + }, + 'librispeech': { + 'default': preprocessing.LibrispeechPreprocessor + }, + 'coco': { + 'default': preprocessing.COCOPreprocessor + }, +} + + +def create_dataset(data_dir, data_name): + """Create a Dataset instance based on data_dir and data_name.""" + if not data_dir and not data_name: + # When using synthetic data, use synthetic imagenet images by default. + data_name = 'imagenet' + + # Infere dataset name from data_dir if data_name is not provided. + if data_name is None: + for supported_name in _SUPPORTED_DATASETS: + if supported_name in data_dir: + data_name = supported_name + break + else: # Failed to identify dataset name from data dir. + raise ValueError('Could not identify name of dataset. ' + 'Please specify with --data_name option.') + if data_name not in _SUPPORTED_DATASETS: + raise ValueError('Unknown dataset. Must be one of %s' % ', '.join( + [key for key in sorted(_SUPPORTED_DATASETS.keys())])) + + return _SUPPORTED_DATASETS[data_name](data_dir) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/flags.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/flags.py new file mode 100644 index 00000000..f65898ae --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/flags.py @@ -0,0 +1,93 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Contains functions to define flags and params. + +Calling a DEFINE_* function will add a ParamSpec namedtuple to the param_spec +dict. The DEFINE_* arguments match those in absl. Calling define_flags() creates +a command-line flag for every ParamSpec defined by a DEFINE_* functions. + +The reason we don't use absl flags directly is that we want to be able to use +tf_cnn_benchmarks as a library. When using it as a library, we don't want to +define any flags, but instead pass parameters to the BenchmarkCNN constructor. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from collections import namedtuple + +from absl import flags as absl_flags +import six + + +FLAGS = absl_flags.FLAGS + + +# ParamSpec describes one of benchmark_cnn.BenchmarkCNN's parameters. +ParamSpec = namedtuple('_ParamSpec', + ['flag_type', 'default_value', 'description', + 'kwargs']) + + +# Maps from parameter name to its ParamSpec. +param_specs = {} + + +def DEFINE_string(name, default, help): # pylint: disable=invalid-name,redefined-builtin + param_specs[name] = ParamSpec('string', default, help, {}) + + +def DEFINE_boolean(name, default, help): # pylint: disable=invalid-name,redefined-builtin + param_specs[name] = ParamSpec('boolean', default, help, {}) + + +def DEFINE_integer(name, default, help, lower_bound=None, upper_bound=None): # pylint: disable=invalid-name,redefined-builtin + kwargs = {'lower_bound': lower_bound, 'upper_bound': upper_bound} + param_specs[name] = ParamSpec('integer', default, help, kwargs) + + +def DEFINE_float(name, default, help, lower_bound=None, upper_bound=None): # pylint: disable=invalid-name,redefined-builtin + kwargs = {'lower_bound': lower_bound, 'upper_bound': upper_bound} + param_specs[name] = ParamSpec('float', default, help, kwargs) + + +def DEFINE_enum(name, default, enum_values, help): # pylint: disable=invalid-name,redefined-builtin + kwargs = {'enum_values': enum_values} + param_specs[name] = ParamSpec('enum', default, help, kwargs) + + +def DEFINE_list(name, default, help): # pylint: disable=invalid-name,redefined-builtin + param_specs[name] = ParamSpec('list', default, help, {}) + + +def define_flags(specs=None): + """Define a command line flag for each ParamSpec in flags.param_specs.""" + specs = specs or param_specs + define_flag = { + 'boolean': absl_flags.DEFINE_boolean, + 'float': absl_flags.DEFINE_float, + 'integer': absl_flags.DEFINE_integer, + 'string': absl_flags.DEFINE_string, + 'enum': absl_flags.DEFINE_enum, + 'list': absl_flags.DEFINE_list + } + for name, param_spec in six.iteritems(specs): + if param_spec.flag_type not in define_flag: + raise ValueError('Unknown flag_type %s' % param_spec.flag_type) + else: + define_flag[param_spec.flag_type](name, param_spec.default_value, + help=param_spec.description, + **param_spec.kwargs) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/leading_indicators_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/leading_indicators_test.py new file mode 100644 index 00000000..1bd87152 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/leading_indicators_test.py @@ -0,0 +1,1003 @@ +# 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. +# ============================================================================== +"""Benchmark various leading indicators CNNs. + +The purpose of these tests is to test each model as a high level baseline and +to ensure the various variable_update options have not regressing. Not all +options are tested. The tests focus on the most viable options. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import ctypes +import logging +import os +import sys + +from absl import flags +from absl.testing import absltest # pylint: disable=unused-import +import tensorflow.compat.v1 as tf # pylint: disable=g-bad-import-order +import benchmark_cnn +from platforms import util as platforms_util + +flags.DEFINE_integer('num_batches', None, + 'number of batches to run, excluding warmup') + + +class BenchmarkBase(tf.test.Benchmark): + """Base class for all benchmarks in this file.""" + + def __init__(self, output_dir=None, root_data_dir=None, **kwargs): + """Base class for all benchmarks in this file. + + Args: + output_dir: directory where to output e.g. log files + root_data_dir: directory under which to look for dataset + **kwargs: arbitrary named arguments. This is needed to make the + constructor forward compatible in case PerfZero provides more + named arguments before updating the constructor. + """ + + # Load default values if the benchmark is not run with absl.app.run() + if not flags.FLAGS.is_parsed(): + flags.FLAGS.mark_as_parsed() + + self.fake_data_dir = os.path.join(platforms_util.get_test_data_dir(), + 'fake_tf_record_data') + self.output_dir = output_dir + if root_data_dir is None: + self.data_dir = ('/readahead/200M/placer/prod/home/distbelief/' + 'imagenet-tensorflow/imagenet-2012-tfrecord') + else: + self.data_dir = os.path.join(root_data_dir, 'imagenet') + + def _run_benchmark(self, params): + """Run a CNN benchmark and report its results. + + Args: + params: Params tuple, typically created by benchmark_cnn.make_params or + benchmark_cnn.make_params_from_flags. + """ + logging.info('Running benchmark [%s]', self._get_name()) + params = benchmark_cnn.setup(params) + bench = benchmark_cnn.BenchmarkCNN(params) + bench.print_info() + stats = bench.run() + extras = {} + extras['examples_per_sec'] = stats.get('images_per_sec') + if 'last_average_loss' in stats: + extras['last_average_loss'] = stats['last_average_loss'] + if 'top_1_accuracy' in stats: + extras['top_1_accuracy'] = stats['top_1_accuracy'] + if 'top_5_accuracy' in stats: + extras['top_5_accuracy'] = stats['top_5_accuracy'] + self.report_benchmark( + iters=stats.get('num_steps'), + wall_time=stats.get('average_wall_time'), + extras=extras) + + def _shared_params(self): + """Returns shared parameters for all benchmarks in this file.""" + params = {} + if flags.FLAGS.num_batches is not None: + params['num_batches'] = flags.FLAGS.num_batches + if self.output_dir is not None: + params['benchmark_log_dir'] = self.output_dir + return benchmark_cnn.make_params(**params) + + def _binary_search_batch_size(self, params, init_batch_size): + """Find the max batch_size using binary search.""" + assert init_batch_size > 0 + low_batch_size = 0 + high_batch_size = None + batch_size = init_batch_size + + # No need to run a warmup or many batches; if it doesn't OOM after 10 + # batches, it should work in general. + params = params._replace(num_batches=10, num_warmup_batches=0) + + # Find high_batch_size first. + tf.logging.info( + 'Looking for upper bound to batch size, starting with %d' % batch_size) + while high_batch_size is None: + tf.logging.info('Trying batch_size %d' % batch_size) + params = params._replace(batch_size=batch_size) + bench = benchmark_cnn.BenchmarkCNN(params) + bench.print_info() + try: + bench.run() + low_batch_size = batch_size + batch_size *= 2 + except tf.errors.ResourceExhaustedError: + high_batch_size = batch_size - 1 + + # Binary Search + tf.logging.info( + 'Max batch size is in range (%d, %d]. Starting binary search to find ' + 'exact max batch size.' % (low_batch_size, batch_size)) + while low_batch_size < high_batch_size: + batch_size = (low_batch_size + high_batch_size + 1) // 2 + tf.logging.info('Trying batch_size %d' % batch_size) + params = params._replace(batch_size=batch_size) + bench = benchmark_cnn.BenchmarkCNN(params) + bench.print_info() + try: + bench.run() + low_batch_size = batch_size + except tf.errors.ResourceExhaustedError: + high_batch_size = batch_size - 1 + self.report_benchmark(extras={'max_batch_size': low_batch_size}) + + +class Resnet50BenchmarksInferenceCpu(BenchmarkBase): + """"Benchmarks for ResNet50 inference on CPU.""" + + def _shared_params(self): + """Returns shared parameters for all ResNet50 benchmarks.""" + return BenchmarkBase._shared_params(self)._replace( + num_gpus=1, + model='resnet50', + num_warmup_batches=5, + num_batches=50, + distortions=False, + forward_only=True, + device='cpu', + data_format='NHWC', + num_intra_threads=0) + + def benchmark_synth_forward_batch1(self): + """Tests 1 CPU batch size 1.""" + params = self._shared_params()._replace(batch_size=1) + self._run_benchmark(params) + + def benchmark_synth_forward_batch16(self): + """Tests 1 CPU batch size 16.""" + params = self._shared_params()._replace(batch_size=16) + self._run_benchmark(params) + + +class FrozenResnet50BenchmarksInferenceCpu(Resnet50BenchmarksInferenceCpu): + """"Benchmarks for ResNet50 frozen graph inference on CPU.""" + + def _shared_params(self): + return super(FrozenResnet50BenchmarksInferenceCpu, + self)._shared_params()._replace(freeze_when_forward_only=True) + + +class Resnet50BenchmarksInference(BenchmarkBase): + """"Benchmarks for ResNet50 inference.""" + + def _shared_params(self): + """Returns shared parameters for all ResNet50 benchmarks.""" + return BenchmarkBase._shared_params(self)._replace( + num_gpus=1, model='resnet50', distortions=False, forward_only=True) + + def benchmark_synth_forward_batch128(self): + """Tests 1 GPU batch size 128.""" + params = self._shared_params()._replace(batch_size=128) + self._run_benchmark(params) + + def benchmark_fp16_synth_forward_batch128(self): + """Tests 1 GPU batch size 128 FP16.""" + params = self._shared_params()._replace(batch_size=128, use_fp16=True) + self._run_benchmark(params) + + def benchmark_fp16_synth_forward_batch16(self): + """Tests 1 GPU batch size 16 FP16.""" + params = self._shared_params()._replace(batch_size=16, use_fp16=True) + self._run_benchmark(params) + + def benchmark_xla_synth_forward_batch128(self): + """Tests 1 GPU batch size 128 with XLA.""" + params = self._shared_params()._replace(batch_size=128, xla=True) + self._run_benchmark(params) + + def benchmark_fp16_xla_synth_forward_batch128(self): + """Tests 1 GPU batch size 128 FP16 with XLA.""" + params = self._shared_params()._replace( + batch_size=128, use_fp16=True, xla=True) + self._run_benchmark(params) + + def benchmark_fp16_xla_synth_forward_batch16(self): + """Tests 1 GPU batch size 16 FP16 with XLA.""" + params = self._shared_params()._replace( + batch_size=16, use_fp16=True, xla=True) + self._run_benchmark(params) + + +class FrozenResnet50BenchmarksInference(Resnet50BenchmarksInference): + """"Benchmarks for ResNet50 frozen graph inference.""" + + def _shared_params(self): + return super(FrozenResnet50BenchmarksInference, + self)._shared_params()._replace(freeze_when_forward_only=True) + + def benchmark_trt_synth_forward_batch128(self): + """Tests 1 GPU batch size 128.""" + params = self._shared_params()._replace(batch_size=128, trt_mode='FP32') + self._run_benchmark(params) + + # TODO(laigd): enable fp16 tests for TF-TRT, it's currently not supported yet. + # def benchmark_fp16_trt_synth_forward_batch128(self): + # """Tests 1 GPU batch size 128 FP16.""" + # params = self._shared_params()._replace( + # batch_size=128, use_fp16=True, trt_mode='FP16') + # self._run_benchmark(params) + + # Test with batch size 16 to compare with native TF GPU implementation and + # XLA. + # def benchmark_fp16_trt_synth_forward_batch16(self): + # """Tests 1 GPU batch size 16 FP16.""" + # params = self._shared_params()._replace( + # batch_size=16, use_fp16=True, trt_mode='FP16') + # self._run_benchmark(params) + + +class Resnet50Benchmarks(BenchmarkBase): + """"Benchmark resnet50 configurations.""" + + def _shared_params(self): + """Returns shared parameters for all ResNet50 benchmarks.""" + return BenchmarkBase._shared_params(self)._replace( + model='resnet50', batch_size=128, distortions=False, + optimizer='momentum') + + def _shared_params_fp16(self): + """Returns shared parameters for all ResNet50 FP16 benchmarks.""" + return BenchmarkBase._shared_params(self)._replace( + model='resnet50', + batch_size=256, + distortions=False, + use_fp16=True, + optimizer='momentum', + loss_type_to_report='base_loss', + compute_lr_on_cpu=True, + single_l2_loss_op=True + ) + + def benchmark_synth_1gpu_gpuparams(self): + """Tests 1 gpu with synthetic data.""" + params = self._shared_params()._replace(num_gpus=1) + self._run_benchmark(params) + + def benchmark_fake_1gpu_gpuparams(self): + """Tests 1 gpu with fake data.""" + params = self._shared_params()._replace( + num_gpus=1, data_dir=self.fake_data_dir, data_name='imagenet') + self._run_benchmark(params) + + def benchmark_synth_1gpu_max_batch_size(self): + """Finds largest batch size that can be run with 1 gpu using synth data.""" + params = self._shared_params()._replace( + num_gpus=1, variable_update='parameter_server') + self._binary_search_batch_size(params, init_batch_size=128) + + def benchmark_synth_4gpu_gpureplicated(self): + """Tests 4 gpu with synthetic data with parameters replicated.""" + params = self._shared_params()._replace( + num_gpus=4, + variable_update='replicated', + all_reduce_spec='nccl', + gradient_repacking=2) + self._run_benchmark(params) + + def benchmark_synth_8gpu_gpureplicated(self): + """Tests 8 gpu with synthetic data with parameters replicated.""" + params = self._shared_params()._replace( + num_gpus=8, + variable_update='replicated', + all_reduce_spec='nccl', + gradient_repacking=2) + self._run_benchmark(params) + + def benchmark_fake_8gpu_gpureplicated(self): + """Tests 8 gpu with fake data with parameters replicated.""" + params = self._shared_params()._replace( + num_gpus=8, + data_dir=self.fake_data_dir, + data_name='imagenet', + variable_update='replicated', + all_reduce_spec='nccl', + gradient_repacking=2) + self._run_benchmark(params) + + # FP16 mixed-precision tests. + + def benchmark_fp16_synth_1gpu_gpuparams(self): + """Tests 1 gpu with synthetic data with parameters on the gpu.""" + params = self._shared_params_fp16()._replace( + num_gpus=1, variable_update='parameter_server') + self._run_benchmark(params) + + def benchmark_fp16_synth_1gpu_gpuparams_batch128(self): + """Tests 1 gpu with synthetic data with parameters on the gpu.""" + params = self._shared_params_fp16()._replace( + num_gpus=1, batch_size=128, variable_update='parameter_server') + self._run_benchmark(params) + + def benchmark_fp16_synth_4gpu_gpureplicated(self): + """Tests 4 gpu with synthetic data with nccl and all_reduce.""" + params = self._shared_params_fp16()._replace( + num_gpus=4, + variable_update='replicated', + all_reduce_spec='nccl', + gradient_repacking=2) + self._run_benchmark(params) + + def benchmark_fp16_synth_8gpu_gpureplicated(self): + """Tests 8 gpu with synthetic with nccl and all_reduce.""" + params = self._shared_params_fp16()._replace( + num_gpus=8, + variable_update='replicated', + all_reduce_spec='nccl', + gradient_repacking=2) + self._run_benchmark(params) + + def benchmark_fp16_fake_1gpu_gpuparams(self): + """Tests 1 gpus with fake data.""" + params = self._shared_params_fp16()._replace( + num_gpus=1, + data_dir=self.fake_data_dir, + data_name='imagenet', + variable_update='parameter_server') + self._run_benchmark(params) + + def benchmark_fp16_fake_8gpu_gpureplicated(self): + """Tests 8 gpus with fake data.""" + params = self._shared_params_fp16()._replace( + num_gpus=8, + data_dir=self.fake_data_dir, + data_name='imagenet', + variable_update='replicated', + all_reduce_spec='nccl', + gradient_repacking=2) + self._run_benchmark(params) + + def benchmark_fp16_fakedistort_8gpu_gpureplicated(self): + """Tests 8 gpus with fake distorted data.""" + params = self._shared_params_fp16()._replace( + num_gpus=8, + data_dir=self.fake_data_dir, + data_name='imagenet', + distortions=True, + variable_update='replicated', + all_reduce_spec='nccl', + gradient_repacking=2) + self._run_benchmark(params) + + # XLA versions of Resnet50 tests only for single GPU. + def benchmark_xla_synth_1gpu_gpuparams(self): + """Tests 1 gpu with synthetic data with XLA.""" + params = self._shared_params()._replace( + num_gpus=1, variable_update='parameter_server', xla=True) + self._run_benchmark(params) + + def benchmark_fp16_xla_synth_1gpu_gpuparams(self): + """Tests 1 gpu with fp16, synthetic data with XLA.""" + params = self._shared_params_fp16()._replace( + num_gpus=1, variable_update='parameter_server', xla=True) + self._run_benchmark(params) + + # Test does not run as part of continuous testing on guitar. + def benchmark_ng_xla_batch64_synth_1gpu_gpuparams(self): + """Tests 1 gpu with XLA, synth data, and batch 64.""" + params = self._shared_params()._replace( + num_gpus=1, batch_size=64, variable_update='parameter_server', xla=True) + self._run_benchmark(params) + + def benchmark_fp16_xla_batch64_synth_1gpu_gpuparams(self): + """Tests 1 gpu with fp16, XLA, synth data, and batch 64.""" + params = self._shared_params_fp16()._replace( + num_gpus=1, + batch_size=64, + variable_update='parameter_server', + xla=True) + self._run_benchmark(params) + + def benchmark_fp16_xla_batch128_synth_1gpu_gpuparams(self): + """Tests 1 gpu with fp16, XLA, and synth data.""" + params = self._shared_params_fp16()._replace( + num_gpus=1, + batch_size=128, + variable_update='parameter_server', + xla=True) + self._run_benchmark(params) + + def benchmark_xla_synth_1gpu_max_batch_size(self): + """Finds largest batch that can be run with XLA, 1 gpu, and synth data.""" + params = self._shared_params()._replace( + num_gpus=1, variable_update='parameter_server', xla=True) + self._binary_search_batch_size(params, init_batch_size=128) + + def benchmark_xla_real_1gpu_gpuparams(self): + """Tests 1 gpu with real data with XLA.""" + params = self._shared_params()._replace( + num_gpus=1, + data_dir=self.data_dir, + variable_update='parameter_server', + xla=True) + self._run_benchmark(params) + + # Test does not run as part of continuous testing. + def benchmark_xla_fake_1gpu_gpuparams(self): + """Tests 1 gpu with fake data with XLA.""" + params = self._shared_params()._replace( + num_gpus=1, + data_dir=self.fake_data_dir, + data_name='imagenet', + variable_update='parameter_server', + xla=True) + self._run_benchmark(params) + + # Test does not run as part of continuous testing. + def benchmark_xla_fakedistort_1gpu_gpuparams(self): + """Tests 1 gpu with fake distorted data with XLA.""" + params = self._shared_params()._replace( + num_gpus=1, + data_dir=self.fake_data_dir, + data_name='imagenet', + distortions=True, + variable_update='parameter_server', + xla=True) + self._run_benchmark(params) + + +class Resnet50v15Benchmarks(BenchmarkBase): + """"Benchmark various ResNet50V1.5 configurations. + + ResNetV1.5 differs from V1 in stride 2 is used in the first 3x3 convolution of + each block instead of the first 1x1 convolution. + """ + + def _shared_params_fp16(self): + """Returns shared parameters for all ResNet50v1.5 FP16 benchmarks.""" + return BenchmarkBase._shared_params(self)._replace( + model='resnet50_v1.5', + batch_size=256, + distortions=False, + use_fp16=True, + optimizer='momentum', + loss_type_to_report='base_loss', + compute_lr_on_cpu=True, + single_l2_loss_op=True + ) + + def benchmark_fp16_synth_1gpu_gpuparams(self): + """Tests 1 gpu with synthetic data.""" + params = self._shared_params_fp16()._replace(num_gpus=1) + self._run_benchmark(params) + + def benchmark_fp16_batch256_synth_8gpu_gpuparams(self): + """Tests 8 gpus with synthetic data at batch 256.""" + params = self._shared_params_fp16()._replace(num_gpus=8) + self._run_benchmark(params) + + def benchmark_fp16_batch128_synth_1gpu_gpuparams(self): + """Tests 1 gpu with synthetic data at batch 128 (useful for small GPUs).""" + params = self._shared_params_fp16()._replace(num_gpus=1, batch_size=128) + self._run_benchmark(params) + + def benchmark_fp16_fake_1gpu_gpuparams(self): + """Tests 1 gpu with fake data.""" + params = self._shared_params_fp16()._replace( + num_gpus=1, data_dir=self.fake_data_dir, data_name='imagenet') + self._run_benchmark(params) + + def benchmark_fp16_synth_8gpu_gpureplicated(self): + """Tests 8 gpu with synthetic data with parameters replicated.""" + params = self._shared_params_fp16()._replace( + num_gpus=8, + num_batches=200, + variable_update='replicated', + all_reduce_spec='nccl', + gradient_repacking=2) + self._run_benchmark(params) + + def benchmark_fp16_fake_8gpu_gpureplicated(self): + """Tests 8 gpu with fake data with parameters replicated.""" + params = self._shared_params_fp16()._replace( + num_gpus=8, + num_batches=200, + data_dir=self.fake_data_dir, + data_name='imagenet', + variable_update='replicated', + all_reduce_spec='nccl', + gradient_repacking=2) + self._run_benchmark(params) + + # XLA versions of Resnet50v1.5 tests. + def benchmark_fp16_xla_synth_1gpu_gpuparams(self): + """Tests 1 gpu with fp16, synthetic data with XLA.""" + params = self._shared_params_fp16()._replace(num_gpus=1, xla=True) + self._run_benchmark(params) + + def benchmark_fp16_xla_batch128_synth_1gpu_gpuparams(self): + """Tests 1 gpu with fp16, batch128, synthetic data with XLA.""" + params = self._shared_params_fp16()._replace( + num_gpus=1, batch_size=128, xla=True) + self._run_benchmark(params) + + def benchmark_fp16_xla_compile_synth_1gpu_gpuparams(self): + """Tests 1 gpu with synthetic data.""" + params = self._shared_params_fp16()._replace(num_gpus=1, xla_compile=True) + self._run_benchmark(params) + + def benchmark_fp16_xla_compile_batch128_synth_1gpu_gpuparams(self): + """Tests 1 gpu with synthetic data at batch 128 (useful for small GPUs).""" + params = self._shared_params_fp16()._replace( + num_gpus=1, num_batches=200, batch_size=128, xla_compile=True) + self._run_benchmark(params) + + def benchmark_fp16_xla_batch256_synth_8gpu_gpuparams(self): + """Tests 8 gpu with synthetic data and xla autojit.""" + params = self._shared_params_fp16()._replace( + num_gpus=8, num_batches=200, batch_size=256, xla=True) + self._run_benchmark(params) + + def benchmark_fp16_xla_compile_fake_1gpu_gpuparams(self): + """Tests 1 gpu with fake data.""" + params = self._shared_params_fp16()._replace( + num_gpus=1, + data_dir=self.fake_data_dir, + data_name='imagenet', + xla_compile=True) + self._run_benchmark(params) + + def benchmark_fp16_xla_compile_synth_8gpu_gpureplicated(self): + """Tests 8 gpu with synthetic data with parameters replicated.""" + params = self._shared_params_fp16()._replace( + num_gpus=8, + num_batches=200, + variable_update='replicated', + all_reduce_spec='nccl', + gradient_repacking=2, + xla_compile=True) + self._run_benchmark(params) + + def benchmark_fp16_xla_synth_8gpu_gpureplicated(self): + """Tests 8 gpu with synthetic data with parameters replicated.""" + params = self._shared_params_fp16()._replace( + num_gpus=8, + num_batches=200, + variable_update='replicated', + all_reduce_spec='nccl', + gradient_repacking=2, + xla=True) + self._run_benchmark(params) + + def benchmark_fp16_xla_compile_fake_8gpu_gpureplicated(self): + """Tests 8 gpu with fake data with parameters replicated.""" + params = self._shared_params_fp16()._replace( + num_gpus=8, + num_batches=200, + data_dir=self.fake_data_dir, + data_name='imagenet', + variable_update='replicated', + all_reduce_spec='nccl', + gradient_repacking=2, + xla_compile=True) + self._run_benchmark(params) + + +class Vgg16Benchmarks(BenchmarkBase): + """"Benchmark various vgg16 configurations.""" + + def _shared_params(self): + """Returns shared parameters for all vgg16 benchmarks.""" + return BenchmarkBase._shared_params(self)._replace( + model='vgg16', batch_size=128, distortions=False) + + def benchmark_synth_1gpu_gpuparams(self): + """Tests 1 gpu with synthetic data with parameters on gpu.""" + params = self._shared_params()._replace( + num_gpus=1, variable_update='parameter_server') + self._run_benchmark(params) + + def benchmark_fp16_synth_1gpu_gpuparams(self): + """Tests 1 gpu with synthetic data with parameters on gpu.""" + params = self._shared_params()._replace( + num_gpus=1, use_fp16=True, variable_update='parameter_server') + self._run_benchmark(params) + + def benchmark_synth_8gpu_gpureplicated(self): + """Tests 8 gpu with synthetic data with parameters replicated.""" + params = self._shared_params()._replace( + num_gpus=8, + all_reduce_spec='nccl', + variable_update='replicated', + compact_gradient_transfer=False, + gradient_repacking=2) + self._run_benchmark(params) + + # XLA versions of VGG16 tests only for single GPU. + def benchmark_xla_synth_1gpu_gpuparams(self): + """Tests 1 gpu with synthetic data and XLA.""" + params = self._shared_params()._replace( + num_gpus=1, variable_update='parameter_server', xla=True) + self._run_benchmark(params) + + def benchmark_fp16_xla_synth_1gpu_gpuparams(self): + """Tests 1 gpu with fp16, synthetic data, and XLA.""" + params = self._shared_params()._replace( + num_gpus=1, variable_update='parameter_server', xla=True, use_fp16=True) + self._run_benchmark(params) + + # Test does not run as part of continuous testing. + def benchmark_xla_fake_1gpu_gpuparams(self): + """Tests 1 gpu with fake data and XLA.""" + params = self._shared_params()._replace( + num_gpus=1, + data_dir=self.fake_data_dir, + data_name='imagenet', + variable_update='parameter_server', + xla=True) + self._run_benchmark(params) + + def benchmark_xla_real_1gpu_gpuparams(self): + """Tests 1 gpu with real data and XLA.""" + params = self._shared_params()._replace( + num_gpus=1, + data_dir=self.data_dir, + variable_update='parameter_server', + xla=True) + self._run_benchmark(params) + + +class TrivialBenchmarks(BenchmarkBase): + """"Benchmarks for trivial model. + + The purpose of these tests is to verify the upper bound for the input + pipeline. Fake data creates an upperbound on the input pipeline throughput. + """ + + def _shared_params(self): + """Returns shared parameters for all trivial benchmarks.""" + return BenchmarkBase._shared_params(self)._replace( + model='trivial', + num_gpus=8, + distortions=False, + variable_update='independent', + data_dir=self.fake_data_dir) + + def benchmark_fake_64batch(self): + params = self._shared_params()._replace(batch_size=64, data_name='imagenet') + self._run_benchmark(params) + + def benchmark_fake_128batch(self): + params = self._shared_params()._replace( + batch_size=128, data_name='imagenet') + self._run_benchmark(params) + + def benchmark_fake_256batch(self): + params = self._shared_params()._replace( + batch_size=256, data_name='imagenet') + self._run_benchmark(params) + + def benchmark_fakedistort_128batch(self): + params = self._shared_params()._replace( + batch_size=128, data_name='imagenet', distortions=True) + self._run_benchmark(params) + + +class AlexnetBenchmarks(BenchmarkBase): + """"Benchmarks for alexnet.""" + + def _shared_params(self): + """Returns shared parameters for all alexnet benchmarks.""" + return BenchmarkBase._shared_params(self)._replace( + model='alexnet', batch_size=512, distortions=False) + + def benchmark_synth_1gpu_gpuparams(self): + """Tests 1 gpu with synthetic data with parameters on gpu.""" + params = self._shared_params()._replace( + num_gpus=1, variable_update='parameter_server') + self._run_benchmark(params) + + def benchmark_fp16_synth_1gpu_gpuparams(self): + """Tests 1 gpu with synthetic data with parameters on gpu.""" + params = self._shared_params()._replace( + num_gpus=1, use_fp16=True, variable_update='parameter_server') + self._run_benchmark(params) + + def benchmark_synth_8gpu_gpureplicated(self): + """Tests 8 gpus with synthetic data with parameters replicated.""" + params = self._shared_params()._replace( + num_gpus=8, + variable_update='replicated', + all_reduce_spec='nccl', + compact_gradient_transfer=False, + gradient_repacking=2) + self._run_benchmark(params) + + def benchmark_fake_8gpu_gpureplicated(self): + """Tests 8 gpus with fake data with parameters replicated.""" + params = self._shared_params()._replace( + num_gpus=8, + data_dir=self.fake_data_dir, + data_name='imagenet', + variable_update='replicated', + all_reduce_spec='nccl', + compact_gradient_transfer=False, + gradient_repacking=2) + self._run_benchmark(params) + + # XLA Benchmark tests for AlexNet. + def benchmark_xla_synth_1gpuparams(self): + """Tests 1 gpu with synthetic data and XLA.""" + params = self._shared_params()._replace( + num_gpus=1, variable_update='parameter_server', xla=True) + self._run_benchmark(params) + + def benchmark_fp16_xla_synth_1gpu_gpuparams(self): + """Tests 1 gpu with fp16, synthetic data and XLA.""" + params = self._shared_params()._replace( + num_gpus=1, variable_update='parameter_server', xla=True, use_fp16=True) + self._run_benchmark(params) + + # Test does not run as part of continuous testing. + def benchmark_xla_fake_1gpuparams(self): + """Tests 1 gpu with fake data and XLA.""" + params = self._shared_params()._replace( + num_gpus=1, + data_dir=self.fake_data_dir, + data_name='imagenet', + variable_update='parameter_server', + xla=True) + self._run_benchmark(params) + + def benchmark_xla_real_1gpuparams(self): + """Tests 1 gpu with real data and XLA.""" + params = self._shared_params()._replace( + num_gpus=1, + data_dir=self.data_dir, + variable_update='parameter_server', + xla=True) + self._run_benchmark(params) + + +class InceptionV3Benchmarks(BenchmarkBase): + """"Benchmark for InceptionV3.""" + + def _shared_params(self): + """Returns shared parameters for all InceptionV3 benchmarks.""" + return BenchmarkBase._shared_params(self)._replace( + model='inception3', batch_size=64, distortions=False) + + def benchmark_synth_1gpu_gpuparams(self): + """Tests 1 gpu with synthetic data.""" + params = self._shared_params()._replace( + num_gpus=1, variable_update='parameter_server') + self._run_benchmark(params) + + def benchmark_fp16_synth_1gpu_gpuparams(self): + """Tests 1 gpu with synthetic data.""" + params = self._shared_params()._replace( + num_gpus=1, use_fp16=True, variable_update='parameter_server') + self._run_benchmark(params) + + def benchmark_synth_1gpu_max_batch_size(self): + """Finds largest batch size that can be run with 1 gpu using synth data.""" + params = self._shared_params()._replace( + num_gpus=1, variable_update='parameter_server') + self._binary_search_batch_size(params, init_batch_size=128) + + def benchmark_xla_synth_1gpu_gpuparams(self): + """Tests 1 gpu with synthetic and XLA.""" + params = self._shared_params()._replace( + num_gpus=1, variable_update='parameter_server', xla=True) + self._run_benchmark(params) + + def benchmark_fp16_xla_synth_1gpu_gpuparams(self): + """Tests 1 gpu with fp16, XLA and synthetic data.""" + params = self._shared_params()._replace( + num_gpus=1, variable_update='parameter_server', xla=True, use_fp16=True) + self._run_benchmark(params) + + def benchmark_xla_synth_1gpu_max_batch_size(self): + """Finds largest batch that can be run with XLA, 1 gpu, and synth data.""" + params = self._shared_params()._replace( + num_gpus=1, variable_update='parameter_server', xla=True) + self._binary_search_batch_size(params, init_batch_size=128) + + # Test does not run as part of continuous testing. + def benchmark_xla_fake_1gpu_gpuparams(self): + """Tests 1 gpu with fake data with XLA.""" + params = self._shared_params()._replace( + num_gpus=1, + data_dir=self.fake_data_dir, + data_name='imagenet', + variable_update='parameter_server', + xla=True) + self._run_benchmark(params) + + def benchmark_xla_real_1gpu_gpuparams(self): + """Tests 1 gpu with real data with XLA.""" + params = self._shared_params()._replace( + num_gpus=1, + data_dir=self.data_dir, + variable_update='parameter_server', + xla=True) + self._run_benchmark(params) + + +class NcfBenchmarks(BenchmarkBase): + """Benchmarks for neural collaborative filtering.""" + + def _shared_params(self): + return BenchmarkBase._shared_params(self)._replace( + model='ncf', batch_size=64*1024, num_gpus=1, num_warmup_batches=1) + + def benchmark_synth_1gpu_gpuparams(self): + params = self._shared_params()._replace(variable_update='parameter_server') + self._run_benchmark(params) + + def benchmark_fp16_synth_1gpu_gpuparams(self): + params = self._shared_params()._replace( + variable_update='parameter_server', use_fp16=True) + self._run_benchmark(params) + + def benchmark_xla_synth_1gpu_gpuparams(self): + params = self._shared_params()._replace( + variable_update='parameter_server', xla=True) + self._run_benchmark(params) + + def benchmark_fp16_xla_synth_1gpu_gpuparams(self): + params = self._shared_params()._replace( + variable_update='parameter_server', xla=True, use_fp16=True) + self._run_benchmark(params) + + def benchmark_xla_compile_synth_1gpu_gpuparams(self): + params = self._shared_params()._replace( + variable_update='parameter_server', xla_compile=True) + self._run_benchmark(params) + + def benchmark_fp16_xla_compile_synth_1gpu_gpuparams(self): + params = self._shared_params()._replace( + variable_update='parameter_server', xla_compile=True, use_fp16=True) + self._run_benchmark(params) + + +class DeepSpeech2Benchmarks(BenchmarkBase): + """Benchmarks for DeepSpeech2 model.""" + + def _shared_params(self): + return BenchmarkBase._shared_params(self)._replace( + model='deepspeech2', batch_size=32, num_gpus=1, data_name='librispeech') + + def benchmark_synth_1gpu_gpuparams(self): + params = self._shared_params()._replace(variable_update='parameter_server') + self._run_benchmark(params) + + def benchmark_xla_synth_1gpu_gpuparams(self): + params = self._shared_params()._replace( + variable_update='parameter_server', xla=True) + self._run_benchmark(params) + + def benchmark_xla_compile_synth_1gpu_gpuparams(self): + params = self._shared_params()._replace( + variable_update='parameter_server', xla_compile=True) + self._run_benchmark(params) + + +class SsdBenchmarks(BenchmarkBase): + """Benchmarks for SSD model.""" + + def _cudnn_version(self): + if sys.platform == 'win32': + return None + + lib = ctypes.cdll.LoadLibrary(None) + if hasattr(lib, 'cudnnGetErrorString'): + version = lib.cudnnGetVersion() + return version + + return None + + def _shared_params(self): + cudnn_version = self._cudnn_version() + if cudnn_version is None or cudnn_version < 7300: + raise RuntimeError( + 'Needs at least cuDNN 7.3 to work with fp16 (b/112048183). ' + 'Build with --define=use_experimental_cudnn=1') + + return BenchmarkBase._shared_params(self)._replace( + # TODO(b/115672206): Replace backbone model and data dir with replicated + # placer location for better performance. + backbone_model_path=platforms_util.get_ssd_backborn_model_file(), # pylint: disable=line-too-long + data_dir=platforms_util.get_ssd_backboard_data_dir(), + batch_size=128, + data_name='coco', + model='ssd300', + num_batches=10, + num_warmup_batches=1, + num_gpus=1, + optimizer='momentum', + momentum=0.9, + weight_decay=5e-4, + loss_type_to_report='base_loss', + single_l2_loss_op=True, + compute_lr_on_cpu=True, + ) + + def benchmark_xla_compile_real_1gpu_gpuparams(self): + params = self._shared_params()._replace( + num_gpus=1, + xla_compile=True, + ) + self._run_benchmark(params) + + def benchmark_real_1gpu_gpuparams(self): + params = self._shared_params()._replace(num_gpus=1,) + self._run_benchmark(params) + + def benchmark_xla_compile_fp16_real_1gpu_gpuparams(self): + params = self._shared_params()._replace( + num_gpus=1, xla_compile=True, use_fp16=True) + self._run_benchmark(params) + + def benchmark_fp16_real_1gpu_gpuparams(self): + params = self._shared_params()._replace(num_gpus=1, use_fp16=True) + self._run_benchmark(params) + + def benchmark_xla_compile_real_8gpu_gpuparams(self): + params = self._shared_params()._replace( + num_gpus=8, + xla_compile=True, + variable_update='replicated', + all_reduce_spec='nccl', + gradient_repacking=2, + num_batches=50, + ) + self._run_benchmark(params) + + def benchmark_real_8gpu_gpuparams(self): + params = self._shared_params()._replace( + num_gpus=8, + variable_update='replicated', + all_reduce_spec='nccl', + gradient_repacking=2, + num_batches=50, + ) + self._run_benchmark(params) + + def benchmark_xla_compile_fp16_real_8gpu_gpuparams(self): + params = self._shared_params()._replace( + num_gpus=8, + xla_compile=True, + use_fp16=True, + variable_update='replicated', + all_reduce_spec='nccl', + gradient_repacking=2, + num_batches=50, + ) + self._run_benchmark(params) + + def benchmark_fp16_real_8gpu_gpuparams(self): + params = self._shared_params()._replace( + num_gpus=8, + use_fp16=True, + variable_update='replicated', + all_reduce_spec='nccl', + gradient_repacking=2, + num_batches=50, + ) + self._run_benchmark(params) + + +if __name__ == '__main__': + tf.disable_v2_behavior() + tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/mlperf.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/mlperf.py new file mode 100644 index 00000000..932f3136 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/mlperf.py @@ -0,0 +1,260 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Contains functions related to MLPerf compliance. + +MLPerf requires submissions to log what the benchmark does, in order to verify +that the benchmark meets the MLPerf requirements. This module contains a global +object `logger` that is used by other files to log what tf_cnn_benchmarks does +for compliance. + +By default, `logger` does nothing, as the MLPerf compliance logs are verbose and +unnecessary if one is not concerned about MLPerf compliance. The logger can be +enabled by using the `mlperf_logger` context manager. + +To enable the logger with `mlperf_logger`, the MLPerf compliance library at +https://github.com/mlperf/training/tree/master/compliance is required. If +the logger is not enabled, the library is not needed. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + + +from collections import namedtuple +import contextlib +import os +import sys + +import tensorflow.compat.v1 as tf + +# pylint: disable=g-import-not-at-top +try: + # Not all users have the MLPerf compliance library, so we don't want to + # unconditionally crash if these imports fail. + from mlperf_compliance import mlperf_log + from mlperf_compliance import resnet_log_helper + from mlperf_compliance import tags + import_successful = True +except ImportError: + # The logger cannot be enabled in this case since the MLPerf library isn't + # found. We return empty strings from the `tags` attribute so that + # the benchmark can still run without crashing. This empty tags are passed + # to an instance of `NullMlPerfLogger`, which does not log anything and + # ignores the tag values. + + class _Tags(object): + + def __getattr__(self, item): + return '' + tags = _Tags() + import_successful = False +# pylint: enable=g-import-not-at-top + + +_ModelInfo = namedtuple('_ModelInfo', ['print_fn', 'tag_set', + 'mlperf_model_name']) + + +_MLPERF_LOG_PREFIX = ':::MLPv0.5.0' + + +class MlPerfLogger(object): + """Logs various aspects about a benchmark run for MLPerf compliance.""" + + def __init__(self, model): + self._root_dir = os.path.split(os.path.abspath(__file__))[0] + mlperf_log.ROOT_DIR_RESNET = self._root_dir + mlperf_log.ROOT_DIR_SSD = self._root_dir + self.model = model + model_to_info = { + 'resnet50_v1.5': _ModelInfo(mlperf_log.resnet_print, + mlperf_log.RESNET_TAG_SET, tags.RESNET), + 'ssd300': _ModelInfo(mlperf_log.ssd_print, mlperf_log.SSD_TAG_SET, + tags.SSD) + } + + try: + self._log_fn, self.tag_set, self.mlperf_model_name = model_to_info[model] + except KeyError: + raise ValueError('--ml_perf_compliance_logging is only compatible when ' + '--model is one of the following: ' + + ', '.join(model_to_info.keys())) + + def log(self, key, value=None, stack_offset=2): + if key in self.tag_set: + self._log_fn(key, value, stack_offset) + else: + print('Ignoring MLPerf logging item key=%s, value=%s for model %s' % + (key, value, self.model)) + + def log_deferred_tensor_value(self, key, tensor_value, global_step, + stack_offset=2, every_n=1): + """Logs the value of a tensor when the graph is run.""" + caller = '(%s)' % mlperf_log.get_caller(stack_offset, self._root_dir) + def create_print_op(): + return tf.print(_MLPERF_LOG_PREFIX, self.mlperf_model_name, + tf.timestamp(), caller, key, + ': { "deferred": true, "value":', tensor_value, '}', + output_stream=sys.stdout) + maybe_print = tf.cond(tf.equal(global_step % every_n, 0), create_print_op, + tf.no_op) + with tf.control_dependencies([maybe_print]): + return tf.identity(tensor_value) + + def log_max_pool(self, input_tensor, output_tensor): + if self.model == 'resnet50_v1.5': + resnet_log_helper.log_max_pool(input_tensor, output_tensor) + + def log_begin_block(self, input_tensor, block_type): + if self.model == 'resnet50_v1.5': + resnet_log_helper.log_begin_block(input_tensor, block_type) + + def log_end_block(self, output_tensor): + if self.model == 'resnet50_v1.5': + resnet_log_helper.log_end_block(output_tensor) + + def log_projection(self, input_tensor, output_tensor): + if self.model == 'resnet50_v1.5': + resnet_log_helper.log_projection(input_tensor, output_tensor) + + def log_conv2d(self, input_tensor, output_tensor, stride_height, stride_width, + filters, initializer, use_bias): + """Log a conv2d call.""" + if self.model == 'resnet50_v1.5': + assert stride_height == stride_width, ( + '--ml_perf_compliance_logging does not support convolutions where ' + 'the stride height is not equal to the stride width. ' + 'stride_height=%d, stride_width=%d' % (stride_height, stride_width)) + if isinstance(initializer, tf.truncated_normal_initializer) or ( + isinstance(initializer, tf.variance_scaling_initializer) and + initializer.distribution == 'truncated_normal'): + initializer = tags.TRUNCATED_NORMAL + elif (isinstance(initializer, tf.glorot_uniform_initializer) or + initializer is None): + initializer = 'glorot_uniform' + resnet_log_helper.log_conv2d(input_tensor, output_tensor, stride_width, + filters, initializer, use_bias) + + def log_batch_norm(self, input_tensor, output_tensor, momentum, epsilon, + center, scale, training): + if self.model == 'resnet50_v1.5': + resnet_log_helper.log_batch_norm(input_tensor, output_tensor, momentum, + epsilon, center, scale, training) + + def log_train_epochs(self, num_epochs): + """Logs all the TRAIN_EPOCHs log lines.""" + num_epochs_int = int(num_epochs) + for i in range(num_epochs_int): + # MLPerf allows us to print all the train epochs at once instead of + # printing them as we do them. + self.log(key=mlperf_log.TRAIN_EPOCH, value=i, stack_offset=3) + if num_epochs_int != num_epochs: + value = (str(num_epochs_int) + + ', but this epoch only has {}% of the examples of a normal epoch' + .format(100 * (num_epochs - num_epochs_int))) + self.log(key=mlperf_log.TRAIN_EPOCH, value=value, stack_offset=3) + + def log_input_resize_aspect_preserving(self, height, width, scale_factor): + assert height == width, ( + '--ml_perf_compliance_logging does not support models with nonsquare ' + 'images. Cannot process image with height=%d and width=%d' % + (height, width)) + self.log(key=tags.INPUT_RESIZE_ASPECT_PRESERVING, + value={'min': int(height * scale_factor)}) + + def log_eval_epoch(self, tag, global_step, batch_size, stack_offset=2): + if self.model == 'resnet50_v1.5': + self.log(key=tag, stack_offset=stack_offset+1) + elif self.model == 'ssd300': + epoch = int(global_step * batch_size / 118287) + self.log(key=tag, value=epoch, stack_offset=stack_offset+1) + + def log_eval_accuracy(self, accuracy, global_step, batch_size, + examples_per_epoch, stack_offset=2): + """Logs eval accuracy.""" + epoch = int(global_step * batch_size / examples_per_epoch) + eval_accuracy = {'epoch': epoch, 'value': accuracy} + eval_iteration_accuracy = {'iteration': global_step, 'value': accuracy} + self.log(key=tags.EVAL_ACCURACY, value=eval_accuracy, + stack_offset=stack_offset+1) + self.log(key=tags.EVAL_ITERATION_ACCURACY, + value=eval_iteration_accuracy, + stack_offset=stack_offset+1) + + +def _empty_fn(*args, **kwargs): + del args, kwargs + + +class NullMlPerfLogger(object): + """A version of `MlPerfLogger` that does not log anything. + + This class has the same interface as `MlPerfLogger`, but does not actually do + anything. This is used when logging is disabled, which is the default + behavior. + """ + + def __getattr__(self, item): + return _empty_fn + + def log_deferred_tensor_value(self, key, tensor_value, *args, **kwargs): + del key, args, kwargs + return tensor_value + + +# A global singleton logger. By default, it's the null logger but can be +# switched to an MlPerfLogger with `mlperf_logger()`. +logger = NullMlPerfLogger() + + +@contextlib.contextmanager +def mlperf_logger(use_mlperf_logger, model): + """Optionally enable the mlperf logger. + + If `use_mlperf_logger` is True, sets the `logger` global variable to an + instance of MlPerfLogger that will print logs for MLPerf compliance. If + `use_mlperf_logger` is False, does nothing. + + Args: + use_mlperf_logger: If True, enables the mlperf logger. If False, this + function does nothing. + model: The model that will be logged. Required, because different models + must log different things for MLPerf compliance. + + Yields: + Nothing. + + Raises: + ImportError: If `use_mlperf_logger` is True but the MLPerf compliance + library cannot be imported + """ + global logger + if use_mlperf_logger: + if not import_successful: + raise ImportError('Failed to import MLPerf compliance library, which is ' + 'required when --ml_perf_compliance_logging is ' + 'specified. Clone this repo and add this directory ' + 'https://github.com/mlperf/training/tree/master/' + 'compliance to the PYTHONPATH environmental variable.') + logger_ = MlPerfLogger(model) + old_logger = logger + try: + logger = logger_ + yield + finally: + logger = old_logger + else: + yield diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/mlperf_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/mlperf_test.py new file mode 100644 index 00000000..7e83fc29 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/mlperf_test.py @@ -0,0 +1,189 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Contains tests related to MLPerf. + +Note this test only passes if the MLPerf compliance library is installed. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from collections import Counter +import logging +import re + +import six +import tensorflow.compat.v1 as tf +import benchmark_cnn +import datasets +import mlperf +import test_util +from models import model +from mlperf_compliance import mlperf_log + + +class _MlPerfTestModel(model.CNNModel): + """A model to test the MLPerf compliance logging on.""" + + def __init__(self): + super(_MlPerfTestModel, self).__init__( + 'mlperf_test_model', image_size=224, batch_size=2, learning_rate=1) + + def add_inference(self, cnn): + assert cnn.top_layer.shape[1:] == (3, 224, 224) + cnn.conv(1, 1, 1, 1, 1, use_batch_norm=True) + cnn.mpool(1, 1, 1, 1, num_channels_in=1) + cnn.reshape([-1, 224 * 224]) + cnn.affine(1, activation=None) + + # Assert that the batch norm variables are filtered out for L2 loss. + variables = tf.global_variables() + tf.local_variables() + assert len(variables) > len(self.filter_l2_loss_vars(variables)) + + +class MlPerfComplianceTest(tf.test.TestCase): + """Tests the MLPerf compliance logs. + + This serves as a quick check that we probably didn't break the compliance + logging. It is not mean to be as comprehensive as the official MLPerf + compliance checker will be. + """ + + def setUp(self): + super(MlPerfComplianceTest, self).setUp() + benchmark_cnn.setup(benchmark_cnn.make_params()) + + # Map between regex and the number of times we expect to see that regex in the + # logs. Entry commented out with the comment FIXME indicate that + # tf_cnn_benchmarks currently fails compliance in that regard, and needs to be + # fixed to be MLPerf compliant. + EXPECTED_LOG_REGEXES = { + # Preprocessing tags + mlperf.tags.INPUT_ORDER: 2, # 1 for training, 1 for eval + # We pass --tf_random_seed=9876 in the test. + r'%s: 9876' % mlperf.tags.RUN_SET_RANDOM_SEED: 2, + # The Numpy random seed is hardcoded to 4321. + r'%s: 4321' % mlperf.tags.RUN_SET_RANDOM_SEED: 2, + r'%s: %d' % (mlperf.tags.PREPROC_NUM_TRAIN_EXAMPLES, + datasets.IMAGENET_NUM_TRAIN_IMAGES): 1, + r'%s: %d' % (mlperf.tags.PREPROC_NUM_EVAL_EXAMPLES, + datasets.IMAGENET_NUM_VAL_IMAGES): 1, + mlperf.tags.PREPROC_NUM_EVAL_EXAMPLES + '.*': 1, + mlperf.tags.INPUT_DISTORTED_CROP_MIN_OBJ_COV + '.*': 1, + mlperf.tags.INPUT_DISTORTED_CROP_RATIO_RANGE + '.*': 1, + mlperf.tags.INPUT_DISTORTED_CROP_AREA_RANGE + '.*': 1, + mlperf.tags.INPUT_DISTORTED_CROP_MAX_ATTEMPTS + '.*': 1, + mlperf.tags.INPUT_RANDOM_FLIP + '.*': 1, + r'%s: \[224, 224\].*' % mlperf.tags.INPUT_CENTRAL_CROP: 1, + + r'%s: \[123.68, 116.78, 103.94\].*' % mlperf.tags.INPUT_MEAN_SUBTRACTION: + 2, + + r'%s: {"min": 256}.*' % mlperf.tags.INPUT_RESIZE_ASPECT_PRESERVING: 1, + + # 1 for training, 1 for eval + r'%s: \[224, 224\].*' % mlperf.tags.INPUT_RESIZE: 2, + + # Resnet model tags + mlperf.tags.MODEL_HP_BATCH_NORM + '.*': 2, + # 2 for training, 2 for eval. Although there's only 1 conv2d, each conv2d + # produces 2 logs. + mlperf.tags.MODEL_HP_CONV2D_FIXED_PADDING + '.*': 4, + mlperf.tags.MODEL_HP_RELU + '.*': 2, + mlperf.tags.MODEL_HP_INITIAL_MAX_POOL + '.*': 2, + mlperf.tags.MODEL_HP_DENSE + '.*': 4, + mlperf.tags.MODEL_HP_DENSE + '.*': 4, + + # Note that tags our test model does not emit, like MODEL_HP_SHORTCUT_ADD, + # are omitted here. + + r'%s: "categorical_cross_entropy".*' % mlperf.tags.MODEL_HP_LOSS_FN: 1, + + # 1 for training, 2 because the _MlPerfTestModel calls this when building + # the model for both training and eval + r'%s: true' % mlperf.tags.MODEL_EXCLUDE_BN_FROM_L2: 3, + + r'%s: 0.5.*' % mlperf.tags.MODEL_L2_REGULARIZATION: 1, + + # Note we do not handle OPT_LR, since that is printed to stderr using + # tf.Print, which we cannot easily intercept. + + # Other tags + '%s: "%s"' % (mlperf.tags.OPT_NAME, mlperf.tags.SGD_WITH_MOMENTUM): 1, + '%s: 0.5' % mlperf.tags.OPT_MOMENTUM: 1, + mlperf.tags.RUN_START: 1, + '%s: 2' % mlperf.tags.INPUT_BATCH_SIZE: 1, + mlperf.tags.TRAIN_LOOP: 1, + mlperf.tags.TRAIN_EPOCH + '.*': 1, + '%s: 2' % mlperf.tags.INPUT_SIZE: 2, + mlperf.tags.EVAL_START: 2, + mlperf.tags.EVAL_STOP: 2, + '%s: 6' % mlperf.tags.EVAL_SIZE: 2, + mlperf.tags.EVAL_ACCURACY + '.*': 2, + '%s: 2.0' % mlperf.tags.EVAL_TARGET: 2, + mlperf.tags.RUN_STOP + '.*': 1, + mlperf.tags.RUN_FINAL: 1 + } + EXPECTED_LOG_REGEXES = Counter({re.compile(k): v for + k, v in EXPECTED_LOG_REGEXES.items()}) + + def testMlPerfCompliance(self): + string_io = six.StringIO() + handler = logging.StreamHandler(string_io) + data_dir = test_util.create_black_and_white_images() + try: + mlperf_log.LOGGER.addHandler(handler) + params = benchmark_cnn.make_params(data_dir=data_dir, + data_name='imagenet', + batch_size=2, + num_warmup_batches=0, + num_batches=2, + num_eval_batches=3, + eval_during_training_every_n_steps=1, + distortions=False, + weight_decay=0.5, + optimizer='momentum', + momentum=0.5, + stop_at_top_1_accuracy=2.0, + tf_random_seed=9876, + ml_perf=True) + with mlperf.mlperf_logger(use_mlperf_logger=True, model='resnet50_v1.5'): + bench_cnn = benchmark_cnn.BenchmarkCNN(params, model=_MlPerfTestModel()) + bench_cnn.run() + logs = string_io.getvalue().splitlines() + log_regexes = Counter() + for log in logs: + for regex in self.EXPECTED_LOG_REGEXES: + if regex.search(log): + log_regexes[regex] += 1 + if log_regexes != self.EXPECTED_LOG_REGEXES: + diff_counter = Counter(log_regexes) + diff_counter.subtract(self.EXPECTED_LOG_REGEXES) + differences = [] + for regex in (k for k in diff_counter.keys() if diff_counter[k]): + found_count = log_regexes[regex] + expected_count = self.EXPECTED_LOG_REGEXES[regex] + differences.append(' For regex %s: Found %d lines matching but ' + 'expected to find %d' % + (regex.pattern, found_count, expected_count)) + raise AssertionError('Logs did not match expected logs. Differences:\n' + '%s' % '\n'.join(differences)) + finally: + mlperf_log.LOGGER.removeHandler(handler) + +if __name__ == '__main__': + tf.disable_v2_behavior() + tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/__init__.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/__init__.py similarity index 100% rename from TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/boosted_trees/__init__.py rename to TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/__init__.py diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/alexnet_model.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/alexnet_model.py new file mode 100644 index 00000000..2f4611fd --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/alexnet_model.py @@ -0,0 +1,93 @@ +# 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. +# ============================================================================== +"""Alexnet model configuration. + +References: + Krizhevsky, Alex, Ilya Sutskever, and Geoffrey E. Hinton + ImageNet Classification with Deep Convolutional Neural Networks + Advances in Neural Information Processing Systems. 2012 +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import tensorflow.compat.v1 as tf +from models import model + + +class AlexnetModel(model.CNNModel): + """Alexnet cnn model.""" + + def __init__(self, params=None): + super(AlexnetModel, self).__init__( + 'alexnet', 224 + 3, 512, 0.005, params=params) + + def add_inference(self, cnn): + # Note: VALID requires padding the images by 3 in width and height + cnn.conv(64, 11, 11, 4, 4, 'VALID') + cnn.mpool(3, 3, 2, 2) + cnn.conv(192, 5, 5) + cnn.mpool(3, 3, 2, 2) + cnn.conv(384, 3, 3) + cnn.conv(384, 3, 3) + cnn.conv(256, 3, 3) + cnn.mpool(3, 3, 2, 2) + cnn.reshape([-1, 256 * 6 * 6]) + cnn.affine(4096) + cnn.dropout() + cnn.affine(4096) + cnn.dropout() + + +class AlexnetCifar10Model(model.CNNModel): + """Alexnet cnn model for cifar datasets. + + The model architecture follows the one defined in the tensorflow tutorial + model. + + Reference model: tensorflow/models/tutorials/image/cifar10/cifar10.py + Paper: http://www.cs.toronto.edu/~kriz/learning-features-2009-TR.pdf + """ + + def __init__(self, params=None): + super(AlexnetCifar10Model, self).__init__( + 'alexnet', 32, 128, 0.1, params=params) + + def add_inference(self, cnn): + cnn.conv(64, 5, 5, 1, 1, 'SAME', stddev=5e-2) + cnn.mpool(3, 3, 2, 2, mode='SAME') + cnn.lrn(depth_radius=4, bias=1.0, alpha=0.001 / 9.0, beta=0.75) + cnn.conv(64, 5, 5, 1, 1, 'SAME', bias=0.1, stddev=5e-2) + cnn.lrn(depth_radius=4, bias=1.0, alpha=0.001 / 9.0, beta=0.75) + cnn.mpool(3, 3, 2, 2, mode='SAME') + shape = cnn.top_layer.get_shape().as_list() + flat_dim = shape[1] * shape[2] * shape[3] + cnn.reshape([-1, flat_dim]) + cnn.affine(384, stddev=0.04, bias=0.1) + cnn.affine(192, stddev=0.04, bias=0.1) + + def get_learning_rate(self, global_step, batch_size): + num_examples_per_epoch = 50000 + num_epochs_per_decay = 100 + decay_steps = ( + num_epochs_per_decay * num_examples_per_epoch // batch_size) + decay_factor = 0.1 + return tf.train.exponential_decay( + self.learning_rate, + global_step, + decay_steps, + decay_factor, + staircase=True) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/densenet_model.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/densenet_model.py new file mode 100644 index 00000000..cb61b9b3 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/densenet_model.py @@ -0,0 +1,100 @@ +# 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. +# ============================================================================== + +"""Densenet model configuration. + +References: + "Densely Connected Convolutional Networks": https://arxiv.org/pdf/1608.06993 +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np +from six.moves import xrange # pylint: disable=redefined-builtin +import tensorflow.compat.v1 as tf +from models import model as model_lib + + +class DensenetCifar10Model(model_lib.CNNModel): + """Densenet cnn network configuration.""" + + def __init__(self, model, layer_counts, growth_rate, params=None): + self.growth_rate = growth_rate + super(DensenetCifar10Model, self).__init__( + model, 32, 64, 0.1, layer_counts=layer_counts, params=params) + self.batch_norm_config = {'decay': 0.9, 'epsilon': 1e-5, 'scale': True} + + def dense_block(self, cnn, growth_rate): + input_layer = cnn.top_layer + c = cnn.batch_norm(input_layer, **self.batch_norm_config) + c = tf.nn.relu(c) + c = cnn.conv(growth_rate, 3, 3, 1, 1, stddev=np.sqrt(2.0/9/growth_rate), + activation=None, input_layer=c) + channel_index = 3 if cnn.channel_pos == 'channels_last' else 1 + cnn.top_layer = tf.concat([input_layer, c], channel_index) + cnn.top_size += growth_rate + + def transition_layer(self, cnn): + in_size = cnn.top_size + cnn.batch_norm(**self.batch_norm_config) + cnn.top_layer = tf.nn.relu(cnn.top_layer) + cnn.conv(in_size, 1, 1, 1, 1, stddev=np.sqrt(2.0/9/in_size)) + cnn.apool(2, 2, 2, 2) + + def add_inference(self, cnn): + if self.layer_counts is None: + raise ValueError('Layer counts not specified for %s' % self.get_model()) + if self.growth_rate is None: + raise ValueError('Growth rate not specified for %s' % self.get_model()) + + cnn.conv(16, 3, 3, 1, 1, activation=None) + # Block 1 + for _ in xrange(self.layer_counts[0]): + self.dense_block(cnn, self.growth_rate) + self.transition_layer(cnn) + # Block 2 + for _ in xrange(self.layer_counts[1]): + self.dense_block(cnn, self.growth_rate) + self.transition_layer(cnn) + # Block 3 + for _ in xrange(self.layer_counts[2]): + self.dense_block(cnn, self.growth_rate) + cnn.batch_norm(**self.batch_norm_config) + cnn.top_layer = tf.nn.relu(cnn.top_layer) + channel_index = 3 if cnn.channel_pos == 'channels_last' else 1 + cnn.top_size = cnn.top_layer.get_shape().as_list()[channel_index] + cnn.spatial_mean() + + def get_learning_rate(self, global_step, batch_size): + num_batches_per_epoch = 50000 // batch_size + boundaries = num_batches_per_epoch * np.array([150, 225, 300], + dtype=np.int64) + boundaries = [x for x in boundaries] + values = [0.1, 0.01, 0.001, 0.0001] + return tf.train.piecewise_constant(global_step, boundaries, values) + + +def create_densenet40_k12_model(): + return DensenetCifar10Model('densenet40_k12', (12, 12, 12), 12) + + +def create_densenet100_k12_model(): + return DensenetCifar10Model('densenet100_k12', (32, 32, 32), 12) + + +def create_densenet100_k24_model(): + return DensenetCifar10Model('densenet100_k24', (32, 32, 32), 24) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/datasets/__init__.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/experimental/__init__.py similarity index 100% rename from TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/datasets/__init__.py rename to TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/experimental/__init__.py diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/experimental/deepspeech.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/experimental/deepspeech.py new file mode 100644 index 00000000..9475e365 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/experimental/deepspeech.py @@ -0,0 +1,449 @@ +# 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. +# ============================================================================== +"""DeepSpeech2 model configuration. + +References: + https://arxiv.org/abs/1512.02595 + Deep Speech 2: End-to-End Speech Recognition in English and Mandarin +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import itertools + +import numpy as np +from six.moves import xrange # pylint: disable=redefined-builtin +import tensorflow.compat.v1 as tf +import constants +from cnn_util import log_fn +from models import model as model_lib +from tensorflow.python.ops import variables # pylint: disable=g-direct-tensorflow-import + + +class DeepSpeechDecoder(object): + """Greedy decoder implementation for Deep Speech model.""" + + def __init__(self, labels, blank_index=28): + """Decoder initialization. + + Args: + labels: a string specifying the speech labels for the decoder to use. + blank_index: an integer specifying index for the blank character. Defaults + to 28. + """ + self.labels = labels + self.blank_index = blank_index + self.int_to_char = dict([(i, c) for (i, c) in enumerate(labels)]) + + def convert_to_string(self, sequence): + """Convert a sequence of indexes into corresponding string.""" + return ''.join([self.int_to_char[i] for i in sequence]) + + def wer(self, decode, target): + """Computes the Word Error Rate (WER). + + WER is defined as the edit distance between the two provided sentences after + tokenizing to words. + + Args: + decode: string of the decoded output. + target: a string for the ground truth label. + + Returns: + A float number for the WER of the current decode-target pair. + """ + try: + from nltk.metrics import distance # pylint: disable=g-import-not-at-top + except ImportError as e: + if 'nltk.metrics' not in e.message: + raise + raise ImportError('To use the experimental deepspeech model, you must ' + 'pip install -U nltk') + + # Map each word to a new char. + words = set(decode.split() + target.split()) + word2char = dict(zip(words, range(len(words)))) + + new_decode = [chr(word2char[w]) for w in decode.split()] + new_target = [chr(word2char[w]) for w in target.split()] + + return distance.edit_distance(''.join(new_decode), ''.join(new_target)) + + def cer(self, decode, target): + """Computes the Character Error Rate (CER). + + CER is defined as the edit distance between the two given strings. + + Args: + decode: a string of the decoded output. + target: a string for the ground truth label. + + Returns: + A float number denoting the CER for the current sentence pair. + """ + try: + from nltk.metrics import distance # pylint: disable=g-import-not-at-top + except ImportError as e: + if 'nltk.metrics' not in e.message: + raise + raise ImportError('To use the experimental deepspeech model, you must ' + 'pip install -U nltk') + return distance.edit_distance(decode, target) + + def decode(self, char_indexes): + """Decode the best guess from logits using greedy algorithm.""" + # Merge repeated chars. + merge = [k for k, _ in itertools.groupby(char_indexes)] + # Remove the blank index in the decoded sequence. + merge_remove_blank = [] + for k in merge: + if k != self.blank_index: + merge_remove_blank.append(k) + + return self.convert_to_string(merge_remove_blank) + + def decode_logits(self, logits): + """Decode the best guess from logits using greedy algorithm.""" + # Choose the class with maximimum probability. + best = list(np.argmax(logits, axis=1)) + return self.decode(best) + + +class DeepSpeech2Model(model_lib.Model): + """Define DeepSpeech2 model.""" + + # Supported rnn cells. + SUPPORTED_RNNS = { + 'lstm': tf.nn.rnn_cell.BasicLSTMCell, + 'rnn': tf.nn.rnn_cell.RNNCell, + 'gru': tf.nn.rnn_cell.GRUCell, + } + + # Parameters for batch normalization. + BATCH_NORM_EPSILON = 1e-5 + BATCH_NORM_DECAY = 0.997 + + # Filters of convolution layer + CONV_FILTERS = 32 + + def __init__(self, + num_rnn_layers=5, + rnn_type='lstm', + is_bidirectional=True, + rnn_hidden_size=800, + use_bias=True, + params=None): + """Initialize DeepSpeech2 model. + + Args: + num_rnn_layers: an integer, the number of rnn layers (default: 5). + rnn_type: a string, one of the supported rnn cells: gru, rnn or lstm. + is_bidirectional: a boolean to indicate if the rnn layer is bidirectional. + rnn_hidden_size: an integer for the number of hidden units in the RNN + cell. + use_bias: a boolean specifying whether to use a bias in the last fc layer. + params: the params from BenchmarkCNN. + """ + super(DeepSpeech2Model, self).__init__( + 'deepspeech2', + batch_size=128, + learning_rate=0.0005, + fp16_loss_scale=128, + params=params) + self.num_rnn_layers = num_rnn_layers + self.rnn_type = rnn_type + self.is_bidirectional = is_bidirectional + self.rnn_hidden_size = rnn_hidden_size + self.use_bias = use_bias + self.num_feature_bins = 161 + self.max_time_steps = 3494 + self.max_label_length = 576 + + def _batch_norm(self, inputs, training): + """Batch normalization layer. + + Note that the momentum to use will affect validation accuracy over time. + Batch norm has different behaviors during training/evaluation. With a large + momentum, the model takes longer to get a near-accurate estimation of the + moving mean/variance over the entire training dataset, which means we need + more iterations to see good evaluation results. If the training data is + evenly distributed over the feature space, we can also try setting a smaller + momentum (such as 0.1) to get good evaluation result sooner. + + Args: + inputs: input data for batch norm layer. + training: a boolean to indicate if it is in training stage. + + Returns: + tensor output from batch norm layer. + """ + return tf.layers.batch_normalization( + inputs=inputs, + momentum=DeepSpeech2Model.BATCH_NORM_DECAY, + epsilon=DeepSpeech2Model.BATCH_NORM_EPSILON, + fused=True, + training=training) + + def _conv_bn_layer(self, inputs, padding, filters, kernel_size, strides, + layer_id, training): + """Defines 2D convolutional + batch normalization layer. + + Args: + inputs: input data for convolution layer. + padding: padding to be applied before convolution layer. + filters: an integer, number of output filters in the convolution. + kernel_size: a tuple specifying the height and width of the 2D convolution + window. + strides: a tuple specifying the stride length of the convolution. + layer_id: an integer specifying the layer index. + training: a boolean to indicate which stage we are in (training/eval). + + Returns: + tensor output from the current layer. + """ + # Perform symmetric padding on the feature dimension of time_step + # This step is required to avoid issues when RNN output sequence is shorter + # than the label length. + inputs = tf.pad( + inputs, + [[0, 0], [padding[0], padding[0]], [padding[1], padding[1]], [0, 0]]) + inputs = tf.layers.conv2d( + inputs=inputs, + filters=filters, + kernel_size=kernel_size, + strides=strides, + padding='valid', + use_bias=False, + activation=tf.nn.relu6, + name='cnn_{}'.format(layer_id)) + return self._batch_norm(inputs, training) + + def _rnn_layer(self, inputs, rnn_cell, rnn_hidden_size, layer_id, + use_batch_norm, is_bidirectional, training): + """Defines a batch normalization + rnn layer. + + Args: + inputs: input tensors for the current layer. + rnn_cell: RNN cell instance to use. + rnn_hidden_size: an integer for the dimensionality of the rnn output + space. + layer_id: an integer for the index of current layer. + use_batch_norm: a boolean specifying whether to perform batch + normalization on input states. + is_bidirectional: a boolean specifying whether the rnn layer is + bi-directional. + training: a boolean to indicate which stage we are in (training/eval). + + Returns: + tensor output for the current layer. + """ + if use_batch_norm: + inputs = self._batch_norm(inputs, training) + + # Construct forward/backward RNN cells. + fw_cell = rnn_cell( + num_units=rnn_hidden_size, name='rnn_fw_{}'.format(layer_id)) + + if is_bidirectional: + bw_cell = rnn_cell( + num_units=rnn_hidden_size, name='rnn_bw_{}'.format(layer_id)) + outputs, _ = tf.nn.bidirectional_dynamic_rnn( + cell_fw=fw_cell, + cell_bw=bw_cell, + inputs=inputs, + dtype=tf.float32, + swap_memory=True) + rnn_outputs = tf.concat(outputs, -1) + else: + rnn_outputs = tf.nn.dynamic_rnn( + fw_cell, inputs, dtype=tf.float32, swap_memory=True) + + return rnn_outputs + + def get_input_data_types(self, subset): + """Returns the list of data types of the inputs.""" + del subset # Same data types for both train and validation subsets. + return [self.data_type, tf.int32, tf.int32, tf.int32] + + def get_input_shapes(self, subset): + """Returns the list of shapes of the padded inputs.""" + del subset # Same shapes for both train and validation subsets + return [ + [self.batch_size, self.max_time_steps, self.num_feature_bins, 1], + [self.batch_size, self.max_label_length], + [self.batch_size, 1], + [self.batch_size, 1], + ] + + def get_synthetic_inputs(self, input_name, nclass): + inputs = tf.random_uniform(self.get_input_shapes('train')[0], + dtype=self.get_input_data_types('train')[0]) + inputs = variables.VariableV1(inputs, trainable=False, + collections=[tf.GraphKeys.LOCAL_VARIABLES], + name=input_name) + labels = tf.convert_to_tensor( + np.random.randint(28, size=[self.batch_size, self.max_label_length])) + input_lengths = tf.convert_to_tensor( + [self.max_time_steps] * self.batch_size) + label_lengths = tf.convert_to_tensor( + [self.max_label_length] * self.batch_size) + return [inputs, labels, input_lengths, label_lengths] + + # TODO(laigd): support fp16. + # TODO(laigd): support multiple gpus. + def build_network(self, inputs, phase_train=True, nclass=29): + """Builds the forward pass of the deepspeech2 model. + + Args: + inputs: The input list of the model. + phase_train: True during training. False during evaluation. + nclass: Number of classes that the input spectrogram can belong to. + + Returns: + A BuildNetworkResult which contains the logits and model-specific extra + information. + """ + inputs = inputs[0] # Get the spectrogram feature. + + # Two cnn layers. + inputs = self._conv_bn_layer( + inputs, + padding=(20, 5), + filters=DeepSpeech2Model.CONV_FILTERS, + kernel_size=(41, 11), + strides=(2, 2), + layer_id=1, + training=phase_train) + + inputs = self._conv_bn_layer( + inputs, + padding=(10, 5), + filters=DeepSpeech2Model.CONV_FILTERS, + kernel_size=(21, 11), + strides=(2, 1), + layer_id=2, + training=phase_train) + + # output of conv_layer2 with the shape of + # [batch_size (N), times (T), features (F), channels (C)]. + # Convert the conv output to rnn input. + + # batch_size = tf.shape(inputs)[0] + feat_size = inputs.get_shape().as_list()[2] + inputs = tf.reshape( + inputs, + [self.batch_size, -1, feat_size * DeepSpeech2Model.CONV_FILTERS]) + + # RNN layers. + rnn_cell = DeepSpeech2Model.SUPPORTED_RNNS[self.rnn_type] + for layer_counter in xrange(self.num_rnn_layers): + # No batch normalization on the first layer. + use_batch_norm = (layer_counter != 0) + inputs = self._rnn_layer(inputs, rnn_cell, self.rnn_hidden_size, + layer_counter + 1, use_batch_norm, + self.is_bidirectional, phase_train) + + # FC layer with batch norm. + inputs = self._batch_norm(inputs, phase_train) + logits = tf.layers.dense(inputs, nclass, use_bias=self.use_bias) + + return model_lib.BuildNetworkResult(logits=logits, extra_info=None) + + def loss_function(self, inputs, build_network_result): + """Computes the ctc loss for the current batch of predictions. + + Args: + inputs: the input list of the model. + build_network_result: a BuildNetworkResult returned by build_network(). + + Returns: + The loss tensor of the model. + """ + logits = build_network_result.logits + actual_time_steps = inputs[2] + probs = tf.nn.softmax(logits) + ctc_time_steps = tf.shape(probs)[1] + ctc_input_length = tf.to_float( + tf.multiply(actual_time_steps, ctc_time_steps)) + ctc_input_length = tf.to_int32( + tf.floordiv(ctc_input_length, tf.to_float(self.max_time_steps))) + + label_length = inputs[3] + label_length = tf.to_int32(tf.squeeze(label_length)) + ctc_input_length = tf.to_int32(tf.squeeze(ctc_input_length)) + + labels = inputs[1] + sparse_labels = tf.to_int32( + tf.keras.backend.ctc_label_dense_to_sparse(labels, label_length)) + y_pred = tf.log( + tf.transpose(probs, perm=[1, 0, 2]) + tf.keras.backend.epsilon()) + + losses = tf.expand_dims( + tf.nn.ctc_loss( + labels=sparse_labels, + inputs=y_pred, + sequence_length=ctc_input_length, + ignore_longer_outputs_than_inputs=True), + axis=1) + loss = tf.reduce_mean(losses) + return loss + + PROBABILITY_TENSOR = 'deepspeech2_prob' + LABEL_TENSOR = 'deepspeech2_label' + + def accuracy_function(self, inputs, logits): + """Returns the ops to evaluate the model performance.""" + # Get probabilities of each predicted class + probs = tf.nn.softmax(logits) + assert probs.shape.as_list()[0] == self.batch_size + return { + (constants.UNREDUCED_ACCURACY_OP_PREFIX + self.PROBABILITY_TENSOR): + probs, + (constants.UNREDUCED_ACCURACY_OP_PREFIX + self.LABEL_TENSOR): + inputs[1], + } + + def postprocess(self, results): + """Postprocess results returned from model in Python.""" + probs = results[self.PROBABILITY_TENSOR] + + total_wer, total_cer = 0, 0 + speech_labels = " abcdefghijklmnopqrstuvwxyz'-" + greedy_decoder = DeepSpeechDecoder(speech_labels) + + # Evaluate the performance using WER (Word Error Rate) and CER (Character + # Error Rate) as metrics. + targets = results[self.LABEL_TENSOR] # The ground truth transcript + for i in range(self.batch_size): + # Decode string. + predicted_str = greedy_decoder.decode_logits(probs[i]) + expected_str = greedy_decoder.decode(targets[i]) + # Compute CER. + total_cer += (greedy_decoder.cer(predicted_str, expected_str) / + len(expected_str)) + # Compute WER. + total_wer += (greedy_decoder.wer(predicted_str, expected_str) / + len(expected_str.split())) + + # Get mean value + total_cer /= self.batch_size + total_wer /= self.batch_size + + log_fn('total CER: {:f}; total WER: {:f}; total example: {:d}.'.format( + total_cer, total_wer, self.batch_size)) + # TODO(laigd): get rid of top_N_accuracy bindings in benchmark_cnn.py + return {'top_1_accuracy': 0., 'top_5_accuracy': 0.} diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/experimental/official_ncf_model.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/experimental/official_ncf_model.py new file mode 100644 index 00000000..9e6ca513 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/experimental/official_ncf_model.py @@ -0,0 +1,172 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Wrap the official recommendation model in a tf_cnn_benchmarks Model. + +This allows the recommendation NCF model to be used in tf_cnn_benchmarks. +Currently, the implementation is fairly hacky, because tf_cnn_benchmarks is +intended to be used only with CNNs. + +Only synthetic data with 1 GPU is currently supported. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import tensorflow.compat.v1 as tf + +from models import model + + +# Obtained by running the official NCF model with the following command: +# python ncf_main.py --dataset ml-20m +# and printing the number of users and items here: +# https://github.com/tensorflow/models/blob/d089975f630a8a01be63e45ef08a31be14bb96b4/official/recommendation/data_preprocessing.py#L68 +_NUM_USERS_20M = 138493 +_NUM_ITEMS_20M = 26744 + + +# TODO(reedwm): Support multi-GPU. Currently keras layers, which this model +# uses, ignore variable_scopes, which we rely on for multi-GPU support. +# TODO(reedwm): Support real data. This will require a significant refactor. +# TODO(reedwm): All-reduce IndexedSlices more effectively. +# TODO(reedwm): Support the 1M variant of this model. + + +class NcfModel(model.Model): + r"""A model.Model wrapper around the official NCF recommendation model. + + To do an NCF run with synthetic data that roughly matches what the official + model does, run: + + python tf_cnn_benchmarks.py --optimizer=adam --model=ncf --batch_size=65536 \ + --weight_decay=0 --sparse_to_dense_grads + """ + + def __init__(self, params=None): + super(NcfModel, self).__init__( + 'official_ncf', batch_size=2048, learning_rate=0.0005, + fp16_loss_scale=128, params=params) + if self.fp16_vars: + raise ValueError('NCF model only supports float32 variables for now.') + + def build_network(self, inputs, phase_train=True, nclass=1001): + try: + from official.recommendation import neumf_model # pylint: disable=g-import-not-at-top + except ImportError as e: + if 'neumf_model' not in e.message: + raise + raise ImportError('To use the experimental NCF model, you must clone the ' + 'repo https://github.com/tensorflow/models and add ' + 'tensorflow/models to the PYTHONPATH.') + del nclass + + users, items, _ = inputs + params = { + 'num_users': _NUM_USERS_20M, + 'num_items': _NUM_ITEMS_20M, + 'model_layers': (256, 256, 128, 64), + 'mf_dim': 64, + 'mf_regularization': 0, + 'mlp_reg_layers': (0, 0, 0, 0), + 'use_tpu': False + } + user_input = tf.keras.layers.Input(tensor=users, name='user_input') + item_input = tf.keras.layers.Input(tensor=items, name='item_input') + if self.data_type == tf.float32: + keras_model = neumf_model.construct_model(user_input, item_input, params) + logits = keras_model.output + else: + assert self.data_type == tf.float16 + old_floatx = tf.keras.backend.floatx() + try: + tf.keras.backend.set_floatx('float16') + # We cannot rely on the variable_scope's fp16 custom getter here, + # because the NCF model uses keras layers, which ignore variable scopes. + # So we use a variable_creator_scope instead. + with tf.variable_creator_scope(_fp16_variable_creator): + keras_model = neumf_model.construct_model(user_input, item_input, + params) + logits = tf.cast(keras_model.output, tf.float32) + finally: + tf.keras.backend.set_floatx(old_floatx) + return model.BuildNetworkResult(logits=logits, extra_info=None) + + def loss_function(self, inputs, build_network_result): + logits = build_network_result.logits + + # Softmax with the first column of ones is equivalent to sigmoid. + # TODO(reedwm): Actually, the first column should be zeros to be equivalent + # to sigmoid. But, we keep it at ones to match the official models. + logits = tf.concat([tf.ones(logits.shape, dtype=logits.dtype), logits], + axis=1) + + return tf.losses.sparse_softmax_cross_entropy( + labels=inputs[2], + logits=logits + ) + + def get_synthetic_inputs(self, input_name, nclass): + """Returns the ops to generate synthetic inputs and labels.""" + def users_init_val(): + return tf.random_uniform((self.batch_size, 1), minval=0, + maxval=_NUM_USERS_20M, dtype=tf.int32) + users = tf.Variable(users_init_val, dtype=tf.int32, trainable=False, + collections=[tf.GraphKeys.LOCAL_VARIABLES], + name='synthetic_users') + def items_init_val(): + return tf.random_uniform((self.batch_size, 1), minval=0, + maxval=_NUM_ITEMS_20M, dtype=tf.int32) + items = tf.Variable(items_init_val, dtype=tf.int32, trainable=False, + collections=[tf.GraphKeys.LOCAL_VARIABLES], + name='synthetic_items') + + def labels_init_val(): + return tf.random_uniform((self.batch_size,), minval=0, maxval=2, + dtype=tf.int32) + labels = tf.Variable(labels_init_val, dtype=tf.int32, trainable=False, + collections=[tf.GraphKeys.LOCAL_VARIABLES], + name='synthetic_labels') + + return [users, items, labels] + + def get_input_shapes(self, subset): + del subset + return [[self.batch_size, 1], [self.batch_size, 1], [self.batch_size]] + + def get_input_data_types(self, subset): + del subset + return [self.int32, tf.int32, tf.int32] + + +def _fp16_variable_creator(next_creator, **kwargs): + """Variable creator to create variables in fp32 and cast them to fp16.""" + dtype = kwargs.get('dtype', None) + initial_value = kwargs.get('initial_value', None) + if dtype is None: + if initial_value is not None and not callable(initial_value): + dtype = initial_value.dtype + if dtype == tf.float16: + if callable(initial_value): + new_initial_value = lambda: tf.cast(initial_value(), tf.float32) + else: + new_initial_value = tf.cast(initial_value, tf.float32) + kwargs['dtype'] = tf.float32 + kwargs['initial_value'] = new_initial_value + var = next_creator(**kwargs) + return tf.cast(var, dtype=tf.float16) + else: + return next_creator(**kwargs) + diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/googlenet_model.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/googlenet_model.py new file mode 100644 index 00000000..3505594e --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/googlenet_model.py @@ -0,0 +1,63 @@ +# 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. +# ============================================================================== +"""Googlenet model configuration. + +References: + Szegedy, Christian, Wei Liu, Yangqing Jia, Pierre Sermanet, Scott Reed, + Dragomir Anguelov, Dumitru Erhan, Vincent Vanhoucke, and Andrew Rabinovich + Going deeper with convolutions + arXiv preprint arXiv:1409.4842 (2014) +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from models import model + + +class GooglenetModel(model.CNNModel): + """GoogLeNet.""" + + def __init__(self, params=None): + super(GooglenetModel, self).__init__( + 'googlenet', 224, 32, 0.005, params=params) + + def add_inference(self, cnn): + + def inception_v1(cnn, k, l, m, n, p, q): + cols = [[('conv', k, 1, 1)], [('conv', l, 1, 1), ('conv', m, 3, 3)], + [('conv', n, 1, 1), ('conv', p, 5, 5)], + [('mpool', 3, 3, 1, 1, 'SAME'), ('conv', q, 1, 1)]] + cnn.inception_module('incept_v1', cols) + + cnn.conv(64, 7, 7, 2, 2) + cnn.mpool(3, 3, 2, 2, mode='SAME') + cnn.conv(64, 1, 1) + cnn.conv(192, 3, 3) + cnn.mpool(3, 3, 2, 2, mode='SAME') + inception_v1(cnn, 64, 96, 128, 16, 32, 32) + inception_v1(cnn, 128, 128, 192, 32, 96, 64) + cnn.mpool(3, 3, 2, 2, mode='SAME') + inception_v1(cnn, 192, 96, 208, 16, 48, 64) + inception_v1(cnn, 160, 112, 224, 24, 64, 64) + inception_v1(cnn, 128, 128, 256, 24, 64, 64) + inception_v1(cnn, 112, 144, 288, 32, 64, 64) + inception_v1(cnn, 256, 160, 320, 32, 128, 128) + cnn.mpool(3, 3, 2, 2, mode='SAME') + inception_v1(cnn, 256, 160, 320, 32, 128, 128) + inception_v1(cnn, 384, 192, 384, 48, 128, 128) + cnn.apool(7, 7, 1, 1, mode='VALID') + cnn.reshape([-1, 1024]) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/inception_model.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/inception_model.py new file mode 100644 index 00000000..b8835edb --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/inception_model.py @@ -0,0 +1,213 @@ +# 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. +# ============================================================================== + +"""Inception model configuration. + +Includes multiple models: inception3, inception4, inception-resnet2. + +References: + Christian Szegedy, Sergey Ioffe, Vincent Vanhoucke, Alex Alemi + Inception-v4, Inception-ResNet and the Impact of Residual Connections on + Learning + + Christian Szegedy, Wei Liu, Yangqing Jia, Pierre Sermanet, Scott Reed, + Dragomir Anguelov, Dumitru Erhan, Vincent Vanhoucke, Andrew Rabinovich + Going Deeper with Convolutions + http://arxiv.org/pdf/1409.4842v1.pdf + + Christian Szegedy, Vincent Vanhoucke, Sergey Ioffe, Jonathon Shlens, + Zbigniew Wojna + Rethinking the Inception Architecture for Computer Vision + arXiv preprint arXiv:1512.00567 (2015) + + Inception v3 model: http://arxiv.org/abs/1512.00567 + + Inception v4 and Resnet V2 architectures: http://arxiv.org/abs/1602.07261 +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from six.moves import xrange # pylint: disable=redefined-builtin +from models import model + + +class Inceptionv3Model(model.CNNModel): + """InceptionV3.""" + + def __init__(self, auxiliary=False, params=None): + self._auxiliary = auxiliary + super(Inceptionv3Model, self).__init__( + 'inception3', 299, 32, 0.005, params=params) + + def add_inference(self, cnn): + def inception_v3_a(cnn, n): + cols = [[('conv', 64, 1, 1)], [('conv', 48, 1, 1), ('conv', 64, 5, 5)], + [('conv', 64, 1, 1), ('conv', 96, 3, 3), ('conv', 96, 3, 3)], + [('apool', 3, 3, 1, 1, 'SAME'), ('conv', n, 1, 1)]] + cnn.inception_module('incept_v3_a', cols) + + def inception_v3_b(cnn): + cols = [[('conv', 384, 3, 3, 2, 2, 'VALID')], + [('conv', 64, 1, 1), + ('conv', 96, 3, 3), + ('conv', 96, 3, 3, 2, 2, 'VALID')], + [('mpool', 3, 3, 2, 2, 'VALID')]] + cnn.inception_module('incept_v3_b', cols) + + def inception_v3_c(cnn, n): + cols = [[('conv', 192, 1, 1)], + [('conv', n, 1, 1), ('conv', n, 1, 7), ('conv', 192, 7, 1)], + [('conv', n, 1, 1), ('conv', n, 7, 1), ('conv', n, 1, 7), + ('conv', n, 7, 1), ('conv', 192, 1, 7)], + [('apool', 3, 3, 1, 1, 'SAME'), ('conv', 192, 1, 1)]] + cnn.inception_module('incept_v3_c', cols) + + def inception_v3_d(cnn): + cols = [[('conv', 192, 1, 1), ('conv', 320, 3, 3, 2, 2, 'VALID')], + [('conv', 192, 1, 1), ('conv', 192, 1, 7), ('conv', 192, 7, 1), + ('conv', 192, 3, 3, 2, 2, 'VALID')], + [('mpool', 3, 3, 2, 2, 'VALID')]] + cnn.inception_module('incept_v3_d', cols) + + def inception_v3_e(cnn, pooltype): + cols = [[('conv', 320, 1, 1)], [('conv', 384, 1, 1), ('conv', 384, 1, 3)], + [('share',), ('conv', 384, 3, 1)], + [('conv', 448, 1, 1), ('conv', 384, 3, 3), ('conv', 384, 1, 3)], + [('share',), ('share',), ('conv', 384, 3, 1)], + [('mpool' if pooltype == 'max' else 'apool', 3, 3, 1, 1, 'SAME'), + ('conv', 192, 1, 1)]] + cnn.inception_module('incept_v3_e', cols) + + def incept_v3_aux(cnn): + assert cnn.aux_top_layer is None + cnn.aux_top_layer = cnn.top_layer + cnn.aux_top_size = cnn.top_size + with cnn.switch_to_aux_top_layer(): + cnn.apool(5, 5, 3, 3, mode='VALID') + cnn.conv(128, 1, 1, mode='SAME') + cnn.conv(768, 5, 5, mode='VALID', stddev=0.01) + cnn.reshape([-1, 768]) + + cnn.use_batch_norm = True + cnn.conv(32, 3, 3, 2, 2, mode='VALID') # 299 x 299 x 3 + cnn.conv(32, 3, 3, 1, 1, mode='VALID') # 149 x 149 x 32 + cnn.conv(64, 3, 3, 1, 1, mode='SAME') # 147 x 147 x 64 + cnn.mpool(3, 3, 2, 2, mode='VALID') # 147 x 147 x 64 + cnn.conv(80, 1, 1, 1, 1, mode='VALID') # 73 x 73 x 80 + cnn.conv(192, 3, 3, 1, 1, mode='VALID') # 71 x 71 x 192 + cnn.mpool(3, 3, 2, 2, 'VALID') # 35 x 35 x 192 + inception_v3_a(cnn, 32) # 35 x 35 x 256 mixed. + inception_v3_a(cnn, 64) # 35 x 35 x 288 mixed_1. + inception_v3_a(cnn, 64) # 35 x 35 x 288 mixed_2 + inception_v3_b(cnn) # 17 x 17 x 768 mixed_3 + inception_v3_c(cnn, 128) # 17 x 17 x 768 mixed_4 + inception_v3_c(cnn, 160) # 17 x 17 x 768 mixed_5 + inception_v3_c(cnn, 160) # 17 x 17 x 768 mixed_6 + inception_v3_c(cnn, 192) # 17 x 17 x 768 mixed_7 + if self._auxiliary: + incept_v3_aux(cnn) # Auxillary Head logits + inception_v3_d(cnn) # 17 x 17 x 1280 mixed_8 + inception_v3_e(cnn, 'avg') # 8 x 8 x 2048 mixed_9 + inception_v3_e(cnn, 'max') # 8 x 8 x 2048 mixed_10 + cnn.apool(8, 8, 1, 1, 'VALID') # 8 x 8 x 2048 + cnn.reshape([-1, 2048]) # 1 x 1 x 2048 + + +# Stem functions +def inception_v4_sa(cnn): + cols = [[('mpool', 3, 3, 2, 2, 'VALID')], [('conv', 96, 3, 3, 2, 2, 'VALID')]] + cnn.inception_module('incept_v4_sa', cols) + + +def inception_v4_sb(cnn): + cols = [[('conv', 64, 1, 1), ('conv', 96, 3, 3, 1, 1, 'VALID')], + [('conv', 64, 1, 1), ('conv', 64, 7, 1), ('conv', 64, 1, 7), + ('conv', 96, 3, 3, 1, 1, 'VALID')]] + cnn.inception_module('incept_v4_sb', cols) + + +def inception_v4_sc(cnn): + cols = [[('conv', 192, 3, 3, 2, 2, 'VALID')], + [('mpool', 3, 3, 2, 2, 'VALID')]] + cnn.inception_module('incept_v4_sc', cols) + + +# Reduction functions +def inception_v4_ra(cnn, k, l, m, n): + cols = [ + [('mpool', 3, 3, 2, 2, 'VALID')], [('conv', n, 3, 3, 2, 2, 'VALID')], + [('conv', k, 1, 1), ('conv', l, 3, 3), ('conv', m, 3, 3, 2, 2, 'VALID')] + ] + cnn.inception_module('incept_v4_ra', cols) + + +def inception_v4_rb(cnn): + cols = [[('mpool', 3, 3, 2, 2, 'VALID')], + [('conv', 192, 1, 1), ('conv', 192, 3, 3, 2, 2, 'VALID')], + [('conv', 256, 1, 1), ('conv', 256, 1, 7), ('conv', 320, 7, 1), + ('conv', 320, 3, 3, 2, 2, 'VALID')]] + cnn.inception_module('incept_v4_rb', cols) + + +class Inceptionv4Model(model.CNNModel): + """Inceptionv4.""" + + def __init__(self, params=None): + super(Inceptionv4Model, self).__init__( + 'inception4', 299, 32, 0.005, params=params) + + def add_inference(self, cnn): + def inception_v4_a(cnn): + cols = [[('apool', 3, 3, 1, 1, 'SAME'), ('conv', 96, 1, 1)], + [('conv', 96, 1, 1)], [('conv', 64, 1, 1), ('conv', 96, 3, 3)], + [('conv', 64, 1, 1), ('conv', 96, 3, 3), ('conv', 96, 3, 3)]] + cnn.inception_module('incept_v4_a', cols) + + def inception_v4_b(cnn): + cols = [[('apool', 3, 3, 1, 1, 'SAME'), ('conv', 128, 1, 1)], + [('conv', 384, 1, 1)], + [('conv', 192, 1, 1), ('conv', 224, 1, 7), ('conv', 256, 7, 1)], + [('conv', 192, 1, 1), ('conv', 192, 1, 7), ('conv', 224, 7, 1), + ('conv', 224, 1, 7), ('conv', 256, 7, 1)]] + cnn.inception_module('incept_v4_b', cols) + + def inception_v4_c(cnn): + cols = [[('apool', 3, 3, 1, 1, 'SAME'), ('conv', 256, 1, 1)], + [('conv', 256, 1, 1)], [('conv', 384, 1, 1), ('conv', 256, 1, 3)], + [('share',), ('conv', 256, 3, 1)], + [('conv', 384, 1, 1), ('conv', 448, 1, 3), ('conv', 512, 3, 1), + ('conv', 256, 3, 1)], [('share',), ('share',), ('share',), + ('conv', 256, 1, 3)]] + cnn.inception_module('incept_v4_c', cols) + + cnn.use_batch_norm = True + cnn.conv(32, 3, 3, 2, 2, mode='VALID') + cnn.conv(32, 3, 3, 1, 1, mode='VALID') + cnn.conv(64, 3, 3) + inception_v4_sa(cnn) + inception_v4_sb(cnn) + inception_v4_sc(cnn) + for _ in xrange(4): + inception_v4_a(cnn) + inception_v4_ra(cnn, 192, 224, 256, 384) + for _ in xrange(7): + inception_v4_b(cnn) + inception_v4_rb(cnn) + for _ in xrange(3): + inception_v4_c(cnn) + cnn.spatial_mean() + cnn.dropout(0.8) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/cloud_lib_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/lenet_model.py similarity index 55% rename from TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/cloud_lib_test.py rename to TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/lenet_model.py index 901576d2..0218daae 100644 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/logs/cloud_lib_test.py +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/lenet_model.py @@ -13,36 +13,32 @@ # limitations under the License. # ============================================================================== -"""Tests for cloud_lib.""" +"""Lenet model configuration. + +References: + LeCun, Yann, Leon Bottou, Yoshua Bengio, and Patrick Haffner + Gradient-based learning applied to document recognition + Proceedings of the IEEE (1998) +""" from __future__ import absolute_import from __future__ import division from __future__ import print_function -import unittest - -import mock -import requests - -from official.utils.logs import cloud_lib - - -class CloudLibTest(unittest.TestCase): - - @mock.patch("requests.get") - def test_on_gcp(self, mock_requests_get): - mock_response = mock.MagicMock() - mock_requests_get.return_value = mock_response - mock_response.status_code = 200 - - self.assertEqual(cloud_lib.on_gcp(), True) +from models import model - @mock.patch("requests.get") - def test_not_on_gcp(self, mock_requests_get): - mock_requests_get.side_effect = requests.exceptions.ConnectionError() - self.assertEqual(cloud_lib.on_gcp(), False) +class Lenet5Model(model.CNNModel): + """Lenet5.""" + def __init__(self, params=None): + super(Lenet5Model, self).__init__('lenet5', 28, 32, 0.005, params=params) -if __name__ == "__main__": - unittest.main() + def add_inference(self, cnn): + # Note: This matches TF's MNIST tutorial model + cnn.conv(32, 5, 5) + cnn.mpool(2, 2) + cnn.conv(64, 5, 5) + cnn.mpool(2, 2) + cnn.reshape([-1, 64 * 7 * 7]) + cnn.affine(512) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/model.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/model.py new file mode 100644 index 00000000..3db13081 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/model.py @@ -0,0 +1,340 @@ +# 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. +# ============================================================================== +"""Base model configuration for CNN benchmarks.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from collections import namedtuple + +import tensorflow.compat.v1 as tf + +import convnet_builder +import mlperf +from tensorflow.python.ops import variables as variables_module # pylint: disable=g-direct-tensorflow-import + +# BuildNetworkResult encapsulate the result (e.g. logits) of a +# Model.build_network() call. +BuildNetworkResult = namedtuple( + 'BuildNetworkResult', + [ + 'logits', # logits of the network + 'extra_info', # Model specific extra information + ]) + + +class Model(object): + """Base model config for DNN benchmarks.""" + + def __init__(self, + model_name, + batch_size, + learning_rate, + fp16_loss_scale, + params=None): + self.model_name = model_name + self.batch_size = batch_size + self.default_batch_size = batch_size + self.learning_rate = learning_rate + # TODO(reedwm) Set custom loss scales for each model instead of using the + # default of 128. + self.fp16_loss_scale = fp16_loss_scale + + # use_tf_layers specifies whether to build the model using tf.layers. + # fp16_vars specifies whether to create the variables in float16. + if params: + self.use_tf_layers = params.use_tf_layers + self.fp16_vars = params.fp16_vars + self.data_type = tf.float16 if params.use_fp16 else tf.float32 + else: + self.use_tf_layers = True + self.fp16_vars = False + self.data_type = tf.float32 + + def get_model_name(self): + return self.model_name + + def get_batch_size(self): + return self.batch_size + + def set_batch_size(self, batch_size): + self.batch_size = batch_size + + def get_default_batch_size(self): + return self.default_batch_size + + def get_fp16_loss_scale(self): + return self.fp16_loss_scale + + def filter_l2_loss_vars(self, variables): + """Filters out variables that the L2 loss should not be computed for. + + By default, this filters out batch normalization variables and keeps all + other variables. This behavior can be overridden by subclasses. + + Args: + variables: A list of the trainable variables. + + Returns: + A list of variables that the L2 loss should be computed for. + """ + mlperf.logger.log(key=mlperf.tags.MODEL_EXCLUDE_BN_FROM_L2, + value=True) + return [v for v in variables if 'batchnorm' not in v.name] + + def get_learning_rate(self, global_step, batch_size): + del global_step + del batch_size + return self.learning_rate + + def get_input_shapes(self, subset): + """Returns the list of expected shapes of all the inputs to this model.""" + del subset + raise NotImplementedError('Must be implemented in derived classes') + + def get_input_data_types(self, subset): + """Returns the list of data types of all the inputs to this model.""" + del subset + raise NotImplementedError('Must be implemented in derived classes') + + def get_synthetic_inputs(self, input_name, nclass): + """Returns the ops to generate synthetic inputs.""" + raise NotImplementedError('Must be implemented in derived classes') + + def build_network(self, inputs, phase_train, nclass): + """Builds the forward pass of the model. + + Args: + inputs: The list of inputs, including labels + phase_train: True during training. False during evaluation. + nclass: Number of classes that the inputs can belong to. + + Returns: + A BuildNetworkResult which contains the logits and model-specific extra + information. + """ + raise NotImplementedError('Must be implemented in derived classes') + + def loss_function(self, inputs, build_network_result): + """Returns the op to measure the loss of the model. + + Args: + inputs: the input list of the model. + build_network_result: a BuildNetworkResult returned by build_network(). + + Returns: + The loss tensor of the model. + """ + raise NotImplementedError('Must be implemented in derived classes') + + # TODO(laigd): have accuracy_function() take build_network_result instead. + def accuracy_function(self, inputs, logits): + """Returns the ops to measure the accuracy of the model.""" + raise NotImplementedError('Must be implemented in derived classes') + + def postprocess(self, results): + """Postprocess results returned from model in Python.""" + return results + + def reached_target(self): + """Define custom methods to stop training when model's target is reached.""" + return False + + +class CNNModel(Model): + """Base model configuration for CNN benchmarks.""" + + # TODO(laigd): reduce the number of parameters and read everything from + # params. + def __init__(self, + model, + image_size, + batch_size, + learning_rate, + layer_counts=None, + fp16_loss_scale=128, + params=None): + super(CNNModel, self).__init__( + model, batch_size, learning_rate, fp16_loss_scale, + params=params) + self.image_size = image_size + self.layer_counts = layer_counts + self.depth = 3 + self.params = params + self.data_format = params.data_format if params else 'NCHW' + + def get_layer_counts(self): + return self.layer_counts + + def skip_final_affine_layer(self): + """Returns if the caller of this class should skip the final affine layer. + + Normally, this class adds a final affine layer to the model after calling + self.add_inference(), to generate the logits. If a subclass override this + method to return True, the caller should not add the final affine layer. + + This is useful for tests. + """ + return False + + def add_backbone_saver(self): + """Creates a tf.train.Saver as self.backbone_saver for loading backbone. + + A tf.train.Saver must be created and saved in self.backbone_saver before + calling load_backbone_model, with correct variable name mapping to load + variables from checkpoint correctly into the current model. + """ + raise NotImplementedError(self.getName() + ' does not have backbone model.') + + def load_backbone_model(self, sess, backbone_model_path): + """Loads variable values from a pre-trained backbone model. + + This should be used at the beginning of the training process for transfer + learning models using checkpoints of base models. + + Args: + sess: session to train the model. + backbone_model_path: path to backbone model checkpoint file. + """ + del sess, backbone_model_path + raise NotImplementedError(self.getName() + ' does not have backbone model.') + + def add_inference(self, cnn): + """Adds the core layers of the CNN's forward pass. + + This should build the forward pass layers, except for the initial transpose + of the images and the final Dense layer producing the logits. The layers + should be build with the ConvNetBuilder `cnn`, so that when this function + returns, `cnn.top_layer` and `cnn.top_size` refer to the last layer and the + number of units of the layer layer, respectively. + + Args: + cnn: A ConvNetBuilder to build the forward pass layers with. + """ + del cnn + raise NotImplementedError('Must be implemented in derived classes') + + def get_input_data_types(self, subset): + """Return data types of inputs for the specified subset.""" + del subset # Same types for both 'train' and 'validation' subsets. + return [self.data_type, tf.int32] + + def get_input_shapes(self, subset): + """Return data shapes of inputs for the specified subset.""" + del subset # Same shapes for both 'train' and 'validation' subsets. + # Each input is of shape [batch_size, height, width, depth] + # Each label is of shape [batch_size] + return [[self.batch_size, self.image_size, self.image_size, self.depth], + [self.batch_size]] + + def get_synthetic_inputs(self, input_name, nclass): + # Synthetic input should be within [0, 255]. + image_shape, label_shape = self.get_input_shapes('train') + inputs = tf.truncated_normal( + image_shape, + dtype=self.data_type, + mean=127, + stddev=60, + name=self.model_name + '_synthetic_inputs') + inputs = variables_module.VariableV1( + inputs, trainable=False, collections=[tf.GraphKeys.LOCAL_VARIABLES], + name=input_name) + labels = tf.random_uniform( + label_shape, + minval=0, + maxval=nclass - 1, + dtype=tf.int32, + name=self.model_name + '_synthetic_labels') + return (inputs, labels) + + def gpu_preprocess_nhwc(self, images, phase_train=True): + del phase_train + return images + + def build_network(self, + inputs, + phase_train=True, + nclass=1001): + """Returns logits from input images. + + Args: + inputs: The input images and labels + phase_train: True during training. False during evaluation. + nclass: Number of classes that the images can belong to. + + Returns: + A BuildNetworkResult which contains the logits and model-specific extra + information. + """ + images = inputs[0] + images = self.gpu_preprocess_nhwc(images, phase_train) + if self.data_format == 'NCHW': + images = tf.transpose(images, [0, 3, 1, 2]) + var_type = tf.float32 + if self.data_type == tf.float16 and self.fp16_vars: + var_type = tf.float16 + network = convnet_builder.ConvNetBuilder( + images, self.depth, phase_train, self.use_tf_layers, self.data_format, + self.data_type, var_type) + with tf.variable_scope('cg', custom_getter=network.get_custom_getter()): + self.add_inference(network) + # Add the final fully-connected class layer + logits = ( + network.affine(nclass, activation='linear') + if not self.skip_final_affine_layer() else network.top_layer) + mlperf.logger.log(key=mlperf.tags.MODEL_HP_FINAL_SHAPE, + value=logits.shape.as_list()[1:]) + aux_logits = None + if network.aux_top_layer is not None: + with network.switch_to_aux_top_layer(): + aux_logits = network.affine(nclass, activation='linear', stddev=0.001) + if self.data_type == tf.float16: + # TODO(reedwm): Determine if we should do this cast here. + logits = tf.cast(logits, tf.float32) + if aux_logits is not None: + aux_logits = tf.cast(aux_logits, tf.float32) + return BuildNetworkResult( + logits=logits, extra_info=None if aux_logits is None else aux_logits) + + def loss_function(self, inputs, build_network_result): + """Returns the op to measure the loss of the model.""" + logits = build_network_result.logits + _, labels = inputs + # TODO(laigd): consider putting the aux logit in the Inception model, + # which could call super.loss_function twice, once with the normal logits + # and once with the aux logits. + aux_logits = build_network_result.extra_info + with tf.name_scope('xentropy'): + mlperf.logger.log(key=mlperf.tags.MODEL_HP_LOSS_FN, value=mlperf.tags.CCE) + cross_entropy = tf.losses.sparse_softmax_cross_entropy( + logits=logits, labels=labels) + loss = tf.reduce_mean(cross_entropy, name='xentropy_mean') + if aux_logits is not None: + with tf.name_scope('aux_xentropy'): + aux_cross_entropy = tf.losses.sparse_softmax_cross_entropy( + logits=aux_logits, labels=labels) + aux_loss = 0.4 * tf.reduce_mean(aux_cross_entropy, name='aux_loss') + loss = tf.add_n([loss, aux_loss]) + return loss + + def accuracy_function(self, inputs, logits): + """Returns the ops to measure the accuracy of the model.""" + _, labels = inputs + top_1_op = tf.reduce_sum( + tf.cast(tf.nn.in_top_k(logits, labels, 1), self.data_type)) + top_5_op = tf.reduce_sum( + tf.cast(tf.nn.in_top_k(logits, labels, 5), self.data_type)) + return {'top_1_accuracy': top_1_op, 'top_5_accuracy': top_5_op} diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/model_config.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/model_config.py new file mode 100644 index 00000000..962bb164 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/model_config.py @@ -0,0 +1,181 @@ +# 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. +# ============================================================================== + +"""Model configurations for CNN benchmarks. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from functools import partial + +from models import alexnet_model +from models import densenet_model +from models import googlenet_model +from models import inception_model +from models import lenet_model +from models import official_resnet_model +from models import overfeat_model +from models import resnet_model +from models import trivial_model +from models import vgg_model +from models.experimental import deepspeech +from models.experimental import official_ncf_model + + +_model_name_to_imagenet_model = { + 'vgg11': vgg_model.Vgg11Model, + 'vgg16': vgg_model.Vgg16Model, + 'vgg19': vgg_model.Vgg19Model, + 'lenet': lenet_model.Lenet5Model, + 'googlenet': googlenet_model.GooglenetModel, + 'overfeat': overfeat_model.OverfeatModel, + 'alexnet': alexnet_model.AlexnetModel, + 'trivial': trivial_model.TrivialModel, + 'inception3': inception_model.Inceptionv3Model, + 'inception4': inception_model.Inceptionv4Model, + 'official_resnet18_v2': + partial(official_resnet_model.ImagenetResnetModel, 18), + 'official_resnet34_v2': + partial(official_resnet_model.ImagenetResnetModel, 34), + 'official_resnet50_v2': + partial(official_resnet_model.ImagenetResnetModel, 50), + 'official_resnet101_v2': + partial(official_resnet_model.ImagenetResnetModel, 101), + 'official_resnet152_v2': + partial(official_resnet_model.ImagenetResnetModel, 152), + 'official_resnet200_v2': + partial(official_resnet_model.ImagenetResnetModel, 200), + 'official_resnet18': + partial(official_resnet_model.ImagenetResnetModel, 18, version=1), + 'official_resnet34': + partial(official_resnet_model.ImagenetResnetModel, 34, version=1), + 'official_resnet50': + partial(official_resnet_model.ImagenetResnetModel, 50, version=1), + 'official_resnet101': + partial(official_resnet_model.ImagenetResnetModel, 101, version=1), + 'official_resnet152': + partial(official_resnet_model.ImagenetResnetModel, 152, version=1), + 'official_resnet200': + partial(official_resnet_model.ImagenetResnetModel, 200, version=1), + 'resnet50': resnet_model.create_resnet50_model, + 'resnet50_v1.5': resnet_model.create_resnet50_v1_5_model, + 'resnet50_v2': resnet_model.create_resnet50_v2_model, + 'resnet101': resnet_model.create_resnet101_model, + 'resnet101_v2': resnet_model.create_resnet101_v2_model, + 'resnet152': resnet_model.create_resnet152_model, + 'resnet152_v2': resnet_model.create_resnet152_v2_model, + 'ncf': official_ncf_model.NcfModel, +} + + +_model_name_to_cifar_model = { + 'alexnet': alexnet_model.AlexnetCifar10Model, + 'resnet20': resnet_model.create_resnet20_cifar_model, + 'resnet20_v2': resnet_model.create_resnet20_v2_cifar_model, + 'resnet32': resnet_model.create_resnet32_cifar_model, + 'resnet32_v2': resnet_model.create_resnet32_v2_cifar_model, + 'resnet44': resnet_model.create_resnet44_cifar_model, + 'resnet44_v2': resnet_model.create_resnet44_v2_cifar_model, + 'resnet56': resnet_model.create_resnet56_cifar_model, + 'resnet56_v2': resnet_model.create_resnet56_v2_cifar_model, + 'resnet110': resnet_model.create_resnet110_cifar_model, + 'resnet110_v2': resnet_model.create_resnet110_v2_cifar_model, + 'trivial': trivial_model.TrivialCifar10Model, + 'densenet40_k12': densenet_model.create_densenet40_k12_model, + 'densenet100_k12': densenet_model.create_densenet100_k12_model, + 'densenet100_k24': densenet_model.create_densenet100_k24_model, +} + + +_model_name_to_object_detection_model = { + 'trivial': trivial_model.TrivialSSD300Model, +} + + +def _get_model_map(dataset_name): + """Get name to model map for specified dataset.""" + if dataset_name == 'cifar10': + return _model_name_to_cifar_model + elif dataset_name in ('imagenet', 'synthetic'): + return _model_name_to_imagenet_model + elif dataset_name == 'librispeech': + return {'deepspeech2': deepspeech.DeepSpeech2Model} + elif dataset_name == 'coco': + return _model_name_to_object_detection_model + else: + raise ValueError('Invalid dataset name: %s' % dataset_name) + + +# A model map dict can have this string as a value when TF2 is used, to indicate +# the model is only available in TF1. +_TF1_ONLY_STRING = 'TF1_ONLY' + + +def get_model_config(model_name, dataset, params): + """Map model name to model network configuration.""" + model_map = _get_model_map(dataset.name) + if model_name not in model_map: + raise ValueError('Invalid model name \'%s\' for dataset \'%s\'' % + (model_name, dataset.name)) + model = model_map[model_name](params=params) + if model == 'TF1_ONLY': + raise ValueError('Model \'%s\' can only be used with TensorFlow 1' + % (model_name,)) + return model + + +def register_model(model_name, dataset_name, model_func): + """Register a new model that can be obtained with `get_model_config`.""" + model_map = _get_model_map(dataset_name) + if model_name in model_map: + raise ValueError('Model "%s" is already registered for dataset "%s"' % + (model_name, dataset_name)) + model_map[model_name] = model_func + + +# pylint: disable=g-import-not-at-top +try: + from tensorflow.contrib import slim # pylint: disable=unused-import + can_import_contrib = True +except ImportError: + can_import_contrib = False + + +def register_tf1_models(): + """Registers all the TensorFlow 1-only models. + + TF 1-only models use contrib, which was removed in TF 2. If contrib can be + imported, the TF 1-only models are registered normally. If contrib cannot be + imported, the models are registered with the 'TF1_ONLY' string instead, which + will cause an error to be thrown if these models are used. + """ + if can_import_contrib: + from models.tf1_only import mobilenet_v2 + from models.tf1_only import nasnet_model + from models.tf1_only import ssd_model + register_model('mobilenet', 'imagenet', mobilenet_v2.MobilenetModel) + register_model('nasnet', 'imagenet', nasnet_model.NasnetModel) + register_model('nasnetlarge', 'imagenet', nasnet_model.NasnetLargeModel) + register_model('nasnet', 'cifar10', nasnet_model.NasnetCifarModel) + register_model('ssd300', 'coco', ssd_model.SSD300Model) + else: + register_model('mobilenet', 'imagenet', 'TF1_ONLY') + register_model('nasnet', 'imagenet', 'TF1_ONLY') + register_model('nasnetlarge', 'imagenet', 'TF1_ONLY') + register_model('nasnet', 'cifar10', 'TF1_ONLY') + register_model('ssd300', 'coco', 'TF1_ONLY') + diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/official_resnet_model.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/official_resnet_model.py new file mode 100644 index 00000000..997ee03e --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/official_resnet_model.py @@ -0,0 +1,77 @@ +# 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. +# ============================================================================== +"""Import official resnet models.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import tensorflow.compat.v1 as tf +import datasets +from models import model as model_lib + + +class ImagenetResnetModel(model_lib.CNNModel): + """Official resnet models.""" + + def __init__(self, resnet_size, version=2, params=None): + """These are the parameters that work for Imagenet data. + + Args: + resnet_size: The number of convolutional layers needed in the model. + version: 1 or 2 for v1 or v2, respectively. + params: params passed by BenchmarkCNN. + """ + default_batch_sizes = { + 50: 128, + 101: 32, + 152: 32 + } + batch_size = default_batch_sizes.get(resnet_size, 32) + default_learning_rate = 0.0125 * batch_size / 32 + model_name = 'official_resnet_{}_v{}'.format(resnet_size, version) + super(ImagenetResnetModel, self).__init__( + model_name, 224, batch_size, default_learning_rate, params=params) + self.resnet_size = resnet_size + self.version = version + + def get_learning_rate(self, global_step, batch_size): + num_batches_per_epoch = ( + float(datasets.IMAGENET_NUM_TRAIN_IMAGES) / batch_size) + boundaries = [int(num_batches_per_epoch * x) for x in [30, 60, 80, 90]] + values = [1, 0.1, 0.01, 0.001, 0.0001] + adjusted_learning_rate = ( + self.learning_rate / self.default_batch_size * batch_size) + values = [v * adjusted_learning_rate for v in values] + return tf.train.piecewise_constant(global_step, boundaries, values) + + def build_network(self, images, phase_train=True, nclass=1001, + data_type=tf.float32): + # pylint: disable=g-import-not-at-top + try: + from official.r1.resnet.imagenet_main import ImagenetModel + except ImportError: + tf.logging.fatal('Please include tensorflow/models to the PYTHONPATH.') + raise + images = tf.cast(images, data_type) + model_class = ImagenetModel(resnet_size=self.resnet_size, + resnet_version=self.version, + # The official model dtype seems to be ignored, + # as the dtype it uses is the dtype of the input + # images. Doesn't hurt to set it though. + dtype=data_type) + logits = model_class(images, phase_train) + logits = tf.cast(logits, tf.float32) + return model_lib.BuildNetworkResult(logits=logits, extra_info=None) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/overfeat_model.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/overfeat_model.py new file mode 100644 index 00000000..7483bcbf --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/overfeat_model.py @@ -0,0 +1,53 @@ +# 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. +# ============================================================================== +"""Overfeat model configuration. + +References: + OverFeat: Integrated Recognition, Localization and Detection using + Convolutional Networks + Pierre Sermanet, David Eigen, Xiang Zhang, Michael Mathieu, Rob Fergus, + Yann LeCun, 2014 + http://arxiv.org/abs/1312.6229 +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from models import model + + +class OverfeatModel(model.CNNModel): + """OverfeatModel.""" + + def __init__(self, params=None): + super(OverfeatModel, self).__init__( + 'overfeat', 231, 32, 0.005, params=params) + + def add_inference(self, cnn): + # Note: VALID requires padding the images by 3 in width and height + cnn.conv(96, 11, 11, 4, 4, mode='VALID') + cnn.mpool(2, 2) + cnn.conv(256, 5, 5, 1, 1, mode='VALID') + cnn.mpool(2, 2) + cnn.conv(512, 3, 3) + cnn.conv(1024, 3, 3) + cnn.conv(1024, 3, 3) + cnn.mpool(2, 2) + cnn.reshape([-1, 1024 * 6 * 6]) + cnn.affine(3072) + cnn.dropout() + cnn.affine(4096) + cnn.dropout() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/resnet_model.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/resnet_model.py new file mode 100644 index 00000000..3b770f7a --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/resnet_model.py @@ -0,0 +1,480 @@ +# 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. +# ============================================================================== + +"""Resnet model configuration. + +References: + Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun + Deep Residual Learning for Image Recognition + arXiv:1512.03385 (2015) + + Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun + Identity Mappings in Deep Residual Networks + arXiv:1603.05027 (2016) + + Liang-Chieh Chen, George Papandreou, Iasonas Kokkinos, Kevin Murphy, + Alan L. Yuille + DeepLab: Semantic Image Segmentation with Deep Convolutional Nets, + Atrous Convolution, and Fully Connected CRFs + arXiv:1606.00915 (2016) +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np +from six.moves import xrange # pylint: disable=redefined-builtin +import tensorflow.compat.v1 as tf +import datasets +import mlperf +from models import model as model_lib + + +def bottleneck_block_v1(cnn, depth, depth_bottleneck, stride): + """Bottleneck block with identity short-cut for ResNet v1. + + Args: + cnn: the network to append bottleneck blocks. + depth: the number of output filters for this bottleneck block. + depth_bottleneck: the number of bottleneck filters for this block. + stride: Stride used in the first layer of the bottleneck block. + """ + input_layer = cnn.top_layer + in_size = cnn.top_size + name_key = 'resnet_v1' + name = name_key + str(cnn.counts[name_key]) + cnn.counts[name_key] += 1 + + with tf.variable_scope(name): + if depth == in_size: + if stride == 1: + shortcut = input_layer + else: + shortcut = cnn.apool( + 1, 1, stride, stride, input_layer=input_layer, + num_channels_in=in_size) + mlperf.logger.log_projection(input_tensor=input_layer, + output_tensor=shortcut) + else: + shortcut = cnn.conv( + depth, 1, 1, stride, stride, activation=None, + use_batch_norm=True, input_layer=input_layer, + num_channels_in=in_size, bias=None) + cnn.conv(depth_bottleneck, 1, 1, stride, stride, + input_layer=input_layer, num_channels_in=in_size, + use_batch_norm=True, bias=None) + cnn.conv(depth_bottleneck, 3, 3, 1, 1, mode='SAME_RESNET', + use_batch_norm=True, bias=None) + res = cnn.conv(depth, 1, 1, 1, 1, activation=None, + use_batch_norm=True, bias=None) + mlperf.logger.log(key=mlperf.tags.MODEL_HP_SHORTCUT_ADD) + mlperf.logger.log(key=mlperf.tags.MODEL_HP_RELU) + output = tf.nn.relu(shortcut + res) + cnn.top_layer = output + cnn.top_size = depth + + +def bottleneck_block_v1_5(cnn, depth, depth_bottleneck, stride): + """Bottleneck block with identity short-cut for ResNet v1.5. + + ResNet v1.5 is the informal name for ResNet v1 where stride 2 is used in the + first 3x3 convolution of each block instead of the first 1x1 convolution. + + First seen at https://github.com/facebook/fb.resnet.torch. Used in the paper + "Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour" + (arXiv:1706.02677v2) and by fast.ai to train to accuracy in 45 epochs using + multiple image sizes. + + Args: + cnn: the network to append bottleneck blocks. + depth: the number of output filters for this bottleneck block. + depth_bottleneck: the number of bottleneck filters for this block. + stride: Stride used in the first layer of the bottleneck block. + """ + input_layer = cnn.top_layer + in_size = cnn.top_size + name_key = 'resnet_v1.5' + name = name_key + str(cnn.counts[name_key]) + cnn.counts[name_key] += 1 + + with tf.variable_scope(name): + if depth == in_size: + if stride == 1: + shortcut = input_layer + else: + shortcut = cnn.apool( + 1, 1, stride, stride, input_layer=input_layer, + num_channels_in=in_size) + mlperf.logger.log_projection(input_tensor=input_layer, + output_tensor=shortcut) + else: + shortcut = cnn.conv( + depth, 1, 1, stride, stride, activation=None, + use_batch_norm=True, input_layer=input_layer, + num_channels_in=in_size, bias=None) + mlperf.logger.log_projection(input_tensor=input_layer, + output_tensor=shortcut) + cnn.conv(depth_bottleneck, 1, 1, 1, 1, + input_layer=input_layer, num_channels_in=in_size, + use_batch_norm=True, bias=None) + cnn.conv(depth_bottleneck, 3, 3, stride, stride, mode='SAME_RESNET', + use_batch_norm=True, bias=None) + res = cnn.conv(depth, 1, 1, 1, 1, activation=None, + use_batch_norm=True, bias=None) + mlperf.logger.log(key=mlperf.tags.MODEL_HP_SHORTCUT_ADD) + mlperf.logger.log(key=mlperf.tags.MODEL_HP_RELU) + output = tf.nn.relu(shortcut + res) + cnn.top_layer = output + cnn.top_size = depth + + +def bottleneck_block_v2(cnn, depth, depth_bottleneck, stride): + """Bottleneck block with identity short-cut for ResNet v2. + + The main difference from v1 is that a batch norm and relu are done at the + start of the block, instead of the end. This initial batch norm and relu is + collectively called a pre-activation. + + Args: + cnn: the network to append bottleneck blocks. + depth: the number of output filters for this bottleneck block. + depth_bottleneck: the number of bottleneck filters for this block. + stride: Stride used in the first layer of the bottleneck block. + """ + input_layer = cnn.top_layer + in_size = cnn.top_size + name_key = 'resnet_v2' + name = name_key + str(cnn.counts[name_key]) + cnn.counts[name_key] += 1 + + preact = cnn.batch_norm() + mlperf.logger.log(key=mlperf.tags.MODEL_HP_RELU) + preact = tf.nn.relu(preact) + with tf.variable_scope(name): + if depth == in_size: + if stride == 1: + shortcut = input_layer + else: + shortcut = cnn.apool( + 1, 1, stride, stride, input_layer=input_layer, + num_channels_in=in_size) + mlperf.logger.log_projection(input_tensor=input_layer, + output_tensor=shortcut) + else: + shortcut = cnn.conv( + depth, 1, 1, stride, stride, activation=None, use_batch_norm=False, + input_layer=preact, num_channels_in=in_size, bias=None) + cnn.conv(depth_bottleneck, 1, 1, stride, stride, + input_layer=preact, num_channels_in=in_size, + use_batch_norm=True, bias=None) + cnn.conv(depth_bottleneck, 3, 3, 1, 1, mode='SAME_RESNET', + use_batch_norm=True, bias=None) + res = cnn.conv(depth, 1, 1, 1, 1, activation=None, + use_batch_norm=False, bias=None) + mlperf.logger.log(key=mlperf.tags.MODEL_HP_SHORTCUT_ADD) + output = shortcut + res + cnn.top_layer = output + cnn.top_size = depth + + +def bottleneck_block(cnn, depth, depth_bottleneck, stride, version): + """Bottleneck block with identity short-cut. + + Args: + cnn: the network to append bottleneck blocks. + depth: the number of output filters for this bottleneck block. + depth_bottleneck: the number of bottleneck filters for this block. + stride: Stride used in the first layer of the bottleneck block. + version: version of ResNet to build. + """ + mlperf.logger.log(key=mlperf.tags.MODEL_HP_BLOCK_TYPE, + value=mlperf.tags.BOTTLENECK_BLOCK) + mlperf.logger.log_begin_block( + input_tensor=cnn.top_layer, block_type=mlperf.tags.BOTTLENECK_BLOCK) + if version == 'v2': + bottleneck_block_v2(cnn, depth, depth_bottleneck, stride) + elif version == 'v1.5': + bottleneck_block_v1_5(cnn, depth, depth_bottleneck, stride) + else: + bottleneck_block_v1(cnn, depth, depth_bottleneck, stride) + mlperf.logger.log_end_block(output_tensor=cnn.top_layer) + + +def residual_block(cnn, depth, stride, version, projection_shortcut=False): + """Residual block with identity short-cut. + + Args: + cnn: the network to append residual blocks. + depth: the number of output filters for this residual block. + stride: Stride used in the first layer of the residual block. + version: version of ResNet to build. + projection_shortcut: indicator of using projection shortcut, even if top + size and depth are equal + """ + pre_activation = True if version == 'v2' else False + input_layer = cnn.top_layer + in_size = cnn.top_size + + if projection_shortcut: + shortcut = cnn.conv( + depth, 1, 1, stride, stride, activation=None, + use_batch_norm=True, input_layer=input_layer, + num_channels_in=in_size, bias=None) + elif in_size != depth: + # Plan A of shortcut. + shortcut = cnn.apool(1, 1, stride, stride, + input_layer=input_layer, + num_channels_in=in_size) + padding = (depth - in_size) // 2 + if cnn.channel_pos == 'channels_last': + shortcut = tf.pad( + shortcut, [[0, 0], [0, 0], [0, 0], [padding, padding]]) + else: + shortcut = tf.pad( + shortcut, [[0, 0], [padding, padding], [0, 0], [0, 0]]) + else: + shortcut = input_layer + if pre_activation: + res = cnn.batch_norm(input_layer) + res = tf.nn.relu(res) + else: + res = input_layer + cnn.conv(depth, 3, 3, stride, stride, + input_layer=res, num_channels_in=in_size, + use_batch_norm=True, bias=None) + if pre_activation: + res = cnn.conv(depth, 3, 3, 1, 1, activation=None, + use_batch_norm=False, bias=None) + output = shortcut + res + else: + res = cnn.conv(depth, 3, 3, 1, 1, activation=None, + use_batch_norm=True, bias=None) + output = tf.nn.relu(shortcut + res) + cnn.top_layer = output + cnn.top_size = depth + + +class ResnetModel(model_lib.CNNModel): + """Resnet cnn network configuration.""" + + def __init__(self, model, layer_counts, params=None): + default_batch_sizes = { + 'resnet50': 64, + 'resnet101': 32, + 'resnet152': 32, + 'resnet50_v1.5': 64, + 'resnet101_v1.5': 32, + 'resnet152_v1.5': 32, + 'resnet50_v2': 64, + 'resnet101_v2': 32, + 'resnet152_v2': 32, + } + batch_size = default_batch_sizes.get(model, 32) + # The ResNet paper uses a starting lr of .1 at bs=256. + self.base_lr_batch_size = 256 + base_lr = 0.128 + if params and params.resnet_base_lr: + base_lr = params.resnet_base_lr + + super(ResnetModel, self).__init__(model, 224, batch_size, base_lr, + layer_counts, params=params) + if 'v2' in model: + self.version = 'v2' + elif 'v1.5' in model: + self.version = 'v1.5' + else: + self.version = 'v1' + + def add_inference(self, cnn): + if self.layer_counts is None: + raise ValueError('Layer counts not specified for %s' % self.get_model()) + # Drop batch size from shape logging. + mlperf.logger.log(key=mlperf.tags.MODEL_HP_INITIAL_SHAPE, + value=cnn.top_layer.shape.as_list()[1:]) + cnn.use_batch_norm = True + cnn.batch_norm_config = {'decay': 0.9, 'epsilon': 1e-5, 'scale': True} + cnn.conv(64, 7, 7, 2, 2, mode='SAME_RESNET', use_batch_norm=True) + cnn.mpool(3, 3, 2, 2, mode='SAME') + for _ in xrange(self.layer_counts[0]): + bottleneck_block(cnn, 256, 64, 1, self.version) + for i in xrange(self.layer_counts[1]): + stride = 2 if i == 0 else 1 + bottleneck_block(cnn, 512, 128, stride, self.version) + for i in xrange(self.layer_counts[2]): + stride = 2 if i == 0 else 1 + bottleneck_block(cnn, 1024, 256, stride, self.version) + for i in xrange(self.layer_counts[3]): + stride = 2 if i == 0 else 1 + bottleneck_block(cnn, 2048, 512, stride, self.version) + if self.version == 'v2': + cnn.batch_norm() + cnn.top_layer = tf.nn.relu(cnn.top_layer) + cnn.spatial_mean() + + def get_learning_rate(self, global_step, batch_size): + rescaled_lr = self.get_scaled_base_learning_rate(batch_size) + num_batches_per_epoch = ( + datasets.IMAGENET_NUM_TRAIN_IMAGES / batch_size) + boundaries = [int(num_batches_per_epoch * x) for x in [30, 60, 80, 90]] + values = [1, 0.1, 0.01, 0.001, 0.0001] + values = [rescaled_lr * v for v in values] + lr = tf.train.piecewise_constant(global_step, boundaries, values) + warmup_steps = int(num_batches_per_epoch * 5) + mlperf.logger.log(key=mlperf.tags.OPT_LR_WARMUP_STEPS, value=warmup_steps) + warmup_lr = ( + rescaled_lr * tf.cast(global_step, tf.float32) / tf.cast( + warmup_steps, tf.float32)) + return tf.cond(global_step < warmup_steps, lambda: warmup_lr, lambda: lr) + + def get_scaled_base_learning_rate(self, batch_size): + """Calculates base learning rate for creating lr schedule. + + In replicated mode, gradients are summed rather than averaged which, with + the sgd and momentum optimizers, increases the effective learning rate by + lr * num_gpus. Dividing the base lr by num_gpus negates the increase. + + Args: + batch_size: Total batch-size. + + Returns: + Base learning rate to use to create lr schedule. + """ + base_lr = self.learning_rate + if self.params.variable_update == 'replicated': + base_lr = self.learning_rate / self.params.num_gpus + scaled_lr = base_lr * (batch_size / self.base_lr_batch_size) + return scaled_lr + + +def create_resnet50_model(params): + return ResnetModel('resnet50', (3, 4, 6, 3), params=params) + + +def create_resnet50_v1_5_model(params): + return ResnetModel('resnet50_v1.5', (3, 4, 6, 3), params=params) + + +def create_resnet50_v2_model(params): + return ResnetModel('resnet50_v2', (3, 4, 6, 3), params=params) + + +def create_resnet101_model(params): + return ResnetModel('resnet101', (3, 4, 23, 3), params=params) + + +def create_resnet101_v2_model(params): + return ResnetModel('resnet101_v2', (3, 4, 23, 3), params=params) + + +def create_resnet152_model(params): + return ResnetModel('resnet152', (3, 8, 36, 3), params=params) + + +def create_resnet152_v2_model(params): + return ResnetModel('resnet152_v2', (3, 8, 36, 3), params=params) + + +class ResnetCifar10Model(model_lib.CNNModel): + """Resnet cnn network configuration for Cifar 10 dataset. + + V1 model architecture follows the one defined in the paper: + https://arxiv.org/pdf/1512.03385.pdf. + + V2 model architecture follows the one defined in the paper: + https://arxiv.org/pdf/1603.05027.pdf. + """ + + def __init__(self, model, layer_counts, params=None): + if 'v2' in model: + self.version = 'v2' + else: + self.version = 'v1' + super(ResnetCifar10Model, self).__init__( + model, 32, 128, 0.1, layer_counts, params=params) + + def add_inference(self, cnn): + if self.layer_counts is None: + raise ValueError('Layer counts not specified for %s' % self.get_model()) + + cnn.use_batch_norm = True + cnn.batch_norm_config = {'decay': 0.9, 'epsilon': 1e-5, 'scale': True} + if self.version == 'v2': + cnn.conv(16, 3, 3, 1, 1, use_batch_norm=True) + else: + cnn.conv(16, 3, 3, 1, 1, activation=None, use_batch_norm=True) + for i in xrange(self.layer_counts[0]): + # reshape to batch_size x 16 x 32 x 32 + residual_block(cnn, 16, 1, self.version) + for i in xrange(self.layer_counts[1]): + # Subsampling is performed at the first convolution with a stride of 2 + stride = 2 if i == 0 else 1 + # reshape to batch_size x 32 x 16 x 16 + residual_block(cnn, 32, stride, self.version) + for i in xrange(self.layer_counts[2]): + stride = 2 if i == 0 else 1 + # reshape to batch_size x 64 x 8 x 8 + residual_block(cnn, 64, stride, self.version) + if self.version == 'v2': + cnn.batch_norm() + cnn.top_layer = tf.nn.relu(cnn.top_layer) + cnn.spatial_mean() + + def get_learning_rate(self, global_step, batch_size): + num_batches_per_epoch = int(50000 / batch_size) + boundaries = num_batches_per_epoch * np.array([82, 123, 300], + dtype=np.int64) + boundaries = [x for x in boundaries] + values = [0.1, 0.01, 0.001, 0.0002] + return tf.train.piecewise_constant(global_step, boundaries, values) + + +def create_resnet20_cifar_model(params): + return ResnetCifar10Model('resnet20', (3, 3, 3), params=params) + + +def create_resnet20_v2_cifar_model(params): + return ResnetCifar10Model('resnet20_v2', (3, 3, 3), params=params) + + +def create_resnet32_cifar_model(params): + return ResnetCifar10Model('resnet32', (5, 5, 5), params=params) + + +def create_resnet32_v2_cifar_model(params): + return ResnetCifar10Model('resnet32_v2', (5, 5, 5), params=params) + + +def create_resnet44_cifar_model(params): + return ResnetCifar10Model('resnet44', (7, 7, 7), params=params) + + +def create_resnet44_v2_cifar_model(params): + return ResnetCifar10Model('resnet44_v2', (7, 7, 7), params=params) + + +def create_resnet56_cifar_model(params): + return ResnetCifar10Model('resnet56', (9, 9, 9), params=params) + + +def create_resnet56_v2_cifar_model(params): + return ResnetCifar10Model('resnet56_v2', (9, 9, 9), params=params) + + +def create_resnet110_cifar_model(params): + return ResnetCifar10Model('resnet110', (18, 18, 18), params=params) + + +def create_resnet110_v2_cifar_model(params): + return ResnetCifar10Model('resnet110_v2', (18, 18, 18), params=params) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/resnet_model_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/resnet_model_test.py new file mode 100644 index 00000000..b4052fcd --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/resnet_model_test.py @@ -0,0 +1,80 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 resnet_model.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import mock +import tensorflow.compat.v1 as tf + +from models import resnet_model + + +class ResNetModelTest(tf.test.TestCase): + + def testGetScaledBaseLearningRateOneGpuLrFromParams(self): + """Verifies setting params.resnet_base_lr pipes through.""" + lr = self._get_scaled_base_learning_rate(1, + 'parameter_server', + 256, + base_lr=.050) + self.assertEqual(lr, .050) + + def testGetScaledBaseLearningRateOneGpu(self): + lr = self._get_scaled_base_learning_rate(1, 'parameter_server', 128) + self.assertEqual(lr, .064) + + def testGetScaledBaseLearningRateEightGpuReplicated(self): + lr = self._get_scaled_base_learning_rate(8, 'replicated', 256 * 8) + self.assertEqual(lr, .128) + + def testGetScaledBaseLearningRateTwoGpuParameter(self): + lr = self._get_scaled_base_learning_rate(2, 'parameter_server', 256 * 2) + self.assertEqual(lr, .256) + + def testGetScaledBaseLearningRateTwoGpuUneven(self): + lr = self._get_scaled_base_learning_rate(2, 'replicated', 13) + self.assertEqual(lr, 0.0032500000000000003) + + def _get_scaled_base_learning_rate(self, + num_gpus, + variable_update, + batch_size, + base_lr=None): + """Simplifies testing different learning rate calculations. + + Args: + num_gpus: Number of GPUs to be used. + variable_update: Type of variable update used. + batch_size: Total batch size. + base_lr: Base learning rate before scaling. + + Returns: + Base learning rate that would be used to create lr schedule. + """ + params = mock.Mock() + params.num_gpus = num_gpus + params.variable_update = variable_update + if base_lr: + params.resnet_base_lr = base_lr + resnet50_model = resnet_model.ResnetModel('resnet50', 50, params=params) + return resnet50_model.get_scaled_base_learning_rate(batch_size) + + +if __name__ == '__main__': + tf.disable_v2_behavior() + tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/keras_application_models/__init__.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/__init__.py similarity index 100% rename from TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/keras_application_models/__init__.py rename to TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/__init__.py diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/mobilenet.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/mobilenet.py new file mode 100644 index 00000000..c56393c2 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/mobilenet.py @@ -0,0 +1,467 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Mobilenet Base Class, branched from slim for fp16 performance study.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +import collections +import contextlib +import copy +import os + +import tensorflow.compat.v1 as tf +from tensorflow.contrib import slim as contrib_slim + +slim = contrib_slim + + +@slim.add_arg_scope +def apply_activation(x, name=None, activation_fn=None): + return activation_fn(x, name=name) if activation_fn else x + + +def _fixed_padding(inputs, kernel_size, rate=1): + """Pads the input along the spatial dimensions independently of input size. + + Pads the input such that if it was used in a convolution with 'VALID' padding, + the output would have the same dimensions as if the unpadded input was used + in a convolution with 'SAME' padding. + + Args: + inputs: A tensor of size [batch, height_in, width_in, channels]. + kernel_size: The kernel to be used in the conv2d or max_pool2d operation. + rate: An integer, rate for atrous convolution. + + Returns: + output: A tensor of size [batch, height_out, width_out, channels] with the + input, either intact (if kernel_size == 1) or padded (if kernel_size > 1). + """ + kernel_size_effective = [kernel_size[0] + (kernel_size[0] - 1) * (rate - 1), + kernel_size[0] + (kernel_size[0] - 1) * (rate - 1)] + pad_total = [kernel_size_effective[0] - 1, kernel_size_effective[1] - 1] + pad_beg = [pad_total[0] // 2, pad_total[1] // 2] + pad_end = [pad_total[0] - pad_beg[0], pad_total[1] - pad_beg[1]] + padded_inputs = tf.pad(inputs, [[0, 0], [pad_beg[0], pad_end[0]], + [pad_beg[1], pad_end[1]], [0, 0]]) + return padded_inputs + + +def _make_divisible(v, divisor, min_value=None): + if min_value is None: + min_value = divisor + new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) + # Make sure that round down does not go down by more than 10%. + if new_v < 0.9 * v: + new_v += divisor + return new_v + + +@contextlib.contextmanager +def _set_arg_scope_defaults(defaults): + """Sets arg scope defaults for all items present in defaults. + + Args: + defaults: dictionary/list of pairs, containing a mapping from + function to a dictionary of default args. + + Yields: + context manager where all defaults are set. + """ + if hasattr(defaults, 'items'): + items = list(defaults.items()) + else: + items = defaults + if not items: + yield + else: + func, default_arg = items[0] + with slim.arg_scope(func, **default_arg): + with _set_arg_scope_defaults(items[1:]): + yield + + +@slim.add_arg_scope +def depth_multiplier(output_params, + multiplier, + divisible_by=8, + min_depth=8, + **unused_kwargs): + if 'num_outputs' not in output_params: + return + d = output_params['num_outputs'] + output_params['num_outputs'] = _make_divisible(d * multiplier, divisible_by, + min_depth) + + +_Op = collections.namedtuple('Op', ['op', 'params', 'multiplier_func']) + + +def op(opfunc, **params): + multiplier = params.pop('multiplier_transorm', depth_multiplier) + return _Op(opfunc, params=params, multiplier_func=multiplier) + + +class NoOpScope(object): + """No-op context manager.""" + + def __enter__(self): + return + + def __exit__(self, exc_type, exc_value, traceback): + return False + + +def safe_arg_scope(funcs, **kwargs): + """Returns `slim.arg_scope` with all None arguments removed. + + Args: + funcs: Functions to pass to `arg_scope`. + **kwargs: Arguments to pass to `arg_scope`. + + Returns: + arg_scope or No-op context manager. + + Note: can be useful if None value should be interpreted as "do not overwrite + this parameter value". + """ + filtered_args = {name: value for name, value in kwargs.items() + if value is not None} + if filtered_args: + return slim.arg_scope(funcs, **filtered_args) + else: + return NoOpScope() + + +@slim.add_arg_scope +def mobilenet_base( # pylint: disable=invalid-name + inputs, + conv_defs, + multiplier=1.0, + final_endpoint=None, + output_stride=None, + use_explicit_padding=False, + scope=None, + is_training=False): + """Mobilenet base network. + + Constructs a network from inputs to the given final endpoint. By default + the network is constructed in inference mode. To create network + in training mode use: + + with slim.arg_scope(mobilenet.training_scope()): + logits, endpoints = mobilenet_base(...) + + Args: + inputs: a tensor of shape [batch_size, height, width, channels]. + conv_defs: A list of op(...) layers specifying the net architecture. + multiplier: Float multiplier for the depth (number of channels) + for all convolution ops. The value must be greater than zero. Typical + usage will be to set this value in (0, 1) to reduce the number of + parameters or computation cost of the model. + final_endpoint: The name of last layer, for early termination for + for V1-based networks: last layer is "layer_14", for V2: "layer_20" + output_stride: An integer that specifies the requested ratio of input to + output spatial resolution. If not None, then we invoke atrous convolution + if necessary to prevent the network from reducing the spatial resolution + of the activation maps. Allowed values are 1 or any even number, excluding + zero. Typical values are 8 (accurate fully convolutional mode), 16 + (fast fully convolutional mode), and 32 (classification mode). + + NOTE- output_stride relies on all consequent operators to support dilated + operators via "rate" parameter. This might require wrapping non-conv + operators to operate properly. + + use_explicit_padding: Use 'VALID' padding for convolutions, but prepad + inputs so that the output dimensions are the same as if 'SAME' padding + were used. + scope: optional variable scope. + is_training: How to setup batch_norm and other ops. Note: most of the time + this does not need be set directly. Use mobilenet.training_scope() to set + up training instead. This parameter is here for backward compatibility + only. It is safe to set it to the value matching + training_scope(is_training=...). It is also safe to explicitly set + it to False, even if there is outer training_scope set to to training. + (The network will be built in inference mode). If this is set to None, + no arg_scope is added for slim.batch_norm's is_training parameter. + + Returns: + tensor_out: output tensor. + end_points: a set of activations for external use, for example summaries or + losses. + + Raises: + ValueError: depth_multiplier <= 0, or the target output_stride is not + allowed. + """ + if multiplier <= 0: + raise ValueError('multiplier is not greater than zero.') + + # Set conv defs defaults and overrides. + conv_defs_defaults = conv_defs.get('defaults', {}) + conv_defs_overrides = conv_defs.get('overrides', {}) + if use_explicit_padding: + conv_defs_overrides = copy.deepcopy(conv_defs_overrides) + conv_defs_overrides[ + (slim.conv2d, slim.separable_conv2d)] = {'padding': 'VALID'} + + if output_stride is not None: + if output_stride == 0 or (output_stride > 1 and output_stride % 2): + raise ValueError('Output stride must be None, 1 or a multiple of 2.') + + # a) Set the tensorflow scope + # b) set padding to default: note we might consider removing this + # since it is also set by mobilenet_scope + # c) set all defaults + # d) set all extra overrides. + with _scope_all(scope, default_scope='Mobilenet'), \ + safe_arg_scope([slim.batch_norm], is_training=is_training), \ + _set_arg_scope_defaults(conv_defs_defaults), \ + _set_arg_scope_defaults(conv_defs_overrides): + # The current_stride variable keeps track of the output stride of the + # activations, i.e., the running product of convolution strides up to the + # current network layer. This allows us to invoke atrous convolution + # whenever applying the next convolution would result in the activations + # having output stride larger than the target output_stride. + current_stride = 1 + + # The atrous convolution rate parameter. + rate = 1 + + net = inputs + # Insert default parameters before the base scope which includes + # any custom overrides set in mobilenet. + end_points = {} + scopes = {} + for i, opdef in enumerate(conv_defs['spec']): + params = dict(opdef.params) + opdef.multiplier_func(params, multiplier) + stride = params.get('stride', 1) + if output_stride is not None and current_stride == output_stride: + # If we have reached the target output_stride, then we need to employ + # atrous convolution with stride=1 and multiply the atrous rate by the + # current unit's stride for use in subsequent layers. + layer_stride = 1 + layer_rate = rate + rate *= stride + else: + layer_stride = stride + layer_rate = 1 + current_stride *= stride + # Update params. + params['stride'] = layer_stride + # Only insert rate to params if rate > 1. + if layer_rate > 1: + params['rate'] = layer_rate + # Set padding + if use_explicit_padding: + if 'kernel_size' in params: + net = _fixed_padding(net, params['kernel_size'], layer_rate) + else: + params['use_explicit_padding'] = True + + end_point = 'layer_%d' % (i + 1) + try: + net = opdef.op(net, **params) + except Exception: + print('Failed to create op %i: %r params: %r' % (i, opdef, params)) + raise + end_points[end_point] = net + scope = os.path.dirname(net.name) + scopes[scope] = end_point + if final_endpoint is not None and end_point == final_endpoint: + break + + # Add all tensors that end with 'output' to + # endpoints + for t in net.graph.get_operations(): + scope = os.path.dirname(t.name) + bn = os.path.basename(t.name) + if scope in scopes and t.name.endswith('output'): + end_points[scopes[scope] + '/' + bn] = t.outputs[0] + return net, end_points + + +@contextlib.contextmanager +def _scope_all(scope, default_scope=None): + with tf.variable_scope(scope, default_name=default_scope) as s,\ + tf.name_scope(s.original_name_scope): + yield s + + +@slim.add_arg_scope +def mobilenet(inputs, + num_classes=1001, + prediction_fn=slim.softmax, + reuse=None, + scope='Mobilenet', + base_only=False, + **mobilenet_args): + """Mobilenet model for classification, supports both V1 and V2. + + Note: default mode is inference, use mobilenet.training_scope to create + training network. + + + Args: + inputs: a tensor of shape [batch_size, height, width, channels]. + num_classes: number of predicted classes. If 0 or None, the logits layer + is omitted and the input features to the logits layer (before dropout) + are returned instead. + prediction_fn: a function to get predictions out of logits + (default softmax). + reuse: whether or not the network and its variables should be reused. To be + able to reuse 'scope' must be given. + scope: Optional variable_scope. + base_only: if True will only create the base of the network (no pooling + and no logits). + **mobilenet_args: passed to mobilenet_base verbatim. + - conv_defs: list of conv defs + - multiplier: Float multiplier for the depth (number of channels) + for all convolution ops. The value must be greater than zero. Typical + usage will be to set this value in (0, 1) to reduce the number of + parameters or computation cost of the model. + - output_stride: will ensure that the last layer has at most total stride. + If the architecture calls for more stride than that provided + (e.g. output_stride=16, but the architecture has 5 stride=2 operators), + it will replace output_stride with fractional convolutions using Atrous + Convolutions. + + Returns: + logits: the pre-softmax activations, a tensor of size + [batch_size, num_classes] + end_points: a dictionary from components of the network to the corresponding + activation tensor. + + Raises: + ValueError: Input rank is invalid. + """ + is_training = mobilenet_args.get('is_training', False) + input_shape = inputs.get_shape().as_list() + if len(input_shape) != 4: + raise ValueError('Expected rank 4 input, was: %d' % len(input_shape)) + + with tf.variable_scope(scope, 'Mobilenet', reuse=reuse) as scope: + inputs = tf.identity(inputs, 'input') + net, end_points = mobilenet_base(inputs, scope=scope, **mobilenet_args) + if base_only: + return net, end_points + + net = tf.identity(net, name='embedding') + + with tf.variable_scope('Logits'): + net = global_pool(net) + end_points['global_pool'] = net + if not num_classes: + return net, end_points + net = slim.dropout(net, scope='Dropout', is_training=is_training) + # 1 x 1 x num_classes + # Note: legacy scope name. + logits = slim.conv2d( + net, + num_classes, [1, 1], + activation_fn=None, + normalizer_fn=None, + biases_initializer=tf.zeros_initializer(), + scope='Conv2d_1c_1x1') + + logits = tf.squeeze(logits, [1, 2]) + + logits = tf.identity(logits, name='output') + end_points['Logits'] = logits + if prediction_fn: + end_points['Predictions'] = prediction_fn(logits, 'Predictions') + return logits, end_points + + +def global_pool(input_tensor, pool_op=tf.nn.avg_pool): + """Applies avg pool to produce 1x1 output. + + NOTE: This function is funcitonally equivalenet to reduce_mean, but it has + baked in average pool which has better support across hardware. + + Args: + input_tensor: input tensor + pool_op: pooling op (avg pool is default) + Returns: + a tensor batch_size x 1 x 1 x depth. + """ + shape = input_tensor.get_shape().as_list() + if shape[1] is None or shape[2] is None: + kernel_size = tf.convert_to_tensor( + [1, tf.shape(input_tensor)[1], + tf.shape(input_tensor)[2], 1]) + else: + kernel_size = [1, shape[1], shape[2], 1] + output = pool_op( + input_tensor, ksize=kernel_size, strides=[1, 1, 1, 1], padding='VALID') + # Recover output shape, for unknown shape. + output.set_shape([None, 1, 1, None]) + return output + + +def training_scope(is_training=True, + weight_decay=0.00004, + stddev=0.09, + dropout_keep_prob=0.8, + bn_decay=0.997): + """Defines Mobilenet training scope. + + Usage: + with tf.contrib.slim.arg_scope(mobilenet.training_scope()): + logits, endpoints = mobilenet_v2.mobilenet(input_tensor) + + # the network created will be trainble with dropout/batch norm + # initialized appropriately. + Args: + is_training: if set to False this will ensure that all customizations are + set to non-training mode. This might be helpful for code that is reused + across both training/evaluation, but most of the time training_scope with + value False is not needed. If this is set to None, the parameters is not + added to the batch_norm arg_scope. + + weight_decay: The weight decay to use for regularizing the model. + stddev: Standard deviation for initialization, if negative uses xavier. + dropout_keep_prob: dropout keep probability (not set if equals to None). + bn_decay: decay for the batch norm moving averages (not set if equals to + None). + + Returns: + An argument scope to use via arg_scope. + """ + # Note: do not introduce parameters that would change the inference + # model here (for example whether to use bias), modify conv_def instead. + batch_norm_params = { + 'decay': bn_decay, + 'is_training': is_training + } + if stddev < 0: + weight_intitializer = slim.initializers.xavier_initializer() + else: + weight_intitializer = tf.truncated_normal_initializer(stddev=stddev) + + # Set weight_decay for weights in Conv and FC layers. + with slim.arg_scope( + [slim.conv2d, slim.fully_connected, slim.separable_conv2d], + weights_initializer=weight_intitializer, + normalizer_fn=slim.batch_norm), \ + slim.arg_scope([mobilenet_base, mobilenet], is_training=is_training),\ + safe_arg_scope([slim.batch_norm], **batch_norm_params), \ + safe_arg_scope([slim.dropout], is_training=is_training, + keep_prob=dropout_keep_prob), \ + slim.arg_scope([slim.conv2d], \ + weights_regularizer=slim.l2_regularizer(weight_decay)), \ + slim.arg_scope([slim.separable_conv2d], weights_regularizer=None) as s: + return s diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/mobilenet_conv_blocks.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/mobilenet_conv_blocks.py new file mode 100644 index 00000000..34016b27 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/mobilenet_conv_blocks.py @@ -0,0 +1,360 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Convolution blocks for mobilenet.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import contextlib +import functools + +import tensorflow.compat.v1 as tf +from tensorflow.contrib import slim + + +def _fixed_padding(inputs, kernel_size, rate=1): + """Pads the input along the spatial dimensions independently of input size. + + Pads the input such that if it was used in a convolution with 'VALID' padding, + the output would have the same dimensions as if the unpadded input was used + in a convolution with 'SAME' padding. + + Args: + inputs: A tensor of size [batch, height_in, width_in, channels]. + kernel_size: The kernel to be used in the conv2d or max_pool2d operation. + rate: An integer, rate for atrous convolution. + + Returns: + output: A tensor of size [batch, height_out, width_out, channels] with the + input, either intact (if kernel_size == 1) or padded (if kernel_size > 1). + """ + kernel_size_effective = [kernel_size[0] + (kernel_size[0] - 1) * (rate - 1), + kernel_size[0] + (kernel_size[0] - 1) * (rate - 1)] + pad_total = [kernel_size_effective[0] - 1, kernel_size_effective[1] - 1] + pad_beg = [pad_total[0] // 2, pad_total[1] // 2] + pad_end = [pad_total[0] - pad_beg[0], pad_total[1] - pad_beg[1]] + padded_inputs = tf.pad(inputs, [[0, 0], [pad_beg[0], pad_end[0]], + [pad_beg[1], pad_end[1]], [0, 0]]) + return padded_inputs + + +def _make_divisible(v, divisor, min_value=None): + if min_value is None: + min_value = divisor + new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) + # Make sure that round down does not go down by more than 10%. + if new_v < 0.9 * v: + new_v += divisor + return new_v + + +def _split_divisible(num, num_ways, divisible_by=8): + """Evenly splits num, num_ways so each piece is a multiple of divisible_by.""" + assert num % divisible_by == 0 + assert num // num_ways >= divisible_by + # Note: want to round down, we adjust each split to match the total. + base = num // num_ways // divisible_by * divisible_by + result = [] + accumulated = 0 + for i in range(num_ways): + r = base + while accumulated + r < num * (i + 1) // num_ways: + r += divisible_by + result.append(r) + accumulated += r + assert accumulated == num + return result + + +@contextlib.contextmanager +def _v1_compatible_scope_naming(scope): # pylint: disable=g-missing-docstring + if scope is None: # Create uniqified separable blocks. + with tf.variable_scope(None, default_name='separable') as s, \ + tf.name_scope(s.original_name_scope): + yield '' + else: + # We use scope_depthwise, scope_pointwise for compatibility with V1 ckpts. + # which provide numbered scopes. + scope += '_' + yield scope + + +@slim.add_arg_scope +def split_separable_conv2d(input_tensor, + num_outputs, + scope=None, + normalizer_fn=None, + stride=1, + rate=1, + endpoints=None, + use_explicit_padding=False): + """Separable mobilenet V1 style convolution. + + Depthwise convolution, with default non-linearity, + followed by 1x1 depthwise convolution. This is similar to + slim.separable_conv2d, but differs in tha it applies batch + normalization and non-linearity to depthwise. This matches + the basic building of Mobilenet Paper + (https://arxiv.org/abs/1704.04861) + + Args: + input_tensor: input + num_outputs: number of outputs + scope: optional name of the scope. Note if provided it will use + scope_depthwise for deptwhise, and scope_pointwise for pointwise. + normalizer_fn: which normalizer function to use for depthwise/pointwise + stride: stride + rate: output rate (also known as dilation rate) + endpoints: optional, if provided, will export additional tensors to it. + use_explicit_padding: Use 'VALID' padding for convolutions, but prepad + inputs so that the output dimensions are the same as if 'SAME' padding + were used. + + Returns: + output tesnor + """ + + with _v1_compatible_scope_naming(scope) as scope: + dw_scope = scope + 'depthwise' + endpoints = endpoints if endpoints is not None else {} + kernel_size = [3, 3] + padding = 'SAME' + if use_explicit_padding: + padding = 'VALID' + input_tensor = _fixed_padding(input_tensor, kernel_size, rate) + net = slim.separable_conv2d( + input_tensor, + None, + kernel_size, + depth_multiplier=1, + stride=stride, + rate=rate, + normalizer_fn=normalizer_fn, + padding=padding, + scope=dw_scope) + + endpoints[dw_scope] = net + + pw_scope = scope + 'pointwise' + net = slim.conv2d( + net, + num_outputs, [1, 1], + stride=1, + normalizer_fn=normalizer_fn, + scope=pw_scope) + endpoints[pw_scope] = net + return net + + +def expand_input_by_factor(n, divisible_by=8): + return lambda num_inputs, **_: _make_divisible(num_inputs * n, divisible_by) + + +@slim.add_arg_scope +def expanded_conv(input_tensor, + num_outputs, + expansion_size=expand_input_by_factor(6), + stride=1, + rate=1, + kernel_size=(3, 3), + residual=True, + normalizer_fn=None, + split_projection=1, + split_expansion=1, + expansion_transform=None, + depthwise_location='expansion', + depthwise_channel_multiplier=1, + endpoints=None, + use_explicit_padding=False, + padding='SAME', + scope=None): + """Depthwise Convolution Block with expansion. + + Builds a composite convolution that has the following structure + expansion (1x1) -> depthwise (kernel_size) -> projection (1x1) + + Args: + input_tensor: input + num_outputs: number of outputs in the final layer. + expansion_size: the size of expansion, could be a constant or a callable. + If latter it will be provided 'num_inputs' as an input. For forward + compatibility it should accept arbitrary keyword arguments. + Default will expand the input by factor of 6. + stride: depthwise stride + rate: depthwise rate + kernel_size: depthwise kernel + residual: whether to include residual connection between input + and output. + normalizer_fn: batchnorm or otherwise + split_projection: how many ways to split projection operator + (that is conv expansion->bottleneck) + split_expansion: how many ways to split expansion op + (that is conv bottleneck->expansion) ops will keep depth divisible + by this value. + expansion_transform: Optional function that takes expansion + as a single input and returns output. + depthwise_location: where to put depthwise covnvolutions supported + values None, 'input', 'output', 'expansion' + depthwise_channel_multiplier: depthwise channel multiplier: + each input will replicated (with different filters) + that many times. So if input had c channels, + output will have c x depthwise_channel_multpilier. + endpoints: An optional dictionary into which intermediate endpoints are + placed. The keys "expansion_output", "depthwise_output", + "projection_output" and "expansion_transform" are always populated, even + if the corresponding functions are not invoked. + use_explicit_padding: Use 'VALID' padding for convolutions, but prepad + inputs so that the output dimensions are the same as if 'SAME' padding + were used. + padding: Padding type to use if `use_explicit_padding` is not set. + scope: optional scope. + + Returns: + Tensor of depth num_outputs + + Raises: + TypeError: on inval + """ + with tf.variable_scope(scope, default_name='expanded_conv') as s, \ + tf.name_scope(s.original_name_scope): + prev_depth = input_tensor.get_shape().as_list()[3] + if depthwise_location not in [None, 'input', 'output', 'expansion']: + raise TypeError('%r is unknown value for depthwise_location' % + depthwise_location) + if use_explicit_padding: + if padding != 'SAME': + raise TypeError('`use_explicit_padding` should only be used with ' + '"SAME" padding.') + padding = 'VALID' + depthwise_func = functools.partial( + slim.separable_conv2d, + num_outputs=None, + kernel_size=kernel_size, + depth_multiplier=depthwise_channel_multiplier, + stride=stride, + rate=rate, + normalizer_fn=normalizer_fn, + padding=padding, + scope='depthwise') + # b1 -> b2 * r -> b2 + # i -> (o * r) (bottleneck) -> o + input_tensor = tf.identity(input_tensor, 'input') + net = input_tensor + + if depthwise_location == 'input': + if use_explicit_padding: + net = _fixed_padding(net, kernel_size, rate) + net = depthwise_func(net, activation_fn=None) + + if callable(expansion_size): + inner_size = expansion_size(num_inputs=prev_depth) + else: + inner_size = expansion_size + + if inner_size > net.shape[3]: + net = split_conv( + net, + inner_size, + num_ways=split_expansion, + scope='expand', + stride=1, + normalizer_fn=normalizer_fn) + net = tf.identity(net, 'expansion_output') + if endpoints is not None: + endpoints['expansion_output'] = net + + if depthwise_location == 'expansion': + if use_explicit_padding: + net = _fixed_padding(net, kernel_size, rate) + net = depthwise_func(net) + + net = tf.identity(net, name='depthwise_output') + if endpoints is not None: + endpoints['depthwise_output'] = net + if expansion_transform: + net = expansion_transform(expansion_tensor=net, input_tensor=input_tensor) + # Note in contrast with expansion, we always have + # projection to produce the desired output size. + net = split_conv( + net, + num_outputs, + num_ways=split_projection, + stride=1, + scope='project', + normalizer_fn=normalizer_fn, + activation_fn=tf.identity) + if endpoints is not None: + endpoints['projection_output'] = net + if depthwise_location == 'output': + if use_explicit_padding: + net = _fixed_padding(net, kernel_size, rate) + net = depthwise_func(net, activation_fn=None) + + if callable(residual): # custom residual + net = residual(input_tensor=input_tensor, output_tensor=net) + elif (residual and + # stride check enforces that we don't add residuals when spatial + # dimensions are None + stride == 1 and + # Depth matches + net.get_shape().as_list()[3] == + input_tensor.get_shape().as_list()[3]): + net += input_tensor + return tf.identity(net, name='output') + + +def split_conv(input_tensor, + num_outputs, + num_ways, + scope, + divisible_by=8, + **kwargs): + """Creates a split convolution. + + Split convolution splits the input and output into + 'num_blocks' blocks of approximately the same size each, + and only connects $i$-th input to $i$ output. + + Args: + input_tensor: input tensor + num_outputs: number of output filters + num_ways: num blocks to split by. + scope: scope for all the operators. + divisible_by: make sure that every part is divisiable by this. + **kwargs: will be passed directly into conv2d operator + Returns: + tensor + """ + b = input_tensor.get_shape().as_list()[3] + + if num_ways == 1 or min(b // num_ways, + num_outputs // num_ways) < divisible_by: + # Don't do any splitting if we end up with less than 8 filters + # on either side. + return slim.conv2d(input_tensor, num_outputs, [1, 1], scope=scope, **kwargs) + + outs = [] + input_splits = _split_divisible(b, num_ways, divisible_by=divisible_by) + output_splits = _split_divisible( + num_outputs, num_ways, divisible_by=divisible_by) + inputs = tf.split(input_tensor, input_splits, axis=3, name='split_' + scope) + base = scope + for i, (input_tensor, out_size) in enumerate(zip(inputs, output_splits)): + scope = base + '_part_%d' % (i,) + n = slim.conv2d(input_tensor, out_size, [1, 1], scope=scope, **kwargs) + n = tf.identity(n, scope + '_output') + outs.append(n) + return tf.concat(outs, 3, name=scope + '_concat') diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/mobilenet_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/mobilenet_test.py new file mode 100644 index 00000000..c0b7d534 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/mobilenet_test.py @@ -0,0 +1,191 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 mobilenet_v2, branched from slim for fp16 performance study.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import copy + +import tensorflow.compat.v1 as tf + +from models.tf1_only import mobilenet +from models.tf1_only import mobilenet_conv_blocks as ops +from models.tf1_only import mobilenet_v2 +from tensorflow.contrib import slim + + +def find_ops(optype): + """Find ops of a given type in graphdef or a graph. + + Args: + optype: operation type (e.g. Conv2D) + Returns: + List of operations. + """ + gd = tf.get_default_graph() + return [var for var in gd.get_operations() if var.type == optype] + + +class MobilenetV2Test(tf.test.TestCase): + + def setUp(self): # pylint: disable=g-missing-super-call + tf.reset_default_graph() + + def testCreation(self): + spec = dict(mobilenet_v2.V2_DEF) + _, ep = mobilenet.mobilenet( + tf.placeholder(tf.float32, (10, 224, 224, 16)), conv_defs=spec) + num_convs = len(find_ops('Conv2D')) + + # This is mostly a sanity test. No deep reason for these particular + # constants. + # + # All but first 2 and last one have two convolutions, and there is one + # extra conv that is not in the spec. (logits) + self.assertEqual(num_convs, len(spec['spec']) * 2 - 2) + # Check that depthwise are exposed. + for i in range(2, 17): + self.assertIn('layer_%d/depthwise_output' % i, ep) + + def testCreationNoClasses(self): + spec = copy.deepcopy(mobilenet_v2.V2_DEF) + net, ep = mobilenet.mobilenet( + tf.placeholder(tf.float32, (10, 224, 224, 16)), conv_defs=spec, + num_classes=None) + self.assertIs(net, ep['global_pool']) + + def testImageSizes(self): + for input_size, output_size in [(224, 7), (192, 6), (160, 5), + (128, 4), (96, 3)]: + tf.reset_default_graph() + _, ep = mobilenet_v2.mobilenet( + tf.placeholder(tf.float32, (10, input_size, input_size, 3))) + + self.assertEqual(ep['layer_18/output'].get_shape().as_list()[1:3], + [output_size] * 2) + + def testWithSplits(self): + spec = copy.deepcopy(mobilenet_v2.V2_DEF) + spec['overrides'] = { + (ops.expanded_conv,): dict(split_expansion=2), + } + _, _ = mobilenet.mobilenet( + tf.placeholder(tf.float32, (10, 224, 224, 16)), conv_defs=spec) + num_convs = len(find_ops('Conv2D')) + # All but 3 op has 3 conv operatore, the remainign 3 have one + # and there is one unaccounted. + self.assertEqual(num_convs, len(spec['spec']) * 3 - 5) + + def testWithOutputStride8(self): + out, _ = mobilenet.mobilenet_base( + tf.placeholder(tf.float32, (10, 224, 224, 16)), + conv_defs=mobilenet_v2.V2_DEF, + output_stride=8, + scope='MobilenetV2') + self.assertEqual(out.get_shape().as_list()[1:3], [28, 28]) + + def testDivisibleBy(self): + tf.reset_default_graph() + mobilenet_v2.mobilenet( + tf.placeholder(tf.float32, (10, 224, 224, 16)), + conv_defs=mobilenet_v2.V2_DEF, + divisible_by=16, + min_depth=32) + s = [op.outputs[0].get_shape().as_list()[-1] for op in find_ops('Conv2D')] + s = set(s) + self.assertSameElements([32, 64, 96, 160, 192, 320, 384, 576, 960, 1280, + 1001], s) + + def testDivisibleByWithArgScope(self): + tf.reset_default_graph() + # Verifies that depth_multiplier arg scope actually works + # if no default min_depth is provided. + with slim.arg_scope((mobilenet.depth_multiplier,), min_depth=32): + mobilenet_v2.mobilenet( + tf.placeholder(tf.float32, (10, 224, 224, 2)), + conv_defs=mobilenet_v2.V2_DEF, depth_multiplier=0.1) + s = [op.outputs[0].get_shape().as_list()[-1] for op in find_ops('Conv2D')] + s = set(s) + self.assertSameElements(s, [32, 192, 128, 1001]) + + def testFineGrained(self): + tf.reset_default_graph() + # Verifies that depth_multiplier arg scope actually works + # if no default min_depth is provided. + + mobilenet_v2.mobilenet( + tf.placeholder(tf.float32, (10, 224, 224, 2)), + conv_defs=mobilenet_v2.V2_DEF, depth_multiplier=0.01, + finegrain_classification_mode=True) + s = [op.outputs[0].get_shape().as_list()[-1] for op in find_ops('Conv2D')] + s = set(s) + # All convolutions will be 8->48, except for the last one. + self.assertSameElements(s, [8, 48, 1001, 1280]) + + def testMobilenetBase(self): + tf.reset_default_graph() + # Verifies that mobilenet_base returns pre-pooling layer. + with slim.arg_scope((mobilenet.depth_multiplier,), min_depth=32): + net, _ = mobilenet_v2.mobilenet_base( + tf.placeholder(tf.float32, (10, 224, 224, 16)), + conv_defs=mobilenet_v2.V2_DEF, depth_multiplier=0.1) + self.assertEqual(net.get_shape().as_list(), [10, 7, 7, 128]) + + def testWithOutputStride16(self): + tf.reset_default_graph() + out, _ = mobilenet.mobilenet_base( + tf.placeholder(tf.float32, (10, 224, 224, 16)), + conv_defs=mobilenet_v2.V2_DEF, + output_stride=16) + self.assertEqual(out.get_shape().as_list()[1:3], [14, 14]) + + def testWithOutputStride8AndExplicitPadding(self): + tf.reset_default_graph() + out, _ = mobilenet.mobilenet_base( + tf.placeholder(tf.float32, (10, 224, 224, 16)), + conv_defs=mobilenet_v2.V2_DEF, + output_stride=8, + use_explicit_padding=True, + scope='MobilenetV2') + self.assertEqual(out.get_shape().as_list()[1:3], [28, 28]) + + def testWithOutputStride16AndExplicitPadding(self): + tf.reset_default_graph() + out, _ = mobilenet.mobilenet_base( + tf.placeholder(tf.float32, (10, 224, 224, 16)), + conv_defs=mobilenet_v2.V2_DEF, + output_stride=16, + use_explicit_padding=True) + self.assertEqual(out.get_shape().as_list()[1:3], [14, 14]) + + def testBatchNormScopeDoesNotHaveIsTrainingWhenItsSetToNone(self): + sc = mobilenet.training_scope(is_training=None) + self.assertNotIn('is_training', sc[slim.arg_scope_func_key( + slim.batch_norm)]) + + def testBatchNormScopeDoesHasIsTrainingWhenItsNotNone(self): + sc = mobilenet.training_scope(is_training=False) + self.assertIn('is_training', sc[slim.arg_scope_func_key(slim.batch_norm)]) + sc = mobilenet.training_scope(is_training=True) + self.assertIn('is_training', sc[slim.arg_scope_func_key(slim.batch_norm)]) + sc = mobilenet.training_scope() + self.assertIn('is_training', sc[slim.arg_scope_func_key(slim.batch_norm)]) + + +if __name__ == '__main__': + tf.disable_v2_behavior() + tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/mobilenet_v2.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/mobilenet_v2.py new file mode 100644 index 00000000..ac811470 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/mobilenet_v2.py @@ -0,0 +1,198 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Mobilenet V2 model, branched from slim models for fp16 performance study. + +Architecture: https://arxiv.org/abs/1801.04381 + +The base model gives 72.2% accuracy on ImageNet, with 300MMadds, +3.4 M parameters. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import copy + +import tensorflow.compat.v1 as tf + +from models import model +from models.tf1_only import mobilenet as lib +from models.tf1_only import mobilenet_conv_blocks as ops +from tensorflow.contrib import slim + +op = lib.op + +expand_input = ops.expand_input_by_factor + +# pyformat: disable +# Architecture: https://arxiv.org/abs/1801.04381 +V2_DEF = dict( + defaults={ + # Note: these parameters of batch norm affect the architecture + # that's why they are here and not in training_scope. + (slim.batch_norm,): {'center': True, 'scale': True}, + (slim.conv2d, slim.fully_connected, slim.separable_conv2d): { + 'normalizer_fn': slim.batch_norm, 'activation_fn': tf.nn.relu6 + }, + (ops.expanded_conv,): { + 'expansion_size': expand_input(6), + 'split_expansion': 1, + 'normalizer_fn': slim.batch_norm, + 'residual': True + }, + (slim.conv2d, slim.separable_conv2d): {'padding': 'SAME'} + }, + spec=[ + op(slim.conv2d, stride=2, num_outputs=32, kernel_size=[3, 3]), + op(ops.expanded_conv, + expansion_size=expand_input(1, divisible_by=1), + num_outputs=16), + op(ops.expanded_conv, stride=2, num_outputs=24), + op(ops.expanded_conv, stride=1, num_outputs=24), + op(ops.expanded_conv, stride=2, num_outputs=32), + op(ops.expanded_conv, stride=1, num_outputs=32), + op(ops.expanded_conv, stride=1, num_outputs=32), + op(ops.expanded_conv, stride=2, num_outputs=64), + op(ops.expanded_conv, stride=1, num_outputs=64), + op(ops.expanded_conv, stride=1, num_outputs=64), + op(ops.expanded_conv, stride=1, num_outputs=64), + op(ops.expanded_conv, stride=1, num_outputs=96), + op(ops.expanded_conv, stride=1, num_outputs=96), + op(ops.expanded_conv, stride=1, num_outputs=96), + op(ops.expanded_conv, stride=2, num_outputs=160), + op(ops.expanded_conv, stride=1, num_outputs=160), + op(ops.expanded_conv, stride=1, num_outputs=160), + op(ops.expanded_conv, stride=1, num_outputs=320), + op(slim.conv2d, stride=1, kernel_size=[1, 1], num_outputs=1280) + ], +) +# pyformat: enable + + +@slim.add_arg_scope +def mobilenet(input_tensor, + num_classes=1001, + depth_multiplier=1.0, + scope='MobilenetV2', + conv_defs=None, + finegrain_classification_mode=False, + min_depth=None, + divisible_by=None, + **kwargs): + """Creates mobilenet V2 network. + + Inference mode is created by default. To create training use training_scope + below. + + with tf.contrib.slim.arg_scope(mobilenet_v2.training_scope()): + logits, endpoints = mobilenet_v2.mobilenet(input_tensor) + + Args: + input_tensor: The input tensor + num_classes: number of classes + depth_multiplier: The multiplier applied to scale number of + channels in each layer. Note: this is called depth multiplier in the + paper but the name is kept for consistency with slim's model builder. + scope: Scope of the operator + conv_defs: Allows to override default conv def. + finegrain_classification_mode: When set to True, the model + will keep the last layer large even for small multipliers. Following + https://arxiv.org/abs/1801.04381 + suggests that it improves performance for ImageNet-type of problems. + *Note* ignored if final_endpoint makes the builder exit earlier. + min_depth: If provided, will ensure that all layers will have that + many channels after application of depth multiplier. + divisible_by: If provided will ensure that all layers # channels + will be divisible by this number. + **kwargs: passed directly to mobilenet.mobilenet: + prediction_fn- what prediction function to use. + reuse-: whether to reuse variables (if reuse set to true, scope + must be given). + Returns: + logits/endpoints pair + + Raises: + ValueError: On invalid arguments + """ + if conv_defs is None: + conv_defs = V2_DEF + if 'multiplier' in kwargs: + raise ValueError('mobilenetv2 doesn\'t support generic ' + 'multiplier parameter use "depth_multiplier" instead.') + if finegrain_classification_mode: + conv_defs = copy.deepcopy(conv_defs) + if depth_multiplier < 1: + conv_defs['spec'][-1].params['num_outputs'] /= depth_multiplier + + depth_args = {} + # NB: do not set depth_args unless they are provided to avoid overriding + # whatever default depth_multiplier might have thanks to arg_scope. + if min_depth is not None: + depth_args['min_depth'] = min_depth + if divisible_by is not None: + depth_args['divisible_by'] = divisible_by + + with slim.arg_scope((lib.depth_multiplier,), **depth_args): + return lib.mobilenet( + input_tensor, + num_classes=num_classes, + conv_defs=conv_defs, + scope=scope, + multiplier=depth_multiplier, + **kwargs) + + +@slim.add_arg_scope +def mobilenet_base(input_tensor, depth_multiplier=1.0, **kwargs): + """Creates base of the mobilenet (no pooling and no logits) .""" + return mobilenet( + input_tensor, depth_multiplier=depth_multiplier, base_only=True, **kwargs) + + +def training_scope(**kwargs): + """Defines MobilenetV2 training scope. + + Usage: + with tf.contrib.slim.arg_scope(mobilenet_v2.training_scope()): + logits, endpoints = mobilenet_v2.mobilenet(input_tensor) + + with slim. + + Args: + **kwargs: Passed to mobilenet.training_scope. The following parameters + are supported: + weight_decay- The weight decay to use for regularizing the model. + stddev- Standard deviation for initialization, if negative uses xavier. + dropout_keep_prob- dropout keep probability + bn_decay- decay for the batch norm moving averages. + + Returns: + An `arg_scope` to use for the mobilenet v2 model. + """ + return lib.training_scope(**kwargs) + + +class MobilenetModel(model.CNNModel): + """Mobilenet model configuration.""" + + def __init__(self, params=None): + super(MobilenetModel, self).__init__( + 'mobilenet', 224, 32, 0.005, params=params) + + def add_inference(self, cnn): + with slim.arg_scope(training_scope(is_training=cnn.phase_train)): + cnn.top_layer, _ = mobilenet(cnn.top_layer, is_training=cnn.phase_train) + cnn.top_size = cnn.top_layer.shape[-1].value diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/nasnet_model.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/nasnet_model.py new file mode 100644 index 00000000..560d86bc --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/nasnet_model.py @@ -0,0 +1,582 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Model configurations for nasnet. + +Paper: https://arxiv.org/abs/1707.07012 +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import tensorflow.compat.v1 as tf + +from models import model +from models.tf1_only import nasnet_utils +from tensorflow.contrib import framework as contrib_framework +from tensorflow.contrib import layers as contrib_layers +from tensorflow.contrib import slim +from tensorflow.contrib import training as contrib_training + +arg_scope = contrib_framework.arg_scope + + +# Notes for training NASNet Cifar Model +# ------------------------------------- +# batch_size: 32 +# learning rate: 0.025 +# cosine (single period) learning rate decay +# auxiliary head loss weighting: 0.4 +# clip global norm of all gradients by 5 +def _cifar_config(is_training=True, data_format=None, total_steps=None): + drop_path_keep_prob = 1.0 if not is_training else 0.6 + return contrib_training.HParams( + stem_multiplier=3.0, + drop_path_keep_prob=drop_path_keep_prob, + num_cells=18, + use_aux_head=1, + num_conv_filters=32, + dense_dropout_keep_prob=1.0, + filter_scaling_rate=2.0, + num_reduction_layers=2, + skip_reduction_layer_input=0, + data_format=data_format or 'NHWC', + # 600 epochs with a batch size of 32 + # This is used for the drop path probabilities since it needs to increase + # the drop out probability over the course of training. + total_training_steps=total_steps or 937500, + ) + + +# Notes for training large NASNet model on ImageNet +# ------------------------------------- +# batch size (per replica): 16 +# learning rate: 0.015 * 100 +# learning rate decay factor: 0.97 +# num epochs per decay: 2.4 +# sync sgd with 100 replicas +# auxiliary head loss weighting: 0.4 +# label smoothing: 0.1 +# clip global norm of all gradients by 10 +def _large_imagenet_config(is_training=True, data_format=None, + total_steps=None): + drop_path_keep_prob = 1.0 if not is_training else 0.7 + return contrib_training.HParams( + stem_multiplier=3.0, + dense_dropout_keep_prob=0.5, + num_cells=18, + filter_scaling_rate=2.0, + num_conv_filters=168, + drop_path_keep_prob=drop_path_keep_prob, + use_aux_head=1, + num_reduction_layers=2, + skip_reduction_layer_input=1, + data_format=data_format or 'NHWC', + total_training_steps=total_steps or 250000, + ) + + +# Notes for training the mobile NASNet ImageNet model +# ------------------------------------- +# batch size (per replica): 32 +# learning rate: 0.04 * 50 +# learning rate scaling factor: 0.97 +# num epochs per decay: 2.4 +# sync sgd with 50 replicas +# auxiliary head weighting: 0.4 +# label smoothing: 0.1 +# clip global norm of all gradients by 10 +def _mobile_imagenet_config(data_format=None, total_steps=None): + return contrib_training.HParams( + stem_multiplier=1.0, + dense_dropout_keep_prob=0.5, + num_cells=12, + filter_scaling_rate=2.0, + drop_path_keep_prob=1.0, + num_conv_filters=44, + use_aux_head=1, + num_reduction_layers=2, + skip_reduction_layer_input=0, + data_format=data_format or 'NHWC', + total_training_steps=total_steps or 250000, + ) + + +def nasnet_cifar_arg_scope(weight_decay=5e-4, + batch_norm_decay=0.9, + batch_norm_epsilon=1e-5): + """Defines the default arg scope for the NASNet-A Cifar model. + + Args: + weight_decay: The weight decay to use for regularizing the model. + batch_norm_decay: Decay for batch norm moving average. + batch_norm_epsilon: Small float added to variance to avoid dividing by zero + in batch norm. + Returns: + An `arg_scope` to use for the NASNet Cifar Model. + """ + batch_norm_params = { + # Decay for the moving averages. + 'decay': batch_norm_decay, + # epsilon to prevent 0s in variance. + 'epsilon': batch_norm_epsilon, + 'scale': True, + 'fused': True, + } + weights_regularizer = contrib_layers.l2_regularizer(weight_decay) + weights_initializer = contrib_layers.variance_scaling_initializer( + mode='FAN_OUT') + with arg_scope( + [slim.fully_connected, slim.conv2d, slim.separable_conv2d], + weights_regularizer=weights_regularizer, + weights_initializer=weights_initializer): + with arg_scope([slim.fully_connected], activation_fn=None, scope='FC'): + with arg_scope( + [slim.conv2d, slim.separable_conv2d], + activation_fn=None, + biases_initializer=None): + with arg_scope([slim.batch_norm], **batch_norm_params) as sc: + return sc + + +def nasnet_mobile_arg_scope(weight_decay=4e-5, + batch_norm_decay=0.9997, + batch_norm_epsilon=1e-3): + """Defines the default arg scope for the NASNet-A Mobile ImageNet model. + + Args: + weight_decay: The weight decay to use for regularizing the model. + batch_norm_decay: Decay for batch norm moving average. + batch_norm_epsilon: Small float added to variance to avoid dividing by zero + in batch norm. + Returns: + An `arg_scope` to use for the NASNet Mobile Model. + """ + batch_norm_params = { + # Decay for the moving averages. + 'decay': batch_norm_decay, + # epsilon to prevent 0s in variance. + 'epsilon': batch_norm_epsilon, + 'scale': True, + 'fused': True, + } + weights_regularizer = contrib_layers.l2_regularizer(weight_decay) + weights_initializer = contrib_layers.variance_scaling_initializer( + mode='FAN_OUT') + with arg_scope( + [slim.fully_connected, slim.conv2d, slim.separable_conv2d], + weights_regularizer=weights_regularizer, + weights_initializer=weights_initializer): + with arg_scope([slim.fully_connected], activation_fn=None, scope='FC'): + with arg_scope( + [slim.conv2d, slim.separable_conv2d], + activation_fn=None, + biases_initializer=None): + with arg_scope([slim.batch_norm], **batch_norm_params) as sc: + return sc + + +def nasnet_large_arg_scope(weight_decay=5e-5, + batch_norm_decay=0.9997, + batch_norm_epsilon=1e-3): + """Defines the default arg scope for the NASNet-A Large ImageNet model. + + Args: + weight_decay: The weight decay to use for regularizing the model. + batch_norm_decay: Decay for batch norm moving average. + batch_norm_epsilon: Small float added to variance to avoid dividing by zero + in batch norm. + Returns: + An `arg_scope` to use for the NASNet Large Model. + """ + batch_norm_params = { + # Decay for the moving averages. + 'decay': batch_norm_decay, + # epsilon to prevent 0s in variance. + 'epsilon': batch_norm_epsilon, + 'scale': True, + 'fused': True, + } + weights_regularizer = contrib_layers.l2_regularizer(weight_decay) + weights_initializer = contrib_layers.variance_scaling_initializer( + mode='FAN_OUT') + with arg_scope( + [slim.fully_connected, slim.conv2d, slim.separable_conv2d], + weights_regularizer=weights_regularizer, + weights_initializer=weights_initializer): + with arg_scope([slim.fully_connected], activation_fn=None, scope='FC'): + with arg_scope( + [slim.conv2d, slim.separable_conv2d], + activation_fn=None, + biases_initializer=None): + with arg_scope([slim.batch_norm], **batch_norm_params) as sc: + return sc + + +def _build_aux_head(net, end_points, num_classes, hparams, scope): + """Auxiliary head used for all models across all datasets.""" + with tf.variable_scope(scope): + aux_logits = tf.identity(net) + with tf.variable_scope('aux_logits'): + aux_logits = slim.avg_pool2d( + aux_logits, [5, 5], stride=3, padding='VALID') + aux_logits = slim.conv2d(aux_logits, 128, [1, 1], scope='proj') + aux_logits = slim.batch_norm(aux_logits, scope='aux_bn0') + aux_logits = tf.nn.relu(aux_logits) + # Shape of feature map before the final layer. + shape = aux_logits.shape + if hparams.data_format == 'NHWC': + shape = shape[1:3] + else: + shape = shape[2:4] + aux_logits = slim.conv2d(aux_logits, 768, shape, padding='VALID') + aux_logits = slim.batch_norm(aux_logits, scope='aux_bn1') + aux_logits = tf.nn.relu(aux_logits) + aux_logits = contrib_layers.flatten(aux_logits) + aux_logits = slim.fully_connected(aux_logits, num_classes) + end_points['AuxLogits'] = aux_logits + + +def _imagenet_stem(inputs, hparams, stem_cell): + """Stem used for models trained on ImageNet.""" + num_stem_cells = 2 + + # 149 x 149 x 32 + num_stem_filters = int(32 * hparams.stem_multiplier) + net = slim.conv2d( + inputs, + num_stem_filters, [3, 3], + stride=2, + scope='conv0', + padding='VALID') + net = slim.batch_norm(net, scope='conv0_bn') + + # Run the reduction cells + cell_outputs = [None, net] + filter_scaling = 1.0 / (hparams.filter_scaling_rate**num_stem_cells) + for cell_num in range(num_stem_cells): + net = stem_cell( + net, + scope='cell_stem_{}'.format(cell_num), + filter_scaling=filter_scaling, + stride=2, + prev_layer=cell_outputs[-2], + cell_num=cell_num) + cell_outputs.append(net) + filter_scaling *= hparams.filter_scaling_rate + return net, cell_outputs + + +def _cifar_stem(inputs, hparams): + """Stem used for models trained on Cifar.""" + num_stem_filters = int(hparams.num_conv_filters * hparams.stem_multiplier) + net = slim.conv2d(inputs, num_stem_filters, 3, scope='l1_stem_3x3') + net = slim.batch_norm(net, scope='l1_stem_bn') + return net, [None, net] + + +def build_nasnet_cifar(images, + num_classes=None, + is_training=True, + data_format=None, + total_steps=None): + """Build NASNet model for the Cifar Dataset.""" + hparams = _cifar_config( + is_training=is_training, data_format=data_format, total_steps=total_steps) + + if tf.test.is_gpu_available() and hparams.data_format == 'NHWC': + tf.logging.info('A GPU is available on the machine, consider using NCHW ' + 'data format for increased speed on GPU.') + + # Calculate the total number of cells in the network + # Add 2 for the reduction cells + total_num_cells = hparams.num_cells + 2 + + normal_cell = nasnet_utils.NasNetANormalCell( + hparams.num_conv_filters, hparams.drop_path_keep_prob, total_num_cells, + hparams.total_training_steps) + reduction_cell = nasnet_utils.NasNetAReductionCell( + hparams.num_conv_filters, hparams.drop_path_keep_prob, total_num_cells, + hparams.total_training_steps) + with arg_scope( + [slim.dropout, nasnet_utils.drop_path, slim.batch_norm], + is_training=is_training): + with arg_scope( + [ + slim.avg_pool2d, slim.max_pool2d, slim.conv2d, slim.batch_norm, + slim.separable_conv2d, nasnet_utils.factorized_reduction, + nasnet_utils.global_avg_pool, nasnet_utils.get_channel_index, + nasnet_utils.get_channel_dim + ], + data_format=hparams.data_format): + return _build_nasnet_base( + images, + normal_cell=normal_cell, + reduction_cell=reduction_cell, + num_classes=num_classes, + hparams=hparams, + is_training=is_training, + stem_type='cifar') + + +build_nasnet_cifar.default_image_size = 32 + + +def build_nasnet_mobile(images, + num_classes=None, + is_training=True, + data_format=None, + total_steps=None, + final_endpoint=None): + """Build NASNet Mobile model for the ImageNet Dataset.""" + hparams = _mobile_imagenet_config( + data_format=data_format, total_steps=total_steps) + + if tf.test.is_gpu_available() and hparams.data_format == 'NHWC': + tf.logging.info('A GPU is available on the machine, consider using NCHW ' + 'data format for increased speed on GPU.') + + # Calculate the total number of cells in the network + # Add 2 for the reduction cells + total_num_cells = hparams.num_cells + 2 + # If ImageNet, then add an additional two for the stem cells + total_num_cells += 2 + + normal_cell = nasnet_utils.NasNetANormalCell( + hparams.num_conv_filters, hparams.drop_path_keep_prob, total_num_cells, + hparams.total_training_steps) + reduction_cell = nasnet_utils.NasNetAReductionCell( + hparams.num_conv_filters, hparams.drop_path_keep_prob, total_num_cells, + hparams.total_training_steps) + with arg_scope( + [slim.dropout, nasnet_utils.drop_path, slim.batch_norm], + is_training=is_training): + with arg_scope( + [ + slim.avg_pool2d, slim.max_pool2d, slim.conv2d, slim.batch_norm, + slim.separable_conv2d, nasnet_utils.factorized_reduction, + nasnet_utils.global_avg_pool, nasnet_utils.get_channel_index, + nasnet_utils.get_channel_dim + ], + data_format=hparams.data_format): + return _build_nasnet_base( + images, + normal_cell=normal_cell, + reduction_cell=reduction_cell, + num_classes=num_classes, + hparams=hparams, + is_training=is_training, + stem_type='imagenet', + final_endpoint=final_endpoint) + + +build_nasnet_mobile.default_image_size = 224 + + +def build_nasnet_large(images, + num_classes=None, + is_training=True, + data_format=None, + total_steps=None, + final_endpoint=None): + """Build NASNet Large model for the ImageNet Dataset.""" + hparams = _large_imagenet_config( + is_training=is_training, data_format=data_format, total_steps=total_steps) + + if tf.test.is_gpu_available() and hparams.data_format == 'NHWC': + tf.logging.info('A GPU is available on the machine, consider using NCHW ' + 'data format for increased speed on GPU.') + + # Calculate the total number of cells in the network + # Add 2 for the reduction cells + total_num_cells = hparams.num_cells + 2 + # If ImageNet, then add an additional two for the stem cells + total_num_cells += 2 + + normal_cell = nasnet_utils.NasNetANormalCell( + hparams.num_conv_filters, hparams.drop_path_keep_prob, total_num_cells, + hparams.total_training_steps) + reduction_cell = nasnet_utils.NasNetAReductionCell( + hparams.num_conv_filters, hparams.drop_path_keep_prob, total_num_cells, + hparams.total_training_steps) + with arg_scope( + [slim.dropout, nasnet_utils.drop_path, slim.batch_norm], + is_training=is_training): + with arg_scope( + [ + slim.avg_pool2d, slim.max_pool2d, slim.conv2d, slim.batch_norm, + slim.separable_conv2d, nasnet_utils.factorized_reduction, + nasnet_utils.global_avg_pool, nasnet_utils.get_channel_index, + nasnet_utils.get_channel_dim + ], + data_format=hparams.data_format): + return _build_nasnet_base( + images, + normal_cell=normal_cell, + reduction_cell=reduction_cell, + num_classes=num_classes, + hparams=hparams, + is_training=is_training, + stem_type='imagenet', + final_endpoint=final_endpoint) + + +build_nasnet_large.default_image_size = 331 + + +def _build_nasnet_base(images, + normal_cell, + reduction_cell, + num_classes, + hparams, + is_training, + stem_type, + final_endpoint=None): + """Constructs a NASNet image model.""" + + end_points = {} + + def add_and_check_endpoint(endpoint_name, net): + end_points[endpoint_name] = net + return final_endpoint and (endpoint_name == final_endpoint) + + # Find where to place the reduction cells or stride normal cells + reduction_indices = nasnet_utils.calc_reduction_layers( + hparams.num_cells, hparams.num_reduction_layers) + stem_cell = reduction_cell + + if stem_type == 'imagenet': + stem = lambda: _imagenet_stem(images, hparams, stem_cell) + elif stem_type == 'cifar': + stem = lambda: _cifar_stem(images, hparams) + else: + raise ValueError('Unknown stem_type: ', stem_type) + net, cell_outputs = stem() + if add_and_check_endpoint('Stem', net): + return net, end_points + + # Setup for building in the auxiliary head. + aux_head_cell_idxes = [] + if len(reduction_indices) >= 2: + aux_head_cell_idxes.append(reduction_indices[1] - 1) + + # Run the cells + filter_scaling = 1.0 + # true_cell_num accounts for the stem cells + true_cell_num = 2 if stem_type == 'imagenet' else 0 + for cell_num in range(hparams.num_cells): + stride = 1 + if hparams.skip_reduction_layer_input: + prev_layer = cell_outputs[-2] + if cell_num in reduction_indices: + filter_scaling *= hparams.filter_scaling_rate + net = reduction_cell( + net, + scope='reduction_cell_{}'.format(reduction_indices.index(cell_num)), + filter_scaling=filter_scaling, + stride=2, + prev_layer=cell_outputs[-2], + cell_num=true_cell_num) + if add_and_check_endpoint( + 'Reduction_Cell_{}'.format(reduction_indices.index(cell_num)), net): + return net, end_points + true_cell_num += 1 + cell_outputs.append(net) + if not hparams.skip_reduction_layer_input: + prev_layer = cell_outputs[-2] + net = normal_cell( + net, + scope='cell_{}'.format(cell_num), + filter_scaling=filter_scaling, + stride=stride, + prev_layer=prev_layer, + cell_num=true_cell_num) + + if add_and_check_endpoint('Cell_{}'.format(cell_num), net): + return net, end_points + true_cell_num += 1 + if (hparams.use_aux_head and cell_num in aux_head_cell_idxes and + num_classes and is_training): + aux_net = tf.nn.relu(net) + _build_aux_head( + aux_net, + end_points, + num_classes, + hparams, + scope='aux_{}'.format(cell_num)) + cell_outputs.append(net) + + # Final softmax layer + with tf.variable_scope('final_layer'): + net = tf.nn.relu(net) + net = nasnet_utils.global_avg_pool(net) + if add_and_check_endpoint('global_pool', net) or num_classes is None: + return net, end_points + net = slim.dropout(net, hparams.dense_dropout_keep_prob, scope='dropout') + logits = slim.fully_connected(net, num_classes) + + if add_and_check_endpoint('Logits', logits): + return net, end_points + + predictions = tf.nn.softmax(logits, name='predictions') + if add_and_check_endpoint('Predictions', predictions): + return net, end_points + return logits, end_points + + +class NasnetModel(model.CNNModel): + """Nasnet model configuration.""" + + def __init__(self, params=None): + super(NasnetModel, self).__init__('nasnet', 224, 32, 0.005, params=params) + + def add_inference(self, cnn): + tf.logging.info('input_image_shape: {}'.format(cnn.top_layer.shape)) + cnn.top_layer, _ = build_nasnet_mobile( + images=cnn.top_layer, + is_training=cnn.phase_train, + data_format=cnn.data_format) + cnn.top_size = cnn.top_layer.shape[-1].value + + +class NasnetLargeModel(model.CNNModel): + """Nasnet model configuration.""" + + def __init__(self, params=None): + super(NasnetLargeModel, self).__init__( + 'nasnet', 331, 16, 0.005, params=params) + + def add_inference(self, cnn): + tf.logging.info('input_image_shape: {}'.format(cnn.top_layer.shape)) + cnn.top_layer, _ = build_nasnet_large( + images=cnn.top_layer, + is_training=cnn.phase_train, + data_format=cnn.data_format) + cnn.top_size = cnn.top_layer.shape[-1].value + + +class NasnetCifarModel(model.CNNModel): + """Nasnet cifar model configuration.""" + + def __init__(self, params=None): + super(NasnetCifarModel, self).__init__( + 'nasnet', 32, 32, 0.025, params=params) + + def add_inference(self, cnn): + tf.logging.info('input_image_shape: {}'.format(cnn.top_layer.shape)) + cnn.top_layer, _ = build_nasnet_cifar( + images=cnn.top_layer, + is_training=cnn.phase_train, + data_format=cnn.data_format) + cnn.top_size = cnn.top_layer.shape[-1].value diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/nasnet_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/nasnet_test.py new file mode 100644 index 00000000..4e3bc377 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/nasnet_test.py @@ -0,0 +1,289 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 nasnet.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import tensorflow.compat.v1 as tf + +from models.tf1_only import nasnet_model as nasnet +from tensorflow.contrib import slim + + +class NASNetTest(tf.test.TestCase): + + def testBuildLogitsCifarModel(self): + batch_size = 5 + height, width = 32, 32 + num_classes = 10 + inputs = tf.random_uniform((batch_size, height, width, 3)) + tf.train.create_global_step() + with slim.arg_scope(nasnet.nasnet_cifar_arg_scope()): + logits, end_points = nasnet.build_nasnet_cifar(inputs, num_classes) + auxlogits = end_points['AuxLogits'] + predictions = end_points['Predictions'] + self.assertListEqual(auxlogits.get_shape().as_list(), + [batch_size, num_classes]) + self.assertListEqual(logits.get_shape().as_list(), + [batch_size, num_classes]) + self.assertListEqual(predictions.get_shape().as_list(), + [batch_size, num_classes]) + + def testBuildLogitsMobileModel(self): + batch_size = 5 + height, width = 224, 224 + num_classes = 1000 + inputs = tf.random_uniform((batch_size, height, width, 3)) + tf.train.create_global_step() + with slim.arg_scope(nasnet.nasnet_mobile_arg_scope()): + logits, end_points = nasnet.build_nasnet_mobile(inputs, num_classes) + auxlogits = end_points['AuxLogits'] + predictions = end_points['Predictions'] + self.assertListEqual(auxlogits.get_shape().as_list(), + [batch_size, num_classes]) + self.assertListEqual(logits.get_shape().as_list(), + [batch_size, num_classes]) + self.assertListEqual(predictions.get_shape().as_list(), + [batch_size, num_classes]) + + def testBuildLogitsLargeModel(self): + batch_size = 5 + height, width = 331, 331 + num_classes = 1000 + inputs = tf.random_uniform((batch_size, height, width, 3)) + tf.train.create_global_step() + with slim.arg_scope(nasnet.nasnet_large_arg_scope()): + logits, end_points = nasnet.build_nasnet_large(inputs, num_classes) + auxlogits = end_points['AuxLogits'] + predictions = end_points['Predictions'] + self.assertListEqual(auxlogits.get_shape().as_list(), + [batch_size, num_classes]) + self.assertListEqual(logits.get_shape().as_list(), + [batch_size, num_classes]) + self.assertListEqual(predictions.get_shape().as_list(), + [batch_size, num_classes]) + + def testBuildPreLogitsCifarModel(self): + batch_size = 5 + height, width = 32, 32 + num_classes = None + inputs = tf.random_uniform((batch_size, height, width, 3)) + tf.train.create_global_step() + with slim.arg_scope(nasnet.nasnet_cifar_arg_scope()): + net, end_points = nasnet.build_nasnet_cifar(inputs, num_classes) + self.assertNotIn('AuxLogits', end_points) + self.assertNotIn('Predictions', end_points) + self.assertTrue(net.op.name.startswith('final_layer/Mean')) + self.assertListEqual(net.get_shape().as_list(), [batch_size, 768]) + + def testBuildPreLogitsMobileModel(self): + batch_size = 5 + height, width = 224, 224 + num_classes = None + inputs = tf.random_uniform((batch_size, height, width, 3)) + tf.train.create_global_step() + with slim.arg_scope(nasnet.nasnet_mobile_arg_scope()): + net, end_points = nasnet.build_nasnet_mobile(inputs, num_classes) + self.assertNotIn('AuxLogits', end_points) + self.assertNotIn('Predictions', end_points) + self.assertTrue(net.op.name.startswith('final_layer/Mean')) + self.assertListEqual(net.get_shape().as_list(), [batch_size, 1056]) + + def testBuildPreLogitsLargeModel(self): + batch_size = 5 + height, width = 331, 331 + num_classes = None + inputs = tf.random_uniform((batch_size, height, width, 3)) + tf.train.create_global_step() + with slim.arg_scope(nasnet.nasnet_large_arg_scope()): + net, end_points = nasnet.build_nasnet_large(inputs, num_classes) + self.assertNotIn('AuxLogits', end_points) + self.assertNotIn('Predictions', end_points) + self.assertTrue(net.op.name.startswith('final_layer/Mean')) + self.assertListEqual(net.get_shape().as_list(), [batch_size, 4032]) + + def testAllEndPointsShapesCifarModel(self): + batch_size = 5 + height, width = 32, 32 + num_classes = 10 + inputs = tf.random_uniform((batch_size, height, width, 3)) + tf.train.create_global_step() + with slim.arg_scope(nasnet.nasnet_cifar_arg_scope()): + _, end_points = nasnet.build_nasnet_cifar(inputs, num_classes) + endpoints_shapes = {'Stem': [batch_size, 32, 32, 96], + 'Cell_0': [batch_size, 32, 32, 192], + 'Cell_1': [batch_size, 32, 32, 192], + 'Cell_2': [batch_size, 32, 32, 192], + 'Cell_3': [batch_size, 32, 32, 192], + 'Cell_4': [batch_size, 32, 32, 192], + 'Cell_5': [batch_size, 32, 32, 192], + 'Cell_6': [batch_size, 16, 16, 384], + 'Cell_7': [batch_size, 16, 16, 384], + 'Cell_8': [batch_size, 16, 16, 384], + 'Cell_9': [batch_size, 16, 16, 384], + 'Cell_10': [batch_size, 16, 16, 384], + 'Cell_11': [batch_size, 16, 16, 384], + 'Cell_12': [batch_size, 8, 8, 768], + 'Cell_13': [batch_size, 8, 8, 768], + 'Cell_14': [batch_size, 8, 8, 768], + 'Cell_15': [batch_size, 8, 8, 768], + 'Cell_16': [batch_size, 8, 8, 768], + 'Cell_17': [batch_size, 8, 8, 768], + 'Reduction_Cell_0': [batch_size, 16, 16, 256], + 'Reduction_Cell_1': [batch_size, 8, 8, 512], + 'global_pool': [batch_size, 768], + # Logits and predictions + 'AuxLogits': [batch_size, num_classes], + 'Logits': [batch_size, num_classes], + 'Predictions': [batch_size, num_classes]} + self.assertCountEqual(endpoints_shapes.keys(), end_points.keys()) + for endpoint_name in endpoints_shapes: + tf.logging.info('Endpoint name: {}'.format(endpoint_name)) + expected_shape = endpoints_shapes[endpoint_name] + self.assertIn(endpoint_name, end_points) + self.assertListEqual(end_points[endpoint_name].get_shape().as_list(), + expected_shape) + + def testAllEndPointsShapesMobileModel(self): + batch_size = 5 + height, width = 224, 224 + num_classes = 1000 + inputs = tf.random_uniform((batch_size, height, width, 3)) + tf.train.create_global_step() + with slim.arg_scope(nasnet.nasnet_mobile_arg_scope()): + _, end_points = nasnet.build_nasnet_mobile(inputs, num_classes) + endpoints_shapes = {'Stem': [batch_size, 28, 28, 88], + 'Cell_0': [batch_size, 28, 28, 264], + 'Cell_1': [batch_size, 28, 28, 264], + 'Cell_2': [batch_size, 28, 28, 264], + 'Cell_3': [batch_size, 28, 28, 264], + 'Cell_4': [batch_size, 14, 14, 528], + 'Cell_5': [batch_size, 14, 14, 528], + 'Cell_6': [batch_size, 14, 14, 528], + 'Cell_7': [batch_size, 14, 14, 528], + 'Cell_8': [batch_size, 7, 7, 1056], + 'Cell_9': [batch_size, 7, 7, 1056], + 'Cell_10': [batch_size, 7, 7, 1056], + 'Cell_11': [batch_size, 7, 7, 1056], + 'Reduction_Cell_0': [batch_size, 14, 14, 352], + 'Reduction_Cell_1': [batch_size, 7, 7, 704], + 'global_pool': [batch_size, 1056], + # Logits and predictions + 'AuxLogits': [batch_size, num_classes], + 'Logits': [batch_size, num_classes], + 'Predictions': [batch_size, num_classes]} + self.assertCountEqual(endpoints_shapes.keys(), end_points.keys()) + for endpoint_name in endpoints_shapes: + tf.logging.info('Endpoint name: {}'.format(endpoint_name)) + expected_shape = endpoints_shapes[endpoint_name] + self.assertIn(endpoint_name, end_points) + self.assertListEqual(end_points[endpoint_name].get_shape().as_list(), + expected_shape) + + def testAllEndPointsShapesLargeModel(self): + batch_size = 5 + height, width = 331, 331 + num_classes = 1000 + inputs = tf.random_uniform((batch_size, height, width, 3)) + tf.train.create_global_step() + with slim.arg_scope(nasnet.nasnet_large_arg_scope()): + _, end_points = nasnet.build_nasnet_large(inputs, num_classes) + endpoints_shapes = {'Stem': [batch_size, 42, 42, 336], + 'Cell_0': [batch_size, 42, 42, 1008], + 'Cell_1': [batch_size, 42, 42, 1008], + 'Cell_2': [batch_size, 42, 42, 1008], + 'Cell_3': [batch_size, 42, 42, 1008], + 'Cell_4': [batch_size, 42, 42, 1008], + 'Cell_5': [batch_size, 42, 42, 1008], + 'Cell_6': [batch_size, 21, 21, 2016], + 'Cell_7': [batch_size, 21, 21, 2016], + 'Cell_8': [batch_size, 21, 21, 2016], + 'Cell_9': [batch_size, 21, 21, 2016], + 'Cell_10': [batch_size, 21, 21, 2016], + 'Cell_11': [batch_size, 21, 21, 2016], + 'Cell_12': [batch_size, 11, 11, 4032], + 'Cell_13': [batch_size, 11, 11, 4032], + 'Cell_14': [batch_size, 11, 11, 4032], + 'Cell_15': [batch_size, 11, 11, 4032], + 'Cell_16': [batch_size, 11, 11, 4032], + 'Cell_17': [batch_size, 11, 11, 4032], + 'Reduction_Cell_0': [batch_size, 21, 21, 1344], + 'Reduction_Cell_1': [batch_size, 11, 11, 2688], + 'global_pool': [batch_size, 4032], + # Logits and predictions + 'AuxLogits': [batch_size, num_classes], + 'Logits': [batch_size, num_classes], + 'Predictions': [batch_size, num_classes]} + self.assertCountEqual(endpoints_shapes.keys(), end_points.keys()) + for endpoint_name in endpoints_shapes: + tf.logging.info('Endpoint name: {}'.format(endpoint_name)) + expected_shape = endpoints_shapes[endpoint_name] + self.assertIn(endpoint_name, end_points) + self.assertListEqual(end_points[endpoint_name].get_shape().as_list(), + expected_shape) + + def testVariablesSetDeviceMobileModel(self): + batch_size = 5 + height, width = 224, 224 + num_classes = 1000 + inputs = tf.random_uniform((batch_size, height, width, 3)) + tf.train.create_global_step() + # Force all Variables to reside on the device. + with tf.variable_scope('on_cpu'), tf.device('/cpu:0'): + with slim.arg_scope(nasnet.nasnet_mobile_arg_scope()): + nasnet.build_nasnet_mobile(inputs, num_classes) + with tf.variable_scope('on_gpu'), tf.device('/gpu:0'): + with slim.arg_scope(nasnet.nasnet_mobile_arg_scope()): + nasnet.build_nasnet_mobile(inputs, num_classes) + for v in tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope='on_cpu'): + self.assertDeviceEqual(v.device, '/cpu:0') + for v in tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope='on_gpu'): + self.assertDeviceEqual(v.device, '/gpu:0') + + def testUnknownBatchSizeMobileModel(self): + batch_size = 1 + height, width = 224, 224 + num_classes = 1000 + with self.test_session() as sess: + inputs = tf.placeholder(tf.float32, (None, height, width, 3)) + with slim.arg_scope(nasnet.nasnet_mobile_arg_scope()): + logits, _ = nasnet.build_nasnet_mobile(inputs, num_classes) + self.assertListEqual(logits.get_shape().as_list(), + [None, num_classes]) + images = tf.random_uniform((batch_size, height, width, 3)) + sess.run(tf.global_variables_initializer()) + output = sess.run(logits, {inputs: images.eval()}) + self.assertEqual(output.shape, (batch_size, num_classes)) + + def testEvaluationMobileModel(self): + batch_size = 2 + height, width = 224, 224 + num_classes = 1000 + with self.test_session() as sess: + eval_inputs = tf.random_uniform((batch_size, height, width, 3)) + with slim.arg_scope(nasnet.nasnet_mobile_arg_scope()): + logits, _ = nasnet.build_nasnet_mobile(eval_inputs, + num_classes, + is_training=False) + predictions = tf.argmax(logits, 1) + sess.run(tf.global_variables_initializer()) + output = sess.run(predictions) + self.assertEqual(output.shape, (batch_size,)) + + +if __name__ == '__main__': + tf.disable_v2_behavior() + tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/nasnet_utils.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/nasnet_utils.py new file mode 100644 index 00000000..9b280b3e --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/nasnet_utils.py @@ -0,0 +1,492 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 custom module for some common operations used by NASNet. + +Functions exposed in this file: +- calc_reduction_layers +- get_channel_index +- get_channel_dim +- global_avg_pool +- factorized_reduction +- drop_path + +Classes exposed in this file: +- NasNetABaseCell +- NasNetANormalCell +- NasNetAReductionCell +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import tensorflow.compat.v1 as tf +from tensorflow.contrib import framework as contrib_framework +from tensorflow.contrib import slim + +arg_scope = contrib_framework.arg_scope + +DATA_FORMAT_NCHW = 'NCHW' +DATA_FORMAT_NHWC = 'NHWC' +INVALID = 'null' + + +def calc_reduction_layers(num_cells, num_reduction_layers): + """Figure out what layers should have reductions.""" + reduction_layers = [] + for pool_num in range(1, num_reduction_layers + 1): + layer_num = (float(pool_num) / (num_reduction_layers + 1)) * num_cells + layer_num = int(layer_num) + reduction_layers.append(layer_num) + return reduction_layers + + +@contrib_framework.add_arg_scope +def get_channel_index(data_format=INVALID): + assert data_format != INVALID + axis = 3 if data_format == 'NHWC' else 1 + return axis + + +@contrib_framework.add_arg_scope +def get_channel_dim(shape, data_format=INVALID): + assert data_format != INVALID + assert len(shape) == 4 + if data_format == 'NHWC': + return int(shape[3]) + elif data_format == 'NCHW': + return int(shape[1]) + else: + raise ValueError('Not a valid data_format', data_format) + + +@contrib_framework.add_arg_scope +def global_avg_pool(x, data_format=INVALID): + """Average pool away the height and width spatial dimensions of x.""" + assert data_format != INVALID + assert data_format in ['NHWC', 'NCHW'] + assert x.shape.ndims == 4 + if data_format == 'NHWC': + return tf.reduce_mean(x, [1, 2]) + else: + return tf.reduce_mean(x, [2, 3]) + + +@contrib_framework.add_arg_scope +def factorized_reduction(net, output_filters, stride, data_format=INVALID): + """Reduces the shape of net without information loss due to striding.""" + assert output_filters % 2 == 0, ( + 'Need even number of filters when using this factorized reduction.') + assert data_format != INVALID + if stride == 1: + net = slim.conv2d(net, output_filters, 1, scope='path_conv') + net = slim.batch_norm(net, scope='path_bn') + return net + if data_format == 'NHWC': + stride_spec = [1, stride, stride, 1] + else: + stride_spec = [1, 1, stride, stride] + + # Skip path 1 + path1 = tf.nn.avg_pool( + net, [1, 1, 1, 1], stride_spec, 'VALID', data_format=data_format) + path1 = slim.conv2d(path1, int(output_filters / 2), 1, scope='path1_conv') + + # Skip path 2 + # First pad with 0's on the right and bottom, then shift the filter to + # include those 0's that were added. + if data_format == 'NHWC': + pad_arr = [[0, 0], [0, 1], [0, 1], [0, 0]] + path2 = tf.pad(net, pad_arr)[:, 1:, 1:, :] + concat_axis = 3 + else: + pad_arr = [[0, 0], [0, 0], [0, 1], [0, 1]] + path2 = tf.pad(net, pad_arr)[:, :, 1:, 1:] + concat_axis = 1 + + path2 = tf.nn.avg_pool( + path2, [1, 1, 1, 1], stride_spec, 'VALID', data_format=data_format) + path2 = slim.conv2d(path2, int(output_filters / 2), 1, scope='path2_conv') + + # Concat and apply BN + final_path = tf.concat(values=[path1, path2], axis=concat_axis) + final_path = slim.batch_norm(final_path, scope='final_path_bn') + return final_path + + +@contrib_framework.add_arg_scope +def drop_path(net, keep_prob, is_training=True): + """Drops out a whole example hiddenstate with the specified probability.""" + if is_training: + batch_size = tf.shape(net)[0] + noise_shape = [batch_size, 1, 1, 1] + keep_prob = tf.cast(keep_prob, dtype=net.dtype) + random_tensor = keep_prob + random_tensor += tf.random_uniform(noise_shape, dtype=net.dtype) + binary_tensor = tf.floor(random_tensor) + net = tf.div(net, keep_prob) * binary_tensor + return net + + +def _operation_to_filter_shape(operation): + splitted_operation = operation.split('x') + filter_shape = int(splitted_operation[0][-1]) + assert filter_shape == int( + splitted_operation[1][0]), 'Rectangular filters not supported.' + return filter_shape + + +def _operation_to_num_layers(operation): + splitted_operation = operation.split('_') + if 'x' in splitted_operation[-1]: + return 1 + return int(splitted_operation[-1]) + + +def _operation_to_info(operation): + """Takes in operation name and returns meta information. + + An example would be 'separable_3x3_4' -> (3, 4). + + Args: + operation: String that corresponds to convolution operation. + + Returns: + Tuple of (filter shape, num layers). + """ + num_layers = _operation_to_num_layers(operation) + filter_shape = _operation_to_filter_shape(operation) + return num_layers, filter_shape + + +def _stacked_separable_conv(net, stride, operation, filter_size): + """Takes in an operations and parses it to the correct sep operation.""" + num_layers, kernel_size = _operation_to_info(operation) + net_type = net.dtype + net = tf.cast(net, tf.float32) if net_type == tf.float16 else net + + for layer_num in range(num_layers - 1): + net = tf.nn.relu(net) + net = slim.separable_conv2d( + net, + filter_size, + kernel_size, + depth_multiplier=1, + scope='separable_{0}x{0}_{1}'.format(kernel_size, layer_num + 1), + stride=stride) + net = slim.batch_norm( + net, scope='bn_sep_{0}x{0}_{1}'.format(kernel_size, layer_num + 1)) + stride = 1 + net = tf.nn.relu(net) + net = slim.separable_conv2d( + net, + filter_size, + kernel_size, + depth_multiplier=1, + scope='separable_{0}x{0}_{1}'.format(kernel_size, num_layers), + stride=stride) + net = slim.batch_norm( + net, scope='bn_sep_{0}x{0}_{1}'.format(kernel_size, num_layers)) + net = tf.cast(net, net_type) + return net + + +def _operation_to_pooling_type(operation): + """Takes in the operation string and returns the pooling type.""" + splitted_operation = operation.split('_') + return splitted_operation[0] + + +def _operation_to_pooling_shape(operation): + """Takes in the operation string and returns the pooling kernel shape.""" + splitted_operation = operation.split('_') + shape = splitted_operation[-1] + assert 'x' in shape + filter_height, filter_width = shape.split('x') + assert filter_height == filter_width + return int(filter_height) + + +def _operation_to_pooling_info(operation): + """Parses the pooling operation string to return its type and shape.""" + pooling_type = _operation_to_pooling_type(operation) + pooling_shape = _operation_to_pooling_shape(operation) + return pooling_type, pooling_shape + + +def _pooling(net, stride, operation): + """Parses operation and performs the correct pooling operation on net.""" + padding = 'SAME' + pooling_type, pooling_shape = _operation_to_pooling_info(operation) + if pooling_type == 'avg': + net = slim.avg_pool2d(net, pooling_shape, stride=stride, padding=padding) + elif pooling_type == 'max': + net = slim.max_pool2d(net, pooling_shape, stride=stride, padding=padding) + else: + raise NotImplementedError('Unimplemented pooling type: ', pooling_type) + return net + + +class NasNetABaseCell(object): # pylint: disable=g-classes-have-attributes + """NASNet Cell class that is used as a 'layer' in image architectures. + + Args: + num_conv_filters: The number of filters for each convolution operation. + operations: List of operations that are performed in the NASNet Cell in + order. + used_hiddenstates: Binary array that signals if the hiddenstate was used + within the cell. This is used to determine what outputs of the cell + should be concatenated together. + hiddenstate_indices: Determines what hiddenstates should be combined + together with the specified operations to create the NASNet cell. + """ + + def __init__(self, num_conv_filters, operations, used_hiddenstates, + hiddenstate_indices, drop_path_keep_prob, total_num_cells, + total_training_steps): + self._num_conv_filters = num_conv_filters + self._operations = operations + self._used_hiddenstates = used_hiddenstates + self._hiddenstate_indices = hiddenstate_indices + self._drop_path_keep_prob = drop_path_keep_prob + self._total_num_cells = total_num_cells + self._total_training_steps = total_training_steps + + def _reduce_prev_layer(self, prev_layer, curr_layer): + """Matches dimension of prev_layer to the curr_layer.""" + # Set the prev layer to the current layer if it is none + if prev_layer is None: + return curr_layer + curr_num_filters = self._filter_size + prev_num_filters = get_channel_dim(prev_layer.shape) + curr_filter_shape = int(curr_layer.shape[2]) + prev_filter_shape = int(prev_layer.shape[2]) + if curr_filter_shape != prev_filter_shape: + prev_layer = tf.nn.relu(prev_layer) + prev_layer = factorized_reduction(prev_layer, curr_num_filters, stride=2) + elif curr_num_filters != prev_num_filters: + prev_layer = tf.nn.relu(prev_layer) + prev_layer = slim.conv2d( + prev_layer, curr_num_filters, 1, scope='prev_1x1') + prev_layer = slim.batch_norm(prev_layer, scope='prev_bn') + return prev_layer + + def _cell_base(self, net, prev_layer): + """Runs the beginning of the conv cell before the predicted ops are run.""" + num_filters = self._filter_size + + # Check to be sure prev layer stuff is setup correctly + prev_layer = self._reduce_prev_layer(prev_layer, net) + + net = tf.nn.relu(net) + net = slim.conv2d(net, num_filters, 1, scope='1x1') + net = slim.batch_norm(net, scope='beginning_bn') + split_axis = get_channel_index() + net = tf.split(axis=split_axis, num_or_size_splits=1, value=net) + for split in net: + assert int(split.shape[split_axis] == int( + self._num_conv_filters * self._filter_scaling)) + net.append(prev_layer) + return net + + def __call__(self, + net, + scope=None, + filter_scaling=1, + stride=1, + prev_layer=None, + cell_num=-1): + """Runs the conv cell.""" + self._cell_num = cell_num + self._filter_scaling = filter_scaling + self._filter_size = int(self._num_conv_filters * filter_scaling) + + i = 0 + with tf.variable_scope(scope): + net = self._cell_base(net, prev_layer) + for iteration in range(5): + with tf.variable_scope('comb_iter_{}'.format(iteration)): + left_hiddenstate_idx, right_hiddenstate_idx = ( + self._hiddenstate_indices[i], self._hiddenstate_indices[i + 1]) + original_input_left = left_hiddenstate_idx < 2 + original_input_right = right_hiddenstate_idx < 2 + h1 = net[left_hiddenstate_idx] + h2 = net[right_hiddenstate_idx] + + operation_left = self._operations[i] + operation_right = self._operations[i + 1] + i += 2 + # Apply conv operations + with tf.variable_scope('left'): + h1 = self._apply_conv_operation(h1, operation_left, stride, + original_input_left) + with tf.variable_scope('right'): + h2 = self._apply_conv_operation(h2, operation_right, stride, + original_input_right) + + # Combine hidden states using 'add'. + with tf.variable_scope('combine'): + h = h1 + h2 + + # Add hiddenstate to the list of hiddenstates we can choose from + net.append(h) + + with tf.variable_scope('cell_output'): + net = self._combine_unused_states(net) + + return net + + def _apply_conv_operation(self, net, operation, stride, + is_from_original_input): + """Applies the predicted conv operation to net.""" + # Dont stride if this is not one of the original hiddenstates + if stride > 1 and not is_from_original_input: + stride = 1 + input_filters = get_channel_dim(net.shape) + filter_size = self._filter_size + if 'separable' in operation: + net = _stacked_separable_conv(net, stride, operation, filter_size) + elif operation in ['none']: + # Check if a stride is needed, then use a strided 1x1 here + if stride > 1 or (input_filters != filter_size): + net = tf.nn.relu(net) + net = slim.conv2d(net, filter_size, 1, stride=stride, scope='1x1') + net = slim.batch_norm(net, scope='bn_1') + elif 'pool' in operation: + net = _pooling(net, stride, operation) + if input_filters != filter_size: + net = slim.conv2d(net, filter_size, 1, stride=1, scope='1x1') + net = slim.batch_norm(net, scope='bn_1') + else: + raise ValueError('Unimplemented operation', operation) + + if operation != 'none': + net = self._apply_drop_path(net) + return net + + def _combine_unused_states(self, net): + """Concatenate the unused hidden states of the cell.""" + used_hiddenstates = self._used_hiddenstates + + final_height = int(net[-1].shape[2]) + final_num_filters = get_channel_dim(net[-1].shape) + assert len(used_hiddenstates) == len(net) + for idx, used_h in enumerate(used_hiddenstates): + curr_height = int(net[idx].shape[2]) + curr_num_filters = get_channel_dim(net[idx].shape) + + # Determine if a reduction should be applied to make the number of + # filters match. + should_reduce = final_num_filters != curr_num_filters + should_reduce = (final_height != curr_height) or should_reduce + should_reduce = should_reduce and not used_h + if should_reduce: + stride = 2 if final_height != curr_height else 1 + with tf.variable_scope('reduction_{}'.format(idx)): + net[idx] = factorized_reduction(net[idx], final_num_filters, stride) + + states_to_combine = ([ + h for h, is_used in zip(net, used_hiddenstates) if not is_used + ]) + + # Return the concat of all the states + concat_axis = get_channel_index() + net = tf.concat(values=states_to_combine, axis=concat_axis) + return net + + @contrib_framework.add_arg_scope # No public API. For internal use only. + def _apply_drop_path(self, + net, + current_step=None, + use_summaries=True, + drop_connect_version='v3'): + """Apply drop_path regularization. + + Args: + net: the Tensor that gets drop_path regularization applied. + current_step: a float32 Tensor with the current global_step value, + to be divided by hparams.total_training_steps. Usually None, which + defaults to tf.train.get_or_create_global_step() properly casted. + use_summaries: a Python boolean. If set to False, no summaries are output. + drop_connect_version: one of 'v1', 'v2', 'v3', controlling whether + the dropout rate is scaled by current_step (v1), layer (v2), or + both (v3, the default). + + Returns: + The dropped-out value of `net`. + """ + drop_path_keep_prob = self._drop_path_keep_prob + if drop_path_keep_prob < 1.0: + assert drop_connect_version in ['v1', 'v2', 'v3'] + if drop_connect_version in ['v2', 'v3']: + # Scale keep prob by layer number + assert self._cell_num != -1 + # The added 2 is for the reduction cells + num_cells = self._total_num_cells + layer_ratio = (self._cell_num + 1) / float(num_cells) + if use_summaries: + with tf.device('/cpu:0'): + tf.summary.scalar('layer_ratio', layer_ratio) + drop_path_keep_prob = 1 - layer_ratio * (1 - drop_path_keep_prob) + if drop_connect_version in ['v1', 'v3']: + # Decrease the keep probability over time + if not current_step: + current_step = tf.cast(tf.train.get_or_create_global_step(), + tf.float32) + drop_path_burn_in_steps = self._total_training_steps + current_ratio = current_step / drop_path_burn_in_steps + current_ratio = tf.minimum(1.0, current_ratio) + if use_summaries: + with tf.device('/cpu:0'): + tf.summary.scalar('current_ratio', current_ratio) + drop_path_keep_prob = (1 - current_ratio * (1 - drop_path_keep_prob)) + if use_summaries: + with tf.device('/cpu:0'): + tf.summary.scalar('drop_path_keep_prob', drop_path_keep_prob) + net = drop_path(net, drop_path_keep_prob) + return net + + +class NasNetANormalCell(NasNetABaseCell): + """NASNetA Normal Cell.""" + + def __init__(self, num_conv_filters, drop_path_keep_prob, total_num_cells, + total_training_steps): + operations = [ + 'separable_5x5_2', 'separable_3x3_2', 'separable_5x5_2', + 'separable_3x3_2', 'avg_pool_3x3', 'none', 'avg_pool_3x3', + 'avg_pool_3x3', 'separable_3x3_2', 'none' + ] + used_hiddenstates = [1, 0, 0, 0, 0, 0, 0] + hiddenstate_indices = [0, 1, 1, 1, 0, 1, 1, 1, 0, 0] + super(NasNetANormalCell, self).__init__( + num_conv_filters, operations, used_hiddenstates, hiddenstate_indices, + drop_path_keep_prob, total_num_cells, total_training_steps) + + +class NasNetAReductionCell(NasNetABaseCell): + """NASNetA Reduction Cell.""" + + def __init__(self, num_conv_filters, drop_path_keep_prob, total_num_cells, + total_training_steps): + operations = [ + 'separable_5x5_2', 'separable_7x7_2', 'max_pool_3x3', 'separable_7x7_2', + 'avg_pool_3x3', 'separable_5x5_2', 'none', 'avg_pool_3x3', + 'separable_3x3_2', 'max_pool_3x3' + ] + used_hiddenstates = [1, 1, 1, 0, 0, 0, 0] + hiddenstate_indices = [0, 1, 0, 1, 0, 1, 3, 2, 2, 0] + super(NasNetAReductionCell, self).__init__( + num_conv_filters, operations, used_hiddenstates, hiddenstate_indices, + drop_path_keep_prob, total_num_cells, total_training_steps) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/ssd_model.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/ssd_model.py new file mode 100644 index 00000000..56d9e16d --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/tf1_only/ssd_model.py @@ -0,0 +1,683 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== + + +"""SSD300 Model Configuration. + +References: + Wei Liu, Dragomir Anguelov, Dumitru Erhan, Christian Szegedy, Scott Reed, + Cheng-Yang Fu, Alexander C. Berg + SSD: Single Shot MultiBox Detector + arXiv:1512.02325 + +Ported from MLPerf reference implementation: + https://github.com/mlperf/reference/tree/ssd/single_stage_detector/ssd + +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import multiprocessing +import os +import re +import threading +import tensorflow.compat.v1 as tf + +# pylint: disable=g-direct-tensorflow-import +import constants +import mlperf +import ssd_constants +from cnn_util import log_fn +from models import model as model_lib +from models import resnet_model +from tensorflow.contrib import layers as contrib_layers +from tensorflow.python.ops import variables + +BACKBONE_MODEL_SCOPE_NAME = 'resnet34_backbone' + + +class SSD300Model(model_lib.CNNModel): + """Single Shot Multibox Detection (SSD) model for 300x300 image datasets.""" + + def __init__(self, label_num=ssd_constants.NUM_CLASSES, batch_size=32, + learning_rate=1e-3, backbone='resnet34', params=None): + super(SSD300Model, self).__init__('ssd300', 300, batch_size, learning_rate, + params=params) + # For COCO dataset, 80 categories + 1 background = 81 labels + self.label_num = label_num + + # Currently only support ResNet-34 as backbone model + if backbone != 'resnet34': + raise ValueError('Invalid backbone model %s for SSD.' % backbone) + mlperf.logger.log(key=mlperf.tags.BACKBONE, value=backbone) + + # Number of channels and default boxes associated with the following layers: + # ResNet34 layer, Conv7, Conv8_2, Conv9_2, Conv10_2, Conv11_2 + self.out_chan = [256, 512, 512, 256, 256, 256] + mlperf.logger.log(key=mlperf.tags.LOC_CONF_OUT_CHANNELS, + value=self.out_chan) + + # Number of default boxes from layers of different scales + # 38x38x4, 19x19x6, 10x10x6, 5x5x6, 3x3x4, 1x1x4 + self.num_dboxes = [4, 6, 6, 6, 4, 4] + mlperf.logger.log(key=mlperf.tags.NUM_DEFAULTS_PER_CELL, + value=self.num_dboxes) + + # TODO(haoyuzhang): in order to correctly restore in replicated mode, need + # to create a saver for each tower before graph is finalized. Use variable + # manager for better efficiency. + self.backbone_savers = [] + + # Collected predictions for eval stage. It maps each image id in eval + # dataset to a dict containing the following information: + # source_id: raw ID of image + # raw_shape: raw shape of image + # pred_box: encoded box coordinates of prediction + # pred_scores: scores of classes in prediction + self.predictions = {} + + # Global step when predictions are collected. + self.eval_global_step = 0 + + # Average precision. In asynchronous eval mode, this is the latest AP we + # get so far and may not be the results at current eval step. + self.eval_coco_ap = 0 + + # Process, queues, and thread for asynchronous evaluation. When enabled, + # create a separate process (async_eval_process) that continuously pull + # intermediate results from the predictions queue (a multiprocessing queue), + # process them, and push final results into results queue (another + # multiprocessing queue). The main thread is responsible to push messages + # into predictions queue, and start a separate thread to continuously pull + # messages from results queue to update final results. + # Message in predictions queue should be a tuple of two elements: + # (evaluation step, predictions) + # Message in results queue should be a tuple of two elements: + # (evaluation step, final results) + self.async_eval_process = None + self.async_eval_predictions_queue = None + self.async_eval_results_queue = None + self.async_eval_results_getter_thread = None + + # The MLPerf reference uses a starting lr of 1e-3 at bs=32. + self.base_lr_batch_size = 32 + + def skip_final_affine_layer(self): + return True + + def gpu_preprocess_nhwc(self, images, phase_train=True): + try: + import ssd_dataloader # pylint: disable=g-import-not-at-top + except ImportError: + raise ImportError('To use the COCO dataset, you must clone the ' + 'repo https://github.com/tensorflow/models and add ' + 'tensorflow/models and tensorflow/models/research to ' + 'the PYTHONPATH, and compile the protobufs by ' + 'following https://github.com/tensorflow/models/blob/' + 'master/research/object_detection/g3doc/installation.md' + '#protobuf-compilation ; To evaluate using COCO' + 'metric, download and install Python COCO API from' + 'https://github.com/cocodataset/cocoapi') + + if phase_train: + images = ssd_dataloader.color_jitter( + images, brightness=0.125, contrast=0.5, saturation=0.5, hue=0.05) + images = ssd_dataloader.normalize_image(images) + return images + + def add_backbone_model(self, cnn): + # -------------------------------------------------------------------------- + # Resnet-34 backbone model -- modified for SSD + # -------------------------------------------------------------------------- + + # Input 300x300, output 150x150 + cnn.conv(64, 7, 7, 2, 2, mode='SAME_RESNET', use_batch_norm=True) + cnn.mpool(3, 3, 2, 2, mode='SAME') + + resnet34_layers = [3, 4, 6, 3] + version = 'v1' + + # ResNet-34 block group 1 + # Input 150x150, output 75x75 + for i in range(resnet34_layers[0]): + # Last argument forces residual_block to use projection shortcut, even + # though the numbers of input and output channels are equal + resnet_model.residual_block(cnn, 64, 1, version) + + # ResNet-34 block group 2 + # Input 75x75, output 38x38 + for i in range(resnet34_layers[1]): + stride = 2 if i == 0 else 1 + resnet_model.residual_block(cnn, 128, stride, version, i == 0) + + # ResNet-34 block group 3 + # This block group is modified: first layer uses stride=1 so that the image + # size does not change in group of layers + # Input 38x38, output 38x38 + for i in range(resnet34_layers[2]): + # The following line is intentionally commented out to differentiate from + # the original ResNet-34 model + # stride = 2 if i == 0 else 1 + resnet_model.residual_block(cnn, 256, stride, version, i == 0) + + # ResNet-34 block group 4: removed final block group + # The following 3 lines are intentionally commented out to differentiate + # from the original ResNet-34 model + # for i in range(resnet34_layers[3]): + # stride = 2 if i == 0 else 1 + # resnet_model.residual_block(cnn, 512, stride, version, i == 0) + + def add_inference(self, cnn): + cnn.use_batch_norm = True + cnn.batch_norm_config = {'decay': ssd_constants.BATCH_NORM_DECAY, + 'epsilon': ssd_constants.BATCH_NORM_EPSILON, + 'scale': True} + + with tf.variable_scope(BACKBONE_MODEL_SCOPE_NAME): + self.add_backbone_model(cnn) + + # -------------------------------------------------------------------------- + # SSD additional layers + # -------------------------------------------------------------------------- + + def add_ssd_layer(cnn, depth, k_size, stride, mode): + return cnn.conv( + depth, + k_size, + k_size, + stride, + stride, + mode=mode, + use_batch_norm=False, + kernel_initializer=contrib_layers.xavier_initializer()) + + # Activations for feature maps of different layers + self.activations = [cnn.top_layer] + # Conv7_1, Conv7_2 + # Input 38x38, output 19x19 + add_ssd_layer(cnn, 256, 1, 1, 'valid') + self.activations.append(add_ssd_layer(cnn, 512, 3, 2, 'same')) + + # Conv8_1, Conv8_2 + # Input 19x19, output 10x10 + add_ssd_layer(cnn, 256, 1, 1, 'valid') + self.activations.append(add_ssd_layer(cnn, 512, 3, 2, 'same')) + + # Conv9_1, Conv9_2 + # Input 10x10, output 5x5 + add_ssd_layer(cnn, 128, 1, 1, 'valid') + self.activations.append(add_ssd_layer(cnn, 256, 3, 2, 'same')) + + # Conv10_1, Conv10_2 + # Input 5x5, output 3x3 + add_ssd_layer(cnn, 128, 1, 1, 'valid') + self.activations.append(add_ssd_layer(cnn, 256, 3, 1, 'valid')) + + # Conv11_1, Conv11_2 + # Input 3x3, output 1x1 + add_ssd_layer(cnn, 128, 1, 1, 'valid') + self.activations.append(add_ssd_layer(cnn, 256, 3, 1, 'valid')) + + self.loc = [] + self.conf = [] + + for nd, ac, oc in zip(self.num_dboxes, self.activations, self.out_chan): + l = cnn.conv( + nd * 4, + 3, + 3, + 1, + 1, + input_layer=ac, + num_channels_in=oc, + activation=None, + use_batch_norm=False, + kernel_initializer=contrib_layers.xavier_initializer()) + scale = l.get_shape()[-1] + # shape = [batch_size, nd * 4, scale, scale] + l = tf.reshape(l, [self.batch_size, nd, 4, scale, scale]) + # shape = [batch_size, nd, 4, scale, scale] + l = tf.transpose(l, [0, 1, 3, 4, 2]) + # shape = [batch_size, nd, scale, scale, 4] + self.loc.append(tf.reshape(l, [self.batch_size, -1, 4])) + # shape = [batch_size, nd * scale * scale, 4] + + c = cnn.conv( + nd * self.label_num, + 3, + 3, + 1, + 1, + input_layer=ac, + num_channels_in=oc, + activation=None, + use_batch_norm=False, + kernel_initializer=contrib_layers.xavier_initializer()) + # shape = [batch_size, nd * label_num, scale, scale] + c = tf.reshape(c, [self.batch_size, nd, self.label_num, scale, scale]) + # shape = [batch_size, nd, label_num, scale, scale] + c = tf.transpose(c, [0, 1, 3, 4, 2]) + # shape = [batch_size, nd, scale, scale, label_num] + self.conf.append(tf.reshape(c, [self.batch_size, -1, self.label_num])) + # shape = [batch_size, nd * scale * scale, label_num] + + # Shape of locs: [batch_size, NUM_SSD_BOXES, 4] + # Shape of confs: [batch_size, NUM_SSD_BOXES, label_num] + locs, confs = tf.concat(self.loc, 1), tf.concat(self.conf, 1) + + # Pack location and confidence outputs into a single output layer + # Shape of logits: [batch_size, NUM_SSD_BOXES, 4+label_num] + logits = tf.concat([locs, confs], 2) + + cnn.top_layer = logits + cnn.top_size = 4 + self.label_num + + return cnn.top_layer + + def get_learning_rate(self, global_step, batch_size): + rescaled_lr = self.get_scaled_base_learning_rate(batch_size) + # Defined in MLPerf reference model + boundaries = [160000, 200000] + boundaries = [b * self.base_lr_batch_size // batch_size for b in boundaries] + decays = [1, 0.1, 0.01] + learning_rates = [rescaled_lr * d for d in decays] + lr = tf.train.piecewise_constant(global_step, boundaries, learning_rates) + warmup_steps = int(118287 / batch_size * 5) + warmup_lr = ( + rescaled_lr * tf.cast(global_step, tf.float32) / tf.cast( + warmup_steps, tf.float32)) + return tf.cond(global_step < warmup_steps, lambda: warmup_lr, lambda: lr) + + def get_scaled_base_learning_rate(self, batch_size): + """Calculates base learning rate for creating lr schedule. + + In replicated mode, gradients are summed rather than averaged which, with + the sgd and momentum optimizers, increases the effective learning rate by + lr * num_gpus. Dividing the base lr by num_gpus negates the increase. + + Args: + batch_size: Total batch-size. + + Returns: + Base learning rate to use to create lr schedule. + """ + base_lr = self.learning_rate + if self.params.variable_update == 'replicated': + base_lr = self.learning_rate / self.params.num_gpus + scaled_lr = base_lr * (batch_size / self.base_lr_batch_size) + return scaled_lr + + def _collect_backbone_vars(self): + backbone_vars = tf.get_collection( + tf.GraphKeys.GLOBAL_VARIABLES, scope='.*'+ BACKBONE_MODEL_SCOPE_NAME) + var_list = {} + + # Assume variables in the checkpoint are following the naming convention of + # a model checkpoint trained with TF official model + # TODO(haoyuzhang): the following variable name parsing is hacky and easy + # to break if there is change in naming convention of either benchmarks or + # official models. + for v in backbone_vars: + # conv2d variable example (model <-- checkpoint): + # v/cg/conv24/conv2d/kernel:0 <-- conv2d_24/kernel + if 'conv2d' in v.name: + re_match = re.search(r'conv(\d+)/conv2d/(.+):', v.name) + if re_match: + layer_id = int(re_match.group(1)) + param_name = re_match.group(2) + vname_in_ckpt = self._var_name_in_official_model_ckpt( + 'conv2d', layer_id, param_name) + var_list[vname_in_ckpt] = v + + # batchnorm varariable example: + # v/cg/conv24/batchnorm25/gamma:0 <-- batch_normalization_25/gamma + elif 'batchnorm' in v.name: + re_match = re.search(r'batchnorm(\d+)/(.+):', v.name) + if re_match: + layer_id = int(re_match.group(1)) + param_name = re_match.group(2) + vname_in_ckpt = self._var_name_in_official_model_ckpt( + 'batch_normalization', layer_id, param_name) + var_list[vname_in_ckpt] = v + + return var_list + + def _var_name_in_official_model_ckpt(self, layer_name, layer_id, param_name): + """Return variable names according to convention in TF official models.""" + vname_in_ckpt = layer_name + if layer_id > 0: + vname_in_ckpt += '_' + str(layer_id) + vname_in_ckpt += '/' + param_name + return vname_in_ckpt + + def loss_function(self, inputs, build_network_result): + logits = build_network_result.logits + + # Unpack model output back to locations and confidence scores of predictions + # Shape of pred_loc: [batch_size, NUM_SSD_BOXES, 4] + # Shape of pred_label: [batch_size, NUM_SSD_BOXES, label_num] + pred_loc, pred_label = tf.split(logits, [4, self.label_num], 2) + + # Shape of gt_loc: [batch_size, NUM_SSD_BOXES, 4] + # Shape of gt_label: [batch_size, NUM_SSD_BOXES, 1] + # Shape of num_gt: [batch_size] + _, gt_loc, gt_label, num_gt = inputs + gt_label = tf.cast(gt_label, tf.int32) + + box_loss = self._localization_loss(pred_loc, gt_loc, gt_label, num_gt) + class_loss = self._classification_loss(pred_label, gt_label, num_gt) + + tf.summary.scalar('box_loss', tf.reduce_mean(box_loss)) + tf.summary.scalar('class_loss', tf.reduce_mean(class_loss)) + return class_loss + box_loss + + def _localization_loss(self, pred_loc, gt_loc, gt_label, num_matched_boxes): + """Computes the localization loss. + + Computes the localization loss using smooth l1 loss. + Args: + pred_loc: a flatten tensor that includes all predicted locations. The + shape is [batch_size, num_anchors, 4]. + gt_loc: a tensor representing box regression targets in + [batch_size, num_anchors, 4]. + gt_label: a tensor that represents the classification groundtruth targets. + The shape is [batch_size, num_anchors, 1]. + num_matched_boxes: the number of anchors that are matched to a groundtruth + targets, used as the loss normalizater. The shape is [batch_size]. + Returns: + box_loss: a float32 representing total box regression loss. + """ + mask = tf.greater(tf.squeeze(gt_label), 0) + float_mask = tf.cast(mask, tf.float32) + + smooth_l1 = tf.reduce_sum(tf.losses.huber_loss( + gt_loc, pred_loc, + reduction=tf.losses.Reduction.NONE + ), axis=2) + smooth_l1 = tf.multiply(smooth_l1, float_mask) + box_loss = tf.reduce_sum(smooth_l1, axis=1) + + return tf.reduce_mean(box_loss / num_matched_boxes) + + def _classification_loss(self, pred_label, gt_label, num_matched_boxes): + """Computes the classification loss. + + Computes the classification loss with hard negative mining. + Args: + pred_label: a flatten tensor that includes all predicted class. The shape + is [batch_size, num_anchors, num_classes]. + gt_label: a tensor that represents the classification groundtruth targets. + The shape is [batch_size, num_anchors, 1]. + num_matched_boxes: the number of anchors that are matched to a groundtruth + targets. This is used as the loss normalizater. + + Returns: + box_loss: a float32 representing total box regression loss. + """ + cross_entropy = tf.losses.sparse_softmax_cross_entropy( + gt_label, pred_label, reduction=tf.losses.Reduction.NONE) + + mask = tf.greater(tf.squeeze(gt_label), 0) + float_mask = tf.cast(mask, tf.float32) + + # Hard example mining + neg_masked_cross_entropy = cross_entropy * (1 - float_mask) + relative_position = tf.argsort( + tf.argsort( + neg_masked_cross_entropy, direction='DESCENDING')) + num_neg_boxes = tf.minimum( + tf.to_int32(num_matched_boxes) * ssd_constants.NEGS_PER_POSITIVE, + ssd_constants.NUM_SSD_BOXES) + top_k_neg_mask = tf.cast(tf.less( + relative_position, + tf.tile(num_neg_boxes[:, tf.newaxis], (1, ssd_constants.NUM_SSD_BOXES)) + ), tf.float32) + + class_loss = tf.reduce_sum( + tf.multiply(cross_entropy, float_mask + top_k_neg_mask), axis=1) + + return tf.reduce_mean(class_loss / num_matched_boxes) + + def add_backbone_saver(self): + # Create saver with mapping from variable names in checkpoint of backbone + # model to variables in SSD model + backbone_var_list = self._collect_backbone_vars() + self.backbone_savers.append(tf.train.Saver(backbone_var_list)) + + def load_backbone_model(self, sess, backbone_model_path): + for saver in self.backbone_savers: + saver.restore(sess, backbone_model_path) + + def get_input_data_types(self, subset): + if subset == 'validation': + return [self.data_type, tf.float32, tf.float32, tf.float32, tf.int32] + return [self.data_type, tf.float32, tf.float32, tf.float32] + + def get_input_shapes(self, subset): + """Return encoded tensor shapes for train and eval data respectively.""" + if subset == 'validation': + # Validation data shapes: + # 1. images + # 2. ground truth locations of boxes + # 3. ground truth classes of objects in boxes + # 4. source image IDs + # 5. raw image shapes + return [ + [self.batch_size, self.image_size, self.image_size, self.depth], + [self.batch_size, ssd_constants.MAX_NUM_EVAL_BOXES, 4], + [self.batch_size, ssd_constants.MAX_NUM_EVAL_BOXES, 1], + [self.batch_size], + [self.batch_size, 3], + ] + + # Training data shapes: + # 1. images + # 2. ground truth locations of boxes + # 3. ground truth classes of objects in boxes + # 4. numbers of objects in images + return [ + [self.batch_size, self.image_size, self.image_size, self.depth], + [self.batch_size, ssd_constants.NUM_SSD_BOXES, 4], + [self.batch_size, ssd_constants.NUM_SSD_BOXES, 1], + [self.batch_size] + ] + + def accuracy_function(self, inputs, logits): + """Returns the ops to measure the mean precision of the model.""" + try: + import ssd_dataloader # pylint: disable=g-import-not-at-top + from object_detection.box_coders import faster_rcnn_box_coder # pylint: disable=g-import-not-at-top + from object_detection.core import box_coder # pylint: disable=g-import-not-at-top + from object_detection.core import box_list # pylint: disable=g-import-not-at-top + except ImportError: + raise ImportError('To use the COCO dataset, you must clone the ' + 'repo https://github.com/tensorflow/models and add ' + 'tensorflow/models and tensorflow/models/research to ' + 'the PYTHONPATH, and compile the protobufs by ' + 'following https://github.com/tensorflow/models/blob/' + 'master/research/object_detection/g3doc/installation.md' + '#protobuf-compilation ; To evaluate using COCO' + 'metric, download and install Python COCO API from' + 'https://github.com/cocodataset/cocoapi') + + # Unpack model output back to locations and confidence scores of predictions + # pred_locs: relative locations (coordinates) of objects in all SSD boxes + # shape: [batch_size, NUM_SSD_BOXES, 4] + # pred_labels: confidence scores of objects being of all categories + # shape: [batch_size, NUM_SSD_BOXES, label_num] + pred_locs, pred_labels = tf.split(logits, [4, self.label_num], 2) + + ssd_box_coder = faster_rcnn_box_coder.FasterRcnnBoxCoder( + scale_factors=ssd_constants.BOX_CODER_SCALES) + anchors = box_list.BoxList( + tf.convert_to_tensor(ssd_dataloader.DefaultBoxes()('ltrb'))) + pred_boxes = box_coder.batch_decode( + encoded_boxes=pred_locs, box_coder=ssd_box_coder, anchors=anchors) + + pred_scores = tf.nn.softmax(pred_labels, axis=2) + + # TODO(haoyuzhang): maybe use `gt_boxes` and `gt_classes` for visualization. + _, gt_boxes, gt_classes, source_id, raw_shape = inputs # pylint: disable=unused-variable + + return { + (constants.UNREDUCED_ACCURACY_OP_PREFIX + + ssd_constants.PRED_BOXES): pred_boxes, + (constants.UNREDUCED_ACCURACY_OP_PREFIX + + ssd_constants.PRED_SCORES): pred_scores, + # TODO(haoyuzhang): maybe use these values for visualization. + # constants.UNREDUCED_ACCURACY_OP_PREFIX+'gt_boxes': gt_boxes, + # constants.UNREDUCED_ACCURACY_OP_PREFIX+'gt_classes': gt_classes, + (constants.UNREDUCED_ACCURACY_OP_PREFIX + + ssd_constants.SOURCE_ID): source_id, + (constants.UNREDUCED_ACCURACY_OP_PREFIX + + ssd_constants.RAW_SHAPE): raw_shape + } + + def postprocess(self, results): + """Postprocess results returned from model.""" + try: + import coco_metric # pylint: disable=g-import-not-at-top + except ImportError: + raise ImportError('To use the COCO dataset, you must clone the ' + 'repo https://github.com/tensorflow/models and add ' + 'tensorflow/models and tensorflow/models/research to ' + 'the PYTHONPATH, and compile the protobufs by ' + 'following https://github.com/tensorflow/models/blob/' + 'master/research/object_detection/g3doc/installation.md' + '#protobuf-compilation ; To evaluate using COCO' + 'metric, download and install Python COCO API from' + 'https://github.com/cocodataset/cocoapi') + + pred_boxes = results[ssd_constants.PRED_BOXES] + pred_scores = results[ssd_constants.PRED_SCORES] + # TODO(haoyuzhang): maybe use these values for visualization. + # gt_boxes = results['gt_boxes'] + # gt_classes = results['gt_classes'] + source_id = results[ssd_constants.SOURCE_ID] + raw_shape = results[ssd_constants.RAW_SHAPE] + + # COCO evaluation requires processing COCO_NUM_VAL_IMAGES exactly once. Due + # to rounding errors (i.e., COCO_NUM_VAL_IMAGES % batch_size != 0), setting + # `num_eval_epochs` to 1 is not enough and will often miss some images. We + # expect user to set `num_eval_epochs` to >1, which will leave some unused + # images from previous steps in `predictions`. Here we check if we are doing + # eval at a new global step. + if results['global_step'] > self.eval_global_step: + self.eval_global_step = results['global_step'] + self.predictions.clear() + + for i, sid in enumerate(source_id): + self.predictions[int(sid)] = { + ssd_constants.PRED_BOXES: pred_boxes[i], + ssd_constants.PRED_SCORES: pred_scores[i], + ssd_constants.SOURCE_ID: source_id[i], + ssd_constants.RAW_SHAPE: raw_shape[i] + } + + # COCO metric calculates mAP only after a full epoch of evaluation. Return + # dummy results for top_N_accuracy to be compatible with benchmar_cnn.py. + if len(self.predictions) >= ssd_constants.COCO_NUM_VAL_IMAGES: + log_fn('Got results for all {:d} eval examples. Calculate mAP...'.format( + ssd_constants.COCO_NUM_VAL_IMAGES)) + + annotation_file = os.path.join(self.params.data_dir, + ssd_constants.ANNOTATION_FILE) + # Size of predictions before decoding about 15--30GB, while size after + # decoding is 100--200MB. When using async eval mode, decoding takes + # 20--30 seconds of main thread time but is necessary to avoid OOM during + # inter-process communication. + decoded_preds = coco_metric.decode_predictions(self.predictions.values()) + self.predictions.clear() + + if self.params.collect_eval_results_async: + def _eval_results_getter(): + """Iteratively get eval results from async eval process.""" + while True: + step, eval_results = self.async_eval_results_queue.get() + self.eval_coco_ap = eval_results['COCO/AP'] + mlperf.logger.log_eval_accuracy( + self.eval_coco_ap, step, self.batch_size * self.params.num_gpus, + ssd_constants.COCO_NUM_TRAIN_IMAGES) + if self.reached_target(): + # Reached target, clear all pending messages in predictions queue + # and insert poison pill to stop the async eval process. + while not self.async_eval_predictions_queue.empty(): + self.async_eval_predictions_queue.get() + self.async_eval_predictions_queue.put('STOP') + break + + if not self.async_eval_process: + # Limiting the number of messages in predictions queue to prevent OOM. + # Each message (predictions data) can potentially consume a lot of + # memory, and normally there should only be few messages in the queue. + # If often blocked on this, consider reducing eval frequency. + self.async_eval_predictions_queue = multiprocessing.Queue(2) + self.async_eval_results_queue = multiprocessing.Queue() + + # Reason to use a Process as opposed to Thread is mainly the + # computationally intensive eval runner. Python multithreading is not + # truly running in parallel, a runner thread would get significantly + # delayed (or alternatively delay the main thread). + self.async_eval_process = multiprocessing.Process( + target=coco_metric.async_eval_runner, + args=(self.async_eval_predictions_queue, + self.async_eval_results_queue, + annotation_file)) + self.async_eval_process.daemon = True + self.async_eval_process.start() + + self.async_eval_results_getter_thread = threading.Thread( + target=_eval_results_getter, args=()) + self.async_eval_results_getter_thread.daemon = True + self.async_eval_results_getter_thread.start() + + self.async_eval_predictions_queue.put( + (self.eval_global_step, decoded_preds)) + return {'top_1_accuracy': 0, 'top_5_accuracy': 0.} + + eval_results = coco_metric.compute_map(decoded_preds, annotation_file) + self.eval_coco_ap = eval_results['COCO/AP'] + ret = {'top_1_accuracy': self.eval_coco_ap, 'top_5_accuracy': 0.} + for metric_key, metric_value in eval_results.items(): + ret[constants.SIMPLE_VALUE_RESULT_PREFIX + metric_key] = metric_value + mlperf.logger.log_eval_accuracy(self.eval_coco_ap, self.eval_global_step, + self.batch_size * self.params.num_gpus, + ssd_constants.COCO_NUM_TRAIN_IMAGES) + return ret + log_fn('Got {:d} out of {:d} eval examples.' + ' Waiting for the remaining to calculate mAP...'.format( + len(self.predictions), ssd_constants.COCO_NUM_VAL_IMAGES)) + return {'top_1_accuracy': self.eval_coco_ap, 'top_5_accuracy': 0.} + + def get_synthetic_inputs(self, input_name, nclass): + """Generating synthetic data matching real data shape and type.""" + inputs = tf.random_uniform( + self.get_input_shapes('train')[0], dtype=self.data_type) + inputs = variables.VariableV1(inputs, trainable=False, + collections=[tf.GraphKeys.LOCAL_VARIABLES], + name=input_name) + boxes = tf.random_uniform( + [self.batch_size, ssd_constants.NUM_SSD_BOXES, 4], dtype=tf.float32) + classes = tf.random_uniform( + [self.batch_size, ssd_constants.NUM_SSD_BOXES, 1], dtype=tf.float32) + nboxes = tf.random_uniform( + [self.batch_size], minval=1, maxval=10, dtype=tf.float32) + return (inputs, boxes, classes, nboxes) + + def reached_target(self): + return (self.params.stop_at_top_1_accuracy and + self.eval_coco_ap >= self.params.stop_at_top_1_accuracy) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/trivial_model.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/trivial_model.py new file mode 100644 index 00000000..3ba84d72 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/trivial_model.py @@ -0,0 +1,73 @@ +# 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. +# ============================================================================== +"""Trivial model configuration.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import tensorflow.compat.v1 as tf +from models import model + + +class TrivialModel(model.CNNModel): + """Trivial model configuration.""" + + def __init__(self, params=None): + super(TrivialModel, self).__init__( + 'trivial', 224 + 3, 32, 0.005, params=params) + + def add_inference(self, cnn): + cnn.reshape([-1, 227 * 227 * 3]) + cnn.affine(1) + cnn.affine(4096) + + +class TrivialCifar10Model(model.CNNModel): + """Trivial cifar10 model configuration.""" + + def __init__(self, params=None): + super(TrivialCifar10Model, self).__init__( + 'trivial', 32, 32, 0.005, params=params) + + def add_inference(self, cnn): + cnn.reshape([-1, 32 * 32 * 3]) + cnn.affine(1) + cnn.affine(4096) + + +class TrivialSSD300Model(model.CNNModel): + """Trivial SSD300 model configuration.""" + + def __init__(self, params=None): + super(TrivialSSD300Model, self).__init__( + 'trivial', 300, params.batch_size, 0.005, params=params) + + def add_inference(self, cnn): + cnn.reshape([-1, 300 * 300 * 3]) + cnn.affine(1) + cnn.affine(4096) + + def get_input_shapes(self, subset): + return [[self.batch_size, 300, 300, 3], + [self.batch_size, 8732, 4], + [self.batch_size, 8732, 1], + [self.batch_size]] + + def loss_function(self, inputs, build_network_result): + images, _, _, labels = inputs + labels = tf.cast(labels, tf.int32) + return super(TrivialSSD300Model, self).loss_function( + (images, labels), build_network_result) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/vgg_model.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/vgg_model.py new file mode 100644 index 00000000..938385c9 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/models/vgg_model.py @@ -0,0 +1,83 @@ +# 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. +# ============================================================================== +"""Vgg model configuration. + +Includes multiple models: vgg11, vgg16, vgg19, corresponding to + model A, D, and E in Table 1 of [1]. + +References: +[1] Simonyan, Karen, Andrew Zisserman + Very Deep Convolutional Networks for Large-Scale Image Recognition + arXiv:1409.1556 (2014) +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from six.moves import xrange # pylint: disable=redefined-builtin +from models import model + + +def _construct_vgg(cnn, num_conv_layers): + """Build vgg architecture from blocks.""" + assert len(num_conv_layers) == 5 + for _ in xrange(num_conv_layers[0]): + cnn.conv(64, 3, 3) + cnn.mpool(2, 2) + for _ in xrange(num_conv_layers[1]): + cnn.conv(128, 3, 3) + cnn.mpool(2, 2) + for _ in xrange(num_conv_layers[2]): + cnn.conv(256, 3, 3) + cnn.mpool(2, 2) + for _ in xrange(num_conv_layers[3]): + cnn.conv(512, 3, 3) + cnn.mpool(2, 2) + for _ in xrange(num_conv_layers[4]): + cnn.conv(512, 3, 3) + cnn.mpool(2, 2) + cnn.reshape([-1, 512 * 7 * 7]) + cnn.affine(4096) + cnn.dropout() + cnn.affine(4096) + cnn.dropout() + + +class Vgg11Model(model.CNNModel): + + def __init__(self, params=None): + super(Vgg11Model, self).__init__('vgg11', 224, 64, 0.005, params=params) + + def add_inference(self, cnn): + _construct_vgg(cnn, [1, 1, 2, 2, 2]) + + +class Vgg16Model(model.CNNModel): + + def __init__(self, params=None): + super(Vgg16Model, self).__init__('vgg16', 224, 64, 0.005, params=params) + + def add_inference(self, cnn): + _construct_vgg(cnn, [2, 2, 3, 3, 3]) + + +class Vgg19Model(model.CNNModel): + + def __init__(self, params=None): + super(Vgg19Model, self).__init__('vgg19', 224, 64, 0.005, params=params) + + def add_inference(self, cnn): + _construct_vgg(cnn, [2, 2, 4, 4, 4]) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/__init__.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/platforms/__init__.py similarity index 100% rename from TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/mnist/__init__.py rename to TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/platforms/__init__.py diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/__init__.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/platforms/default/__init__.py similarity index 100% rename from TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/recommendation/__init__.py rename to TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/platforms/default/__init__.py diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/platforms/default/util.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/platforms/default/util.py new file mode 100644 index 00000000..e64b9137 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/platforms/default/util.py @@ -0,0 +1,90 @@ +# 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. +# ============================================================================== + +"""Utility code for the default platform.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os +import sys +import tempfile + +import cnn_util +from models import model_config + + +_ROOT_PROJECT_DIR = os.path.dirname(cnn_util.__file__) + + +def define_platform_params(): + """Defines platform-specific parameters. + + Currently there are no platform-specific parameters to be defined. + """ + pass + + +def get_cluster_manager(params, config_proto): + """Returns the cluster manager to be used.""" + return cnn_util.GrpcClusterManager(params, config_proto) + + +def get_command_to_run_python_module(module): + """Returns a command to run a Python module.""" + python_interpretter = sys.executable + if not python_interpretter: + raise ValueError('Could not find Python interpreter') + return [python_interpretter, + os.path.join(_ROOT_PROJECT_DIR, module + '.py')] + + +def get_test_output_dir(): + """Returns a directory where test outputs should be placed.""" + base_dir = os.environ.get('TEST_OUTPUTS_DIR', + '/tmp/tf_cnn_benchmarks_test_outputs') + if not os.path.exists(base_dir): + os.mkdir(base_dir) + return tempfile.mkdtemp(dir=base_dir) + + +def get_test_data_dir(): + """Returns the path to the test_data directory.""" + return os.path.join(_ROOT_PROJECT_DIR, 'test_data') + + +def get_ssd_backborn_model_file(): + raise NotImplementedError + + +def get_ssd_backboard_data_dir(): + raise NotImplementedError + + +def _initialize(params, config_proto): + del params, config_proto + model_config.register_tf1_models() + + +_is_initalized = False + + +def initialize(params, config_proto): + global _is_initalized + if _is_initalized: + return + _is_initalized = True + _initialize(params, config_proto) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/mock_lib.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/platforms/util.py similarity index 58% rename from TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/mock_lib.py rename to TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/platforms/util.py index ee4de3c4..9d569691 100644 --- a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/utils/testing/mock_lib.py +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/platforms/util.py @@ -1,4 +1,4 @@ -# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# 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. @@ -13,24 +13,18 @@ # limitations under the License. # ============================================================================== -"""Mock objects and related functions for testing.""" +"""Utility code for a certain platform. + +This file simply imports everything from the default platform. To switch to a +different platform, the import statement can be changed to point to a new +platform. + +Creating a custom platform can be useful to, e.g., run some initialization code +required by the platform or register a platform-specific model. +""" from __future__ import absolute_import from __future__ import division from __future__ import print_function - -class MockBenchmarkLogger(object): - """This is a mock logger that can be used in dependent tests.""" - - def __init__(self): - self.logged_metric = [] - - def log_metric(self, name, value, unit=None, global_step=None, - extras=None): - self.logged_metric.append({ - "name": name, - "value": float(value), - "unit": unit, - "global_step": global_step, - "extras": extras}) +from platforms.default.util import * # pylint: disable=unused-import,wildcard-import diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/preprocessing.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/preprocessing.py new file mode 100644 index 00000000..16fa71b2 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/preprocessing.py @@ -0,0 +1,1339 @@ +# 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. +# ============================================================================== + +"""Image pre-processing utilities. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math +from six.moves import xrange # pylint: disable=redefined-builtin +import tensorflow.compat.v1 as tf + +# pylint: disable=g-direct-tensorflow-import +import cnn_util +from tensorflow.python.data.ops import multi_device_iterator_ops +from tensorflow.python.framework import function +from tensorflow.python.layers import utils +from tensorflow.python.ops import data_flow_ops +from tensorflow.python.platform import gfile +import mlperf + + +def parse_example_proto(example_serialized): + """Parses an Example proto containing a training example of an image. + + The output of the build_image_data.py image preprocessing script is a dataset + containing serialized Example protocol buffers. Each Example proto contains + the following fields: + + image/height: 462 + image/width: 581 + image/colorspace: 'RGB' + image/channels: 3 + image/class/label: 615 + image/class/synset: 'n03623198' + image/class/text: 'knee pad' + image/object/bbox/xmin: 0.1 + image/object/bbox/xmax: 0.9 + image/object/bbox/ymin: 0.2 + image/object/bbox/ymax: 0.6 + image/object/bbox/label: 615 + image/format: 'JPEG' + image/filename: 'ILSVRC2012_val_00041207.JPEG' + image/encoded: + + Args: + example_serialized: scalar Tensor tf.string containing a serialized + Example protocol buffer. + + Returns: + image_buffer: Tensor tf.string containing the contents of a JPEG file. + label: Tensor tf.int32 containing the label. + bbox: 3-D float Tensor of bounding boxes arranged [1, num_boxes, coords] + where each coordinate is [0, 1) and the coordinates are arranged as + [ymin, xmin, ymax, xmax]. + text: Tensor tf.string containing the human-readable label. + """ + # Dense features in Example proto. + feature_map = { + 'image/encoded': tf.FixedLenFeature([], dtype=tf.string, + default_value=''), + 'image/class/label': tf.FixedLenFeature([1], dtype=tf.int64, + default_value=-1), + 'image/class/text': tf.FixedLenFeature([], dtype=tf.string, + default_value=''), + } + sparse_float32 = tf.VarLenFeature(dtype=tf.float32) + # Sparse features in Example proto. + feature_map.update( + {k: sparse_float32 for k in ['image/object/bbox/xmin', + 'image/object/bbox/ymin', + 'image/object/bbox/xmax', + 'image/object/bbox/ymax']}) + + features = tf.parse_single_example(example_serialized, feature_map) + label = tf.cast(features['image/class/label'], dtype=tf.int32) + + xmin = tf.expand_dims(features['image/object/bbox/xmin'].values, 0) + ymin = tf.expand_dims(features['image/object/bbox/ymin'].values, 0) + xmax = tf.expand_dims(features['image/object/bbox/xmax'].values, 0) + ymax = tf.expand_dims(features['image/object/bbox/ymax'].values, 0) + + # Note that we impose an ordering of (y, x) just to make life difficult. + bbox = tf.concat([ymin, xmin, ymax, xmax], 0) + + # Force the variable number of bounding boxes into the shape + # [1, num_boxes, coords]. + bbox = tf.expand_dims(bbox, 0) + bbox = tf.transpose(bbox, [0, 2, 1]) + + return features['image/encoded'], label, bbox, features['image/class/text'] + + +_RESIZE_METHOD_MAP = { + 'nearest': tf.image.ResizeMethod.NEAREST_NEIGHBOR, + 'bilinear': tf.image.ResizeMethod.BILINEAR, + 'bicubic': tf.image.ResizeMethod.BICUBIC, + 'area': tf.image.ResizeMethod.AREA +} + + +def get_image_resize_method(resize_method, batch_position=0): + """Get tensorflow resize method. + + If resize_method is 'round_robin', return different methods based on batch + position in a round-robin fashion. NOTE: If the batch size is not a multiple + of the number of methods, then the distribution of methods will not be + uniform. + + Args: + resize_method: (string) nearest, bilinear, bicubic, area, or round_robin. + batch_position: position of the image in a batch. NOTE: this argument can + be an integer or a tensor + Returns: + one of resize type defined in tf.image.ResizeMethod. + """ + + if resize_method != 'round_robin': + return _RESIZE_METHOD_MAP[resize_method] + + # return a resize method based on batch position in a round-robin fashion. + resize_methods = list(_RESIZE_METHOD_MAP.values()) + def lookup(index): + return resize_methods[index] + + def resize_method_0(): + return utils.smart_cond(batch_position % len(resize_methods) == 0, + lambda: lookup(0), resize_method_1) + + def resize_method_1(): + return utils.smart_cond(batch_position % len(resize_methods) == 1, + lambda: lookup(1), resize_method_2) + + def resize_method_2(): + return utils.smart_cond(batch_position % len(resize_methods) == 2, + lambda: lookup(2), lambda: lookup(3)) + + # NOTE(jsimsa): Unfortunately, we cannot use a single recursive function here + # because TF would not be able to construct a finite graph. + + return resize_method_0() + + +def decode_jpeg(image_buffer, scope=None): # , dtype=tf.float32): + """Decode a JPEG string into one 3-D float image Tensor. + + Args: + image_buffer: scalar string Tensor. + scope: Optional scope for op_scope. + Returns: + 3-D float Tensor with values ranging from [0, 1). + """ + # with tf.op_scope([image_buffer], scope, 'decode_jpeg'): + # with tf.name_scope(scope, 'decode_jpeg', [image_buffer]): + with tf.name_scope(scope or 'decode_jpeg'): + # Decode the string as an RGB JPEG. + # Note that the resulting image contains an unknown height and width + # that is set dynamically by decode_jpeg. In other words, the height + # and width of image is unknown at compile-time. + image = tf.image.decode_jpeg(image_buffer, channels=3, + fancy_upscaling=False, + dct_method='INTEGER_FAST') + + # image = tf.Print(image, [tf.shape(image)], 'Image shape: ') + + return image + + +_R_MEAN = 123.68 +_G_MEAN = 116.78 +_B_MEAN = 103.94 +_CHANNEL_MEANS = [_R_MEAN, _G_MEAN, _B_MEAN] + + +def normalized_image(images): + # Rescale from [0, 255] to [0, 2] + images = tf.multiply(images, 1. / 127.5) + # Rescale to [-1, 1] + mlperf.logger.log(key=mlperf.tags.INPUT_MEAN_SUBTRACTION, value=[1.0] * 3) + return tf.subtract(images, 1.0) + + +def eval_image(image, + height, + width, + batch_position, + resize_method, + summary_verbosity=0): + """Get the image for model evaluation. + + We preprocess the image simiarly to Slim, see + https://github.com/tensorflow/models/blob/master/research/slim/preprocessing/vgg_preprocessing.py + Validation images do not have bounding boxes, so to crop the image, we first + resize the image such that the aspect ratio is maintained and the resized + height and width are both at least 1.145 times `height` and `width` + respectively. Then, we do a central crop to size (`height`, `width`). + + Args: + image: 3-D float Tensor representing the image. + height: The height of the image that will be returned. + width: The width of the image that will be returned. + batch_position: position of the image in a batch, which affects how images + are distorted and resized. NOTE: this argument can be an integer or a + tensor + resize_method: one of the strings 'round_robin', 'nearest', 'bilinear', + 'bicubic', or 'area'. + summary_verbosity: Verbosity level for summary ops. Pass 0 to disable both + summaries and checkpoints. + Returns: + An image of size (output_height, output_width, 3) that is resized and + cropped as described above. + """ + # TODO(reedwm): Currently we resize then crop. Investigate if it's faster to + # crop then resize. + with tf.name_scope('eval_image'): + if summary_verbosity >= 3: + tf.summary.image( + 'original_image', tf.expand_dims(image, 0)) + + shape = tf.shape(image) + image_height = shape[0] + image_width = shape[1] + image_height_float = tf.cast(image_height, tf.float32) + image_width_float = tf.cast(image_width, tf.float32) + + # This value is chosen so that in resnet, images are cropped to a size of + # 256 x 256, which matches what other implementations do. The final image + # size for resnet is 224 x 224, and floor(224 * 1.145) = 256. + scale_factor = 1.145 + + # Compute resize_height and resize_width to be the minimum values such that + # 1. The aspect ratio is maintained (i.e. resize_height / resize_width is + # image_height / image_width), and + # 2. resize_height >= height * `scale_factor`, and + # 3. resize_width >= width * `scale_factor` + max_ratio = tf.maximum(height / image_height_float, + width / image_width_float) + resize_height = tf.cast(image_height_float * max_ratio * scale_factor, + tf.int32) + resize_width = tf.cast(image_width_float * max_ratio * scale_factor, + tf.int32) + mlperf.logger.log_input_resize_aspect_preserving(height, width, + scale_factor) + + # Resize the image to shape (`resize_height`, `resize_width`) + image_resize_method = get_image_resize_method(resize_method, batch_position) + distorted_image = tf.image.resize_images(image, + [resize_height, resize_width], + image_resize_method, + align_corners=False) + + # Do a central crop of the image to size (height, width). + # MLPerf requires us to log (height, width) with two different keys. + mlperf.logger.log(key=mlperf.tags.INPUT_CENTRAL_CROP, value=[height, width]) + mlperf.logger.log(key=mlperf.tags.INPUT_RESIZE, value=[height, width]) + total_crop_height = (resize_height - height) + crop_top = total_crop_height // 2 + total_crop_width = (resize_width - width) + crop_left = total_crop_width // 2 + distorted_image = tf.slice(distorted_image, [crop_top, crop_left, 0], + [height, width, 3]) + + distorted_image.set_shape([height, width, 3]) + if summary_verbosity >= 3: + tf.summary.image( + 'cropped_resized_image', tf.expand_dims(distorted_image, 0)) + image = distorted_image + return image + + +def train_image(image_buffer, + height, + width, + bbox, + batch_position, + resize_method, + distortions, + scope=None, + summary_verbosity=0, + distort_color_in_yiq=False, + fuse_decode_and_crop=False): + """Distort one image for training a network. + + Distorting images provides a useful technique for augmenting the data + set during training in order to make the network invariant to aspects + of the image that do not effect the label. + + Args: + image_buffer: scalar string Tensor representing the raw JPEG image buffer. + height: integer + width: integer + bbox: 3-D float Tensor of bounding boxes arranged [1, num_boxes, coords] + where each coordinate is [0, 1) and the coordinates are arranged + as [ymin, xmin, ymax, xmax]. + batch_position: position of the image in a batch, which affects how images + are distorted and resized. NOTE: this argument can be an integer or a + tensor + resize_method: round_robin, nearest, bilinear, bicubic, or area. + distortions: If true, apply full distortions for image colors. + scope: Optional scope for op_scope. + summary_verbosity: Verbosity level for summary ops. Pass 0 to disable both + summaries and checkpoints. + distort_color_in_yiq: distort color of input images in YIQ space. + fuse_decode_and_crop: fuse the decode/crop operation. + Returns: + 3-D float Tensor of distorted image used for training. + """ + # with tf.op_scope([image, height, width, bbox], scope, 'distort_image'): + # with tf.name_scope(scope, 'distort_image', [image, height, width, bbox]): + with tf.name_scope(scope or 'distort_image'): + # A large fraction of image datasets contain a human-annotated bounding box + # delineating the region of the image containing the object of interest. We + # choose to create a new bounding box for the object which is a randomly + # distorted version of the human-annotated bounding box that obeys an + # allowed range of aspect ratios, sizes and overlap with the human-annotated + # bounding box. If no box is supplied, then we assume the bounding box is + # the entire image. + min_object_covered = 0.1 + aspect_ratio_range = [0.75, 1.33] + area_range = [0.05, 1.0] + max_attempts = 100 + mlperf.logger.log(key=mlperf.tags.INPUT_DISTORTED_CROP_MIN_OBJ_COV, + value=min_object_covered) + mlperf.logger.log(key=mlperf.tags.INPUT_DISTORTED_CROP_RATIO_RANGE, + value=aspect_ratio_range) + mlperf.logger.log(key=mlperf.tags.INPUT_DISTORTED_CROP_AREA_RANGE, + value=area_range) + mlperf.logger.log(key=mlperf.tags.INPUT_DISTORTED_CROP_MAX_ATTEMPTS, + value=max_attempts) + + sample_distorted_bounding_box = tf.image.sample_distorted_bounding_box( + tf.image.extract_jpeg_shape(image_buffer), + bounding_boxes=bbox, + min_object_covered=min_object_covered, + aspect_ratio_range=aspect_ratio_range, + area_range=area_range, + max_attempts=max_attempts, + use_image_if_no_bounding_boxes=True) + bbox_begin, bbox_size, distort_bbox = sample_distorted_bounding_box + if summary_verbosity >= 3: + image = tf.image.decode_jpeg(image_buffer, channels=3, + dct_method='INTEGER_FAST') + image = tf.image.convert_image_dtype(image, dtype=tf.float32) + image_with_distorted_box = tf.image.draw_bounding_boxes( + tf.expand_dims(image, 0), distort_bbox) + tf.summary.image( + 'images_with_distorted_bounding_box', + image_with_distorted_box) + + # Crop the image to the specified bounding box. + if fuse_decode_and_crop: + offset_y, offset_x, _ = tf.unstack(bbox_begin) + target_height, target_width, _ = tf.unstack(bbox_size) + crop_window = tf.stack([offset_y, offset_x, target_height, target_width]) + image = tf.image.decode_and_crop_jpeg( + image_buffer, crop_window, channels=3) + else: + image = tf.image.decode_jpeg(image_buffer, channels=3, + dct_method='INTEGER_FAST') + image = tf.slice(image, bbox_begin, bbox_size) + + mlperf.logger.log(key=mlperf.tags.INPUT_RANDOM_FLIP) + distorted_image = tf.image.random_flip_left_right(image) + + # This resizing operation may distort the images because the aspect + # ratio is not respected. + mlperf.logger.log(key=mlperf.tags.INPUT_RESIZE, value=[height, width]) + image_resize_method = get_image_resize_method(resize_method, batch_position) + distorted_image = tf.image.resize_images( + distorted_image, [height, width], + image_resize_method, + align_corners=False) + # Restore the shape since the dynamic slice based upon the bbox_size loses + # the third dimension. + distorted_image.set_shape([height, width, 3]) + if summary_verbosity >= 3: + tf.summary.image('cropped_resized_maybe_flipped_image', + tf.expand_dims(distorted_image, 0)) + + if distortions: + distorted_image = tf.cast(distorted_image, dtype=tf.float32) + # Images values are expected to be in [0,1] for color distortion. + distorted_image /= 255. + # Randomly distort the colors. + distorted_image = distort_color(distorted_image, batch_position, + distort_color_in_yiq=distort_color_in_yiq) + + # Note: This ensures the scaling matches the output of eval_image + distorted_image *= 255 + + if summary_verbosity >= 3: + tf.summary.image( + 'final_distorted_image', + tf.expand_dims(distorted_image, 0)) + return distorted_image + + +def distort_color(image, batch_position=0, distort_color_in_yiq=False, + scope=None): + """Distort the color of the image. + + Each color distortion is non-commutative and thus ordering of the color ops + matters. Ideally we would randomly permute the ordering of the color ops. + Rather then adding that level of complication, we select a distinct ordering + of color ops based on the position of the image in a batch. + + Args: + image: float32 Tensor containing single image. Tensor values should be in + range [0, 1]. + batch_position: the position of the image in a batch. NOTE: this argument + can be an integer or a tensor + distort_color_in_yiq: distort color of input images in YIQ space. + scope: Optional scope for op_scope. + Returns: + color-distorted image + """ + if distort_color_in_yiq: + try: + from tensorflow.contrib.image.python.ops import distort_image_ops # pylint: disable=g-import-not-at-top + except ImportError: + raise ValueError( + 'In TF2, you cannot pass --distortions unless you also pass ' + '--nodistort_color_in_yiq. This is because the random_hsv_in_yiq was ' + 'removed in TF2. --distortions does not improve accuracy on resnet ' + 'so it is not recommended. --nodistort_color_in_yiq also has no ' + 'impact on accuracy, but may hurt performance.') + + with tf.name_scope(scope or 'distort_color'): + + def distort_fn_0(image=image): + """Variant 0 of distort function.""" + image = tf.image.random_brightness(image, max_delta=32. / 255.) + if distort_color_in_yiq: + image = distort_image_ops.random_hsv_in_yiq( + image, lower_saturation=0.5, upper_saturation=1.5, + max_delta_hue=0.2 * math.pi) + else: + image = tf.image.random_saturation(image, lower=0.5, upper=1.5) + image = tf.image.random_hue(image, max_delta=0.2) + image = tf.image.random_contrast(image, lower=0.5, upper=1.5) + return image + + def distort_fn_1(image=image): + """Variant 1 of distort function.""" + image = tf.image.random_brightness(image, max_delta=32. / 255.) + image = tf.image.random_contrast(image, lower=0.5, upper=1.5) + if distort_color_in_yiq: + image = distort_image_ops.random_hsv_in_yiq( + image, lower_saturation=0.5, upper_saturation=1.5, + max_delta_hue=0.2 * math.pi) + else: + image = tf.image.random_saturation(image, lower=0.5, upper=1.5) + image = tf.image.random_hue(image, max_delta=0.2) + return image + + image = utils.smart_cond(batch_position % 2 == 0, distort_fn_0, + distort_fn_1) + # The random_* ops do not necessarily clamp. + image = tf.clip_by_value(image, 0.0, 1.0) + return image + + +class InputPreprocessor(object): + """Base class for all model preprocessors.""" + + def __init__(self, batch_size, output_shapes): + self.batch_size = batch_size + self.output_shapes = output_shapes + + def supports_datasets(self): + """Whether this preprocessor supports dataset.""" + return False + + def minibatch(self, dataset, subset, params, shift_ratio=-1): + """Returns tensors representing a minibatch of all the input.""" + raise NotImplementedError('Must be implemented by subclass.') + + # The methods added below are only supported/used if supports_datasets() + # returns True. + # TODO(laigd): refactor benchmark_cnn.py and put the logic of + # _build_input_processing() into InputPreprocessor. + + def parse_and_preprocess(self, value, batch_position): + """Function to parse and preprocess an Example proto in input pipeline.""" + raise NotImplementedError('Must be implemented by subclass.') + + # TODO(laigd): figure out how to remove these parameters, since the + # preprocessor itself has self.batch_size, self.num_splits, etc defined. + def build_multi_device_iterator(self, batch_size, num_splits, cpu_device, + params, gpu_devices, dataset, doing_eval): + """Creates a MultiDeviceIterator.""" + assert self.supports_datasets() + assert num_splits == len(gpu_devices) + with tf.name_scope('batch_processing'): + if doing_eval: + subset = 'validation' + else: + subset = 'train' + batch_size_per_split = batch_size // num_splits + ds = self.create_dataset( + batch_size, + num_splits, + batch_size_per_split, + dataset, + subset, + train=(not doing_eval), + datasets_repeat_cached_sample=params.datasets_repeat_cached_sample, + num_threads=params.datasets_num_private_threads, + datasets_use_caching=params.datasets_use_caching, + datasets_parallel_interleave_cycle_length=( + params.datasets_parallel_interleave_cycle_length), + datasets_sloppy_parallel_interleave=( + params.datasets_sloppy_parallel_interleave), + datasets_parallel_interleave_prefetch=( + params.datasets_parallel_interleave_prefetch)) + multi_device_iterator = multi_device_iterator_ops.MultiDeviceIterator( + ds, + gpu_devices, + source_device=cpu_device, + max_buffer_size=params.multi_device_iterator_max_buffer_size) + tf.add_to_collection(tf.GraphKeys.TABLE_INITIALIZERS, + multi_device_iterator.initializer) + return multi_device_iterator + + def create_dataset(self, + batch_size, + num_splits, + batch_size_per_split, + dataset, + subset, + train, + datasets_repeat_cached_sample, + num_threads=None, + datasets_use_caching=False, + datasets_parallel_interleave_cycle_length=None, + datasets_sloppy_parallel_interleave=False, + datasets_parallel_interleave_prefetch=None): + """Creates a dataset for the benchmark.""" + raise NotImplementedError('Must be implemented by subclass.') + + def create_iterator(self, ds): + ds_iterator = tf.data.make_initializable_iterator(ds) + tf.add_to_collection(tf.GraphKeys.TABLE_INITIALIZERS, + ds_iterator.initializer) + return ds_iterator + + def minibatch_fn(self, batch_size, model_input_shapes, num_splits, + dataset, subset, train, datasets_repeat_cached_sample, + num_threads, datasets_use_caching, + datasets_parallel_interleave_cycle_length, + datasets_sloppy_parallel_interleave, + datasets_parallel_interleave_prefetch): + """Returns a function and list of args for the fn to create a minibatch.""" + assert self.supports_datasets() + batch_size_per_split = batch_size // num_splits + assert batch_size_per_split == model_input_shapes[0][0] + with tf.name_scope('batch_processing'): + ds = self.create_dataset(batch_size, num_splits, batch_size_per_split, + dataset, subset, train, + datasets_repeat_cached_sample, num_threads, + datasets_use_caching, + datasets_parallel_interleave_cycle_length, + datasets_sloppy_parallel_interleave, + datasets_parallel_interleave_prefetch) + ds_iterator = self.create_iterator(ds) + + ds_iterator_string_handle = ds_iterator.string_handle() + + @function.Defun(tf.string) + def _fn(h): + remote_iterator = tf.data.Iterator.from_string_handle( + h, ds_iterator.output_types, ds_iterator.output_shapes) + input_list = remote_iterator.get_next() + reshaped_input_list = [ + tf.reshape(input_list[i], shape=model_input_shapes[i]) + for i in range(len(input_list)) + ] + return reshaped_input_list + + return _fn, [ds_iterator_string_handle] + + +class BaseImagePreprocessor(InputPreprocessor): + """Base class for all image model preprocessors.""" + + def __init__(self, + batch_size, + output_shapes, + num_splits, + dtype, + train, + distortions, + resize_method, + shift_ratio=-1, + summary_verbosity=0, + distort_color_in_yiq=True, + fuse_decode_and_crop=True, + match_mlperf=False): + super(BaseImagePreprocessor, self).__init__(batch_size, output_shapes) + image_shape = output_shapes[0] + # image_shape is in form (batch_size, height, width, depth) + self.height = image_shape[1] + self.width = image_shape[2] + self.depth = image_shape[3] + self.num_splits = num_splits + self.dtype = dtype + self.train = train + self.resize_method = resize_method + self.shift_ratio = shift_ratio + self.distortions = distortions + self.distort_color_in_yiq = distort_color_in_yiq + self.fuse_decode_and_crop = fuse_decode_and_crop + if self.batch_size % self.num_splits != 0: + raise ValueError( + ('batch_size must be a multiple of num_splits: ' + 'batch_size %d, num_splits: %d') % + (self.batch_size, self.num_splits)) + self.batch_size_per_split = self.batch_size // self.num_splits + self.summary_verbosity = summary_verbosity + self.match_mlperf = match_mlperf + + def parse_and_preprocess(self, value, batch_position): + assert self.supports_datasets() + image_buffer, label_index, bbox, _ = parse_example_proto(value) + if self.match_mlperf: + bbox = tf.zeros((1, 0, 4), dtype=bbox.dtype) + mlperf.logger.log(key=mlperf.tags.INPUT_CROP_USES_BBOXES, value=False) + else: + mlperf.logger.log(key=mlperf.tags.INPUT_CROP_USES_BBOXES, value=True) + image = self.preprocess(image_buffer, bbox, batch_position) + return (image, label_index) + + def preprocess(self, image_buffer, bbox, batch_position): + raise NotImplementedError('Must be implemented by subclass.') + + def create_dataset(self, + batch_size, + num_splits, + batch_size_per_split, + dataset, + subset, + train, + datasets_repeat_cached_sample, + num_threads=None, + datasets_use_caching=False, + datasets_parallel_interleave_cycle_length=None, + datasets_sloppy_parallel_interleave=False, + datasets_parallel_interleave_prefetch=None): + """Creates a dataset for the benchmark.""" + assert self.supports_datasets() + glob_pattern = dataset.tf_record_pattern(subset) + file_names = gfile.Glob(glob_pattern) + if not file_names: + raise ValueError('Found no files in --data_dir matching: {}' + .format(glob_pattern)) + ds = tf.data.TFRecordDataset.list_files(file_names, shuffle=train) + ds = ds.apply( + tf.data.experimental.parallel_interleave( + tf.data.TFRecordDataset, + cycle_length=datasets_parallel_interleave_cycle_length or 10, + sloppy=datasets_sloppy_parallel_interleave, + prefetch_input_elements=datasets_parallel_interleave_prefetch)) + if datasets_repeat_cached_sample: + # Repeat a single sample element indefinitely to emulate memory-speed IO. + ds = ds.take(1).cache().repeat() + counter = tf.data.Dataset.range(batch_size) + counter = counter.repeat() + ds = tf.data.Dataset.zip((ds, counter)) + ds = ds.prefetch(buffer_size=batch_size) + if datasets_use_caching: + ds = ds.cache() + if train: + buffer_size = 10000 + mlperf.logger.log(key=mlperf.tags.INPUT_SHARD, value=buffer_size) + ds = ds.apply( + tf.data.experimental.shuffle_and_repeat(buffer_size=buffer_size)) + else: + ds = ds.repeat() + ds = ds.apply( + tf.data.experimental.map_and_batch( + map_func=self.parse_and_preprocess, + batch_size=batch_size_per_split, + num_parallel_batches=num_splits)) + ds = ds.prefetch(buffer_size=num_splits) + if num_threads: + options = tf.data.Options() + options.experimental_threading.private_threadpool_size = num_threads + ds = ds.with_options(options) + return ds + + +class RecordInputImagePreprocessor(BaseImagePreprocessor): + """Preprocessor for images with RecordInput format.""" + + def preprocess(self, image_buffer, bbox, batch_position): + """Preprocessing image_buffer as a function of its batch position.""" + if self.train: + image = train_image(image_buffer, self.height, self.width, bbox, + batch_position, self.resize_method, self.distortions, + None, summary_verbosity=self.summary_verbosity, + distort_color_in_yiq=self.distort_color_in_yiq, + fuse_decode_and_crop=self.fuse_decode_and_crop) + else: + image = tf.image.decode_jpeg( + image_buffer, channels=3, dct_method='INTEGER_FAST') + image = eval_image(image, self.height, self.width, batch_position, + self.resize_method, + summary_verbosity=self.summary_verbosity) + # Note: image is now float32 [height,width,3] with range [0, 255] + + # image = tf.cast(image, tf.uint8) # HACK TESTING + + if self.match_mlperf: + mlperf.logger.log(key=mlperf.tags.INPUT_MEAN_SUBTRACTION, + value=_CHANNEL_MEANS) + normalized = image - _CHANNEL_MEANS + else: + normalized = normalized_image(image) + return tf.cast(normalized, self.dtype) + + def minibatch(self, + dataset, + subset, + params, + shift_ratio=-1): + if shift_ratio < 0: + shift_ratio = self.shift_ratio + with tf.name_scope('batch_processing'): + # Build final results per split. + images = [[] for _ in range(self.num_splits)] + labels = [[] for _ in range(self.num_splits)] + if params.use_datasets: + ds = self.create_dataset( + self.batch_size, self.num_splits, self.batch_size_per_split, + dataset, subset, self.train, + datasets_repeat_cached_sample=params.datasets_repeat_cached_sample, + num_threads=params.datasets_num_private_threads, + datasets_use_caching=params.datasets_use_caching, + datasets_parallel_interleave_cycle_length=( + params.datasets_parallel_interleave_cycle_length), + datasets_sloppy_parallel_interleave=( + params.datasets_sloppy_parallel_interleave), + datasets_parallel_interleave_prefetch=( + params.datasets_parallel_interleave_prefetch)) + ds_iterator = self.create_iterator(ds) + for d in xrange(self.num_splits): + images[d], labels[d] = ds_iterator.get_next() + + # TODO(laigd): consider removing the --use_datasets option, it should + # always use datasets. + else: + record_input = data_flow_ops.RecordInput( + file_pattern=dataset.tf_record_pattern(subset), + seed=301, + parallelism=64, + buffer_size=10000, + batch_size=self.batch_size, + shift_ratio=shift_ratio, + name='record_input') + records = record_input.get_yield_op() + records = tf.split(records, self.batch_size, 0) + records = [tf.reshape(record, []) for record in records] + for idx in xrange(self.batch_size): + value = records[idx] + (image, label) = self.parse_and_preprocess(value, idx) + split_index = idx % self.num_splits + labels[split_index].append(label) + images[split_index].append(image) + + for split_index in xrange(self.num_splits): + if not params.use_datasets: + images[split_index] = tf.parallel_stack(images[split_index]) + labels[split_index] = tf.concat(labels[split_index], 0) + images[split_index] = tf.reshape( + images[split_index], + shape=[self.batch_size_per_split, self.height, self.width, + self.depth]) + labels[split_index] = tf.reshape(labels[split_index], + [self.batch_size_per_split]) + return images, labels + + def supports_datasets(self): + return True + + +class ImagenetPreprocessor(RecordInputImagePreprocessor): + + def preprocess(self, image_buffer, bbox, batch_position): + # pylint: disable=g-import-not-at-top + try: + from official.r1.resnet.imagenet_preprocessing import preprocess_image + except ImportError: + tf.logging.fatal('Please include tensorflow/models to the PYTHONPATH.') + raise + if self.train: + image = preprocess_image( + image_buffer, bbox, self.height, self.width, self.depth, + is_training=True) + else: + image = preprocess_image( + image_buffer, bbox, self.height, self.width, self.depth, + is_training=False) + return tf.cast(image, self.dtype) + + +class Cifar10ImagePreprocessor(BaseImagePreprocessor): + """Preprocessor for Cifar10 input images.""" + + def _distort_image(self, image): + """Distort one image for training a network. + + Adopted the standard data augmentation scheme that is widely used for + this dataset: the images are first zero-padded with 4 pixels on each side, + then randomly cropped to again produce distorted images; half of the images + are then horizontally mirrored. + + Args: + image: input image. + Returns: + distorted image. + """ + image = tf.image.resize_image_with_crop_or_pad( + image, self.height + 8, self.width + 8) + distorted_image = tf.random_crop(image, + [self.height, self.width, self.depth]) + # Randomly flip the image horizontally. + distorted_image = tf.image.random_flip_left_right(distorted_image) + if self.summary_verbosity >= 3: + tf.summary.image('distorted_image', tf.expand_dims(distorted_image, 0)) + return distorted_image + + def _eval_image(self, image): + """Get the image for model evaluation.""" + distorted_image = tf.image.resize_image_with_crop_or_pad( + image, self.width, self.height) + if self.summary_verbosity >= 3: + tf.summary.image('cropped.image', tf.expand_dims(distorted_image, 0)) + return distorted_image + + def preprocess(self, raw_image): + """Preprocessing raw image.""" + if self.summary_verbosity >= 3: + tf.summary.image('raw.image', tf.expand_dims(raw_image, 0)) + if self.train and self.distortions: + image = self._distort_image(raw_image) + else: + image = self._eval_image(raw_image) + normalized = normalized_image(image) + return tf.cast(normalized, self.dtype) + + def minibatch(self, + dataset, + subset, + params, + shift_ratio=-1): + # TODO(jsimsa): Implement datasets code path + del shift_ratio, params + with tf.name_scope('batch_processing'): + all_images, all_labels = dataset.read_data_files(subset) + all_images = tf.constant(all_images) + all_labels = tf.constant(all_labels) + input_image, input_label = tf.train.slice_input_producer( + [all_images, all_labels]) + input_image = tf.cast(input_image, self.dtype) + input_label = tf.cast(input_label, tf.int32) + # Ensure that the random shuffling has good mixing properties. + min_fraction_of_examples_in_queue = 0.4 + min_queue_examples = int(dataset.num_examples_per_epoch(subset) * + min_fraction_of_examples_in_queue) + raw_images, raw_labels = tf.train.shuffle_batch( + [input_image, input_label], batch_size=self.batch_size, + capacity=min_queue_examples + 3 * self.batch_size, + min_after_dequeue=min_queue_examples) + + images = [[] for i in range(self.num_splits)] + labels = [[] for i in range(self.num_splits)] + + # Create a list of size batch_size, each containing one image of the + # batch. Without the unstack call, raw_images[i] would still access the + # same image via a strided_slice op, but would be slower. + raw_images = tf.unstack(raw_images, axis=0) + raw_labels = tf.unstack(raw_labels, axis=0) + for i in xrange(self.batch_size): + split_index = i % self.num_splits + # The raw image read from data has the format [depth, height, width] + # reshape to the format returned by minibatch. + raw_image = tf.reshape(raw_images[i], + [dataset.depth, dataset.height, dataset.width]) + raw_image = tf.transpose(raw_image, [1, 2, 0]) + image = self.preprocess(raw_image) + images[split_index].append(image) + + labels[split_index].append(raw_labels[i]) + + for split_index in xrange(self.num_splits): + images[split_index] = tf.parallel_stack(images[split_index]) + labels[split_index] = tf.parallel_stack(labels[split_index]) + return images, labels + + +class COCOPreprocessor(BaseImagePreprocessor): + """Preprocessor for COCO dataset input images, boxes, and labels.""" + + def minibatch(self, + dataset, + subset, + params, + shift_ratio=-1): + del shift_ratio # Not used when using datasets instead of data_flow_ops + with tf.name_scope('batch_processing'): + ds = self.create_dataset( + batch_size=self.batch_size, + num_splits=self.num_splits, + batch_size_per_split=self.batch_size_per_split, + dataset=dataset, + subset=subset, + train=self.train, + datasets_repeat_cached_sample=params.datasets_repeat_cached_sample, + num_threads=params.datasets_num_private_threads, + datasets_use_caching=params.datasets_use_caching, + datasets_parallel_interleave_cycle_length=( + params.datasets_parallel_interleave_cycle_length), + datasets_sloppy_parallel_interleave=( + params.datasets_sloppy_parallel_interleave), + datasets_parallel_interleave_prefetch=( + params.datasets_parallel_interleave_prefetch)) + ds_iterator = self.create_iterator(ds) + + # Training data: 4 tuple + # Validation data: 5 tuple + # See get_input_shapes in models/ssd_model.py for details. + input_len = 4 if subset == 'train' else 5 + input_lists = [[None for _ in range(self.num_splits)] + for _ in range(input_len)] + for d in xrange(self.num_splits): + input_list = ds_iterator.get_next() + for i in range(input_len): + input_lists[i][d] = input_list[i] + return input_lists + + def preprocess(self, data): + try: + import ssd_dataloader # pylint: disable=g-import-not-at-top + import ssd_constants # pylint: disable=g-import-not-at-top + from object_detection.core import preprocessor # pylint: disable=g-import-not-at-top + except ImportError: + raise ImportError('To use the COCO dataset, you must clone the ' + 'repo https://github.com/tensorflow/models and add ' + 'tensorflow/models and tensorflow/models/research to ' + 'the PYTHONPATH, and compile the protobufs by ' + 'following https://github.com/tensorflow/models/blob/' + 'master/research/object_detection/g3doc/installation.md' + '#protobuf-compilation') + image_buffer = data['image_buffer'] + boxes = data['groundtruth_boxes'] + classes = tf.reshape(data['groundtruth_classes'], [-1, 1]) + source_id = tf.string_to_number(data['source_id']) + raw_shape = data['raw_shape'] + + ssd_encoder = ssd_dataloader.Encoder() + + # Only 80 of the 90 COCO classes are used. + class_map = tf.convert_to_tensor(ssd_constants.CLASS_MAP) + classes = tf.gather(class_map, classes) + classes = tf.cast(classes, dtype=tf.float32) + + if self.train: + image, boxes, classes = ssd_dataloader.ssd_decode_and_crop( + image_buffer, boxes, classes, raw_shape) + # ssd_crop resizes and returns image of dtype float32 and does not change + # its range (i.e., value in between 0--255). Divide by 255. converts it + # to [0, 1] range. Not doing this before cropping to avoid dtype cast + # (which incurs additional memory copy). + image /= 255. + + image, boxes = preprocessor.random_horizontal_flip( + image=image, boxes=boxes) + # Random horizontal flip probability is 50% + # See https://github.com/tensorflow/models/blob/master/research/object_detection/core/preprocessor.py # pylint: disable=line-too-long + mlperf.logger.log(key=mlperf.tags.RANDOM_FLIP_PROBABILITY, value=0.5) + + image = tf.cast(image, self.dtype) + + encoded_returns = ssd_encoder.encode_labels(boxes, classes) + encoded_classes, encoded_boxes, num_matched_boxes = encoded_returns + + # Shape of image: [width, height, channel] + # Shape of encoded_boxes: [NUM_SSD_BOXES, 4] + # Shape of encoded_classes: [NUM_SSD_BOXES, 1] + # Shape of num_matched_boxes: [1] + return (image, encoded_boxes, encoded_classes, num_matched_boxes) + + else: + image = tf.image.decode_jpeg(image_buffer) + image = tf.image.resize_images( + image, size=(ssd_constants.IMAGE_SIZE, ssd_constants.IMAGE_SIZE)) + # resize_image returns image of dtype float32 and does not change its + # range. Divide by 255 to convert image to [0, 1] range. + image /= 255. + + image = ssd_dataloader.normalize_image(image) + image = tf.cast(image, self.dtype) + + def trim_and_pad(inp_tensor): + """Limit the number of boxes, and pad if necessary.""" + inp_tensor = inp_tensor[:ssd_constants.MAX_NUM_EVAL_BOXES] + num_pad = ssd_constants.MAX_NUM_EVAL_BOXES - tf.shape(inp_tensor)[0] + inp_tensor = tf.pad(inp_tensor, [[0, num_pad], [0, 0]]) + return tf.reshape(inp_tensor, [ssd_constants.MAX_NUM_EVAL_BOXES, + inp_tensor.get_shape()[1]]) + + boxes, classes = trim_and_pad(boxes), trim_and_pad(classes) + + # Shape of boxes: [MAX_NUM_EVAL_BOXES, 4] + # Shape of classes: [MAX_NUM_EVAL_BOXES, 1] + # Shape of source_id: [] (scalar tensor) + # Shape of raw_shape: [3] + return (image, boxes, classes, source_id, raw_shape) + + def create_dataset(self, + batch_size, + num_splits, + batch_size_per_split, + dataset, + subset, + train, + datasets_repeat_cached_sample, + num_threads=None, + datasets_use_caching=False, + datasets_parallel_interleave_cycle_length=None, + datasets_sloppy_parallel_interleave=False, + datasets_parallel_interleave_prefetch=None): + """Creates a dataset for the benchmark.""" + try: + import ssd_dataloader # pylint: disable=g-import-not-at-top + except ImportError: + raise ImportError('To use the COCO dataset, you must clone the ' + 'repo https://github.com/tensorflow/models and add ' + 'tensorflow/models and tensorflow/models/research to ' + 'the PYTHONPATH, and compile the protobufs by ' + 'following https://github.com/tensorflow/models/blob/' + 'master/research/object_detection/g3doc/installation.md' + '#protobuf-compilation') + assert self.supports_datasets() + + glob_pattern = dataset.tf_record_pattern(subset) + ds = tf.data.TFRecordDataset.list_files(glob_pattern, shuffle=train) + # TODO(haoyuzhang): Enable map+filter fusion after cl/218399112 in release + # options = tf.data.Options() + # options.experimental_optimization = tf.data.experimental.OptimizationOptions() # pylint: disable=line-too-long + # options.experimental_optimization.map_and_filter_fusion = True + # ds = ds.with_options(options) + + ds = ds.apply( + tf.data.experimental.parallel_interleave( + tf.data.TFRecordDataset, + cycle_length=datasets_parallel_interleave_cycle_length or 10, + sloppy=datasets_sloppy_parallel_interleave)) + mlperf.logger.log(key=mlperf.tags.INPUT_ORDER) + if datasets_repeat_cached_sample: + # Repeat a single sample element indefinitely to emulate memory-speed IO. + ds = ds.take(1).cache().repeat() + ds = ds.prefetch(buffer_size=batch_size) + if datasets_use_caching: + ds = ds.cache() + if train: + ds = ds.apply(tf.data.experimental.shuffle_and_repeat(buffer_size=10000)) + mlperf.logger.log(key=mlperf.tags.INPUT_SHARD, value=10000) + mlperf.logger.log(key=mlperf.tags.INPUT_ORDER) + else: + ds = ds.repeat() + + ds = ds.map(ssd_dataloader.ssd_parse_example_proto, num_parallel_calls=64) + ds = ds.filter( + lambda data: tf.greater(tf.shape(data['groundtruth_boxes'])[0], 0)) + ds = ds.apply( + tf.data.experimental.map_and_batch( + map_func=self.preprocess, + batch_size=batch_size_per_split, + num_parallel_batches=num_splits, + drop_remainder=train)) + ds = ds.prefetch(buffer_size=num_splits) + if num_threads: + options = tf.data.Options() + options.experimental_threading.private_threadpool_size = num_threads + ds = ds.with_options(options) + return ds + + def supports_datasets(self): + return True + + +class TestImagePreprocessor(BaseImagePreprocessor): + """Preprocessor used for testing. + + set_fake_data() sets which images and labels will be output by minibatch(), + and must be called before minibatch(). This allows tests to easily specify + a set of images to use for training, without having to create any files. + + Queue runners must be started for this preprocessor to work. + """ + + def __init__(self, + batch_size, + output_shapes, + num_splits, + dtype, + train=None, + distortions=None, + resize_method=None, + shift_ratio=0, + summary_verbosity=0, + distort_color_in_yiq=False, + fuse_decode_and_crop=False, + match_mlperf=False): + super(TestImagePreprocessor, self).__init__( + batch_size, output_shapes, num_splits, dtype, train, distortions, + resize_method, shift_ratio, summary_verbosity=summary_verbosity, + distort_color_in_yiq=distort_color_in_yiq, + fuse_decode_and_crop=fuse_decode_and_crop, match_mlperf=match_mlperf) + self.expected_subset = None + + def set_fake_data(self, fake_images, fake_labels): + assert len(fake_images.shape) == 4 + assert len(fake_labels.shape) == 1 + num_images = fake_images.shape[0] + assert num_images == fake_labels.shape[0] + assert num_images % self.batch_size == 0 + self.fake_images = fake_images + self.fake_labels = fake_labels + + def minibatch(self, + dataset, + subset, + params, + shift_ratio=0): + """Get test image batches.""" + del dataset, params + if (not hasattr(self, 'fake_images') or + not hasattr(self, 'fake_labels')): + raise ValueError('Must call set_fake_data() before calling minibatch ' + 'on TestImagePreprocessor') + if self.expected_subset is not None: + assert subset == self.expected_subset + + shift_ratio = shift_ratio or self.shift_ratio + fake_images = cnn_util.roll_numpy_batches(self.fake_images, self.batch_size, + shift_ratio) + fake_labels = cnn_util.roll_numpy_batches(self.fake_labels, self.batch_size, + shift_ratio) + + with tf.name_scope('batch_processing'): + image_slice, label_slice = tf.train.slice_input_producer( + [fake_images, fake_labels], + shuffle=False, + name='image_slice') + raw_images, raw_labels = tf.train.batch( + [image_slice, label_slice], batch_size=self.batch_size, + name='image_batch') + images = [[] for _ in range(self.num_splits)] + labels = [[] for _ in range(self.num_splits)] + for i in xrange(self.batch_size): + split_index = i % self.num_splits + raw_image = tf.cast(raw_images[i], self.dtype) + images[split_index].append(raw_image) + labels[split_index].append(raw_labels[i]) + for split_index in xrange(self.num_splits): + images[split_index] = tf.parallel_stack(images[split_index]) + labels[split_index] = tf.parallel_stack(labels[split_index]) + + normalized = [normalized_image(part) for part in images] + return [[tf.cast(part, self.dtype) for part in normalized], labels] + + +class LibrispeechPreprocessor(InputPreprocessor): + """Preprocessor for librispeech class for all image model preprocessors.""" + + def __init__(self, batch_size, output_shapes, num_splits, dtype, train, + **kwargs): + del kwargs + super(LibrispeechPreprocessor, self).__init__(batch_size, output_shapes) + self.num_splits = num_splits + self.dtype = dtype + self.is_train = train + if self.batch_size % self.num_splits != 0: + raise ValueError(('batch_size must be a multiple of num_splits: ' + 'batch_size %d, num_splits: %d') % (self.batch_size, + self.num_splits)) + self.batch_size_per_split = self.batch_size // self.num_splits + + def create_dataset(self, + batch_size, + num_splits, + batch_size_per_split, + dataset, + subset, + train, + datasets_repeat_cached_sample, + num_threads=None, + datasets_use_caching=False, + datasets_parallel_interleave_cycle_length=None, + datasets_sloppy_parallel_interleave=False, + datasets_parallel_interleave_prefetch=None): + """Creates a dataset for the benchmark.""" + # TODO(laigd): currently the only difference between this and the one in + # BaseImagePreprocessor is, this uses map() and padded_batch() while the + # latter uses tf.data.experimental.map_and_batch(). Try to merge them. + assert self.supports_datasets() + glob_pattern = dataset.tf_record_pattern(subset) + file_names = gfile.Glob(glob_pattern) + if not file_names: + raise ValueError('Found no files in --data_dir matching: {}' + .format(glob_pattern)) + ds = tf.data.TFRecordDataset.list_files(file_names, shuffle=train) + ds = ds.apply( + tf.data.experimental.parallel_interleave( + tf.data.TFRecordDataset, + cycle_length=datasets_parallel_interleave_cycle_length or 10, + sloppy=datasets_sloppy_parallel_interleave, + prefetch_input_elements=datasets_parallel_interleave_prefetch)) + if datasets_repeat_cached_sample: + # Repeat a single sample element indefinitely to emulate memory-speed IO. + ds = ds.take(1).cache().repeat() + counter = tf.data.Dataset.range(batch_size) + counter = counter.repeat() + ds = tf.data.Dataset.zip((ds, counter)) + ds = ds.prefetch(buffer_size=batch_size) + if datasets_use_caching: + ds = ds.cache() + if train: + ds = ds.apply(tf.data.experimental.shuffle_and_repeat(buffer_size=10000)) + else: + ds = ds.repeat() + ds = ds.map(map_func=self.parse_and_preprocess, + num_parallel_calls=batch_size_per_split*num_splits) + ds = ds.padded_batch( + batch_size=batch_size_per_split, + padded_shapes=tuple([ + tf.TensorShape(output_shape[1:]) + for output_shape in self.output_shapes + ]), + drop_remainder=True) + ds = ds.prefetch(buffer_size=num_splits) + if num_threads: + options = tf.data.Options() + options.experimental_threading.private_threadpool_size = num_threads + ds = ds.with_options(options) + return ds + + def minibatch(self, dataset, subset, params, shift_ratio=-1): + assert params.use_datasets + # TODO(laigd): unify this with CNNModel's minibatch() + # TODO(laigd): in distributed mode we use shift_ratio so different workers + # won't work on same inputs, so we should respect that. + del shift_ratio + with tf.name_scope('batch_processing'): + ds = self.create_dataset( + self.batch_size, + self.num_splits, + self.batch_size_per_split, + dataset, + subset, + self.is_train, + datasets_repeat_cached_sample=params.datasets_repeat_cached_sample, + num_threads=params.datasets_num_private_threads, + datasets_use_caching=params.datasets_use_caching, + datasets_parallel_interleave_cycle_length=( + params.datasets_parallel_interleave_cycle_length), + datasets_sloppy_parallel_interleave=( + params.datasets_sloppy_parallel_interleave), + datasets_parallel_interleave_prefetch=( + params.datasets_parallel_interleave_prefetch)) + ds_iterator = self.create_iterator(ds) + + # The four lists are: input spectrogram feature, labels, input lengths, + # label lengths + input_lists = [[None for _ in range(self.num_splits)] for _ in range(4)] + for d in xrange(self.num_splits): + input_list = ds_iterator.get_next() + for i in range(4): + input_lists[i][d] = input_list[i] + + assert self.output_shapes == [ + input_lists[i][0].shape.as_list() for i in range(4) + ] + return tuple(input_lists) + + def supports_datasets(self): + return True + + def parse_and_preprocess(self, value, batch_position): + """Parse an TFRecord.""" + del batch_position + assert self.supports_datasets() + context_features = { + 'labels': tf.VarLenFeature(dtype=tf.int64), + 'input_length': tf.FixedLenFeature([], dtype=tf.int64), + 'label_length': tf.FixedLenFeature([], dtype=tf.int64), + } + sequence_features = { + 'features': tf.FixedLenSequenceFeature([161], dtype=tf.float32) + } + context_parsed, sequence_parsed = tf.parse_single_sequence_example( + serialized=value, + context_features=context_features, + sequence_features=sequence_features, + ) + + return [ + # Input + tf.expand_dims(sequence_parsed['features'], axis=2), + # Label + tf.cast( + tf.reshape( + tf.sparse_tensor_to_dense(context_parsed['labels']), [-1]), + dtype=tf.int32), + # Input length + tf.cast( + tf.reshape(context_parsed['input_length'], [1]), + dtype=tf.int32), + # Label length + tf.cast( + tf.reshape(context_parsed['label_length'], [1]), + dtype=tf.int32), + ] diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/run.sh b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/run.sh new file mode 100644 index 00000000..7276d791 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/run.sh @@ -0,0 +1,18 @@ +#!/bin/bash +source /public/home/qianyj/virtualenv/dtk21.10.1/dtk21.10.1_tf1.15/venv/bin/activate +export ROCM_PATH=/public/home/qianyj/package/dtk-21.10.1/dtk-21.10.1 +export HIP_PATH=${ROCM_PATH}/hip +export CPACK_INSTLL_PREFIX=$ROCM_PATH +export AMDGPU_TARGETS="gfx900;gfx906" +export PATH=${ROCM_PATH}/bin:${ROCM_PATH}/llvm/bin:${ROCM_PATH}/hip/bin:$PATH +export LD_LIBRARY_PATH=${ROCM_PATH}/lib:${ROCM_PATH}/lib64:$LD_LIBRARY_PATH +export LD_LIBRARY_PATH=${ROCM_PATH}/hip/lib:${ROCM_PATH}/llvm/lib:$LD_LIBRARY_PATH +export C_INCLUDE_PATH=${ROCM_PATH}/include:${ROCM_PATH}/llvm/include${C_INCLUDE_PATH:+:${C_INCLUDE_PATH}} +export CPLUS_INCLUDE_PATH=${ROCM_PATH}/include:${ROCM_PATH}/llvm/include${CPLUS_INCLUDE_PATH:+:${CPLUS_INCLUDE_PATH}} + +export HSA_FORCE_FINE_GRAIN_PCIE=1 +export MIOPEN_FIND_MODE=3 + +export TF_CPP_MIN_VLOG_LEVEL=2 + +HIP_VISIBLE_DEVICES=0,1,2,3 numactl --cpunodebind=0,1,2,3 --membind=0,1,2,3 nohup python3 tf_cnn_benchmarks.py --data_format=NCHW --batch_size=128 --model=resnet50 --save_model_steps=20000 --optimizer=momentum --variable_update=replicated --print_training_accuracy=true --eval_during_training_every_n_epochs=1 --nodistortions --num_gpus=4 --num_epochs=90 --weight_decay=1e-4 --data_dir=/public/software/apps/DeepLearning/Data/ImageNet-tensorflow/ --use_fp16=False --data_name=imagenet --train_dir=/public/home/qianyj/TF_test/dtk21.10.1/tf1.15/benchmarks-master/scripts/checkpoint >logfile 2>&1 & diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/run_tests.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/run_tests.py new file mode 100644 index 00000000..5b3dcd32 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/run_tests.py @@ -0,0 +1,107 @@ +# 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. +# ============================================================================== +"""Runs the tf_cnn_benchmarks tests.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import sys +import unittest + +from absl import app +from absl import flags as absl_flags +import tensorflow.compat.v1 as tf + +import all_reduce_benchmark_test +import allreduce_test +import benchmark_cnn_distributed_test +import benchmark_cnn_test +import cnn_util_test +import variable_mgr_util_test +from models import model_config + +# Ideally, we wouldn't need this option, and run both distributed tests and non- +# distributed tests. But, TensorFlow allocates all the GPU memory by default, so +# the non-distributed tests allocate all the GPU memory. The distributed tests +# spawn processes that run TensorFlow, and cannot run if all the GPU memory is +# already allocated. If a non-distributed test is run, then a distributed test +# is run in the same process, the distributed test will fail because there is no +# more GPU memory for the spawned processes to allocate. +absl_flags.DEFINE_boolean('run_distributed_tests', False, + 'If True, run the distributed tests. If False, the' + 'non-distributed tests.') + +absl_flags.DEFINE_boolean('full_tests', False, + 'If True, all distributed or non-distributed tests ' + 'are run, which can take hours. If False, only a ' + 'subset of tests will be run. This subset runs much ' + 'faster and tests almost all the functionality as ' + 'the full set of tests, so it is recommended to keep ' + 'this option set to False.') + +FLAGS = absl_flags.FLAGS + + +def main(_): + loader = unittest.defaultTestLoader + if FLAGS.full_tests: + suite = unittest.TestSuite([ + loader.loadTestsFromModule(allreduce_test), + loader.loadTestsFromModule(cnn_util_test), + loader.loadTestsFromModule(variable_mgr_util_test), + loader.loadTestsFromModule(benchmark_cnn_test), + loader.loadTestsFromModule(all_reduce_benchmark_test), + ]) + if model_config.can_import_contrib: + from models.tf1_only import nasnet_test # pylint: disable=g-import-not-at-top + suite.addTest(loader.loadTestsFromModule(nasnet_test)) + dist_suite = unittest.TestSuite([ + loader.loadTestsFromModule(benchmark_cnn_distributed_test), + ]) + else: + suite = unittest.TestSuite([ + loader.loadTestsFromModule(allreduce_test), + loader.loadTestsFromModule(cnn_util_test), + loader.loadTestsFromModule(all_reduce_benchmark_test), + loader.loadTestsFromModule(variable_mgr_util_test), + loader.loadTestsFromTestCase(benchmark_cnn_test.TestAlexnetModel), + loader.loadTestsFromTestCase(benchmark_cnn_test.TfCnnBenchmarksTest), + loader.loadTestsFromTestCase(benchmark_cnn_test.VariableUpdateTest), + loader.loadTestsFromTestCase( + benchmark_cnn_test.VariableMgrLocalReplicatedTest), + ]) + dist_suite = unittest.TestSuite([ + loader.loadTestsFromNames([ + 'benchmark_cnn_distributed_test.DistributedVariableUpdateTest' + '.testVarUpdateDefault', + + 'benchmark_cnn_distributed_test.TfCnnBenchmarksDistributedTest' + '.testParameterServer', + ]), + ]) + + if FLAGS.run_distributed_tests: + print('Running distributed tests') + result = unittest.TextTestRunner(verbosity=2).run(dist_suite) + else: + print('Running non-distributed tests') + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) + + +if __name__ == '__main__': + tf.disable_v2_behavior() + app.run(main) diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/ssd_constants.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/ssd_constants.py new file mode 100644 index 00000000..77fa0149 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/ssd_constants.py @@ -0,0 +1,118 @@ +# Copyright 2018 Google. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Central location for all constants related to MLPerf SSD.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +# ============================================================================== +# == Model ===================================================================== +# ============================================================================== +IMAGE_SIZE = 300 + +# TODO(taylorrobie): MLPerf uses 80, but COCO documents 90. (RetinaNet uses 90) +# Update(taylorrobie): Labels > 81 show up in the pipeline. This will need to +# be resolved. +NUM_CLASSES = 81 # Including "no class". Not all COCO classes are used. + +# Note: Zero is special. (Background class) CLASS_INV_MAP[0] must be zero. +CLASS_INV_MAP = ( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 27, 28, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 67, 70, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 84, 85, 86, 87, + 88, 89, 90) +_MAP = {j: i for i, j in enumerate(CLASS_INV_MAP)} +CLASS_MAP = tuple(_MAP.get(i, -1) for i in range(max(CLASS_INV_MAP) + 1)) + +NUM_SSD_BOXES = 8732 + +RESNET_DEPTH = 34 + +"""SSD specific""" +MIN_LEVEL = 3 +MAX_LEVEL = 8 + +FEATURE_SIZES = (38, 19, 10, 5, 3, 1) +STEPS = (8, 16, 32, 64, 100, 300) + +# https://github.com/amdegroot/ssd.pytorch/blob/master/data/config.py +SCALES = (21, 45, 99, 153, 207, 261, 315) +ASPECT_RATIOS = ((2,), (2, 3), (2, 3), (2, 3), (2,), (2,)) +NUM_DEFAULTS = (4, 6, 6, 6, 4, 4) +NUM_DEFAULTS_BY_LEVEL = {3: 4, 4: 6, 5: 6, 6: 6, 7: 4, 8: 4} +SCALE_XY = 0.1 +SCALE_HW = 0.2 +BOX_CODER_SCALES = (1 / SCALE_XY, 1 / SCALE_XY, 1 / SCALE_HW, 1 / SCALE_HW) +MATCH_THRESHOLD = 0.5 + +# https://discuss.pytorch.org/t/how-to-preprocess-input-for-pre-trained-networks/683 +NORMALIZATION_MEAN = (0.485, 0.456, 0.406) +NORMALIZATION_STD = (0.229, 0.224, 0.225) + +# SSD Cropping +NUM_CROP_PASSES = 50 +CROP_MIN_IOU_CHOICES = (0, 0.1, 0.3, 0.5, 0.7, 0.9) +P_NO_CROP_PER_PASS = 1 / (len(CROP_MIN_IOU_CHOICES) + 1) + +# Hard example mining +NEGS_PER_POSITIVE = 3 + +# Batch normalization +BATCH_NORM_DECAY = 0.997 +BATCH_NORM_EPSILON = 1e-4 + + +# ============================================================================== +# == Optimizer ================================================================= +# ============================================================================== +LEARNING_RATE_SCHEDULE = ( + (0, 1e-3), + (160000, 1e-4), + (200000, 1e-5), +) +MOMENTUM = 0.9 +WEIGHT_DECAY = 5e-4 + + +# ============================================================================== +# == Keys ====================================================================== +# ============================================================================== +BOXES = "boxes" +CLASSES = "classes" +NUM_MATCHED_BOXES = "num_matched_boxes" +IMAGE = "image" +SOURCE_ID = "source_id" +RAW_SHAPE = "raw_shape" +PRED_BOXES = "pred_boxes" +PRED_SCORES = "pred_scores" + + +# ============================================================================== +# == Evaluation ================================================================ +# ============================================================================== + +# Note: This is based on a batch size of 32 +# https://github.com/mlperf/reference/blob/master/single_stage_detector/ssd/train.py#L21-L37 +CHECKPOINT_FREQUENCY = 20000 +MAX_NUM_EVAL_BOXES = 200 +OVERLAP_CRITERIA = 0.5 # Used for nonmax supression +MIN_SCORE = 0.05 # Minimum score to be considered during evaluation. +DUMMY_SCORE = -1e5 # If no boxes are matched. + +ANNOTATION_FILE = "annotations/instances_val2017.json" +COCO_NUM_TRAIN_IMAGES = 118287 +COCO_NUM_VAL_IMAGES = 4952 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/ssd_dataloader.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/ssd_dataloader.py new file mode 100644 index 00000000..907d3090 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/ssd_dataloader.py @@ -0,0 +1,405 @@ +# Copyright 2018 Google. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Data loader and processing.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import itertools as it +import math + +import numpy as np +import tensorflow.compat.v1 as tf + +from object_detection.box_coders import faster_rcnn_box_coder +from object_detection.core import box_list +from object_detection.core import region_similarity_calculator +from object_detection.core import target_assigner +from object_detection.matchers import argmax_matcher +import mlperf +import ssd_constants + + +class DefaultBoxes(object): + """Default bounding boxes for 300x300 5 layer SSD. + + Default bounding boxes generation follows the order of (W, H, anchor_sizes). + Therefore, the tensor converted from DefaultBoxes has a shape of + [anchor_sizes, H, W, 4]. The last dimension is the box coordinates; 'ltrb' + is [ymin, xmin, ymax, xmax] while 'xywh' is [cy, cx, h, w]. + """ + + def __init__(self): + fk = ssd_constants.IMAGE_SIZE / np.array(ssd_constants.STEPS) + + self.default_boxes = [] + # size of feature and number of feature + for idx, feature_size in enumerate(ssd_constants.FEATURE_SIZES): + sk1 = ssd_constants.SCALES[idx] / ssd_constants.IMAGE_SIZE + sk2 = ssd_constants.SCALES[idx+1] / ssd_constants.IMAGE_SIZE + sk3 = math.sqrt(sk1*sk2) + all_sizes = [(sk1, sk1), (sk3, sk3)] + + for alpha in ssd_constants.ASPECT_RATIOS[idx]: + w, h = sk1 * math.sqrt(alpha), sk1 / math.sqrt(alpha) + all_sizes.append((w, h)) + all_sizes.append((h, w)) + + assert len(all_sizes) == ssd_constants.NUM_DEFAULTS[idx] + + for w, h in all_sizes: + for i, j in it.product(range(feature_size), repeat=2): + cx, cy = (j + 0.5) / fk[idx], (i + 0.5) / fk[idx] + box = tuple(np.clip(k, 0, 1) for k in (cy, cx, h, w)) + self.default_boxes.append(box) + + assert len(self.default_boxes) == ssd_constants.NUM_SSD_BOXES + + mlperf.logger.log(key=mlperf.tags.FEATURE_SIZES, + value=ssd_constants.FEATURE_SIZES) + mlperf.logger.log(key=mlperf.tags.STEPS, + value=ssd_constants.STEPS) + mlperf.logger.log(key=mlperf.tags.SCALES, + value=ssd_constants.SCALES) + mlperf.logger.log(key=mlperf.tags.ASPECT_RATIOS, + value=ssd_constants.ASPECT_RATIOS) + mlperf.logger.log(key=mlperf.tags.NUM_DEFAULTS, + value=ssd_constants.NUM_SSD_BOXES) + + def to_ltrb(cy, cx, h, w): + return cy - h / 2, cx - w / 2, cy + h / 2, cx + w / 2 + + # For IoU calculation + self.default_boxes_ltrb = tuple(to_ltrb(*i) for i in self.default_boxes) + + def __call__(self, order='ltrb'): + if order == 'ltrb': return self.default_boxes_ltrb + if order == 'xywh': return self.default_boxes + + +def calc_iou_tensor(boxes1, boxes2): + """Calculation of IoU based on two boxes tensor. + + Reference to https://github.com/kuangliu/pytorch-ssd + + Args: + boxes1: shape (N, 4), four coordinates of N boxes + boxes2: shape (M, 4), four coordinates of M boxes + Returns: + IoU: shape (N, M), IoU of the i-th box in `boxes1` and j-th box in `boxes2` + """ + b1_left, b1_top, b1_right, b1_bottom = tf.split(boxes1, 4, axis=1) + b2_left, b2_top, b2_right, b2_bottom = tf.split(boxes2, 4, axis=1) + + # Shape of intersect_* (N, M) + intersect_left = tf.maximum(b1_left, tf.transpose(b2_left)) + intersect_top = tf.maximum(b1_top, tf.transpose(b2_top)) + intersect_right = tf.minimum(b1_right, tf.transpose(b2_right)) + intersect_bottom = tf.minimum(b1_bottom, tf.transpose(b2_bottom)) + + boxes1_area = (b1_right - b1_left) * (b1_bottom - b1_top) + boxes2_area = (b2_right - b2_left) * (b2_bottom - b2_top) + + intersect = tf.multiply(tf.maximum((intersect_right - intersect_left), 0), + tf.maximum((intersect_bottom - intersect_top), 0)) + union = boxes1_area + tf.transpose(boxes2_area) - intersect + iou = intersect / union + + return iou + + +def ssd_parse_example_proto(example_serialized): + """Parses an Example proto containing a training example of an image. + + Each Example proto contains the following fields that we care about: + + image/encoded: + image/source_id: tf.string + image/height: tf.int64 + image/width: tf.int64 + image/object/bbox/xmin: tf.VarLenFeature(tf.float32) + image/object/bbox/xmax: tf.VarLenFeature(tf.float32) + image/object/bbox/ymin: tf.VarLenFeature(tf.float32 + image/object/bbox/ymax: tf.VarLenFeature(tf.float32) + image/object/class/label: tf.VarLenFeature(tf.int64) + image/object/class/text: tf.VarLenFeature(tf.string) + + Complete decoder can be found in: + https://github.com/tensorflow/models/blob/master/research/object_detection/data_decoders/tf_example_decoder.py + + Args: + example_serialized: scalar Tensor tf.string containing a serialized + Example protocol buffer. + + Returns: + A dictionary with the following key-values: + image_buffer: Tensor tf.string containing the contents of a JPEG file. + groundtruth_boxes: Tensor tf.float32 of shape [num_boxes, 4], containing + coordinates of object bounding boxes. + groundtruth_classeS: Tensor tf.int64 of shape [num_boxes, 1], containing + class labels of objects. + source_id: unique image identifier. + raw_shape: [height, width, 3]. + """ + feature_map = { + 'image/encoded': tf.FixedLenFeature( + (), dtype=tf.string, default_value=''), + 'image/source_id': tf.FixedLenFeature((), tf.string, default_value=''), + 'image/height': tf.FixedLenFeature((), tf.int64, default_value=1), + 'image/width': tf.FixedLenFeature((), tf.int64, default_value=1), + 'image/object/bbox/xmin': tf.VarLenFeature(dtype=tf.float32), + 'image/object/bbox/ymin': tf.VarLenFeature(dtype=tf.float32), + 'image/object/bbox/xmax': tf.VarLenFeature(dtype=tf.float32), + 'image/object/bbox/ymax': tf.VarLenFeature(dtype=tf.float32), + 'image/object/class/label': tf.VarLenFeature(dtype=tf.int64), + } + features = tf.parse_single_example(example_serialized, feature_map) + + xmin = tf.expand_dims(features['image/object/bbox/xmin'].values, 1) + ymin = tf.expand_dims(features['image/object/bbox/ymin'].values, 1) + xmax = tf.expand_dims(features['image/object/bbox/xmax'].values, 1) + ymax = tf.expand_dims(features['image/object/bbox/ymax'].values, 1) + + image_buffer = features['image/encoded'] + # Bounding box coordinates should be in ltrb order + boxes = tf.concat([ymin, xmin, ymax, xmax], 1) + classes = tf.expand_dims(features['image/object/class/label'].values, 1) + source_id = features['image/source_id'] + raw_shape = tf.stack([features['image/height'], features['image/width'], 3]) + + return {'image_buffer': image_buffer, + 'groundtruth_boxes': boxes, + 'groundtruth_classes': classes, + 'source_id': source_id, + 'raw_shape': raw_shape} + + +def ssd_decode_and_crop(image_buffer, boxes, classes, raw_shape): + """Crop image randomly and decode the cropped region. + + This function will crop an image to meet the following requirements: + 1. height to width ratio between 0.5 and 2; + 2. IoUs of some boxes exceed specified threshold; + 3. At least one box center is in the cropped region. + We defer the jpeg decoding task until after the crop to avoid wasted work. + + Reference: https://github.com/chauhan-utk/ssd.DomainAdaptation + + Args: + image_buffer: Tensor tf.string containing the contents of a JPEG file. + boxes: Tensor tf.float32 of shape [num_boxes, 4], containing coordinates of + object bounding boxes. + classes: Tensor tf.int64 of shape [num_boxes, 1], containing class labels + of objects. + raw_shape: [height, width, 3]. + + Returns: + resized_image: decoded, cropped, and resized image Tensor tf.float32 of + shape [ssd_constants.IMAGE_SIZE, ssd_constants.IMAGE_SIZE, 3], value + range 0--255. + cropped_boxes: box coordinates for objects in the cropped region. + cropped_classes: class labels for objects in the cropped region. + """ + + num_boxes = tf.shape(boxes)[0] + + def no_crop_check(): + return (tf.random_uniform(shape=(), minval=0, maxval=1, dtype=tf.float32) + < ssd_constants.P_NO_CROP_PER_PASS) + + def no_crop_proposal(): + return ( + tf.ones((), tf.bool), + tf.convert_to_tensor([0, 0, 1, 1], dtype=tf.float32), + tf.ones((num_boxes,), tf.bool), + ) + + def crop_proposal(): + rand_vec = lambda minval, maxval: tf.random_uniform( + shape=(ssd_constants.NUM_CROP_PASSES, 1), minval=minval, maxval=maxval, + dtype=tf.float32) + + width, height = rand_vec(0.3, 1), rand_vec(0.3, 1) + left, top = rand_vec(0, 1-width), rand_vec(0, 1-height) + + right = left + width + bottom = top + height + + ltrb = tf.concat([left, top, right, bottom], axis=1) + + min_iou = tf.random_shuffle(ssd_constants.CROP_MIN_IOU_CHOICES)[0] + ious = calc_iou_tensor(ltrb, boxes) + + # discard any bboxes whose center not in the cropped image + xc, yc = [tf.tile(0.5 * (boxes[:, i + 0] + boxes[:, i + 2])[tf.newaxis, :], + (ssd_constants.NUM_CROP_PASSES, 1)) for i in range(2)] + + masks = tf.reduce_all(tf.stack([ + tf.greater(xc, tf.tile(left, (1, num_boxes))), + tf.less(xc, tf.tile(right, (1, num_boxes))), + tf.greater(yc, tf.tile(top, (1, num_boxes))), + tf.less(yc, tf.tile(bottom, (1, num_boxes))), + ], axis=2), axis=2) + + # Checks of whether a crop is valid. + valid_aspect = tf.logical_and(tf.less(height/width, 2), + tf.less(width/height, 2)) + valid_ious = tf.reduce_all(tf.greater(ious, min_iou), axis=1, keepdims=True) + valid_masks = tf.reduce_any(masks, axis=1, keepdims=True) + + valid_all = tf.cast(tf.reduce_all(tf.concat( + [valid_aspect, valid_ious, valid_masks], axis=1), axis=1), tf.int32) + + # One indexed, as zero is needed for the case of no matches. + index = tf.range(1, 1 + ssd_constants.NUM_CROP_PASSES, dtype=tf.int32) + + # Either one-hot, or zeros if there is no valid crop. + selection = tf.equal(tf.reduce_max(index * valid_all), index) + + use_crop = tf.reduce_any(selection) + output_ltrb = tf.reduce_sum(tf.multiply(ltrb, tf.tile(tf.cast( + selection, tf.float32)[:, tf.newaxis], (1, 4))), axis=0) + output_masks = tf.reduce_any(tf.logical_and(masks, tf.tile( + selection[:, tf.newaxis], (1, num_boxes))), axis=0) + + return use_crop, output_ltrb, output_masks + + def proposal(*args): + return tf.cond( + pred=no_crop_check(), + true_fn=no_crop_proposal, + false_fn=crop_proposal, + ) + + _, crop_bounds, box_masks = tf.while_loop( + cond=lambda x, *_: tf.logical_not(x), + body=proposal, + loop_vars=[tf.zeros((), tf.bool), tf.zeros((4,), tf.float32), tf.zeros((num_boxes,), tf.bool)], + ) + + filtered_boxes = tf.boolean_mask(boxes, box_masks, axis=0) + + mlperf.logger.log(key=mlperf.tags.NUM_CROPPING_ITERATIONS, + value=ssd_constants.NUM_CROP_PASSES) + + # Clip boxes to the cropped region. + filtered_boxes = tf.stack([ + tf.maximum(filtered_boxes[:, 0], crop_bounds[0]), + tf.maximum(filtered_boxes[:, 1], crop_bounds[1]), + tf.minimum(filtered_boxes[:, 2], crop_bounds[2]), + tf.minimum(filtered_boxes[:, 3], crop_bounds[3]), + ], axis=1) + + left = crop_bounds[0] + top = crop_bounds[1] + width = crop_bounds[2] - left + height = crop_bounds[3] - top + + cropped_boxes = tf.stack([ + (filtered_boxes[:, 0] - left) / width, + (filtered_boxes[:, 1] - top) / height, + (filtered_boxes[:, 2] - left) / width, + (filtered_boxes[:, 3] - top) / height, + ], axis=1) + + # crop_window containing integer coordinates of cropped region. A normalized + # coordinate value of y should be mapped to the image coordinate at + # y * (height - 1). + raw_shape = tf.cast(raw_shape, tf.float32) + crop_window = tf.stack([left * (raw_shape[0] - 1), + top * (raw_shape[1] - 1), + width * raw_shape[0], + height * raw_shape[1]]) + crop_window = tf.cast(crop_window, tf.int32) + + # Fused op only decodes the cropped portion of an image + cropped_image = tf.image.decode_and_crop_jpeg( + image_buffer, crop_window, channels=3) + + # Resize converts image dtype from uint8 to float32, without rescaling values. + resized_image = tf.image.resize_images( + cropped_image, [ssd_constants.IMAGE_SIZE, ssd_constants.IMAGE_SIZE]) + mlperf.logger.log(key=mlperf.tags.INPUT_SIZE, + value=ssd_constants.IMAGE_SIZE) + + cropped_classes = tf.boolean_mask(classes, box_masks, axis=0) + + return resized_image, cropped_boxes, cropped_classes + + +def color_jitter(image, brightness=0, contrast=0, saturation=0, hue=0): + """Distort the color of the image.""" + with tf.name_scope('distort_color'): + if brightness > 0: + image = tf.image.random_brightness(image, max_delta=brightness) + if contrast > 0: + image = tf.image.random_contrast( + image, lower=1-contrast, upper=1+contrast) + if saturation > 0: + image = tf.image.random_saturation( + image, lower=1-saturation, upper=1+saturation) + if hue > 0: + image = tf.image.random_hue(image, max_delta=hue) + return image + + +def normalize_image(images): + """Normalize image to zero mean and unit variance. + + Args: + images: a tensor representing images, at least 3-D. + Returns: + images normalized by mean and stdev. + """ + data_type = images.dtype + mean = tf.constant(ssd_constants.NORMALIZATION_MEAN, data_type) + std = tf.constant(ssd_constants.NORMALIZATION_STD, data_type) + images = tf.divide(tf.subtract(images, mean), std) + + mlperf.logger.log(key=mlperf.tags.DATA_NORMALIZATION_MEAN, + value=ssd_constants.NORMALIZATION_MEAN) + mlperf.logger.log(key=mlperf.tags.DATA_NORMALIZATION_STD, + value=ssd_constants.NORMALIZATION_STD) + return images + + +class Encoder(object): + """Encoder for SSD boxes and labels.""" + + def __init__(self): + similarity_calc = region_similarity_calculator.IouSimilarity() + matcher = argmax_matcher.ArgMaxMatcher( + matched_threshold=ssd_constants.MATCH_THRESHOLD, + unmatched_threshold=ssd_constants.MATCH_THRESHOLD, + negatives_lower_than_unmatched=True, + force_match_for_each_row=True) + + box_coder = faster_rcnn_box_coder.FasterRcnnBoxCoder( + scale_factors=ssd_constants.BOX_CODER_SCALES) + + self.default_boxes = DefaultBoxes()('ltrb') + self.default_boxes = box_list.BoxList( + tf.convert_to_tensor(self.default_boxes)) + self.assigner = target_assigner.TargetAssigner( + similarity_calc, matcher, box_coder) + + def encode_labels(self, gt_boxes, gt_labels): + target_boxes = box_list.BoxList(gt_boxes) + encoded_classes, _, encoded_boxes, _, matches = self.assigner.assign( + self.default_boxes, target_boxes, gt_labels) + num_matched_boxes = tf.reduce_sum( + tf.cast(tf.not_equal(matches, -1), tf.float32)) + return encoded_classes, encoded_boxes, num_matched_boxes diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/__init__.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/__init__.py similarity index 100% rename from TensorFlow/ComputeVision/Accuracy_Validation/ResNet50_Official/official/resnet/__init__.py rename to TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/__init__.py diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/fake_tf_record_data/train-00000-of-00008 b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/fake_tf_record_data/train-00000-of-00008 new file mode 100644 index 0000000000000000000000000000000000000000..4e65b92a9a5f252f7b1a9d9048e834217f468971 GIT binary patch literal 101327 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 literal 0 HcmV?d00001 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/fake_tf_record_data/train-00001-of-00008 b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/fake_tf_record_data/train-00001-of-00008 new file mode 100644 index 0000000000000000000000000000000000000000..1cf1fec734f3d6bfd74a6e38ac7b0f43d24eaaab GIT binary patch literal 101943 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�yv8DjBloUJUn_X~o_`6;{Xooz2EOtksViygeh9!3-L$>b&Kk7!^{=_KG zmqE5RAp5arP?cus>P?wNb6O2`L$dn?3winQYyTPpUsaJd4-;)8ZSnkeXval6MF6(| zZmMER{|>jF{c>vKmy<=nSOly>1gv}<>}mLKrRMeBr3Jwh^IF;I%ag~{MNJ6Kdt3&IOua4zC66eMVRI&O^N%iF1q7@Qq?TY$LEX*-PI&l6>e``a3KZF=oNqh-ue`Q)yXzee0Mv465Vms^a`|x&_+pG033Di;| z@Yw+^{<-wMfx1nCv)!}RjcnL}Zh~!Z#W%rz-D-q{Wl@Y-7A+q@LZe|vD_PqfkfUwO z`4gmAnbRiNB6qmu4mVYCJY+FSO*&YPRX2tgZ!@YsWsPs4l-yT=D}vhRa@ zj!I{qw$z+VBR*HY&XFAAZ%$@a(e-qHs~$>x$={`;#Mhime3^LT7ftN$nBJ6ZAkLCg zJ`Eh*b`b~^`fJ>pbeLR){EWm!dv{$WmLJT02$|UvP8-F)1=tOcqXxd_=IClLzSag` zYbqA|q@8BWuE*z*ACy%wm_qQ*kOH*TzIwEg17*2sOM84s?fSRXB^r5i&_Y)WW0_cTaG@9D>xFB&3 z;t_@49Ygys|NNspLI?Lw!8{IjqLgxFpEo(z{x-#(ku-FujIz+F0m?5;{lEYd z)v#5Adxv|QF^V=ApHgQX2TK`)_PBnSk~W4nH%lUDkwS1^q$jBj!SyDpoep+nCpn+ zS}|DD2CO~$%sFNN5UtZu_b0=8b^4K}hv*qwOb-PN;0M2m7a)$Nb?W$~#7R_f7@AdL zF~-)&852GbT;VN!e)NZl?i7$#mS4-c+X21X0#5@9(h~W#gSdHt=GQ(?>OxkU1C%LE z2S_eS5>jR82`97qTO;f?LXIlT>Vw<>hSwUwYnG6HzzJ!5!f;yWu_31ytVs3~eBa)y z4?YX4EX2K%L*+=t6tm-owi_n6W`fHm71*Y_7r3DeU9^ENEN-iCHsL`=Kp*96p>b2m zww!%o08ulGq8(=Y(?q+nH0p>~__+k)1&E@3@rvnKo+MBrjk*vY>_*e59uqo+7PWY} zPpY8o6ECI#OmZhO$PhWoR?=d|k5pB+wB5!HVdSF?@?qK0DGzoKyAsE`jybxV2F%Lh ziHFC!qrm*^_kB@d_9w3i*oONH=*iY?8oGvh4_}$~t^mv#t!K}LUnDo*m&FrLuIh}9 zbOcJo6BAME8jUA5zSUJ?qC-|Zk!pPKHiw6O4%-$Vkq~K%v~D!QdLyh+h4GQd&1dYO z4R&BN4$5X6XmbdaIYo9`a2Ty!NRL@c$@8)~gf&+~Lr=yvO-~aVcG+t)#=smiQ6D#x zqmM<5b8vo3ayyrunsLYUa99uds%XX~a?KdVX#?XPdK9u^gpDwLODv-e2ke#35!vXgtFZ83wR`ID#X@F!0vV$HVzRCuBJ>=kE1LVpgM zdT5paQ&BD+x$q%6R}gWkKDmkIB?rnRof!MEwdb6`?0_Ej>mg4SV3xsoF)-5xm_4+d z8|qaOF+>EhQM)2(N=8}wD{!Dc3S#dkeu!3&Ey*=+243?Tj|~jS+$$i|)dd^6!5-?6 zXrL74mWea-W?%G{45ubY3`I1?a5)ggWi**qrOuzo1@U%S$uvoXEu|YscN$4eHNYeT zq^M#fwK2DyA*?nK_Q?ybDV*&}7(9xG%%)Dpz$VcGvTZ+qq#qPWK!dn!>_|_1#_6JN zB#OsDWYvjJbfN0dGuw@<4x7kPhhm{iQ1@G$wBd3+EY(AnDzu?5*MuRLMv&_d$Fpem zRb^r{As;QFRjyQp6x1i+-ojG??#vq0qY!>D1LZWvFP#KJmH`$UV4*4m!eFj9`!&3TWHLrV`H7V59;HYKB%o9Jxjl zL*8q=1IZ$yw~Tqjj309XJ%xto`Jyr1VXkCDw5&$FIJ7SMg!(R;gUPHQU$V>!MxT%l zdqSK4+>Kl#9#_~jb!aG*q>dRr3VC4^#$U`@i<-ADx(JjZd+^;0aa0 z+x|jqQgT9cs>KL;@ap_hyAE*P2tM|gtciV+`da&14jN&<5%wC3elI$~mFwKTQ+tjV zz8dq5Ij-m^*QWUYZ2#iwm+d01zW4t7iZ*fnM&6t2FMF+sndCR39r5{yv}*~>nyc`Tj|Tc9BrsQs6xny zsf&ztTCd(*vD?0Dbwgv4N1C8g$M)Q_w3Wx8r!db+<=tScLom`I_%w1qtiYbGj?41wCNwum^o|qCv!3uFIk$o?6c*c zf4%0LZ?nEzo4sz!)@|E&?EG=p?)?XTJ^0(9-wz)-dFu3;v**rVxOnOMUwMDu$iI23 zpwPB1p7XrFbodmG)q1~=kj7i=HmFk9_tashEwBnVOZeb%`TG{=DN$Wbod3*B0FVEATi{kECFJz>1 z4cWIQ_n_!7REKh6a7v0#N=_LV-N$M%fDSynb!h#-i}BGZDS@fhp{e#4Q->r$ZKz~_ zXHv`nYoFA>n3$xYfkPAG5-moH5XXkxENlVQz+b$2@de_eW32J^=;t9^-16XSDZ>&| ztf>~^lYtK*Uwl|X^icc7Ke?=rlKYvG8<>%_qy+?&#NODrcxz&Gg4N;!W-z5DN5>`h z3T_qxl}ci8e`{R7{;3vyH81d(-~lzEvh(U5x__DVm)v6S8m*&pLf3zlXRt?xgF7+- zuK8hiZQL^lz3Wi&T_54myY?i1{LuRw`_FD6QBd*WG*-MNX&#hxC7a=>PuEs&$0WDf z8@}*{72YaHa@-MK$C6xe@>YG4yVENpy&=cG*&eWGKvmCxrvPAT+y^icMlpc^VN&}i zTdi{Wt^_1x7$aIzP>LrFrG%0%E}@k3sa{c=1}#1@K`3c#Us)HX4}x(fxwnbUiU7RGpDrPQwSPxM5ewNsR{+322)~`;GB-Bqon-h!=ZFCI z=wP=FcIwoC0Ajg7h5)pI0B-$|-f*FZo4@_A@=B(HD2W4atR78^OF+OuY_vRy{dqcCo`jRjlaqLPq9fn66r9SX)FYQCXLwhl6SX{< z=v1EEHp4A5+%T&lIGyCqu;8SP;KbNB#z3-X;PE~KLMoSsvT=jaI-_875G|MMXt_L` zoE4>9UM=OHQ58o#m;8d*j`}P&AP#nUV{fxot%8!M*Bj=e`96YNi&)WGL{Gj$Dcv8r z7f4R`%N4SbQmI(Xb*ID)6@CE}B7A|#u~3<4f^-v%F{uFySouZ_3upri+^+5BBE{WB z54)cG3rTEGZG|RMOKx2B5iS4dY56CjTZNbRW$5f2R`X5U+QR}f#!odR*MQUT#+pHH zDJ6}y&-5M11l9Adlz_7IVTyIIHIbeb@(hky;iLhM8{nux1zCZ&aL==>ppC3>cXkGA zV3kD!&(?52BCYSu4BFg-ffi|-k}obH{n|Uqqmcdh%*8Fz+D+Gl?iukT973ys z*ZiuXmrm(B2CW7n$<@FRv>Gsy7cW%~ufAJs)J@T(`ss}zX!AuiO zS4BNAg!g5UUmKDC-fYGUJzi$$N#B1wV@*3LM0NQ|JtHk+MIQI{Y4wHCUK>m-YF-ys z_+xSW&L|;?k!H-6xD>~!#7&WpBSgRA1DAZ@ypIYXI(L?@%L27V0`*T%dZ1wU#}Wy9 z2DJ4I(ArjsafxW#YRk5cG}_Qag9gpWI6x|9TR+nXtvIu92)rdS#NUj}HJ-)iO-$x4 zUFz~CJ9>Fx^|j-0&!4uWAZ$sdHUc+EBnXDx7e=gp{a_379jANHml_fjgjUfU`QxQ3 zIixEv)Ke?ga0J0YUq|e<)fYDT!UkUzU@so{iVS;c1A7&5QH2dN zh6DR%d%&In_6(@_8Hf@P9hLAPf)7cRS=Tz5#`SQ8IqO~-CXNKZe6Q^rknc+d6cV1f zS@m#p47UW60nZH_qVeR9m+(yTTp;6_9STQ+B|K9+Ba|ed4aADZJF3_2 zGlLqIG0G-muzfE3eSRoQI}~K4@E|&w)rHo+Q074t`gc>5>+f-{BU&5Fco1#FYh#c3 z&(HZYTQnEw^i<3Q$dWY|D4CwFP$3At>&=JzAWfH$piuId-*ae#0<|C-F*JZY-#y5! z^WAoP2r9@KuXil>3v`gLgFIE(6FkQaV&%Rz%KgV~P*jS!=ahFQ&|)p!NyT$*dUuOR zJN7Dk=OdVm(vG+MrXaJ(Awgke+HpS~T2Y&n4{BJ*#nv8$v?IZW?=(2Zp?v^91LS}T z8mZiVZV!t?+K5As#3lt(x3SmDUQzhJw&yklrM$l1W>tms)s{Rpo|gTTdG%O~4Wc$( z+Y_-#RP+p<^TRmU;Dg;$la`lh9HlbPfoET>kJb_9YZcHsLdGm;0WK6p&4MoK>yT6Q zXqQs7{PSV+f0*i&ql-*%#{>mxPz3P-+-R1gwUMJ4O;z?Z729*?J%5P0b1rJ`j`nL& zAm+2~GixM4zK<*Pj>O+Mu!A;BmmH^7c1QiN_lL#}hAaf$Sc!rTL~K9hzNb>{}7Y+(eMEzHB&Le$7C^JE8dDYBGb zw{lVE9Fh8rihS`6_|cCxJc3$8tf@oHu;r4Wm>ss`52oi=hB*paAC5lfhhy%EG0HeMae?Gh0_vDj=O}R5GZL z#1uV#d5=c=8lKryu4uYg0 zJ~ z7o`jFG7R<8`!XRiYEfOEKZl9du3hHGu7Z4!Z-f>*M&vk|o@N^$%K&TCV0y|8;^wo| zp^?D*u3+bl0>BQG(x8Pz+C*jzR!##TsOG5W(K5Ql(vz!t^nh{1Cp-2@4%5rWBO9a|T1k9wv9XyV^uA#8l`KViGT2QAOKI~lI zA*xiaN>X*vy+>zq#Qe#X>Vn?pP`9m7A@>uDm>P+g)(Z+L4R=KgcAZDV%CmiawEOx{ zmU95dr7%nw`R})BSx%W@LX*rEXr1!v_#RkGBv{5Up%CY*JZ9xZR{UOcRr)Jyw=b;q z2iZQl-63GSlZ$bIk8M~zPZcLf+~EJglA|_~<6}1gU}Ht48!KY_#P&&x&w$c#xt*Uf=-z) z#ni$0X*bpn-a!kxF!B(`K(z0Kx-`vty=l}5gK21@Xp*N(pP37nWhUdKMA2!~$d=@L z3c#9W;pk1Hz7<97BFRG&F|PE==(ZJ`b&l3jU+Q3`4wkFJ;x2%TVWC_bq1;ts@FBvB zfK`Z)P?`Z=+Bb|wcq#M9`K4d=sDIzC*=vj#eG0M*2f%3RTwH`4hAr{$yqs1U#ksrR zN|y;=FJj><0u5iN)^%NPZ*ur*=}P#L^f?Y}rp@5ts8jUXrGp(h*s6-?^&;1jMK5hc zFSi>!C>7i{%RNu4we@RKd!KqwB*L$~n>Y+^p~nk6GeKtF^^Qv&X<6FcuG*IV?g>FX z$LUn(Y#+$-fi-I2vGlGu=|Ct9Esj*MH(c-+R*a4hAU{B~*jf=M?MkiP;K41n@YI1a3Vy}?kNHVMa7Rvg_Q0CISa{S4vQ8k17yd1^k z^g2$}TJla@0gJvGiM~uru5j>Lh@P^Yrefa+BFL18`0a<|XoMA&G;bz{U`t5zPe%r$ z;WKaj7&Lq~BZtpUX!u0MDx&e!@whz~VTLz(i=_A+_q2(8 z16N$R0lMDgiWBHQYJ}g7a8MOe?IGM17WTCf_L)>$^-^tUm_c2GGAZ}m!I3mjPnU9& zL#|{I)#v93G%NX}3awl^s1FGuXQd%%R^oCRHE7gG>;nBK`Ccc;ET_zu!=XoOlVjg@ zvfsVv4d=Y!v>NPp`D)y87VNbV?3o8WK!DPL{9K7*@nzpR-7v@kag4s?e4vFiV+TWtD<^>+dd40-(q=913 z=9MXPKZPqGx8S%$i_xM7ujvqY=cz>=ayNH6O|x4n<8VWb}JmnQ56rnT)>Wpz2Yz`hAmANsPZ^ ze;=O@(*cctnxNYhs_IzSh2$>IvGkfY;p61~WQ}4e+f^cK3zt-t?CACr$?br1Kem3^emRmH{)wvsWcG*?xlP&fcW9>6OTIREJ-Fb2fIE=fJm zbYyoF5Qmci@lqTRqh<=3F*3yOdRGAP8}oPOH5V%izt%IoKsZodesh%_Yut6U;F*qW zF6Uhx-CUVtjW^?)i%PwJ^cFU^!owYc;<~V%GO0H~@;TXwRKKf-LOtA61tYYXYsV6_ zHWIYkrQT(Mx8402594ArmmdIUAKr#2J11M<*s)<5NmVv?}0j=TTyl zk`tm+Ek+O;rk!7E*FoN#)~=G@Yj$K{Hke_ZS&-1Fz=@4-#4Tr0Q5#W_3E?x!Mlj5T z+1lM_r3S0t@FrT@nXVM~9J#({hFID(-?XhgERa2Ts$`_+uyWk&W0TLdYKP`Je^Vkcnw%$GsVuu)BbBN~jWY()?%1 zoKjz9(iFlk@Jpxu^*kfYF$yOLs-Sw}E^vKWOxH+EXYz9w2Q|wo7uY_B)`}^O#Jzlt zDYS$@m$)z6(K#zy>}bE5KH{#;?vR*(B(|)!Bf%&wv0McIuRVXvfC!OWr7>3Xz|ZxrjaJlMzm+DbE;w+Q*^V}>5BK#?2j^4 z{IcGHE=|7eO;^QVzxI_cCnmDgDx(WOiAOS27rtjsb#f$o)$M&2 zbx%s2Wh-2+K{=T-E_qmzVB_iY)qchcgCW5sa&ea~ZIkR}tZC4bJR zz6m7<%(d?4-H$rO%UuT8VSufwh?fc6S{5(05igmP*v$Y{sVw0R2Ag|SSz(&@~cD?Kl7D%nquILJ_R-sFp z$;nL;NUZy|3Yw8u)|`#<_A-ZGU&48N!@&Fxn%)*qV9Gm{ITvpE%kP-|$m$$lM;$}9 zFJ$?`8ecWc!{+g|S(?;Fnq*4k(vUUmXUc{YWBbhZ`M-Sz6cVYIhtH zzn$acaGh;}EEB9zh5R*@ugqYhMqnc|J7Cl1+<%*@AN+ZRX?s`dbXwXr-OIdCXQ9}> zO!!Z)JeX&O8%{mL(*`(UfMcrY8J^)@W6@I^(UX}6l$m*eO(aikSXCv+){C)e5Se+D zMljy${xmHF(G~RMi%YcTl=Wtm)?6Jk3lmm^$@spU!D&s@_a*)pL1sx3R=noIIC9 z`sb&!#b)Bx18+VHS^h+NZkmT%Gl)&xDy0{~V9$7`j%9(5ElKD3s35)Y9B*OiMI-5j z867a{NfGtLQnLC|@{{yC6W{@Y00LJP(k7mi`9Or7!+jtI4~;~r0T~CT;cjqX`fOnJ zs1Zlzg|-nZ`rLmn?gyK^$=fE89tUnTKr8x?1)0ss75!GUq7OyeeW;tn*Kf=CX3ce} zqL&@4v)f5bck1_zFu^?|+*XBc;#O`h16|qxT~D-~1UpWt?2c1XYTc2ZMs-M{yAh4( z_|$vtOmGl@&IOHx5ptHH>w%A~zf>a_9ajxIYk6#IS@CqqQ|lg{qvW zL7C}+Xeh$m;I!7|dI$2D)C5-yD$Z^z*s)d|{XO4rbS`}R;Ndic{5js~aGC-G`h3JHrKNBV4hBv>!E{*t4K)65)sB=~(7$jTa7 zGbVNr+1^gC?6w!`rc3MaI+i!IkvEvvQAXKyz*x7D z?KOJ_p4?|ZX@`2)fJx0M7*3g6)v!J7Pz)E|^tOy-Mv@_G5e`|67-Z~pSH`q>o_hzn?7m;)otX0i6Xcs9 zPYujDSH#U@kWm|uk@;Pk8oS=axwz(vFw%&*YTA$5C>$9O&WtR&( zq0s*I1u^I_X)`j`U4dg=)T?Y?rTS!g_H7p&8>(EIR5eJfBRc-{VnbMk>N}z?G(xxN z-GdexH@A3%*bWauiyzd};&iZ#VZ)~`5y%vCku)c1N1hR`8R3d5q#f~mb(V^>k&2iZ zQJI+$LBotE>a$RDBqX2iPk)a#Sx9$4nzg<0mS05h{?eA%&9KKzG&*ptbticlICODs zRym8E20o@5VUiKXt0MMxXN^0p=p2|;&XdZ*C}byIsnwlUY0xcT$YqQqllHh6g2L#y zZlha}&zS0t!Du)5o^^Q^q7wQqfmNcBO@)cM!SK2N1LTZDRJPgA6!|!+&#w5uB_BAi z3Wu3m+*yW*wSkE5F^3r!$eAZKlC%4IygWbK|I+QhLd4Cp-8NYjdt5)+W;6$MFrZw3zHg8~i|B#72i3qdvGDS9Q2pFAR$4mIHTv zY1c$W+xyq~qjA3exIh`v_OF-|X1E(f+aunT(b`>grQo9cer=8;`^=7Ub)y;9n<3k* qhPe7Gca_CeZNyc^W|fZ3V*AAQiS`UAWIMYib)zM8ow3KEVgC<5td)iU literal 0 HcmV?d00001 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/fake_tf_record_data/train-00004-of-00008 b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/fake_tf_record_data/train-00004-of-00008 new file mode 100644 index 0000000000000000000000000000000000000000..32818ec981b1b648ea605e351012c4e58a075454 GIT binary patch literal 94955 zcmeHQ30PF+8a^{H!^}6LfV)|iijkRuvZ{!=kfL2mE0BnoZcy?NA45Gi&^3ZOP{sb$!3l}OW0HMLU#|*Ukcz^-idc4p9el>!p{kzuq zfeC}GgM5w|;YTAB8cT{wPI6vRkzFG>9skix*No#zj&L1I|7H7%+z;E6$bIa+$CZ4| zd7E^;Tq8GKLr(9lbMw}f?Bp77oX%kX8T(=K|JJ*?8;l+%Gg$Z^*yzRSb#88YcQ=E< z-JSpLd;Iq~cW;BwQvuH#tG^uM(QrhKmg8qFHZ|(7ZhOrh`+jTODmHO~8EVz8Q@5UP zlc)WjX&M+591g3NV-xp5t;T`HzE^CwR(fZy z-PiWz?zGWSl+#)tMA*Dsi@VIy%lMnw!_vkXU9n=GF;r(3t1jAxtt;6i^btC`e zKKFkwDK$RHn(V{B*FhIde)81eF)8*Z@4NP?2&tbbyMWB;Y3-mv8PW`n8)l7<8E*CQ z1Phpx6Jz4y`?m}VhFWDHHN+bC=8$9`V?B4!FW1A9P}}+L9=Lvq`!Bo1&JE2GIV->0 zInl%ZCb)2Z(YG+Jn$%U7J?!P5>Y;ab?cIs!orRF^?1B!xGY|45rrbOH{`)&cK+Ojs zzx3FS>!gugIv7wff4{{M^S4_d&jMR5D!_d10(YEYerYUIJ?7udT$1L;u}^je>~BEP z-+&v>D;~c`^F>}^2A=3AS>Lh7OU0kEz}|Sp92&FmpfQV($M!a>{e_Ue9O~2{1~yqB z-Q*Z#Zh63U54h@~h9GmD>&1eMHiFEbnSM*sIQCh2XYfGML#RPWB3EhlfFakk!B2%# zav~EAxkAZLF6Km6eu_jhhq38Bv(WFUK24vUO{9WjxP7+?L;TG zdBp&i4RA>nZ1Ytvn&GfEaQM%F3zbx3d4Oe#h0jubLjuktZ+CK|@kkwwGzO3vnMfKx z`>iWV8l%HwQPLPdPJUlSlV9D~qR7U*1sZ%JXk8KHr|#eu6Xcp;gDNTrC%IG>XS5M#?(}EU14Yt<%9ig?r4PwN60K-0`L)pOEC#%dhUB=NCv0wO!Fj>P~)j@%UyP_8>>n zEM2)5eD*sPe6|}Q&j?%9pw{QQa#<{FY9nmk%+rWc-=@fy>_iSu7?~*b_AkpLet6N` zh9W<4e=Bz7WB0r^wXK%FeM)53&91j-ZB6|b*kzJZoB29cj!OnS~=?-H^0TF`4|!R9vX$#L^xN>%J(T~m!{uJqWVOtjx5 zVc^Tgb4?@p5=CxNcWRhKvZOEfWa$VvvWGnY`2gR53fKdet*gt}gErX1U&7j~*GNLh z?gO{sE48toZ9a}xai!bXqo4TQf3%=!82q0eo8V>@a?}yyY4Jy$(ZJZN(c5TXl<}9B(S^$?*hn^mN=<)MoWJb^XFnO?oDoi|qW67~3ukbq z5jd-KYi&iB*zt1s!uiu_m`K$Cp+f}Pgm|Ht`0Rz*Z7@s>BlpZN;^{j|>k1c!M*Oht z=)S)TY_M;a{?(ueo!yYOHiXOuedT~F#Rj*|;8+V^WPpzikfjPP*p%~S;HeGpbkVVE zb9GYL0MeeDv^tuGM;xs^Ct(^D%ZZ66LQyW`V>31j9PRv@ zlYWMc7IF<#?7VG=X-kHh5y}!z2NRjibEodnU)#753~aOkHkD^ft!Re!sJ@;;7~eKO zi3a1!?71C!)j?rA;mYK8WY)M%htSQC02Ie{H@kJZ8YlF!hUVtYg>w?UED^`6Y-~Vo z6wQ*Q&J=^J1CZO?z+0WVNUO}S!VF7Q(M3AS&0;{V5g=!7;7XdW@TdkB?79>Np8d0U z`Yt_aUzt1j

}yePH+FmHETQL57XpAYg=hX$Vu>t@?GOQyA7={`L&rin@Oyx;Mp`Ak#(0_f{zLxRaFn#O3- zjGEPxPehSR&a>r|oFfk0?6F0_udYpFr`zm!MA#RP$SbNQip>A;d<<WX%EXfd)~agpq>khI zF_6;+$UR!Fy+0s3^&E67JApgy{9SP?pR3M`K zaD+b()}%7?R5^3w=?VN+i=$~}l?7HMR{cH5uJPr*V@dMv!= zB*i~%hLdJErV1(k8m^GVLT$uC7Y&?jB`aG?tMqR`*=pBz5bRW zGm=<;*$wSp=*ayQF`1pdr6c)>K9SSlp$BkvuWsqaeZm5`HUhYIOwoLjAqqeoy4x=7 z{!Mz}Om#vDcPyeG3dL10s5f=1m1nvn zsx51ou5{DK?oCzqj`p!^N4pucft<{K%Iv39`>9fy(%9naG=@(1dhWh_iZcidJ*QpJ z&oBXH=sq(oXrzg*ej95Hga(o+{4`vNgc>;~U-cw2^rmtIm(r#L+vZC#xgwB+LZ@-O zyNs~I2;0;!j#p>mnlk>bjVSccU7i)W5y+<4CsJLJelO0ROQYX()99@$6VHtk6h9+^ zdlbN33B`{{zyC3oAG|`mCXs$mpO2c`LnZ7%C-71?Y7c7lMq-_@X z(gIslAxb*I{laih8@R_LGuo2Ou-|9p{61+?rNJ(Eb0;rWk8H9QD6h9x%@XW_zeW z|EsId1+!4AjZj-@2HuKHJ(RqbQloLv--2j`9%V-34NGfA%&2wy_EiCA_Y9u=9{eUT zFkj4jV!F3M^PUAmdZY3ciKU?~j731*x6>1S$$8J}^3Qv|F*$`N=3B?1h3wyH)Gk2T`PjMvl72RqmUYf=$15tO{cLpGa`P3J>g$EhbLSl& zbb}HLy~4fU3Gud@AkPF_)qr?9KkgNVc-lZb=ET)XZG?t^E;)J|edR+d{wv!(+I{uo zDC2cMwg;^~NDN|FiB})cK@2-<)+bjVUXf9K=pT^LI#q}Wy*@iQ0j{H3q@oCXR$5Nu z@n}*YVI5nDACZLYUKF*jj^uU!q>((MWgHPbLKZlg(zcjwV~sYb!IY+Zf*Z{uq(&kn zGibB@Fq{&WKgqz?%YcZcC`V!732k0x(v4c=^3>5rZOj}aTz|N@y@cT66*Bsh&nyYA9XpRM$l|4 zzpj=<2w##x2rm@RPR|gaYWe!b@4*}iLMn=bI@afBInalAhDmrl!_m7(J)R%c=}x}; z0W$9XK054RX|nmvsLf1uZ^a1b1~Aap253Kg|0mC-hoqD<~^PO*F7-^lGvuV4A&>>$yX$%~L(V1p_acP!(sG7!=T2>s6Y6;(hgeP`hzCtIvQSeEG@qqn}|a&_l!#CtuGVBjg+58#VZPxb0j^79z9} zA~feNrRpE-)^6e(TBOaP{i%pT_d`mdMNEp1$;Sxpt9!kGB8o2;f~N8kJD|7YJR0@c z&)D3YoXfYAF`gvhbpDKR0U4kE{A6qR0+n75__%L7vbB@HJEX!9h?X)?@8|yfXUMnT zS;p;C7E;-~pEI4b*LRKZhY@b5Bn)(BVL%&UKvVWkxf<&S_0?fP#3T5FBTA!WHO903Z6N@7MZfhZ&>+6lS|x6H z%$wF0LrO(;K8AX3pi4ywn4vFR|1(pdgy&z*Z3J^I7_F0V+PN^m6?NME^aO9bC-vwwf(b%>J@RAeTJM006JYc^XoOawAE|CGGHUN@2?b7_R<+4Oq9;cK^j(?rnxl`uU z3%}6;jQ~Ht{(L*P9p6P?7TBioR6jFZl3<%+a4CxEiavGsrk`Wl<}e92-vqq6fx7t$ zi}`hK58M?B_rFu0n=zanr>v< zp1M0FO<<`PgQCnZ)n;V#u`}L*rR**#H9pCj>|+3)_oDkZqfWb_(AewRnKoovJq%;W zp(yW0T_5k&Zba5kte2&qkTfCMEjUM<$nYKy$oGJ6R6&M^aqAg4Y6BdZ@2|XTB7FUW zs*gg-sE^N_Kr5q~dOr2t2yX$Ie{`F~!304ll)ve;x&m)ez&lh658Va z7&3>U$Sgst#UgG*cYdbZv?d4`{P$=yw?2sXI|Ss6%zMb+rC{6%c#aEe<|=Q(=2Qc;Co02K+f{z-5=EEd@Fx{ zGPlJ65;b2e_^cf{sgIYF)Kg4h)VOxn$uY*;5iS_utSTB{VO%^zPHiA(1v$p<-#ln@ zj}ak4)K$ebA4ubM+}>OI%woY!kms{E1&-UcwMz230P^dJ#mLn5{M;_$HHo}#K`y@; zua-k2%UI7H^yBpyoCTo74z*47x%@nt*v*qg>_l{O=swweQyZLI35x+10xVF2D?z8@ z<}q~A20A@_YgBnBwCwpFJ!e2g{7w||^s!No(|EdWY5nZT?PUSa@8&khL+o>{{3Yyj zv+#*FsC{nIw5sG-lrF3MD;bMW``m1&Ks6rVPcz(B#k?DDE}aD`jRY!YpJVfG6wSLS zuONjdSDTUs(~>J?Hn`%A{B#E-lR9^5ecNM}fn)zB1hiY#CLVx-D$=t^?OvQkPQ%SU zFhd&OZB+xT$YbV%Uh_E@a~PL$tlHhy^Om2$ss=+ogi#gYv1?a7$~Vq?=$arS|YaH0(lnL zs){9I7r6s0wrC@^Fn8TU)?2%x$!0(Ea5e80alSTPJ$;)lF-sA9P=QNU($UY%Kej~aIIU*mBQ{-!TNI@>!J8%v$n3%4j6KY0XhagYG~zE_{M4G>|jpkqJJjW;;6gK9iIvC(FP7#HN^(1@`z z$LXgt`N^!r3lNo9U-evr5@U%m02w$jHvaSFk4)sjzmvvh01?k=0?B!uTz z0$eu0B~=ixIUH+EXoP@OntfAg`Fm)CG#Y!O8!)8+qbyl`=?NVQD({4MmXLSyi(wJ9 zdI*{=qF(dYdo?C!i@q{O$+g}wzD9W(QhnyOM$Zr4;8p!Zc`WYUu7L(Q$3iab`$WQgI+XmZ!heOq@tf#iDV9*B_HfO1$6*8edH z6aON;95zcaspdG1R$1TynI14#6{A%Gbq!c}(MEWw1Rv`Co>VpbztXLX`nhdr@j}_w z#j4)8gSI$eW_bRwt?_#VoK5`K)ojSONL6p*;_Z77`uvA44bdwI0RiOFLndDCMUA)l zzIt+;$yV3|B=q?fIQ9A7H^CGWOjJdmFO$n=aI6hDX2#o!$N>JI*La{X;h-O|dG?XQ zM9+hhX@v=83m*H`hlusRtnwcz$SdHtyqWMjDz7Nn6-GwZ6$+8n;dpE|(%nwxp^au( zZ{~Y@B}FACIj^Y5u92LM|7fOb#&IP_xQ?a&vVBGFhwVw^KK9<@O1|d2ODv6v#_Yr=AW<*OFN3lOCn>67%ppIHqT&>*y_W?OO);&X@EP*oV>J0vDP-a0JFzPQ4T;z-)+6lYeMVTBo% zsv^!D0Xt81-R{EMI(9NR1%;G!AvAE|riLpt{&3l-W36{_@{D&3pYv&Ij!f`~GE zUrWDEC>3t}YdA`UCG36tn5`$Fww;ZHX)_Ou$WluFgr&@?R}l5R3Qrv zU!<^GDMDOu=IiJ&0{CT3xO*FJD|7elUWVhjclrtx&n4V_ucOC@y8CX<;^!5Rb^|q$ zlQmE!NBf-IecKH1r2)36LR|hj*M!9hZN!QHr(8|@S(@<0m8F+q$0yOKq~VFD$K5Cr z(CFmws4H-gs)Y%bDq57u$43q3ZGcE&<3@d6{!CnR*#@h31fyt#Zl?V1utblN*RD%p zHU;KIIC7|@+l-29oV+esMwn-W*{WFj(1x4C;7A*A^vHV=l~U9X4hGS1&UoRQ8OM$Y z)lTcU&2yp1QwpZapiEaI1ckDFwfO}$h~7eC>tZfWOi?FHUXH|Y*%Pw5f|6AWyK_&3 zIzlyljnjD9EP#0cv(+#Moy+A;FsRc8)K!|TPwPcbc7NqlB)1ihi40k^#tb-<(FW>) z_x)=z?<-i)9iICR_sGf;D1BG2w|BujR3H9Biy(4_-dqkPmJB0yM*%VJf}z+9$IWm= z6$*xB+#v>K8i6wAIAlsW*-AND0ow6<3Pal3x}9mQL~8LFHDX%(@M9#2)LK;4UB1N2 T`iQePmSM9u0>Uo!`P~116N*K7 literal 0 HcmV?d00001 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/fake_tf_record_data/train-00005-of-00008 b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/fake_tf_record_data/train-00005-of-00008 new file mode 100644 index 0000000000000000000000000000000000000000..5f4e651519673b3b61726b5a3b0d21a8c962deb5 GIT binary patch literal 98266 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 jsItF!yqJo;(VZncRCO^S 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 DV5*s% literal 0 HcmV?d00001 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/images/black_image.jpg b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/images/black_image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..312873be3bd305bfb5962896ea8ae507ca44b572 GIT binary patch literal 825 zcmex=kx|JhscGpMnOVgprDf$6l~v6xt!?ccon4bAPnkMx`iz;g7A;<~ zblLJ1D_3pWyk+aQ?K^hvI&}ER(PPI?oIG{u@|COCuHU$M>*1rvPo6$|{^I4UkDoq& z`TFhqkDtFl{$gZch6fqMV`%;o1p14Kg@u`g9po=Yrg9)=7Gz;nG-MNU3}jC%6jm~7 z&~|C<06)cvvm literal 0 HcmV?d00001 diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/images/white_image.jpg b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/test_data/images/white_image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ad96f25af79ca0d683642c3dbef1049cc7061f84 GIT binary patch literal 823 zcmex=kx|JhscGpMnOVgprDf$6l~v6xt!?ccon4bAPnkMx`iz;g7A;<~ zblLJ1D_3pWyk+aQ?K^hvI&}ER(PPI?oIG{u@|COCuHU$M>*1rvPo6$|{^I4UkDoq& z`TFhqkDtFl{$gZch6fqMV`%;o1p14Kg@u`g9po=Yrg9)=7Gz;nG-MNU3}jC%6jm~7 z= 1 + return outputs + + +def get_evaluation_outputs_from_logs(logs): + """Returns the top 1 and 5 accuracies by parsing the logs of an eval run. + + Args: + logs: A list of strings, each which is a line from the standard output of + tf_cnn_benchmarks from evaluation. Only lines in the form: + Accuracy @ 1 = 0.5000 Accuracy @ 5 = 1.0000 [80 examples] + is parsed. + Returns: + A list of EvalOutputs. Normally this list only has one EvalOutput, but can + contain multiple if training is done and + --eval_during_training_every_n_steps is specified. + """ + eval_outputs = [] + for log in logs: + if 'Accuracy @ ' in log: + # Example log: + # Accuracy @ 1 = 0.5000 Accuracy @ 5 = 1.0000 [80 examples] + parts = log.split() + assert len(parts) == 12 + top_1_accuracy = float(parts[4]) + top_5_accuracy = float(parts[9]) + eval_outputs.append(EvalOutput(top_1_accuracy, top_5_accuracy)) + assert eval_outputs + return eval_outputs + + +def check_training_outputs_are_reasonable(testcase, training_outputs, + print_training_accuracy, + max_final_loss=10., + previous_final_loss=None): + """Checks the outputs from training a model are reasonable. + + An assert is failed if the outputs are not reasonable. The final top-1 and + top-5 accuracies are asserted to be 1, and so the dataset used to train should + be trivial to learn. For example, the dataset could consist of a black image + with label 0 and a white image with label 1. + + Args: + testcase: A tf.test.TestCase used for assertions. + training_outputs: A list of TrainingOutputs, as returned from + get_training_outputs_from_logs(). + print_training_accuracy: Whether training accuracies were printed and stored + in training_outputs. + max_final_loss: The loss of the final training output is asserted to be at + most this value. + previous_final_loss: If training was resumed from a checkpoint, the loss of + the final step from the previous training run that saved the checkpoint. + """ + if previous_final_loss is not None: + # Ensure the loss hasn't raised significantly from the final loss of the + # previous training run. + testcase.assertLessEqual(training_outputs[0].loss, + previous_final_loss * 1.01) + for output in training_outputs: + testcase.assertLessEqual(output.loss, 100.) + last_output = training_outputs[-1] + if print_training_accuracy: + testcase.assertEqual(last_output.top_1_accuracy, 1.0) + testcase.assertEqual(last_output.top_5_accuracy, 1.0) + if max_final_loss is not None: + testcase.assertLessEqual(last_output.loss, max_final_loss) + + +def train_and_eval(testcase, + run_fn, + params, + check_output_values, + max_final_loss=10., + skip=None): + """Trains a model then evaluates it. + + This function should be used to verify training and evaluating + BenchmarkCNN works without crashing and that it outputs reasonable + values. BenchmarkCNN will be run three times. First, it will train a + model from scratch, saving a checkpoint. Second, it will load the checkpoint + to continue training. Finally, it evaluates based on the loaded checkpoint. + + Args: + testcase: A tf.test.TestCase used for assertions. + run_fn: Must run `BenchmarkCNN` exactly once. BenchmarkCNN is + never used directly, but instead is only run through `run_fn`. `run_fn` + has the signature (run_type, inner_params) -> output_list, where: + * run_type is a string indicating how BenchmarkCNN will be run. + Either 'InitialTraining', 'TrainingFromCheckpoint' or 'Evaluation'. + * inner_params is the params BenchmarkCNN should be run with. + * output_list[i] is a list of lines from the ith worker's stdout. + params: The params BenchmarkCNN will be run with. + Will be passed to `run_fn` slightly modified in order to run with both + training and evaluation. + check_output_values: Whether the outputs of the workers, such as training + accuracy, should be checked to make sure their values are reasonable. + Fails an assert on `testcase` if a check fails. + max_final_loss: The loss of the final training output is asserted to be at + most this value for both training runs. + skip: If 'eval', evaluation is not done. if + 'eval_and_train_from_checkpoint', evaluation and training from a + checkpoint are both not done. + """ + + assert not skip or skip in {'eval', 'eval_and_train_from_checkpoint'} + + # Part 1: Train from scratch. + tf.logging.info('Training model from scratch') + print_training_accuracy = (params.print_training_accuracy or + params.forward_only) + initial_train_logs = run_fn('InitialTraining', params) + testcase.assertGreaterEqual(len(initial_train_logs), 1) + for lines in initial_train_logs: + initial_train_outputs = get_training_outputs_from_logs( + lines, print_training_accuracy) + if params.cross_replica_sync and params.batch_group_size == 1: + testcase.assertEqual(len(initial_train_outputs), params.num_batches) + if check_output_values: + check_training_outputs_are_reasonable(testcase, initial_train_outputs, + print_training_accuracy, + max_final_loss=max_final_loss) + if params.train_dir is not None: + train_dir_entries = set(os.listdir(params.train_dir)) + testcase.assertGreater(len(train_dir_entries), 0) + else: + train_dir_entries = None + + if skip == 'eval_and_train_from_checkpoint': + return + + # Part 2: Train from the loaded checkpoint. + testcase.assertIsNotNone(train_dir_entries) + tf.logging.info('Training model from loaded checkpoint') + # Run for same number of batches as before. + params = params._replace(num_batches=params.num_batches * 2) + train_logs_from_ckpt = run_fn('TrainingFromCheckpoint', params) + testcase.assertGreaterEqual(len(train_logs_from_ckpt), 1) + for lines in train_logs_from_ckpt: + train_outputs_from_ckpt = get_training_outputs_from_logs( + lines, print_training_accuracy) + if params.cross_replica_sync and params.batch_group_size == 1: + testcase.assertEqual(len(train_outputs_from_ckpt), + params.num_batches // 2 - params.num_warmup_batches) + if check_output_values: + check_training_outputs_are_reasonable( + testcase, train_outputs_from_ckpt, print_training_accuracy, + max_final_loss=max_final_loss, + previous_final_loss=initial_train_outputs[-1].loss) + # Ensure a new checkpoint was written out. + testcase.assertNotEqual(train_dir_entries, set(os.listdir(params.train_dir))) + + if skip == 'eval': + return + + # Part 3: Evaluate from the loaded checkpoint. + tf.logging.info('Evaluating model from checkpoint') + params = params._replace(num_batches=params.num_batches // 2, eval=True) + eval_logs = run_fn('Evaluation', params) + testcase.assertGreaterEqual(len(eval_logs), 1) + for lines in eval_logs: + eval_outputs = get_evaluation_outputs_from_logs(lines) + assert len(eval_outputs) == 1 + top_1_accuracy, top_5_accuracy = eval_outputs[0] + if check_output_values: + testcase.assertEqual(top_1_accuracy, 1.0) + testcase.assertEqual(top_5_accuracy, 1.0) + + +def get_temp_dir(dir_name): + dir_path = os.path.join(test.get_temp_dir(), dir_name) + os.mkdir(dir_path) + return dir_path + + +def create_black_and_white_images(): + dir_path = get_temp_dir('black_and_white_images') + tfrecord_image_generator.write_black_and_white_tfrecord_data(dir_path, + num_classes=1) + return dir_path + + +def get_params(train_dir_name): + """Returns params that can be used to train.""" + params = benchmark_cnn.make_params( + batch_size=2, + display_every=1, + init_learning_rate=0.005, + model='trivial', + num_batches=20, + num_gpus=2, + num_warmup_batches=5, + optimizer='sgd', + print_training_accuracy=True, + train_dir=get_temp_dir(train_dir_name), + variable_update='parameter_server', + weight_decay=0, + distortions=True, + distort_color_in_yiq=False) + return benchmark_cnn.set_default_param_values_and_env_vars(params) + + +def get_var_update_params(): + """Returns params that are used when testing variable updates.""" + params = benchmark_cnn.make_params( + batch_size=2, + model='test_model', + num_gpus=2, + display_every=1, + num_warmup_batches=0, + num_batches=4, + weight_decay=2 ** -4, + init_learning_rate=2 ** -4, + optimizer='sgd') + return benchmark_cnn.set_default_param_values_and_env_vars(params) + + +def get_fake_var_update_inputs(): + """Returns fake input 1x1 images to use in variable update tests.""" + # BenchmarkCNN divides by 127.5 then subtracts 1.0 from the images, so after + # that, the images will be -1., 0., 1., ..., 14. + return np.resize(127.5 * np.array(range(16)), (16, 1, 1, 1)) + + +def _worker_batches_in_numpy_array(numpy_inputs, batch_size, shift_ratio): + """Yields batches from a numpy array, for a single worker.""" + numpy_inputs = cnn_util.roll_numpy_batches(numpy_inputs, batch_size, + shift_ratio) + i = 0 + total_batches = numpy_inputs.shape[0] + assert total_batches % batch_size == 0 + while True: + yield numpy_inputs[i:i + batch_size, ...] + i = (i + batch_size) % total_batches + + +def manually_compute_losses(numpy_inputs, inputs_placeholder, loss, num_workers, + params): + """Manually compute the losses each worker should report in tf_cnn_benchmarks. + + This function essentially simulates tf_cnn_benchmarks, computing what the loss + of each worker should be. The caller should create a model, that takes in + images from `inputs_placeholder`, a tf.placeholder, and computes `loss`. + + This function, and all ops passed to this function, must be run under a + tf.device('cpu:0') context manager. + + Non-SGD optimizers are not supported with multiple workers. + + Args: + numpy_inputs: A Numpy array to use as the input images. + inputs_placeholder: A tf.placeholder tensor, where input images can be fed + into. + loss: A scalar tensor representing the loss of the model, which is obtained + from the input images in inputs_placeholder. + num_workers: How many workers should be simulated. + params: Params tuple. This doesn't have to have information about the + distributed cluster, such as --num_workers, as num_workers is passed in + separately. + + Returns: + A list of list of losses. return_value[i][j] is the loss of the ith worker + after the jth step. + """ + batch_size = params.batch_size * params.num_gpus + assert numpy_inputs.shape[0] % (num_workers * batch_size) == 0 + l2_loss = tf.add_n([tf.nn.l2_loss(x) for x in tf.trainable_variables()]) + total_loss = loss + params.weight_decay * l2_loss + reported_loss = (loss if params.loss_type_to_report == 'base_loss' + else total_loss) + gradient_multiplier = 1 + if params.variable_update in ('replicated', 'distributed_all_reduce'): + # In certain variable updates, tf_cnn_benchmarks add the gradients of the + # GPUs instead of taking their mean, making the gradients effectively + # params.num_gpu times higher. + # TODO(b/62722498): Make all variable updates consistent. + gradient_multiplier = params.num_gpus + + opt = benchmark_cnn.get_optimizer(params, params.init_learning_rate) + grad_vars = opt.compute_gradients( + total_loss, grad_loss=tf.constant(gradient_multiplier, dtype=tf.float32)) + grads = [g for g, _ in grad_vars] + # We apply gradients from a placeholder. That way, we can first compute the + # gradients from each worker, then afterwards apply them one by one by feeding + # them into the placeholder. + placeholder_grad_vars = [(tf.placeholder(g.dtype, g.shape), v) + for g, v in grad_vars] + placeholder_grads = [g for g, _ in placeholder_grad_vars] + apply_grads_op = opt.apply_gradients(placeholder_grad_vars) + + batch_iterators = [_worker_batches_in_numpy_array(numpy_inputs, batch_size, + shift_ratio=i / num_workers) + for i in range(num_workers)] + # Set the GPU count to 0, to avoid taking all the GPU memory. Unfortunately, + # doing so still takes up about ~1GB for some reason. + config = tf.ConfigProto(device_count={'GPU': 0}) + config.graph_options.rewrite_options.pin_to_host_optimization = ( + rewriter_config_pb2.RewriterConfig.OFF) + with tf.Session(config=config) as sess: + sess.run(tf.global_variables_initializer()) + losses = [[] for _ in range(num_workers)] + for i in range(params.num_batches): + computed_grads = [] + for j in range(num_workers): + batch_feed = next(batch_iterators[j]) + batch_feed = batch_feed / 127.5 - 1 + worker_loss, worker_grads = sess.run((reported_loss, grads), + {inputs_placeholder: batch_feed}) + losses[j].append(worker_loss) + computed_grads.append(worker_grads) + for worker_grads in computed_grads: + # TODO(reedwm): With multiple workers, applying the gradients + # sequentially per worker is not equivalent to what tf_cnn_benchmarks + # does when the optmizer is not SGD. Therefore, this currently does not + # work currently when num_workers > 1 and params.optimizer != 'sgd'. + feed_dict = dict(zip(placeholder_grads, worker_grads)) + sess.run(apply_grads_op, feed_dict) + return losses + + +class TestCNNModel(model.CNNModel): + """A simple model used for testing. + + The input is a 1-channel 1x1 image, consisting of a single number. The model + has two scalar variables: A and B, initialized to 1 and 2 respectively. Given + an image x, the loss is defined as: + + loss = x * A * B + """ + + def __init__(self): + super(TestCNNModel, self).__init__( + 'test_cnn_model', image_size=1, batch_size=1, learning_rate=1) + self.depth = 1 + + VAR_A_INITIAL_VALUE = 1. + VAR_B_INITIAL_VALUE = 2. + + def add_inference(self, cnn): + # This model only supports 1x1 images with 1 channel + assert cnn.top_layer.shape[1:] == (1, 1, 1) + # Multiply by variable A. + with tf.name_scope('mult_by_var_A'): + cnn.conv(1, 1, 1, 1, 1, use_batch_norm=None, activation=None, bias=None, + kernel_initializer=tf.constant_initializer( + self.VAR_A_INITIAL_VALUE)) + # Multiply by variable B. + with tf.name_scope('mult_by_var_B'): + cnn.conv(1, 1, 1, 1, 1, use_batch_norm=None, activation=None, bias=None, + kernel_initializer=tf.constant_initializer( + self.VAR_B_INITIAL_VALUE)) + with tf.name_scope('reshape_to_scalar'): + cnn.reshape([-1, 1]) + + def skip_final_affine_layer(self): + return True + + def loss_function(self, inputs, build_network_result): + del inputs + return tf.reduce_mean(build_network_result.logits) + + def manually_compute_losses(self, inputs, num_workers, params): + with tf.Graph().as_default(), tf.device('/cpu:0'): + a = tf.Variable(self.VAR_A_INITIAL_VALUE, name='A') + b = tf.Variable(self.VAR_B_INITIAL_VALUE, name='B') + inputs_placeholder = tf.placeholder(tf.float32, + (None, 1, 1, 1), + name='inputs_placeholder') + inputs_reshaped = tf.reshape(inputs_placeholder, (-1, 1)) + loss = self.loss_function( + None, + model.BuildNetworkResult(logits=inputs_reshaped * a * b, + extra_info=None)) + return manually_compute_losses(inputs, inputs_placeholder, loss, + num_workers, params) + + def accuracy_function(self, inputs, logits): + del inputs + # Let the accuracy be the same as the loss function. + return {'top_1_accuracy': logits, 'top_5_accuracy': logits} + + +class TestDataSet(datasets.ImageDataset): + """A Dataset consisting of 1x1 images with a depth of 1.""" + + def __init__(self, height=1, width=1, depth=1): + super(TestDataSet, self).__init__('test_dataset', height=height, + width=width, depth=depth, data_dir=None, + queue_runner_required=True, num_classes=1) + + def num_examples_per_epoch(self, subset='train'): + del subset + return 1 + + def get_input_preprocessor(self, input_preprocessor='default'): + return preprocessing.TestImagePreprocessor + + def use_synthetic_gpu_inputs(self): + return False diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/tf_cnn_benchmarks.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/tf_cnn_benchmarks.py new file mode 100644 index 00000000..29702aa1 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/tf_cnn_benchmarks.py @@ -0,0 +1,73 @@ +# 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. +# ============================================================================== + +"""Benchmark script for TensorFlow. + +See the README for more information. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from absl import app +from absl import flags as absl_flags +import tensorflow.compat.v1 as tf + +import benchmark_cnn +import cnn_util +import flags +import mlperf +from cnn_util import log_fn + + +flags.define_flags() +for name in flags.param_specs.keys(): + absl_flags.declare_key_flag(name) + +absl_flags.DEFINE_boolean( + 'ml_perf_compliance_logging', False, + 'Print logs required to be compliant with MLPerf. If set, must clone the ' + 'MLPerf training repo https://github.com/mlperf/training and add ' + 'https://github.com/mlperf/training/tree/master/compliance to the ' + 'PYTHONPATH') + + +def main(positional_arguments): + # Command-line arguments like '--distortions False' are equivalent to + # '--distortions=True False', where False is a positional argument. To prevent + # this from silently running with distortions, we do not allow positional + # arguments. + assert len(positional_arguments) >= 1 + if len(positional_arguments) > 1: + raise ValueError('Received unknown positional arguments: %s' + % positional_arguments[1:]) + + params = benchmark_cnn.make_params_from_flags() + with mlperf.mlperf_logger(absl_flags.FLAGS.ml_perf_compliance_logging, + params.model): + params = benchmark_cnn.setup(params) + bench = benchmark_cnn.BenchmarkCNN(params) + + tfversion = cnn_util.tensorflow_version_tuple() + log_fn('TensorFlow: %i.%i' % (tfversion[0], tfversion[1])) + + bench.print_info() + bench.run() + + +if __name__ == '__main__': + tf.disable_v2_behavior() + app.run(main) # Raises error on invalid flags, unlike tf.app.run() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/variable_mgr.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/variable_mgr.py new file mode 100644 index 00000000..119b0278 --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/variable_mgr.py @@ -0,0 +1,839 @@ +# 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. +# ============================================================================== +"""Defines VariableMgr and subclasses used to manage variables. + +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import contextlib +import re + +import tensorflow.compat.v1 as tf + +import allreduce +import batch_allreduce +import variable_mgr_util + + +class VariableMgr(object): + """Abstract superclass for class used by BenchmarkCNN to control variables. + + Functions on this class are used to control how variables are created and + managed, and how gradients are computed and applied. + """ + + def __init__(self, benchmark_cnn): + self.benchmark_cnn = benchmark_cnn + self.staging_delta_ops = [] + self.use_resource_vars = benchmark_cnn.params.use_resource_vars + + # A variable for automatic loss scaling. + self.grad_has_inf_nan = None + + self._reuse_vars = False + + def each_tower_has_variables(self): + """Returns True if each GPU tower of the model has separate variables.""" + assert False, 'Must be implemented in subclass' + + def supports_staged_vars(self): + """Whether staged variable management is supported.""" + return False + + def create_outer_variable_scope(self, device_num): + """Create the tf.variable_scope around all model graph operations.""" + del device_num # unused by this implementation + assert False, 'Must be implemented in subclass' + + def preprocess_device_grads(self, device_grads): + """Preprocess the device gradients prior to applying them. + + Args: + device_grads: List of lists of (gradient, variable) tuples. + device_grads[t][g] = (gradient, variable), where t is the index of the + tower and g is the index of the gradient-variable pair. + + Returns: a tuple of (apply_gradients_devices, gradient_state). + gradient_state is an opaque structure that should be passed to + get_gradients_to_apply() and append_apply_gradients_ops() (in that order). + apply_gradients_devices is a list of devices where the gradients will be + applied with get_gradients_to_apply() and append_apply_gradients_ops(). + """ + del device_grads # unused by this implementation + assert False, 'Must be implemented in subclass' + + def get_gradients_to_apply(self, device_num, gradient_state): + """Returns the [(gradient, variable)] list to apply for device_num. + + Args: + device_num: indexes into apply_gradients_devices, which was returned by an + earlier call to preprocess_device_grads. + gradient_state: from previous call to apply_gradients_devices. + """ + del device_num, gradient_state # unused by this implementation + assert False, 'Must be implemented in subclass' + + def append_apply_gradients_ops(self, gradient_state, opt, grads, training_ops, + loss_scale_params): + """Adds training ops for grads to 'training_ops'. + + + + Args: + gradient_state: from previous call to apply_gradients_devices. + opt: the underlying optimizer + grads: [(grad, var)] to apply + training_ops: list to which to add ops + loss_scale_params: parameters for loss scaling. + """ + del gradient_state # unused by this implementation + + def get_apply_gradients_ops_func(): + """Returns the apply_gradients op.""" + return [opt.apply_gradients(grads)] + + variable_mgr_util.append_gradients_with_loss_scale( + training_ops, get_apply_gradients_ops_func, loss_scale_params, + self.grad_has_inf_nan) + + def get_post_init_ops(self): + """Returns ops that should run post-initialization.""" + return [] + + def get_devices(self): + """Returns devices to use for computation; includes replica selection.""" + assert False, 'Must be implemented in subclass' + + def savable_variables(self): + """Returns a list/dict of savable variables to pass to tf.train.Saver.""" + return tf.global_variables() + + def trainable_variables_on_device(self, + rel_device_num, + abs_device_num, + writable=False): + """Return the set of trainable variables on device. + + Args: + rel_device_num: local worker device index. + abs_device_num: global graph device index. + writable: whether to get a reference to the underlying variable. + + Returns: + The set of trainable variables on the specified device. + """ + del rel_device_num, writable + if self.each_tower_has_variables(): + params = [ + v for v in tf.trainable_variables() + if v.name.startswith('v%s/' % abs_device_num) + ] + else: + params = tf.trainable_variables() + return params + + @contextlib.contextmanager + def reuse_variables(self): + """Context manager that causes variables requested to be reused. + + Variables requested under this context manager must already exist, and will + be reused instead of being created again. This should be used if the + evaluation model is being built after the training model has already been + built. This is because the evaluation model should reuse variables from the + training model. + + Yields: + Nothing. + """ + old_reuse_vars = self._reuse_vars + try: + self._reuse_vars = True + yield + finally: + self._reuse_vars = old_reuse_vars + + +class VariableMgrIndependent(VariableMgr): + """VariableMgr that implements the --independent mode for local jobs. + + Each GPU has its own copy of the variables, and gradients are + not shared between towers. This can be used to check + performance when no data is moved between GPUs. + """ + + def each_tower_has_variables(self): + return True + + def create_outer_variable_scope(self, device_num): + return tf.variable_scope('v%s' % device_num, reuse=self._reuse_vars, + use_resource=self.use_resource_vars) + + def preprocess_device_grads(self, device_grads): + return (self.benchmark_cnn.devices, device_grads) + + def get_gradients_to_apply(self, device_num, gradient_state): + device_grads = gradient_state + tower_grad = device_grads[device_num] + + if self.benchmark_cnn.enable_auto_loss_scale and device_num == 0: + # Since we don't aggregate variables in --independent mode, we cannot tell + # if there are NaNs on all GPUs. So we arbitrarily choose to only check + # NaNs on the first GPU. + has_inf_nan_list = [] + for grad, _ in tower_grad: + has_inf_nan_list.append(tf.reduce_all(tf.is_finite(grad))) + self.grad_has_inf_nan = tf.logical_not(tf.reduce_all(has_inf_nan_list)) + + return tower_grad + + def get_devices(self): + return self.benchmark_cnn.raw_devices + + +class VariableMgrLocalFetchFromPS(VariableMgr): + """VariableMgr that implements the --parameter_server mode for local jobs. + + Variables are stored on a parameter server. For each step, each tower gets + a copy of the variables from the parameter server, and sends its gradients + to the param server. + """ + + def each_tower_has_variables(self): + return False + + def create_outer_variable_scope(self, device_num): + return tf.variable_scope('v', reuse=bool(device_num) or self._reuse_vars, + use_resource=self.use_resource_vars) + + def preprocess_device_grads(self, device_grads): + return ([self.benchmark_cnn.param_server_device], device_grads) + + def get_gradients_to_apply(self, device_num, gradient_state): + assert device_num == 0 + device_grads = gradient_state + agg_grads, self.grad_has_inf_nan = ( + variable_mgr_util. + aggregate_gradients_using_copy_with_variable_colocation( + device_grads, + use_mean=True, + check_inf_nan=self.benchmark_cnn.enable_auto_loss_scale)) + return agg_grads + + def get_devices(self): + raw_devices = self.benchmark_cnn.raw_devices + if self.benchmark_cnn.local_parameter_device_flag == 'gpu': + return [ + variable_mgr_util.ParamServerDeviceSetter(d, raw_devices) + for d in raw_devices + ] + else: + return [ + tf.train.replica_device_setter( + worker_device=d, + ps_device=self.benchmark_cnn.param_server_device, + ps_tasks=1) for d in raw_devices + ] + + +class VariableMgrLocalFetchFromStagedPS(VariableMgrLocalFetchFromPS): + """Implements fetching a local variable through staging buffers. + """ + + def __init__(self, benchmark_cnn): + super(VariableMgrLocalFetchFromStagedPS, self).__init__(benchmark_cnn) + # A data structure to track where the variables are used on each device. + # Indexed by device_num and var_name, each entry stores the "put" and "get" + # ops used for that variable on that device: + # staging_vars_on_devices[device_num][var_name] == (put_op, get_op) + self.staging_vars_on_devices = [ + dict() for _ in self.benchmark_cnn.raw_devices + ] + + def supports_staged_vars(self): + return True + + def create_outer_variable_scope(self, device_num): + self._custom_getter = variable_mgr_util.StagedVariableGetter( + device_num, self.benchmark_cnn.raw_devices, None, self) + return tf.variable_scope( + 'v', reuse=bool(device_num) or self._reuse_vars, + custom_getter=self._custom_getter, use_resource=self.use_resource_vars) + + def trainable_variables_on_device(self, + rel_device_num, + abs_device_num, + writable=False): + return self._custom_getter.trainable_variables_on_device( + rel_device_num, abs_device_num, writable=writable) + + +class VariableMgrLocalReplicated(VariableMgr): + """VariableMgr that implements the --replicated mode for local jobs. + + Each GPU has its own copy of the variables. To apply gradients, + either a local all-reduce algorithm is applied or a regular + cross-device aggregation is used to replicate the combined + gradients to all towers. + """ + + def __init__(self, benchmark_cnn, all_reduce_spec, + agg_small_grads_max_bytes, agg_small_grads_max_group, + allreduce_merge_scope): + super(VariableMgrLocalReplicated, self).__init__(benchmark_cnn) + if all_reduce_spec: + spec = allreduce.parse_all_reduce_spec(all_reduce_spec) + if len(spec) != 1: + raise ValueError( + 'replicated mode does not support hybrid all-reduce strategies') + self._all_reduce_spec = spec[0] + else: + self._all_reduce_spec = None + self._agg_small_grads_max_bytes = agg_small_grads_max_bytes + self._agg_small_grads_max_group = agg_small_grads_max_group + self._warmup_ops = [] + self._allreduce_merge_scope = allreduce_merge_scope + self._gradient_put_ops = None + + def each_tower_has_variables(self): + return True + + def create_outer_variable_scope(self, device_num): + return tf.variable_scope('v%s' % device_num, reuse=self._reuse_vars, + use_resource=self.use_resource_vars) + + def preprocess_device_grads(self, device_grads): + compact_grads = (self.benchmark_cnn.params.use_fp16 and + self.benchmark_cnn.params.compact_gradient_transfer) + defer_grads = (self.benchmark_cnn.params.variable_consistency == 'relaxed') + + grads_to_reduce = [[g for g, _ in grad_vars] for grad_vars in device_grads] + algorithm = batch_allreduce.algorithm_from_params(self.benchmark_cnn.params) + reduced_grads, self._warmup_ops = algorithm.batch_all_reduce( + grads_to_reduce, self.benchmark_cnn.params.gradient_repacking, + compact_grads, defer_grads, self.benchmark_cnn.params.xla_compile) + if self.benchmark_cnn.enable_auto_loss_scale: + # Check for infs or nans + is_finite_list = [] + with tf.name_scope('check_for_inf_and_nan'): + for tower_grads in reduced_grads: + with tf.colocate_with(tower_grads[0]): + # TODO(tanmingxing): Create fused op that takes in a list of tensors + # as input and returns scalar boolean True if there are any + # infs/nans. + is_finite_list.append(tf.reduce_all( + [tf.reduce_all(tf.is_finite(g)) for g in tower_grads])) + self.grad_has_inf_nan = tf.logical_not(tf.reduce_all(is_finite_list)) + reduced_device_grads = [[ + (g, v) for g, (_, v) in zip(grads, grad_vars) + ] for grads, grad_vars in zip(reduced_grads, device_grads)] + return self.benchmark_cnn.devices, reduced_device_grads + + def get_gradients_to_apply(self, device_num, gradient_state): + device_grads = gradient_state + return device_grads[device_num] + + def get_post_init_ops(self): + # Copy initialized values for variables on GPU 0 to other GPUs. + global_vars = tf.global_variables() + var_by_name = dict([(v.name, v) for v in global_vars]) + post_init_ops = [] + for v in global_vars: + split_name = v.name.split('/') + # TODO(b/62630508): use more specific prefix than v or v0. + if split_name[0] == 'v0' or not v.name.startswith('v'): + continue + split_name[0] = 'v0' + copy_from = var_by_name['/'.join(split_name)] + post_init_ops.append(v.assign(copy_from.read_value())) + post_init_ops += self._warmup_ops + return post_init_ops + + def savable_variables(self): + """Return the set of variables used for saving/loading the model.""" + params = [] + for v in tf.global_variables(): + split_name = v.name.split('/') + if split_name[0] == 'v0' or not v.name.startswith('v'): + params.append(v) + return params + + def get_devices(self): + return self.benchmark_cnn.raw_devices + + +class VariableMgrDistributedAllReduce(VariableMgr): + """VariableMgr that implements the --distributed_all_reduce mode. + + Each GPU has its own copy of the variables. To apply gradients, + the specified all-reduce algorithm is used to reduce the gradients + and replicate the final value to all GPUs. + """ + + def __init__(self, benchmark_cnn, all_reduce_spec, job_name, + num_workers, agg_small_grads_max_bytes, + agg_small_grads_max_group, allreduce_merge_scope): + super(VariableMgrDistributedAllReduce, self).__init__(benchmark_cnn) + if not all_reduce_spec: + raise ValueError( + 'distributed_all_reduce requires a non-empty all_reduce_spec') + self._all_reduce_spec = allreduce.parse_all_reduce_spec(all_reduce_spec) + self._all_reduce_device_prefixes = ( + allreduce.build_all_reduce_device_prefixes(job_name, num_workers)) + self._num_workers = num_workers + self._agg_small_grads_max_bytes = agg_small_grads_max_bytes + self._agg_small_grads_max_group = agg_small_grads_max_group + self._allreduce_merge_scope = allreduce_merge_scope + if not self._all_reduce_spec: + raise ValueError('all_reduce_spec must be specified') + self._single_session = True + + def each_tower_has_variables(self): + return True + + def create_outer_variable_scope(self, device_num): + """Create a scope for the named device. + + Args: + device_num: index of device for variable scope. (Note that + device_num spans all processes in cluster since a single global + graph is used.) + + Returns: + the requested variable_scope + """ + return tf.variable_scope('v%s' % device_num, reuse=self._reuse_vars, + use_resource=self.use_resource_vars) + + def preprocess_device_grads(self, device_grads): + remaining_grads = device_grads + aggregated_grads = [] + for spec_tuple in self._all_reduce_spec: + if spec_tuple.limit < 0: + this_grads = remaining_grads + remaining_grads = [] + else: + (this_grads, remaining_grads) = allreduce.split_grads_by_size( + spec_tuple.limit, remaining_grads) + if this_grads: + range_agg_grads = allreduce.sum_gradients_all_reduce( + self._single_session, + self._all_reduce_device_prefixes, + this_grads, + self._num_workers, + spec_tuple.alg, + spec_tuple.shards, + self.benchmark_cnn.gpu_indices, + agg_small_grads_max_bytes=self._agg_small_grads_max_bytes, + agg_small_grads_max_group=self._agg_small_grads_max_group, + allreduce_merge_scope=self._allreduce_merge_scope) + if not aggregated_grads: + aggregated_grads = range_agg_grads + else: + assert len(aggregated_grads) == len(range_agg_grads) + for i in range(len(aggregated_grads)): + aggregated_grads[i] += range_agg_grads[i] + assert not remaining_grads + full_device_set = [] + for grads in device_grads: + g, v = grads[0] + del v + full_device_set.append(g.device) + return (full_device_set, aggregated_grads) + + def get_gradients_to_apply(self, device_num, gradient_state): + device_grads = gradient_state + if device_num >= len(device_grads): + raise ValueError('device_num %d exceeds length of device_grads (%d)' % + (device_num, len(device_grads))) + return device_grads[device_num] + + def get_post_init_ops(self): + """Copy initialized values for variables to other devices.""" + global_vars = tf.global_variables() + var_by_name = dict([(v.name, v) for v in global_vars]) + post_init_ops = [] + for v in global_vars: + split_name = v.name.split('/') + # TODO(b/62630508): use more specific prefix than v or v0. + if split_name[0] == 'v0' or not v.name.startswith('v'): + continue + split_name[0] = 'v0' + copy_from = var_by_name['/'.join(split_name)] + post_init_ops.append(v.assign(copy_from.read_value())) + return post_init_ops + + def savable_variables(self): + """Return the set of variables used for saving/loading the model.""" + params = [] + for v in tf.global_variables(): + split_name = v.name.split('/') + if split_name[0] == 'v0' or not v.name.startswith('v'): + params.append(v) + return params + + def get_devices(self): + return self.benchmark_cnn.raw_devices + + +# TODO(tucker): Merge this mode with DistributedAllReduce. +class VariableMgrCollectiveAllReduce(VariableMgr): + """VariableMgr that implements the --collective_all_reduce mode. + + Each GPU has its own copy of the variables. To apply gradients + the TF native collective all-reduce op is used to reduce the gradients + and replicate the final value to all GPUs. + """ + + def __init__(self, benchmark_cnn, all_reduce_spec, + num_workers, num_gpus, task_id, allreduce_merge_scope): + super(VariableMgrCollectiveAllReduce, self).__init__(benchmark_cnn) + if not all_reduce_spec: + raise ValueError( + 'collective_all_reduce requires a non-empty all_reduce_spec: %s' + % all_reduce_spec) + parsed_spec = allreduce.parse_all_reduce_spec(all_reduce_spec) + # So far we only support a length-1 all_reduce_spec + if len(parsed_spec) > 1 or parsed_spec[0].limit > 0: + raise ValueError( + 'collective_all_reduce requires one single-range all_reduce_spec %s' + % parsed_spec) + self._all_reduce_spec = parsed_spec[0] + if self._all_reduce_spec.alg != 'collective': + raise ValueError( + 'VariableMgrCollectiveAllReduce initialized with non-collective ' + 'all_reduce_spec %s' % self.all_reduce_spec) + self._num_workers = num_workers + self._num_gpus = num_gpus + self._task_id = task_id + self._allreduce_merge_scope = allreduce_merge_scope + self._instance_key_counter = 10000 + self._instance_key_table = dict() + self._single_session = False + # List of prefixes for generating PS devices, unused here. + self._all_reduce_device_prefixes = None + + def each_tower_has_variables(self): + return True + + def create_outer_variable_scope(self, device_num): + """Create a scope for the named device. + + Args: + device_num: index of device for variable scope. + + Returns: + the requested variable_scope + """ + return tf.variable_scope('v%s' % device_num, reuse=self._reuse_vars) + + def preprocess_device_grads(self, device_grads): + reduced_grads = allreduce.sum_gradients_all_reduce( + self._single_session, + self._all_reduce_device_prefixes, + device_grads, + self._num_workers, + 'collective', + self._all_reduce_spec.shards, + self.benchmark_cnn.gpu_indices, + allreduce_merge_scope=self._allreduce_merge_scope) + assert len(reduced_grads) == len(device_grads) + full_device_set = [] + for grads in device_grads: + g, _ = grads[0] + full_device_set.append(g.device) + return (full_device_set, reduced_grads) + + def get_gradients_to_apply(self, device_num, gradient_state): + device_grads = gradient_state + if device_num >= len(device_grads): + raise ValueError('device_num %d exceeds length of device_grads (%d)' % + (device_num, len(device_grads))) + return device_grads[device_num] + + def _get_instance_key(self, name): + if name not in self._instance_key_table.keys(): + self._instance_key_counter += 1 + self._instance_key_table[name] = self._instance_key_counter + return self._instance_key_table[name] + + def get_post_init_ops(self): + """Broadcast initialized values of variables to other devices. + + Returns: + At task 0 device 0, broadcast_send. + At all other devices and tasks, broadcast_recv. + """ + global_vars = tf.global_variables() + group_size = self._num_workers * self._num_gpus + post_init_ops = [] + # Gather variables into same-var-different-device groups. + vars_by_suffix = dict() + for v in global_vars: + split_name = v.name.split('/') + mo = re.match(r'v(\d+)$', split_name[0]) + if mo: + device_id = int(mo.group(1)) + suffix = '/'.join(split_name[1:]) + if suffix in vars_by_suffix.keys(): + vars_by_suffix[suffix].append(v) + else: + vars_by_suffix[suffix] = [v] + # Generate broadcast ops for each such group. + for suffix in sorted(vars_by_suffix): + vlist = vars_by_suffix[suffix] + assert self._num_gpus == len(vlist) + devices = [v.device for v in vlist] + # NOTE: this key should generate the same value for all tasks + group_key = allreduce.collective_group_key(devices) + group_size = self._num_workers * len(devices) + instance_key = self._get_instance_key(suffix) + for v in vlist: + split_name = v.name.split('/') + mo = re.match(r'v(\d+)$', split_name[0]) + if mo: + device_id = int(mo.group(1)) + if (self._task_id == 0 and device_id == 0): + with tf.device(v.device): + bcast_send = allreduce.broadcast_send( + v, v.shape, v.dtype, group_size, group_key, instance_key) + post_init_ops.append(v.assign(bcast_send)) + else: + with tf.device(v.device): + bcast_recv = allreduce.broadcast_recv( + v.shape, v.dtype, group_size, group_key, instance_key) + post_init_ops.append(v.assign(bcast_recv)) + return post_init_ops + + def savable_variables(self): + """Return the set of variables used for saving/loading the model.""" + params = [] + if self._task_id == 0: + for v in tf.global_variables(): + split_name = v.name.split('/') + if split_name[0] == 'v0' or not v.name.startswith('v'): + params.append(v) + return params + + def get_devices(self): + return self.benchmark_cnn.raw_devices + + +class VariableMgrDistributedFetchFromPS(VariableMgr): + """Implements --variable_update=parameter_server mode for distributed jobs. + + Variables are stored on a parameter server. For each step, each tower gets + a copy of the variables from the parameter server, and sends its gradients + to the param server. + """ + + def each_tower_has_variables(self): + return False + + def create_outer_variable_scope(self, device_num): + if self.benchmark_cnn.local_parameter_device_flag == 'gpu': + caching_devices = self.benchmark_cnn.raw_devices + else: + caching_devices = [self.benchmark_cnn.cpu_device] + custom_getter = variable_mgr_util.OverrideCachingDevice( + caching_devices, self.benchmark_cnn.cpu_device, 1024 * 64) + return tf.variable_scope( + 'v', reuse=bool(device_num) or self._reuse_vars, + custom_getter=custom_getter, use_resource=self.use_resource_vars) + + def preprocess_device_grads(self, device_grads): + # Returns (gradient_devices, gradient_state) + return ([self.benchmark_cnn.param_server_device], device_grads) + + def get_gradients_to_apply(self, device_num, gradient_state): + assert device_num == 0 + agg_grads, self.grad_has_inf_nan = ( + variable_mgr_util.aggregate_gradients_using_copy( + gradient_state, + use_mean=True, + check_inf_nan=self.benchmark_cnn.enable_auto_loss_scale)) + return agg_grads + + def get_devices(self): + ps_strategy = variable_mgr_util.GreedyLoadBalancingStrategy( + self.benchmark_cnn.num_ps, variable_mgr_util.byte_size_load_fn) + return [ + tf.train.replica_device_setter( + worker_device=d, + cluster=self.benchmark_cnn.cluster_manager.get_cluster_spec(), + ps_strategy=ps_strategy) for d in self.benchmark_cnn.raw_devices + ] + + +class VariableMgrDistributedFetchFromStagedPS( + VariableMgrDistributedFetchFromPS): + """Extends VariableMgrDistributedFetchFromPS for --staged_vars.""" + + def __init__(self, benchmark_cnn): + super(VariableMgrDistributedFetchFromStagedPS, self).__init__(benchmark_cnn) + self.staging_vars_on_devices = [ + dict() for _ in self.benchmark_cnn.raw_devices + ] + self.staged_vars_on_cpu = {} + + def create_outer_variable_scope(self, device_num): + self._custom_getter = variable_mgr_util.StagedVariableGetter( + device_num, self.benchmark_cnn.raw_devices, + self.benchmark_cnn.cpu_device, self) + return tf.variable_scope( + 'v', reuse=bool(device_num) or self._reuse_vars, + custom_getter=self._custom_getter, use_resource=self.use_resource_vars) + + def supports_staged_vars(self): + return True + + def trainable_variables_on_device(self, + rel_device_num, + abs_device_num, + writable=False): + return self._custom_getter.trainable_variables_on_device( + rel_device_num, abs_device_num, writable=writable) + + +class VariableMgrDistributedReplicated(VariableMgr): + """VariableMgr that implements the --distributed_replicated mode. + + Each GPU has a copy of the variables, and updates its copy after the + parameter servers are all updated with the gradients from all servers. Only + works with cross_replica_sync=true. Unlike 'replicated', does not use nccl + all-reduce for replicating within a server. + """ + + def each_tower_has_variables(self): + return True + + def create_outer_variable_scope(self, device_num): + return tf.variable_scope( + 'v%s' % device_num, reuse=self._reuse_vars, + custom_getter=variable_mgr_util.OverrideToLocalVariableIfNotPsVar(), + use_resource=self.use_resource_vars) + + def preprocess_device_grads(self, device_grads): + return ([self.benchmark_cnn.param_server_device], device_grads) + + def get_gradients_to_apply(self, device_num, gradient_state): + device_grads = gradient_state # From 2nd result of preprocess_device_grads. + + avg_grads, self.grad_has_inf_nan = ( + variable_mgr_util.aggregate_gradients_using_copy_with_device_selection( + self.benchmark_cnn, + device_grads, + use_mean=True, + check_inf_nan=self.benchmark_cnn.enable_auto_loss_scale)) + + # Make shadow variable on a parameter server for each original trainable + # variable. + for i, (g, v) in enumerate(avg_grads): + my_name = variable_mgr_util.PS_SHADOW_VAR_PREFIX + '/' + v.name + if my_name.endswith(':0'): + my_name = my_name[:-2] + new_v = tf.get_variable( + my_name, + dtype=v.dtype.base_dtype, + initializer=v.initial_value, + trainable=True) + avg_grads[i] = (g, new_v) + return avg_grads + + def append_apply_gradients_ops(self, gradient_state, opt, grads, training_ops, + loss_scale_params): + device_grads = gradient_state # From 2nd result of preprocess_device_grads. + + def get_apply_gradients_ops_func(): + """Returns a list of ops for updating gradients.""" + apply_gradients_ops = [] + # For each variable, apply the combined gradients for this server on + # the parameter server, and then wait for all other servers to do this. + for i, (g, v) in enumerate(grads): + apply_gradient_op = opt.apply_gradients([(g, v)]) + barrier = self.benchmark_cnn.add_sync_queues_and_barrier( + 'replicate_variable_%s' % i, [apply_gradient_op]) + with tf.control_dependencies([barrier]): + with tf.device(self.benchmark_cnn.cpu_device): + updated_value = v.read_value() + for my_d in range(len(self.benchmark_cnn.devices)): + apply_gradients_ops.append( + device_grads[my_d][i][1].assign(updated_value)) + return apply_gradients_ops + + variable_mgr_util.append_gradients_with_loss_scale( + training_ops, get_apply_gradients_ops_func, loss_scale_params, + self.grad_has_inf_nan) + + def _strip_port(self, s): + if s.endswith(':0'): + return s[:-2] + return s + + def get_post_init_ops(self): + # Copy initialized variables for variables on the parameter server + # to the local copy of the variable. + + local_vars = tf.local_variables() + local_var_by_name = dict( + [(self._strip_port(v.name), v) for v in local_vars]) + post_init_ops = [] + for v in tf.global_variables(): + if v.name.startswith(variable_mgr_util.PS_SHADOW_VAR_PREFIX + '/v0/'): + prefix = self._strip_port( + v.name[len(variable_mgr_util.PS_SHADOW_VAR_PREFIX + '/v0'):]) + for i in range(self.benchmark_cnn.num_gpus): + name = 'v%s%s' % (i, prefix) + if name in local_var_by_name: + copy_to = local_var_by_name[name] + post_init_ops.append(copy_to.assign(v.read_value())) + return post_init_ops + + def _remove_shadow_var_prefix_if_present(self, var_name): + if var_name.startswith(variable_mgr_util.PS_SHADOW_VAR_PREFIX + '/'): + return var_name[len(variable_mgr_util.PS_SHADOW_VAR_PREFIX + '/'):] + else: + return var_name + + def var_dict_name(self, v): + return self._strip_port(self._remove_shadow_var_prefix_if_present(v.name)) + + def savable_variables(self): + """Returns a list/dict of savable variables to pass to tf.train.Saver.""" + params = {} + for v in tf.global_variables(): + assert (v.name.startswith(variable_mgr_util.PS_SHADOW_VAR_PREFIX + '/v0/') + or v.name in ('global_step:0', 'loss_scale:0', + 'loss_scale_normal_steps:0')), ( + 'Invalid global variable: %s' % v) + # We store variables in the checkpoint with the shadow variable prefix + # removed so we can evaluate checkpoints in non-distributed replicated + # mode. The checkpoints can also be loaded for training in + # distributed_replicated mode. + name = self._strip_port(self._remove_shadow_var_prefix_if_present(v.name)) + params[name] = v + for v in tf.local_variables(): + # Non-trainable variables, such as batch norm moving averages, do not have + # corresponding global shadow variables, so we add them here. Trainable + # local variables have corresponding global shadow variables, which were + # added in the global variable loop above. + if v.name.startswith('v0/') and v not in tf.trainable_variables(): + params[self._strip_port(v.name)] = v + return params + + def get_devices(self): + return self.benchmark_cnn.raw_devices diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/variable_mgr_util.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/variable_mgr_util.py new file mode 100644 index 00000000..94ce3e4b --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/variable_mgr_util.py @@ -0,0 +1,676 @@ +# 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. +# ============================================================================== +"""Utilities for VariableMgr.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import collections as pycoll +import operator + +import numpy as np +import tensorflow.compat.v1 as tf + +# pylint: disable=g-direct-tensorflow-import +from tensorflow.python.framework import ops +from tensorflow.python.framework import tensor_shape +from tensorflow.python.ops import array_ops +from tensorflow.python.ops import data_flow_ops +from tensorflow.python.ops import math_ops + + +PS_SHADOW_VAR_PREFIX = 'ps_var' + +AutoLossScaleParams = pycoll.namedtuple( + 'AutoLossScaleParams', + [ + # If true, enable automatic loss scaling. + 'enable_auto_loss_scale', + # The value to scale the loss before computing gradients. + 'loss_scale', + # Number of normal steps with the current `loss_scale`. + 'loss_scale_normal_steps', + # Increase loss scale every n steps. + 'inc_loss_scale_every_n', + # If true, the current worker is chief. The current implementation + # relies on the chief to update loss_scale value, but in future, we + # might change this to ask the parameter server to update loss_scales + # for better performance. + # TODO(tanmingxing): remove this if loss_scale is updated in ps. + 'is_chief', + ]) + + +def get_loss_scale_update_op(loss_scale, loss_scale_normal_steps, + inc_loss_scale_every_n): + """Returns the update op for loss scaling variables. + + We maintain the counter `loss_scale_normal_steps` to count the number of steps + we have been using the current `loss_scale`. In most cases, this function + increments `loss_scale_normal_steps`. However, if `loss_scale_normal_steps` is + greater than the threshold `inc_loss_scale_every_n`, we double `loss_scale` + and reset `loss_scale_normal_steps` to zero. + + This op is only called if the gradients don't have any infs or nans. Instead, + if infs or nans occur in the gradients, we immeditately halve `loss_scale` and + reset `loss_scale_normal_steps` to zero. + + Args: + loss_scale: a tf.Variable represneting the loss_scale value. + loss_scale_normal_steps: a tf.Variable representing the number of training + steps that have run since the loss_scale last changed. + inc_loss_scale_every_n: a Python integer threshold. `loss_scale` is + increased every `inc_loss_scale_every_n` steps, unless the gradients have + infs or nans. + + Returns: + An op for updating `loss_scale` and `loss_scale_normal_steps`. + """ + + def increment_loss_scale_normal_steps_func(): + return tf.group(loss_scale_normal_steps.assign_add(1)) + + def increase_loss_scale_func(): + return tf.group( + tf.assign(loss_scale_normal_steps, 0), + tf.assign(loss_scale, loss_scale * 2)) + + # true_fn and false_fn must have the same type. + return tf.cond(loss_scale_normal_steps < inc_loss_scale_every_n, + increment_loss_scale_normal_steps_func, + increase_loss_scale_func) + + +def append_gradients_with_loss_scale(training_ops, get_apply_gradients_ops_func, + loss_scale_params, grad_has_inf_nan): + """Selectively appends gradients update ops with loss scaling. + + Args: + training_ops: a list of training ops to be executed. + get_apply_gradients_ops_func: a function that returns a list of ops for + applying gradients. Here, we must pass a function instead of the actual + list of ops; otherwise, those ops would be executed unconditionally due to + the semantics of tf.cond. + loss_scale_params: An AutoLossScaleParams tuple. + grad_has_inf_nan: Boolean tensor indicating whether the gradients have infs + or nans. + """ + is_chief = loss_scale_params.is_chief + loss_scale = loss_scale_params.loss_scale + loss_scale_normal_steps = loss_scale_params.loss_scale_normal_steps + inc_loss_scale_every_n = loss_scale_params.inc_loss_scale_every_n + enable_auto_loss_scale = loss_scale_params.enable_auto_loss_scale + + if loss_scale is None or not enable_auto_loss_scale or not is_chief: + training_ops.extend(get_apply_gradients_ops_func()) + else: + # If nans/infs occurred, skip applying gradients and instead update + # loss_scale (halve loss_scale and reset loss_scale_normal_steps to zero). + def update_op_if_nan_or_inf(): + """Update loss_scale and discard gradients if nans/infs occurred.""" + return tf.group( + tf.assign(loss_scale, loss_scale / 2.), + tf.assign(loss_scale_normal_steps, 0)) + + # Otherwise, apply gradients, and update loss_scale and + # loss_scale_normal_steps. + def update_op_if_no_nan_or_inf(): + """Apply gradients, and update loss scaling.""" + return tf.group( + get_loss_scale_update_op(loss_scale, loss_scale_normal_steps, + inc_loss_scale_every_n), + *get_apply_gradients_ops_func()) + + # TODO(tanmingxing): Add support for independent and distributed all_reduce. + assert grad_has_inf_nan is not None + update_op = tf.cond( + grad_has_inf_nan, + update_op_if_nan_or_inf, + update_op_if_no_nan_or_inf, + name='cond_if_grad_has_inf_nan' + ) + training_ops.append(update_op) + + +# To be used with custom_getter on tf.get_variable. +class OverrideCachingDevice(object): + """Variable getter which caches variables on the least loaded device. + + Variables smaller than a certain threshold are cached on a single specific + device, as specified in the constructor. All other variables are load balanced + across a pool of devices, by caching each variable on the least loaded device. + + Note that variable creation only happen when building the model graph on the + first device (see how it sets the 'reuse' parameter in + VariableMgr.*.create_outer_variable_scope()). That means, for all other + devices, the variable scope will reuse the variables created before, which + requires that we set the caching_device correctly as otherwise it may not be + able to find the previously created variable and will create a new one. This + requires when building the model graph on different devices, variables with + the same name should have same size. + + TODO(laigd): consider adding tests or verification logic to enforce this, or + refactor it. + """ + + def __init__(self, devices, device_for_small_variables, + small_variable_size_threshold): + self.devices = devices + self.sizes = [0] * len(self.devices) + self.device_for_small_variables = device_for_small_variables + self.small_variable_size_threshold = small_variable_size_threshold + + def __call__(self, getter, *args, **kwargs): + size = tf.TensorShape(kwargs['shape']).num_elements() + if size < self.small_variable_size_threshold: + device_name = self.device_for_small_variables + else: + device_index, _ = min(enumerate(self.sizes), key=operator.itemgetter(1)) + device_name = self.devices[device_index] + self.sizes[device_index] += size + + kwargs['caching_device'] = device_name + var = getter(*args, **kwargs) + return var + + +# To be used with custom_getter on tf.get_variable. Ensures the created variable +# is in LOCAL_VARIABLES and not GLOBAL_VARIBLES collection. +class OverrideToLocalVariableIfNotPsVar(object): + + # args and kwargs come from the custom_getter interface for Tensorflow + # variables, and matches tf.get_variable's signature, with the addition of + # 'getter' at the beginning. + def __call__(self, getter, name, *args, **kwargs): + if name.startswith(PS_SHADOW_VAR_PREFIX): + return getter(*args, **kwargs) + + if 'collections' in kwargs: + collections = kwargs['collections'] + if not collections: + collections = [tf.GraphKeys.GLOBAL_VARIABLES] + else: + collections = collections[:] + collections.remove(tf.GraphKeys.GLOBAL_VARIABLES) + collections.append(tf.GraphKeys.LOCAL_VARIABLES) + kwargs['collections'] = list(collections) + return getter(name, *args, **kwargs) + + +class ParamServerDeviceSetter(object): + """Helper class to assign variables on the least loaded ps-device.""" + + def __init__(self, worker_device, ps_devices): + """Initializer for ParamServerDevicSetter. + + Args: + worker_device: the device to use for computer ops. + ps_devices: a list of device to use for Variable ops. Each variable is + assigned to the least loaded device. + """ + self.ps_devices = ps_devices + self.worker_device = worker_device + self.ps_sizes = [0] * len(self.ps_devices) + + def __call__(self, op): + if op.device: + return op.device + if op.type not in ['Variable', 'VariableV2']: + return self.worker_device + + device_index, _ = min(enumerate(self.ps_sizes), key=operator.itemgetter(1)) + device_name = self.ps_devices[device_index] + var_size = op.outputs[0].get_shape().num_elements() + self.ps_sizes[device_index] += var_size + + return device_name + + +class StagedModelVariable(object): + """Staging variable wrapper that decouples reads and updates. + + This class represents a variable through a staging buffer. Reads from this + variable directly gets from the staging buffer. Updates are stacked into + another staging buffer, and will be processed later. + """ + + def __init__(self, real_var, var_stage_get, variable_mgr): + """Initializer for the model variables through a staging buffer. + + Args: + real_var: the underlying real variable. + var_stage_get: the read op from the staging buffer. + variable_mgr: the parent variable-manager. + """ + self.real_var = real_var + self.var_stage_get = var_stage_get + self.variable_mgr = variable_mgr + + def _value(self): + """The read access of this variable. The content from the staging buffer.""" + return self.var_stage_get + + def _ref(self): + """Return the underlying variable ref, required by tf.colocate_with.""" + return self.real_var._ref() # pylint: disable=protected-access + + def read_value(self): + """Mimics tf.Variable.read_value().""" + return tf.identity(self.var_stage_get, name='read') + + @property + def dtype(self): + """Return the non-reference dtype.""" + return self.var_stage_get.dtype + + def assign_sub(self, delta, name=None, read_value=True): + """Mimic the updates to the variable. + + Args: + delta: is pushed into a staging buffer and will be pumped later. + name: currently ignored; names of ops and the StagingArea are + computed without using this pass name. + read_value: if True, will return something which evaluates to the new + value of the variable; if False will return the assign op. + Returns: + The actual updates. The colocation constraint will be reapplied. + """ + # This parameter is ignored: the StagingArea only supports setting + # the shared name, not the names of individual ops it uses. + del name + + # colocate_with(None, True) clears the colocation constraints. + # Push the delta into a staging buffer. + with ops.colocate_with(None, True), tf.device(self.var_stage_get.device): + delta_staging_area = data_flow_ops.StagingArea( + [self.var_stage_get.dtype], shapes=[self.var_stage_get.shape]) + delta_put_op = delta_staging_area.put([delta]) + self.variable_mgr.staging_delta_ops.append(delta_put_op) + delta_get_op = delta_staging_area.get()[0] + # Return the actual updates. The colocation constraint will be reapplied. + return self.real_var.assign_sub(delta_get_op, read_value=read_value) + + @staticmethod + # pylint: disable=bad-staticmethod-argument,invalid-name + def _TensorConversionFunction(self, dtype=None, name=None, as_ref=False): + """Utility function for converting a StagedModelVariable to a Tensor.""" + del dtype, name # unused: this function returns the cached ref or value. + if as_ref: + return self._ref() + else: + return self._value() + + +ops.register_tensor_conversion_function( + StagedModelVariable, StagedModelVariable._TensorConversionFunction) # pylint: disable=protected-access + + +class StagedVariableGetter(object): + """A variable getter through staging buffers on devices. + + Instead of a caching device, this getter tracks where the variable is used. + And on each device, it goes through a staging buffer. + """ + + def __init__(self, device_num, devices, cpu_device, variable_mgr): + """Initializer for StagedVariableGetter. + + Args: + device_num: the current device index. + devices: a list of all the devices to build towers. + cpu_device: a cpu_device for this replica. If None, no cpu-caching is + done. + variable_mgr: the parent variable manager. + """ + self.device_num = device_num + self.devices = devices + self.cpu_device = cpu_device + self.variable_mgr = variable_mgr + + def __call__(self, getter, name, *args, **kwargs): + staging_ops = self.variable_mgr.staging_vars_on_devices[self.device_num] + if name in staging_ops: + put_op, get_op = staging_ops[name] + return get_op + real_var = getter(name, *args, **kwargs) + shape = kwargs['shape'] + dtype = kwargs['dtype'] + trainable = kwargs['trainable'] + if self.cpu_device: + with tf.device(self.cpu_device): + # This helps copying the weights from the parameter to this server only + # once. + if name in self.variable_mgr.staged_vars_on_cpu: + cpu_var = self.variable_mgr.staged_vars_on_cpu[name] + else: + cpu_var = tf.identity(real_var) + self.variable_mgr.staged_vars_on_cpu[name] = cpu_var + var_to_stage = cpu_var + else: + var_to_stage = tf.identity(real_var) # de-reference the variable. + + with tf.device(self.devices[self.device_num]): + staging_area = data_flow_ops.StagingArea([dtype], shapes=[shape]) + put_op = staging_area.put([var_to_stage]) + get_op = staging_area.get()[0] + staging_ops[name] = (put_op, get_op) + if trainable: + # For trainable variables, they are managed separatedly through + # apply_gradients. + return get_op + else: + # For other shadow variables, the access is decoupled through a wrapper + # class. + return StagedModelVariable(real_var, get_op, self.variable_mgr) + + def trainable_variables_on_device(self, rel_device_num, abs_device_num, + writable): + """Return the set of trainable variables on the specified device. + + Args: + rel_device_num: local worker device index. + abs_device_num: global graph device index. + writable: whether the returned variables is writable or read-only. + + Returns: + Return the set of trainable variables on the specified device. + """ + del abs_device_num + params_refs = tf.trainable_variables() + if writable: + return params_refs + params = [] + for param in params_refs: + var_name = param.name.split(':')[0] + _, var_get_op = self.variable_mgr.staging_vars_on_devices[rel_device_num][ + var_name] + params.append(var_get_op) + return params + + +def aggregate_gradients_using_copy_with_device_selection( + benchmark_cnn, tower_grads, use_mean, check_inf_nan): + """Aggregate gradients, controlling device for the aggregation. + + Args: + benchmark_cnn: benchmark_cnn class. + tower_grads: List of lists of (gradient, variable) tuples. The outer list + is over towers. The inner list is over individual gradients. + use_mean: if True, mean is taken, else sum of gradients is taken. + check_inf_nan: If true, check grads for nans and infs. + + Returns: + The tuple ([(average_gradient, variable),], has_nan_or_inf) where the + gradient has been averaged across all towers. The variable is chosen from + the first tower. The has_nan_or_inf indicates the grads has nan or inf. + """ + if benchmark_cnn.local_parameter_device_flag == 'gpu': + avail_devices = benchmark_cnn.raw_devices + else: + avail_devices = [benchmark_cnn.param_server_device] + agg_grads = [] + has_nan_or_inf_list = [] + for i, single_grads in enumerate(zip(*tower_grads)): + with tf.device(avail_devices[i % len(avail_devices)]): + grad_and_var, has_nan_or_inf = aggregate_single_gradient_using_copy( + single_grads, use_mean, check_inf_nan) + agg_grads.append(grad_and_var) + has_nan_or_inf_list.append(has_nan_or_inf) + if check_inf_nan: + return agg_grads, tf.reduce_any(has_nan_or_inf_list) + else: + return agg_grads, None + + +def aggregate_gradients_using_copy_with_variable_colocation( + tower_grads, use_mean, check_inf_nan): + """Aggregate gradients, colocating computation with the gradient's variable. + + Args: + tower_grads: List of lists of (gradient, variable) tuples. The outer list + is over towers. The inner list is over individual gradients. All variables + of the same gradient across towers must be the same (that is, + tower_grads[x][a][1] == tower_grads[y][a][1] for all indices x, y, and a) + use_mean: if True, mean is taken, else sum of gradients is taken. + check_inf_nan: If true, check grads for nans and infs. + + Returns: + The tuple ([(average_gradient, variable),], has_nan_or_inf) where the + gradient has been averaged across all towers. The variable is chosen from + the first tower. The has_nan_or_inf indicates the grads has nan or inf. + """ + agg_grads = [] + has_nan_or_inf_list = [] + for single_grads in zip(*tower_grads): + # Note that each single_grads looks like the following: + # ((grad0_gpu0, var0_gpu0), ... , (grad0_gpuN, var0_gpuN)) + var = single_grads[0][1] + + for _, v in single_grads: + assert v == var + + with tf.device(var.device): + grad_and_var, has_nan_or_inf = aggregate_single_gradient_using_copy( + single_grads, use_mean, check_inf_nan) + agg_grads.append(grad_and_var) + has_nan_or_inf_list.append(has_nan_or_inf) + + if check_inf_nan: + return agg_grads, tf.reduce_any(has_nan_or_inf_list) + else: + return agg_grads, None + + +def aggregate_gradients_using_copy(tower_grads, use_mean, check_inf_nan): + """Calculate the average gradient for each shared variable across all towers. + + Note that this function provides a synchronization point across all towers. + + Args: + tower_grads: List of lists of (gradient, variable) tuples. The outer list + is over towers. The inner list is over individual gradients. + use_mean: if True, mean is taken, else sum of gradients is taken. + check_inf_nan: check grads for nans and infs. + + Returns: + The tuple ([(average_gradient, variable),], has_nan_or_inf) where the + gradient has been averaged across all towers. The variable is chosen from + the first tower. The has_nan_or_inf indicates the grads has nan or inf. + """ + agg_grads = [] + has_nan_or_inf_list = [] + + for single_grads in zip(*tower_grads): + grad_and_var, has_nan_or_inf = aggregate_single_gradient_using_copy( + single_grads, use_mean, check_inf_nan) + agg_grads.append(grad_and_var) + has_nan_or_inf_list.append(has_nan_or_inf) + + if check_inf_nan: + return agg_grads, tf.reduce_any(has_nan_or_inf_list) + else: + return agg_grads, None + + +# The following two functions are copied from +# tensorflow/python/eager/backprop.py. We do not directly use them as they are +# not exported and subject to change at any time. +def flatten_nested_indexed_slices(grad): + assert isinstance(grad, ops.IndexedSlices) + if isinstance(grad.values, ops.Tensor): + return grad + else: + assert isinstance(grad.values, ops.IndexedSlices) + g = flatten_nested_indexed_slices(grad.values) + return ops.IndexedSlices(g.values, array_ops.gather(grad.indices, + g.indices), + g.dense_shape) + + +def aggregate_indexed_slices_gradients(grads): + """Aggregates gradients containing `IndexedSlices`s.""" + if len(grads) < 1: + return None + elif len(grads) == 1: + return grads[0] + else: + grads = [g for g in grads if g is not None] + # If any gradient is a `Tensor`, sum them up and return a dense tensor + # object. + if any(isinstance(g, ops.Tensor) for g in grads): + return math_ops.add_n(grads) + + # The following `_as_indexed_slices_list` casts ids of IndexedSlices into + # int64. It is to make sure the inputs of `concat` all have same the data + # type. + grads = math_ops._as_indexed_slices_list(grads) # pylint: disable=protected-access + + grads = [flatten_nested_indexed_slices(x) for x in grads] + # Form IndexedSlices out of the concatenated values and indices. + concat_grad = ops.IndexedSlices( + array_ops.concat([x.values for x in grads], axis=0), + array_ops.concat([x.indices for x in grads], axis=0), + grads[0].dense_shape) + + return concat_grad + + +def aggregate_single_gradient_using_copy(grad_and_vars, use_mean, + check_inf_nan): + """Calculate the average gradient for a shared variable across all towers. + + Note that this function provides a synchronization point across all towers. + + Args: + grad_and_vars: A list or tuple of (gradient, variable) tuples. Each + (gradient, variable) pair within the outer list represents the gradient + of the variable calculated for a single tower, and the number of pairs + equals the number of towers. + use_mean: if True, mean is taken, else sum of gradients is taken. + check_inf_nan: check grads for nans and infs. + + Returns: + The tuple ([(average_gradient, variable),], has_nan_or_inf) where the + gradient has been averaged across all towers. The variable is chosen from + the first tower. The has_nan_or_inf indicates the grads has nan or inf. + """ + grads = [g for g, _ in grad_and_vars] + if any(isinstance(g, tf.IndexedSlices) for g in grads): + # TODO(reedwm): All-reduce IndexedSlices more effectively. + grad = aggregate_indexed_slices_gradients(grads) + else: + grad = tf.add_n(grads) + + if use_mean and len(grads) > 1: + grad = tf.scalar_mul(1.0 / len(grads), grad) + + v = grad_and_vars[0][1] + if check_inf_nan: + with tf.name_scope('check_for_inf_and_nan'): + has_nan_or_inf = tf.logical_not(tf.reduce_all(tf.is_finite(grads))) + return (grad, v), has_nan_or_inf + else: + return (grad, v), None + + +# This class is copied from +# https://github.com/tensorflow/tensorflow/blob/590d6eef7e91a6a7392c8ffffb7b58f2e0c8bc6b/tensorflow/contrib/training/python/training/device_setter.py#L56. +# We copy it since contrib has been removed from TensorFlow. +class GreedyLoadBalancingStrategy(object): + """Returns the least-loaded ps task for op placement. + + The load is calculated by a user-specified load function passed in at + construction. There are no units for load, and the load function is + responsible for providing an internally consistent measure. + + Note that this strategy is very sensitive to the exact order in which + ps ops (typically variables) are created, as it greedily places ops + on the least-loaded ps at the point each op is processed. + + One reasonable heuristic is the `byte_size_load_fn`, which + estimates load as the number of bytes that would be used to store and + transmit the entire variable. More advanced load functions + could consider the difference in access patterns across ops, or trade + off CPU-intensive ops with RAM-intensive ops with network bandwidth. + + This class is intended to be used as a `ps_strategy` in + `tf.compat.v1.train.replica_device_setter`. + """ + + def __init__(self, num_tasks, load_fn): + """Create a new `LoadBalancingStrategy`. + + Args: + num_tasks: Number of ps tasks to cycle among. + load_fn: A callable that takes an `Operation` and returns a + numeric load value for that op. + """ + self._num_tasks = num_tasks + self._load_fn = load_fn + self._ps_loads = np.zeros(num_tasks) + + def __call__(self, op): + """Choose a ps task index for the given `Operation`. + + Args: + op: A `Operation` to be placed on ps. + + Returns: + The next ps task index to use for the `Operation`. Greedily + places the op on the least-loaded ps task so far, as determined + by the load function. + """ + task = np.argmin(self._ps_loads) + self._ps_loads[task] += self._load_fn(op) + return task + + +# This function is copied from +# https://github.com/tensorflow/tensorflow/blob/590d6eef7e91a6a7392c8ffffb7b58f2e0c8bc6b/tensorflow/contrib/training/python/training/device_setter.py#L105. +# We copy it since contrib has been removed from TensorFlow. +def byte_size_load_fn(op): + """Load function that computes the byte size of a single-output `Operation`. + + This is intended to be used with `"Variable"` ops, which have a single + `Tensor` output with the contents of the variable. However, it can also be + used for calculating the size of any op that has a single output. + + Intended to be used with `GreedyLoadBalancingStrategy`. + + Args: + op: An `Operation` with a single output, typically a "Variable" op. + + Returns: + The number of bytes in the output `Tensor`. + + Raises: + ValueError: if `op` does not have a single output, or if the shape of the + single output is not fully-defined. + """ + if len(op.outputs) != 1: + raise ValueError('Op %s must have a single output' % op) + output = op.outputs[0] + elem_size = output.dtype.size + shape = output.get_shape() + if not shape.is_fully_defined(): + # Due to legacy behavior, scalar "Variable" ops have output Tensors that + # have unknown shape when the op is created (and hence passed to this + # load function for placement), even though the scalar shape is set + # explicitly immediately afterward. + shape = tensor_shape.TensorShape(op.get_attr('shape')) + shape.assert_is_fully_defined() + return shape.num_elements() * elem_size + diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/variable_mgr_util_test.py b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/variable_mgr_util_test.py new file mode 100644 index 00000000..0915224f --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/benchmarks-master/scripts/tf_cnn_benchmarks/variable_mgr_util_test.py @@ -0,0 +1,153 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 variable_mgr_util.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import tensorflow.compat.v1 as tf +import variable_mgr_util + + +class VariableMgrUtilTest(tf.test.TestCase): + + def testGetLossScaleUpdateOpTruePath(self): + loss_scale = tf.Variable(4) + # loss_scale_normal_steps >= inc_loss_scale_every_n + loss_scale_normal_steps = tf.Variable(10) + inc_loss_scale_every_n = 10 + update_op = variable_mgr_util.get_loss_scale_update_op( + loss_scale, loss_scale_normal_steps, inc_loss_scale_every_n) + + with self.test_session() as sess: + sess.run(tf.global_variables_initializer()) + sess.run(update_op) + + self.assertEqual(sess.run(loss_scale), 8) + self.assertEqual(sess.run(loss_scale_normal_steps), 0) + + def testGetLossScaleUpdateOpFalsePath(self): + loss_scale = tf.Variable(4) + # loss_scale_normal_steps < inc_loss_scale_every_n + loss_scale_normal_steps = tf.Variable(9) + inc_loss_scale_every_n = 10 + update_op = variable_mgr_util.get_loss_scale_update_op( + loss_scale, loss_scale_normal_steps, inc_loss_scale_every_n) + + with self.test_session() as sess: + sess.run(tf.global_variables_initializer()) + sess.run(update_op) + + self.assertEqual(sess.run(loss_scale), 4) + self.assertEqual(sess.run(loss_scale_normal_steps), 10) + + def testAppendGradientsWithLossScaleWithAutoScaleDisabled(self): + v = tf.Variable(0) + training_ops = [] + get_apply_gradients_ops_func = lambda: [tf.assign(v, v + 1)] + loss_scale_params = variable_mgr_util.AutoLossScaleParams( + enable_auto_loss_scale=False, # no auto loss scale. + loss_scale=tf.Variable(4), + loss_scale_normal_steps=tf.Variable(10), + inc_loss_scale_every_n=10, + is_chief=True) + variable_mgr_util.append_gradients_with_loss_scale( + training_ops, + get_apply_gradients_ops_func, + loss_scale_params, + grad_has_inf_nan=True) + + with self.test_session() as sess: + sess.run(tf.global_variables_initializer()) + sess.run(training_ops) + self.assertEqual(sess.run(v), 1) + self.assertEqual(sess.run(loss_scale_params.loss_scale), 4) + self.assertEqual(sess.run(loss_scale_params.loss_scale_normal_steps), 10) + + def testAppendGradientsWithLossScaleForNonChiefWorker(self): + v = tf.Variable(0) + training_ops = [] + get_apply_gradients_ops_func = lambda: [tf.assign(v, v + 1)] + loss_scale_params = variable_mgr_util.AutoLossScaleParams( + enable_auto_loss_scale=True, + loss_scale=tf.Variable(4), + loss_scale_normal_steps=tf.Variable(10), + inc_loss_scale_every_n=10, + is_chief=False) # Non-chief + variable_mgr_util.append_gradients_with_loss_scale( + training_ops, + get_apply_gradients_ops_func, + loss_scale_params, + grad_has_inf_nan=False) + + with self.test_session() as sess: + sess.run(tf.global_variables_initializer()) + sess.run(training_ops) + self.assertEqual(sess.run(v), 1) + self.assertEqual(sess.run(loss_scale_params.loss_scale), 4) + self.assertEqual(sess.run(loss_scale_params.loss_scale_normal_steps), 10) + + def testAppendGradientsWithLossScaleWithoutNan(self): + v = tf.Variable(0) + training_ops = [] + get_apply_gradients_ops_func = lambda: [tf.assign(v, v + 1)] + loss_scale_params = variable_mgr_util.AutoLossScaleParams( + enable_auto_loss_scale=True, + loss_scale=tf.Variable(4, dtype=tf.float32), + loss_scale_normal_steps=tf.Variable(10), + inc_loss_scale_every_n=10, + is_chief=True) + variable_mgr_util.append_gradients_with_loss_scale( + training_ops, + get_apply_gradients_ops_func, + loss_scale_params, + grad_has_inf_nan=tf.constant(False)) + + with self.test_session() as sess: + sess.run(tf.global_variables_initializer()) + sess.run(training_ops) + self.assertEqual(sess.run(v), 1) + self.assertEqual(sess.run(loss_scale_params.loss_scale), 8) + self.assertEqual(sess.run(loss_scale_params.loss_scale_normal_steps), 0) + + def testAppendGradientsWithLossScaleWithtNan(self): + v = tf.Variable(0) + training_ops = [] + get_apply_gradients_ops_func = lambda: [tf.assign(v, v + 1)] + loss_scale_params = variable_mgr_util.AutoLossScaleParams( + enable_auto_loss_scale=True, + loss_scale=tf.Variable(4, dtype=tf.float32), + loss_scale_normal_steps=tf.Variable(10), + inc_loss_scale_every_n=10, + is_chief=True) + variable_mgr_util.append_gradients_with_loss_scale( + training_ops, + get_apply_gradients_ops_func, + loss_scale_params, + grad_has_inf_nan=tf.constant(True)) + + with self.test_session() as sess: + sess.run(tf.global_variables_initializer()) + sess.run(training_ops) + self.assertEqual(sess.run(v), 0) # Skip updating for v. + # halve loss_scale and reset local_scale_normal_steps. + self.assertEqual(sess.run(loss_scale_params.loss_scale), 2) + self.assertEqual(sess.run(loss_scale_params.loss_scale_normal_steps), 0) + + +if __name__ == '__main__': + tf.disable_v2_behavior() + tf.test.main() diff --git a/TensorFlow/ComputeVision/Accuracy_Validation/scripts-run/single_process.sh b/TensorFlow/ComputeVision/Accuracy_Validation/scripts-run/single_process.sh new file mode 100755 index 00000000..a8bed85a --- /dev/null +++ b/TensorFlow/ComputeVision/Accuracy_Validation/scripts-run/single_process.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +lrank=$OMPI_COMM_WORLD_LOCAL_RANK +comm_rank=$OMPI_COMM_WORLD_RANK +comm_size=$OMPI_COMM_WORLD_SIZE + +APP=" python3 ./benchmarks-master/scripts/tf_cnn_benchmarks/tf_cnn_benchmarks.py --data_format=NCHW --batch_size=128 --model=resnet50 --optimizer=momentum --variable_update=horovod --print_training_accuracy=true --nodistortions --num_gpus=1 --num_epochs=90 --weight_decay=1e-4 --data_dir=$data_dir_path --use_fp16=False --data_name=imagenet --train_dir=$save_checkpoint_path + +case ${l_rank} in +[0]) + export HIP_VISIBLE_DEVICES=0,1,2,3 + export UCX_NET_DEVICES=mlx5_0:1 + export UCX_IB_PCI_BW=mlx5_0:50Gbs + echo numactl --cpunodebind=0 --membind=0 ${APP} + numactl --cpunodebind=0 --membind=0 ${APP} + ;; +[1]) + export HIP_VISIBLE_DEVICES=0,1,2,3 + export UCX_NET_DEVICES=mlx5_1:1 + export UCX_IB_PCI_BW=mlx5_1:50Gbs + echo numactl --cpunodebind=1 --membind=1 ${APP} + numactl --cpunodebind=1 --membind=1 ${APP} + ;; +[2]) + export HIP_VISIBLE_DEVICES=0,1,2,3 + export UCX_NET_DEVICES=mlx5_2:1 + export UCX_IB_PCI_BW=mlx5_2:50Gbs + echo numactl --cpunodebind=2 --membind=2 ${APP} + numactl --cpunodebind=2 --membind=2 ${APP} + ;; +[3]) + export HIP_VISIBLE_DEVICES=0,1,2,3 + export UCX_NET_DEVICES=mlx5_3:1 + export UCX_IB_PCI_BW=mlx5_3:50Gbs + echo numactl --cpunodebind=3 --membind=3 ${APP} + numactl --cpunodebind=3 --membind=3 ${APP} + ;; +esac diff --git a/TensorFlow/ComputeVision/Classification/.README.md.swp b/TensorFlow/ComputeVision/Classification/.README.md.swp deleted file mode 100644 index a73523ad028501f2d889a2794d8c801baf01eed8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2%WoS+9LFa;&S%aiOH?Awp;kL*Qo-H1^3^E;D?&g71JzC!4bq0^yfgO1Bzz3~BRrAhbYMpB!qk33ndLlcT1V8lmA4C=3>wU^s2VqQ#*B=S(s}TBAn|bT{zN37 z{=GP|UlNc6HY2bT^>jzOf}Y*C--d7Q9N27-Y?A~e0ZBj-kOU+FNk9^i1SEm~C;@JF zpkt8RmS%o;H}4(4xHli=O%jj_M9uX~$5Lgr;(N-Fc3wp`8?^8qa8lqmk%d4NTi`x-n@Im8BfR<}fIu z1w-1eZR+yx46p}|gJE~-cw@NOSQ_z8ok0l0mA4J2K(Rt?e8-Elkk_(y;U%mnIg9a( zVG`W>peRX$rWI~sYEgolnJDnK$_$z{iDo-iiZB*oIf%6zqRpRmCrTpDV|`C*gMA14 z9(g>W^(F@U;)#Q?E^H6+oJD(ZJ5h}dQJy2*NVVZa>>+TJhtsEK>;Pd;5WcSq8j?Cs zPuY0aW$fG4P3NQ7)iKZuD9njsFXPx6naeC7UsJAt0=tC_$R6frDX2dR6Q52ff0urdI{KC+FN+75BG}cH_o{=|*YXJH6~r zF1qK6?)2n0pN-dR@8Q;(_E%r^Yj4z7s`a%o=xbC;e&w`#Vj2G&TXQFZh3>sss@In5 zD`WMQ33qzj|76XrzVDwM@rD-~#Z{QXJvj=~H7XzawW9mlxHnn$r=ey3VzCTe!8U;1 zx2t|>7`MLU2q{?X0`=Ov{yQ&=DO&FYlZJ1I7-A$$?ar_7AYp9L{`K(jU}qH%BL86e z4N$z&sQ1<~77npHx8@e7Afo?q&7WIwPkbtnKuW^Qz3t5qyOmk*)Oj%DzA`Ez_~kLT oIu%e|z2LuI4nWL&• Officially maintained, supported, and kept up to date with the latest TensorFlow 2 APIs by TensorFlow
• Reasonably optimized for fast performance while still being easy to read | -| [research](research) | • A collection of research model implementations in TensorFlow 1 or 2 by researchers
• Maintained and supported by researchers | -| [community](community) | • A curated list of the GitHub repositories with machine learning models and implementations powered by TensorFlow 2 | -| [orbit](orbit) | • A flexible and lightweight library that users can easily use or fork when writing customized training loop code in TensorFlow 2.x. It seamlessly integrates with `tf.distribute` and supports running on different device types (CPU, GPU, and TPU). | - -## [Announcements](https://github.com/tensorflow/models/wiki/Announcements) - -## Contributions - -[![help wanted:paper implementation](https://img.shields.io/github/issues/tensorflow/models/help%20wanted%3Apaper%20implementation)](https://github.com/tensorflow/models/labels/help%20wanted%3Apaper%20implementation) - -If you want to contribute, please review the [contribution guidelines](https://github.com/tensorflow/models/wiki/How-to-contribute). - -## License - -[Apache License 2.0](LICENSE) - -## Citing TensorFlow Model Garden - -If you use TensorFlow Model Garden in your research, please cite this repository. - -``` -@misc{tensorflowmodelgarden2020, - author = {Hongkun Yu and Chen Chen and Xianzhi Du and Yeqing Li and - Abdullah Rashwan and Le Hou and Pengchong Jin and Fan Yang and - Frederick Liu and Jaeyoun Kim and Jing Li}, - title = {{TensorFlow Model Garden}}, - howpublished = {\url{https://github.com/tensorflow/models}}, - year = {2020} -} -``` - diff --git a/TensorFlow2x/Accuracy_Validation/ResNet50_Official/scripts-run/single_process.sh b/TensorFlow2x/Accuracy_Validation/ResNet50_Official/scripts-run/single_process.sh new file mode 100644 index 00000000..c0f203d6 --- /dev/null +++ b/TensorFlow2x/Accuracy_Validation/ResNet50_Official/scripts-run/single_process.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +lrank=$OMPI_COMM_WORLD_LOCAL_RANK +drank=$OMPI_COMM_WORLD_RANK + +APP="python3 ../official/vision/image_classification/resnet/resnet_ctl_imagenet_main.py --num_gpus=1 --skip_eval=true --batch_size=128 --train_epochs=90 --use_synthetic_data=false --distribution_strategy=multi_worker_mirrored --all_reduce_alg=nccl --data_dir=${data_dir} --task_index=${drank} " +case ${lrank} in +[0]) + export HIP_VISIBLE_DEVICES=0 + export UCX_NET_DEVICES=mlx5_0:1 + export UCX_IB_PCI_BW=mlx5_0:50Gbs + numactl --cpunodebind=0 --membind=0 ${APP} + ;; +[1]) + export HIP_VISIBLE_DEVICES=1 + export UCX_NET_DEVICES=mlx5_0:1 + export UCX_IB_PCI_BW=mlx5_0:50Gbs + numactl --cpunodebind=1 --membind=1 ${APP} + ;; +[2]) + export HIP_VISIBLE_DEVICES=2 + export UCX_NET_DEVICES=mlx5_0:1 + export UCX_IB_PCI_BW=mlx5_0:50Gbs + numactl --cpunodebind=2 --membind=2 ${APP} + ;; +[3]) + export HIP_VISIBLE_DEVICES=3 + export UCX_NET_DEVICES=mlx5_0:1 + export UCX_IB_PCI_BW=mlx5_0:50Gbs + numactl --cpunodebind=3 --membind=3 ${APP} + ;; +esac + diff --git a/TensorFlow2x/ComputeVision/Classification/README.md b/TensorFlow2x/ComputeVision/Classification/README.md index 440de824..d8954e1c 100644 --- a/TensorFlow2x/ComputeVision/Classification/README.md +++ b/TensorFlow2x/ComputeVision/Classification/README.md @@ -1,33 +1,28 @@ +# 简介 -TenorFlow 框架 训练 图像分类相关网络的代码,tensorflow 官方基准测试程序,使用的数据集是 imagenet。 +该测试用例用于TensorFlow分类模型性能测试,使用的数据集是imagenet。 -# 测试运行 +* 该脚本支持horovod等分布式通信库方式 -- 测试代码分为两部分,基础性能测试和大规模性能测试。 +# 运行 -## 基础 benchmark +## 单卡 -- 创建 TensorFlow 运行时环境后,以 resnet50 网络为例,计算其 batch_size=128 num_gpu=1 条件下不同精度下的性能。 + python3 ./benchmarks-master/scripts/tf_cnn_benchmarks/tf_cnn_benchmarks.py --data_format=NCHW --batch_size=128 --model=resnet50 --optimizer=momentum --variable_update=parameter_server --print_training_accuracy=true --nodistortions --num_gpus=1 --num_epochs=90 --weight_decay=1e-4 --data_dir=$data_dir_path --use_fp16=False --data_name=imagenet --train_dir=$save_checkpoint_path -### fp32 train +## 单机多卡 - python3 ./benchmarks-master/scripts/tf_cnn_benchmarks/tf_cnn_benchmarks.py --data_format=NCHW --batch_size=128 --model=resnet50 --save_model_steps=10020 --optimizer=momentum --variable_update=parameter_server --print_training_accuracy=true --eval_during_training_every_n_epochs=1 --nodistortions --num_gpus=1 --num_epochs=90 --weight_decay=1e-4 --data_dir=$data_dir_path --use_fp16=False --data_name=imagenet --train_dir=$save_checkpoint_path + python3 ./benchmarks-master/scripts/tf_cnn_benchmarks/tf_cnn_benchmarks.py --data_format=NCHW --batch_size=128 --model=resnet50 --optimizer=momentum --variable_update=parameter_server --print_training_accuracy=true --nodistortions --num_gpus=4 --num_epochs=90 --weight_decay=1e-4 --data_dir=$data_dir_path --use_fp16=False --data_name=imagenet --train_dir=$save_checkpoint_path -### fp16 train +## 分布式多卡 - python3 ./benchmarks-master/scripts/tf_cnn_benchmarks/tf_cnn_benchmarks.py --data_format=NCHW --batch_size=128 --model=resnet50 --save_model_steps=10020 --optimizer=momentum --variable_update=parameter_server --print_training_accuracy=true --eval_during_training_every_n_epochs=1 --nodistortions --num_gpus=1 --num_epochs=90 --weight_decay=1e-4 --data_dir=$data_dir_path --use_fp16=True --data_name=imagenet --train_dir=$save_checkpoint_path + mpirun -np ${num_gpu} --hostfile hostfile --bind-to none scripts-run/single_process.sh -## 大规模测试 +hostfile格式参考: -### 单卡 - - HIP_VISIBLE_DEVICES=0 python3 ./benchmarks-master/scripts/tf_cnn_benchmarks/tf_cnn_benchmarks.py --data_format=NCHW --batch_size=128 --model=resnet50 --save_model_steps=10020 --optimizer=momentum --variable_update=parameter_server --print_training_accuracy=true --eval_during_training_every_n_epochs=1 --nodistortions --num_gpus=1 --num_epochs=90 --weight_decay=1e-4 --data_dir=$data_dir_path --use_fp16=False --data_name=imagenet --train_dir=$save_checkpoint_path - -### 多卡 - - mpirun -np ${num_gpu} --hostfile hostfile --bind-to none scripts-run/single_process.sh + node1 slots=4 + node2 slots=4 # 参考资料 [https://github.com/tensorflow/benchmarks/tree/master/scripts/tf_cnn_benchmarks] [https://github.com/horovod/horovod] - diff --git a/TensorFlow2x/ComputeVision/Classification/scripts-run/single_process.sh b/TensorFlow2x/ComputeVision/Classification/scripts-run/single_process.sh index 2b1b209c..a1e00c11 100644 --- a/TensorFlow2x/ComputeVision/Classification/scripts-run/single_process.sh +++ b/TensorFlow2x/ComputeVision/Classification/scripts-run/single_process.sh @@ -1,6 +1,11 @@ #!/bin/bash + lrank=$OMPI_COMM_WORLD_LOCAL_RANK -APP=" python3 ./benchmarks-master/scripts/tf_cnn_benchmarks/tf_cnn_benchmarks.py --data_format=NCHW --batch_size=128 --model=resnet50 --optimizer=momentum --variable_update=horovod --print_training_accuracy=true --eval_during_training_every_n_epochs=1 --nodistortions --num_gpus=1 --num_epochs=90 --weight_decay=1e-4 --data_dir=$data_dir_path --use_fp16=False --data_name=imagenet --train_dir=$save_checkpoint_path +comm_rank=$OMPI_COMM_WORLD_RANK +comm_size=$OMPI_COMM_WORLD_SIZE + +APP=" python3 ./benchmarks-master/scripts/tf_cnn_benchmarks/tf_cnn_benchmarks.py --data_format=NCHW --batch_size=128 --model=resnet50 --optimizer=momentum --variable_update=horovod --print_training_accuracy=true --nodistortions --num_gpus=1 --num_epochs=90 --weight_decay=1e-4 --data_dir=$data_dir_path --use_fp16=False --data_name=imagenet --train_dir=$save_checkpoint_path + case ${lrank} in [0]) export HIP_VISIBLE_DEVICES=0,1,2,3 -- GitLab