From 6f6ecbec4e95394d8ac9089c571d70a9abf98ab9 Mon Sep 17 00:00:00 2001 From: guru4elephant <35550832+guru4elephant@users.noreply.github.com> Date: Mon, 8 Jul 2019 11:28:58 +0800 Subject: [PATCH] remove benchmark folder, since there is a benchmark repo already, distributed benchmark will be maintained in fleet repo (#18537) test=develop --- benchmark/.gitignore | 12 - benchmark/caffe/image/alexnet.prototxt | 347 --- benchmark/caffe/image/googlenet.prototxt | 2334 ----------------- benchmark/caffe/image/run.sh | 30 - benchmark/caffe/image/run_multi.sh | 24 - .../caffe/image/smallnet_mnist_cifar.prototxt | 198 -- benchmark/caffe/image/solver.prototxt | 10 - benchmark/figs/alexnet-4gpu.png | Bin 83783 -> 0 bytes benchmark/figs/alexnet-cpu-infer.png | Bin 15428 -> 0 bytes benchmark/figs/alexnet-cpu-train.png | Bin 16013 -> 0 bytes benchmark/figs/googlenet-4gpu.png | Bin 83784 -> 0 bytes benchmark/figs/googlenet-cpu-infer.png | Bin 14391 -> 0 bytes benchmark/figs/googlenet-cpu-train.png | Bin 19250 -> 0 bytes benchmark/figs/resnet-cpu-infer.png | Bin 14004 -> 0 bytes benchmark/figs/resnet-cpu-train.png | Bin 17991 -> 0 bytes benchmark/figs/rnn_lstm_4gpus.png | Bin 73243 -> 0 bytes benchmark/figs/rnn_lstm_cls.png | Bin 117634 -> 0 bytes benchmark/figs/vgg-cpu-infer.png | Bin 13985 -> 0 bytes benchmark/figs/vgg-cpu-train.png | Bin 17134 -> 0 bytes benchmark/fluid/Dockerfile | 30 - benchmark/fluid/README.md | 99 - benchmark/fluid/args.py | 151 -- benchmark/fluid/check_env.sh | 261 -- benchmark/fluid/fluid_benchmark.py | 369 --- benchmark/fluid/imagenet_reader.py | 344 --- benchmark/fluid/kube_gen_job.py | 210 -- benchmark/fluid/kube_templates/__init__.py | 66 - benchmark/fluid/kube_templates/pserver.py | 58 - benchmark/fluid/kube_templates/trainer.py | 70 - benchmark/fluid/models/__init__.py | 18 - benchmark/fluid/models/machine_translation.py | 217 -- benchmark/fluid/models/mnist.py | 117 - benchmark/fluid/models/resnet.py | 237 -- .../fluid/models/resnet_with_preprocess.py | 263 -- benchmark/fluid/models/se_resnext.py | 280 -- .../fluid/models/stacked_dynamic_lstm.py | 130 - benchmark/fluid/models/vgg.py | 120 - benchmark/fluid/recordio_converter.py | 164 -- benchmark/fluid/run.sh | 109 - benchmark/fluid/run_fluid_benchmark.sh | 9 - benchmark/tensorflow/image/alexnet.py | 312 --- .../tensorflow/image/alexnet_multi_gpu.py | 379 --- benchmark/tensorflow/image/googlenet.py | 325 --- .../tensorflow/image/googlenet_multi_gpu.py | 425 --- benchmark/tensorflow/image/run.sh | 30 - benchmark/tensorflow/image/run_multi.sh | 24 - .../tensorflow/image/smallnet_mnist_cifar.py | 318 --- benchmark/tensorflow/machine_translation.py | 624 ----- benchmark/tensorflow/mnist.py | 179 -- benchmark/tensorflow/resnet.py | 503 ---- benchmark/tensorflow/rnn/README.md | 5 - benchmark/tensorflow/rnn/reader.py | 106 - benchmark/tensorflow/rnn/requirements.txt | 1 - benchmark/tensorflow/rnn/rnn.py | 223 -- benchmark/tensorflow/rnn/rnn_multi_gpu.py | 322 --- benchmark/tensorflow/rnn/run.sh | 31 - benchmark/tensorflow/rnn/run_multi.sh | 29 - benchmark/tensorflow/stacked_dynamic_lstm.py | 218 -- benchmark/tensorflow/vgg.py | 323 --- 59 files changed, 10654 deletions(-) delete mode 100644 benchmark/.gitignore delete mode 100644 benchmark/caffe/image/alexnet.prototxt delete mode 100644 benchmark/caffe/image/googlenet.prototxt delete mode 100755 benchmark/caffe/image/run.sh delete mode 100755 benchmark/caffe/image/run_multi.sh delete mode 100644 benchmark/caffe/image/smallnet_mnist_cifar.prototxt delete mode 100644 benchmark/caffe/image/solver.prototxt delete mode 100644 benchmark/figs/alexnet-4gpu.png delete mode 100644 benchmark/figs/alexnet-cpu-infer.png delete mode 100644 benchmark/figs/alexnet-cpu-train.png delete mode 100644 benchmark/figs/googlenet-4gpu.png delete mode 100644 benchmark/figs/googlenet-cpu-infer.png delete mode 100644 benchmark/figs/googlenet-cpu-train.png delete mode 100644 benchmark/figs/resnet-cpu-infer.png delete mode 100644 benchmark/figs/resnet-cpu-train.png delete mode 100644 benchmark/figs/rnn_lstm_4gpus.png delete mode 100644 benchmark/figs/rnn_lstm_cls.png delete mode 100644 benchmark/figs/vgg-cpu-infer.png delete mode 100644 benchmark/figs/vgg-cpu-train.png delete mode 100644 benchmark/fluid/Dockerfile delete mode 100644 benchmark/fluid/README.md delete mode 100644 benchmark/fluid/args.py delete mode 100755 benchmark/fluid/check_env.sh delete mode 100644 benchmark/fluid/fluid_benchmark.py delete mode 100644 benchmark/fluid/imagenet_reader.py delete mode 100644 benchmark/fluid/kube_gen_job.py delete mode 100644 benchmark/fluid/kube_templates/__init__.py delete mode 100644 benchmark/fluid/kube_templates/pserver.py delete mode 100644 benchmark/fluid/kube_templates/trainer.py delete mode 100644 benchmark/fluid/models/__init__.py delete mode 100644 benchmark/fluid/models/machine_translation.py delete mode 100644 benchmark/fluid/models/mnist.py delete mode 100644 benchmark/fluid/models/resnet.py delete mode 100644 benchmark/fluid/models/resnet_with_preprocess.py delete mode 100644 benchmark/fluid/models/se_resnext.py delete mode 100644 benchmark/fluid/models/stacked_dynamic_lstm.py delete mode 100644 benchmark/fluid/models/vgg.py delete mode 100644 benchmark/fluid/recordio_converter.py delete mode 100755 benchmark/fluid/run.sh delete mode 100644 benchmark/fluid/run_fluid_benchmark.sh delete mode 100644 benchmark/tensorflow/image/alexnet.py delete mode 100644 benchmark/tensorflow/image/alexnet_multi_gpu.py delete mode 100644 benchmark/tensorflow/image/googlenet.py delete mode 100644 benchmark/tensorflow/image/googlenet_multi_gpu.py delete mode 100755 benchmark/tensorflow/image/run.sh delete mode 100755 benchmark/tensorflow/image/run_multi.sh delete mode 100644 benchmark/tensorflow/image/smallnet_mnist_cifar.py delete mode 100644 benchmark/tensorflow/machine_translation.py delete mode 100644 benchmark/tensorflow/mnist.py delete mode 100644 benchmark/tensorflow/resnet.py delete mode 100644 benchmark/tensorflow/rnn/README.md delete mode 100755 benchmark/tensorflow/rnn/reader.py delete mode 100644 benchmark/tensorflow/rnn/requirements.txt delete mode 100755 benchmark/tensorflow/rnn/rnn.py delete mode 100755 benchmark/tensorflow/rnn/rnn_multi_gpu.py delete mode 100755 benchmark/tensorflow/rnn/run.sh delete mode 100755 benchmark/tensorflow/rnn/run_multi.sh delete mode 100644 benchmark/tensorflow/stacked_dynamic_lstm.py delete mode 100644 benchmark/tensorflow/vgg.py diff --git a/benchmark/.gitignore b/benchmark/.gitignore deleted file mode 100644 index fb4114356d..0000000000 --- a/benchmark/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -paddle/image/logs -paddle/image/*.pyc -paddle/image/train.list -paddle/rnn/logs -paddle/rnn/*.pyc -paddle/rnn/imdb.pkl -caffe/image/logs -tensorflow/image/logs -tensorflow/rnn/logs -fluid/models/*.pyc -fluid/logs -fluid/nohup.out diff --git a/benchmark/caffe/image/alexnet.prototxt b/benchmark/caffe/image/alexnet.prototxt deleted file mode 100644 index aca184ddaf..0000000000 --- a/benchmark/caffe/image/alexnet.prototxt +++ /dev/null @@ -1,347 +0,0 @@ -name: "alexnet" -input: "data" -input_dim: 64 -input_dim: 3 -input_dim: 227 -input_dim: 227 -input: "label" -input_dim: 64 -input_dim: 1 -input_dim: 1 -input_dim: 1 -force_backward: true -layer { - name: "conv1" - type: "Convolution" - bottom: "data" - top: "conv1" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 96 - kernel_size: 11 - stride: 4 - weight_filler { - type: "gaussian" - std: 0.01 - } - bias_filler { - type: "constant" - value: 0 - } - } -} -layer { - name: "relu1" - type: "ReLU" - bottom: "conv1" - top: "conv1" -} -layer { - name: "norm1" - type: "LRN" - bottom: "conv1" - top: "norm1" - lrn_param { - local_size: 5 - alpha: 0.0001 - beta: 0.75 - } -} -layer { - name: "pool1" - type: "Pooling" - bottom: "norm1" - top: "pool1" - pooling_param { - pool: MAX - kernel_size: 3 - stride: 2 - } -} -layer { - name: "conv2" - type: "Convolution" - bottom: "pool1" - top: "conv2" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 256 - pad: 2 - kernel_size: 5 - group: 1 - weight_filler { - type: "gaussian" - std: 0.01 - } - bias_filler { - type: "constant" - value: 0.1 - } - } -} -layer { - name: "relu2" - type: "ReLU" - bottom: "conv2" - top: "conv2" -} -layer { - name: "norm2" - type: "LRN" - bottom: "conv2" - top: "norm2" - lrn_param { - local_size: 5 - alpha: 0.0001 - beta: 0.75 - } -} -layer { - name: "pool2" - type: "Pooling" - bottom: "norm2" - top: "pool2" - pooling_param { - pool: MAX - kernel_size: 3 - stride: 2 - } -} -layer { - name: "conv3" - type: "Convolution" - bottom: "pool2" - top: "conv3" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 384 - pad: 1 - kernel_size: 3 - weight_filler { - type: "gaussian" - std: 0.01 - } - bias_filler { - type: "constant" - value: 0 - } - } -} -layer { - name: "relu3" - type: "ReLU" - bottom: "conv3" - top: "conv3" -} -layer { - name: "conv4" - type: "Convolution" - bottom: "conv3" - top: "conv4" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 384 - pad: 1 - kernel_size: 3 - group: 1 - weight_filler { - type: "gaussian" - std: 0.01 - } - bias_filler { - type: "constant" - value: 0.1 - } - } -} -layer { - name: "relu4" - type: "ReLU" - bottom: "conv4" - top: "conv4" -} -layer { - name: "conv5" - type: "Convolution" - bottom: "conv4" - top: "conv5" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 256 - pad: 1 - kernel_size: 3 - group: 1 - weight_filler { - type: "gaussian" - std: 0.01 - } - bias_filler { - type: "constant" - value: 0.1 - } - } -} -layer { - name: "relu5" - type: "ReLU" - bottom: "conv5" - top: "conv5" -} -layer { - name: "pool5" - type: "Pooling" - bottom: "conv5" - top: "pool5" - pooling_param { - pool: MAX - kernel_size: 3 - stride: 2 - } -} -layer { - name: "fc6" - type: "InnerProduct" - bottom: "pool5" - top: "fc6" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - inner_product_param { - num_output: 4096 - weight_filler { - type: "gaussian" - std: 0.005 - } - bias_filler { - type: "constant" - value: 0.1 - } - } -} -layer { - name: "relu6" - type: "ReLU" - bottom: "fc6" - top: "fc6" -} -layer { - name: "drop6" - type: "Dropout" - bottom: "fc6" - top: "fc6" - dropout_param { - dropout_ratio: 0.5 - } -} -layer { - name: "fc7" - type: "InnerProduct" - bottom: "fc6" - top: "fc7" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - inner_product_param { - num_output: 4096 - weight_filler { - type: "gaussian" - std: 0.005 - } - bias_filler { - type: "constant" - value: 0.1 - } - } -} -layer { - name: "relu7" - type: "ReLU" - bottom: "fc7" - top: "fc7" -} -layer { - name: "drop7" - type: "Dropout" - bottom: "fc7" - top: "fc7" - dropout_param { - dropout_ratio: 0.5 - } -} -layer { - name: "fc8" - type: "InnerProduct" - bottom: "fc7" - top: "fc8" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - inner_product_param { - num_output: 1000 - weight_filler { - type: "gaussian" - std: 0.01 - } - bias_filler { - type: "constant" - value: 0 - } - } -} -layer { - name: "loss" - type: "SoftmaxWithLoss" - bottom: "fc8" - bottom: "label" - top: "loss" -} diff --git a/benchmark/caffe/image/googlenet.prototxt b/benchmark/caffe/image/googlenet.prototxt deleted file mode 100644 index c5f3b4fe3e..0000000000 --- a/benchmark/caffe/image/googlenet.prototxt +++ /dev/null @@ -1,2334 +0,0 @@ -name: "googlenet" -input: "data" -input_dim: 128 -input_dim: 3 -input_dim: 224 -input_dim: 224 -input: "label" -input_dim: 128 -input_dim: 1 -input_dim: 1 -input_dim: 1 -layer { - name: "conv1/7x7_s2" - type: "Convolution" - bottom: "data" - top: "conv1/7x7_s2" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 64 - pad: 3 - kernel_size: 7 - stride: 2 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "conv1/relu_7x7" - type: "ReLU" - bottom: "conv1/7x7_s2" - top: "conv1/7x7_s2" -} -layer { - name: "pool1/3x3_s2" - type: "Pooling" - bottom: "conv1/7x7_s2" - top: "pool1/3x3_s2" - pooling_param { - pool: MAX - kernel_size: 3 - stride: 2 - } -} -#layer { -# name: "pool1/norm1" -# type: "LRN" -# bottom: "pool1/3x3_s2" -# top: "pool1/norm1" -# lrn_param { -# local_size: 5 -# alpha: 0.0001 -# beta: 0.75 -# } -#} -layer { - name: "conv2/3x3_reduce" - type: "Convolution" -# bottom: "pool1/norm1" - bottom: "pool1/3x3_s2" - top: "conv2/3x3_reduce" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 64 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "conv2/relu_3x3_reduce" - type: "ReLU" - bottom: "conv2/3x3_reduce" - top: "conv2/3x3_reduce" -} -layer { - name: "conv2/3x3" - type: "Convolution" - bottom: "conv2/3x3_reduce" - top: "conv2/3x3" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 192 - pad: 1 - kernel_size: 3 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "conv2/relu_3x3" - type: "ReLU" - bottom: "conv2/3x3" - top: "conv2/3x3" -} -#layer { -# name: "conv2/norm2" -# type: "LRN" -# bottom: "conv2/3x3" -# top: "conv2/norm2" -# lrn_param { -# local_size: 5 -# alpha: 0.0001 -# beta: 0.75 -# } -#} -layer { - name: "pool2/3x3_s2" - type: "Pooling" -# bottom: "conv2/norm2" - bottom: "conv2/3x3" - top: "pool2/3x3_s2" - pooling_param { - pool: MAX - kernel_size: 3 - stride: 2 - } -} -layer { - name: "inception_3a/1x1" - type: "Convolution" - bottom: "pool2/3x3_s2" - top: "inception_3a/1x1" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 64 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_3a/relu_1x1" - type: "ReLU" - bottom: "inception_3a/1x1" - top: "inception_3a/1x1" -} -layer { - name: "inception_3a/3x3_reduce" - type: "Convolution" - bottom: "pool2/3x3_s2" - top: "inception_3a/3x3_reduce" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 96 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_3a/relu_3x3_reduce" - type: "ReLU" - bottom: "inception_3a/3x3_reduce" - top: "inception_3a/3x3_reduce" -} -layer { - name: "inception_3a/3x3" - type: "Convolution" - bottom: "inception_3a/3x3_reduce" - top: "inception_3a/3x3" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 128 - pad: 1 - kernel_size: 3 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_3a/relu_3x3" - type: "ReLU" - bottom: "inception_3a/3x3" - top: "inception_3a/3x3" -} -layer { - name: "inception_3a/5x5_reduce" - type: "Convolution" - bottom: "pool2/3x3_s2" - top: "inception_3a/5x5_reduce" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 16 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_3a/relu_5x5_reduce" - type: "ReLU" - bottom: "inception_3a/5x5_reduce" - top: "inception_3a/5x5_reduce" -} -layer { - name: "inception_3a/5x5" - type: "Convolution" - bottom: "inception_3a/5x5_reduce" - top: "inception_3a/5x5" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 32 - pad: 2 - kernel_size: 5 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_3a/relu_5x5" - type: "ReLU" - bottom: "inception_3a/5x5" - top: "inception_3a/5x5" -} -layer { - name: "inception_3a/pool" - type: "Pooling" - bottom: "pool2/3x3_s2" - top: "inception_3a/pool" - pooling_param { - pool: MAX - kernel_size: 3 - stride: 1 - pad: 1 - } -} -layer { - name: "inception_3a/pool_proj" - type: "Convolution" - bottom: "inception_3a/pool" - top: "inception_3a/pool_proj" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 32 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_3a/relu_pool_proj" - type: "ReLU" - bottom: "inception_3a/pool_proj" - top: "inception_3a/pool_proj" -} -layer { - name: "inception_3a/output" - type: "Concat" - bottom: "inception_3a/1x1" - bottom: "inception_3a/3x3" - bottom: "inception_3a/5x5" - bottom: "inception_3a/pool_proj" - top: "inception_3a/output" -} -layer { - name: "inception_3b/1x1" - type: "Convolution" - bottom: "inception_3a/output" - top: "inception_3b/1x1" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 128 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_3b/relu_1x1" - type: "ReLU" - bottom: "inception_3b/1x1" - top: "inception_3b/1x1" -} -layer { - name: "inception_3b/3x3_reduce" - type: "Convolution" - bottom: "inception_3a/output" - top: "inception_3b/3x3_reduce" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 128 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_3b/relu_3x3_reduce" - type: "ReLU" - bottom: "inception_3b/3x3_reduce" - top: "inception_3b/3x3_reduce" -} -layer { - name: "inception_3b/3x3" - type: "Convolution" - bottom: "inception_3b/3x3_reduce" - top: "inception_3b/3x3" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 192 - pad: 1 - kernel_size: 3 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_3b/relu_3x3" - type: "ReLU" - bottom: "inception_3b/3x3" - top: "inception_3b/3x3" -} -layer { - name: "inception_3b/5x5_reduce" - type: "Convolution" - bottom: "inception_3a/output" - top: "inception_3b/5x5_reduce" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 32 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_3b/relu_5x5_reduce" - type: "ReLU" - bottom: "inception_3b/5x5_reduce" - top: "inception_3b/5x5_reduce" -} -layer { - name: "inception_3b/5x5" - type: "Convolution" - bottom: "inception_3b/5x5_reduce" - top: "inception_3b/5x5" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 96 - pad: 2 - kernel_size: 5 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_3b/relu_5x5" - type: "ReLU" - bottom: "inception_3b/5x5" - top: "inception_3b/5x5" -} -layer { - name: "inception_3b/pool" - type: "Pooling" - bottom: "inception_3a/output" - top: "inception_3b/pool" - pooling_param { - pool: MAX - kernel_size: 3 - stride: 1 - pad: 1 - } -} -layer { - name: "inception_3b/pool_proj" - type: "Convolution" - bottom: "inception_3b/pool" - top: "inception_3b/pool_proj" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 64 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_3b/relu_pool_proj" - type: "ReLU" - bottom: "inception_3b/pool_proj" - top: "inception_3b/pool_proj" -} -layer { - name: "inception_3b/output" - type: "Concat" - bottom: "inception_3b/1x1" - bottom: "inception_3b/3x3" - bottom: "inception_3b/5x5" - bottom: "inception_3b/pool_proj" - top: "inception_3b/output" -} -layer { - name: "pool3/3x3_s2" - type: "Pooling" - bottom: "inception_3b/output" - top: "pool3/3x3_s2" - pooling_param { - pool: MAX - kernel_size: 3 - stride: 2 - } -} -layer { - name: "inception_4a/1x1" - type: "Convolution" - bottom: "pool3/3x3_s2" - top: "inception_4a/1x1" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 192 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4a/relu_1x1" - type: "ReLU" - bottom: "inception_4a/1x1" - top: "inception_4a/1x1" -} -layer { - name: "inception_4a/3x3_reduce" - type: "Convolution" - bottom: "pool3/3x3_s2" - top: "inception_4a/3x3_reduce" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 96 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4a/relu_3x3_reduce" - type: "ReLU" - bottom: "inception_4a/3x3_reduce" - top: "inception_4a/3x3_reduce" -} -layer { - name: "inception_4a/3x3" - type: "Convolution" - bottom: "inception_4a/3x3_reduce" - top: "inception_4a/3x3" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 208 - pad: 1 - kernel_size: 3 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4a/relu_3x3" - type: "ReLU" - bottom: "inception_4a/3x3" - top: "inception_4a/3x3" -} -layer { - name: "inception_4a/5x5_reduce" - type: "Convolution" - bottom: "pool3/3x3_s2" - top: "inception_4a/5x5_reduce" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 16 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4a/relu_5x5_reduce" - type: "ReLU" - bottom: "inception_4a/5x5_reduce" - top: "inception_4a/5x5_reduce" -} -layer { - name: "inception_4a/5x5" - type: "Convolution" - bottom: "inception_4a/5x5_reduce" - top: "inception_4a/5x5" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 48 - pad: 2 - kernel_size: 5 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4a/relu_5x5" - type: "ReLU" - bottom: "inception_4a/5x5" - top: "inception_4a/5x5" -} -layer { - name: "inception_4a/pool" - type: "Pooling" - bottom: "pool3/3x3_s2" - top: "inception_4a/pool" - pooling_param { - pool: MAX - kernel_size: 3 - stride: 1 - pad: 1 - } -} -layer { - name: "inception_4a/pool_proj" - type: "Convolution" - bottom: "inception_4a/pool" - top: "inception_4a/pool_proj" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 64 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4a/relu_pool_proj" - type: "ReLU" - bottom: "inception_4a/pool_proj" - top: "inception_4a/pool_proj" -} -layer { - name: "inception_4a/output" - type: "Concat" - bottom: "inception_4a/1x1" - bottom: "inception_4a/3x3" - bottom: "inception_4a/5x5" - bottom: "inception_4a/pool_proj" - top: "inception_4a/output" -} -#layer { -# name: "loss1/ave_pool" -# type: "Pooling" -# bottom: "inception_4a/output" -# top: "loss1/ave_pool" -# pooling_param { -# pool: AVE -# kernel_size: 5 -# stride: 3 -# } -#} -#layer { -# name: "loss1/conv" -# type: "Convolution" -# bottom: "loss1/ave_pool" -# top: "loss1/conv" -# param { -# lr_mult: 1 -# decay_mult: 1 -# } -# param { -# lr_mult: 2 -# decay_mult: 0 -# } -# convolution_param { -# num_output: 128 -# kernel_size: 1 -# weight_filler { -# type: "xavier" -# } -# bias_filler { -# type: "constant" -# value: 0.2 -# } -# } -#} -#layer { -# name: "loss1/relu_conv" -# type: "ReLU" -# bottom: "loss1/conv" -# top: "loss1/conv" -#} -#layer { -# name: "loss1/fc" -# type: "InnerProduct" -# bottom: "loss1/conv" -# top: "loss1/fc" -# param { -# lr_mult: 1 -# decay_mult: 1 -# } -# param { -# lr_mult: 2 -# decay_mult: 0 -# } -# inner_product_param { -# num_output: 1024 -# weight_filler { -# type: "xavier" -# } -# bias_filler { -# type: "constant" -# value: 0.2 -# } -# } -#} -#layer { -# name: "loss1/relu_fc" -# type: "ReLU" -# bottom: "loss1/fc" -# top: "loss1/fc" -#} -#layer { -# name: "loss1/drop_fc" -# type: "Dropout" -# bottom: "loss1/fc" -# top: "loss1/fc" -# dropout_param { -# dropout_ratio: 0.7 -# } -#} -#layer { -# name: "loss1/classifier" -# type: "InnerProduct" -# bottom: "loss1/fc" -# top: "loss1/classifier" -# param { -# lr_mult: 1 -# decay_mult: 1 -# } -# param { -# lr_mult: 2 -# decay_mult: 0 -# } -# inner_product_param { -# num_output: 1000 -# weight_filler { -# type: "xavier" -# } -# bias_filler { -# type: "constant" -# value: 0 -# } -# } -#} -#layer { -# name: "loss1/loss" -# type: "SoftmaxWithLoss" -# bottom: "loss1/classifier" -# bottom: "label" -# top: "loss1/loss1" -# loss_weight: 0.3 -#} -layer { - name: "inception_4b/1x1" - type: "Convolution" - bottom: "inception_4a/output" - top: "inception_4b/1x1" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 160 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4b/relu_1x1" - type: "ReLU" - bottom: "inception_4b/1x1" - top: "inception_4b/1x1" -} -layer { - name: "inception_4b/3x3_reduce" - type: "Convolution" - bottom: "inception_4a/output" - top: "inception_4b/3x3_reduce" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 112 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4b/relu_3x3_reduce" - type: "ReLU" - bottom: "inception_4b/3x3_reduce" - top: "inception_4b/3x3_reduce" -} -layer { - name: "inception_4b/3x3" - type: "Convolution" - bottom: "inception_4b/3x3_reduce" - top: "inception_4b/3x3" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 224 - pad: 1 - kernel_size: 3 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4b/relu_3x3" - type: "ReLU" - bottom: "inception_4b/3x3" - top: "inception_4b/3x3" -} -layer { - name: "inception_4b/5x5_reduce" - type: "Convolution" - bottom: "inception_4a/output" - top: "inception_4b/5x5_reduce" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 24 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4b/relu_5x5_reduce" - type: "ReLU" - bottom: "inception_4b/5x5_reduce" - top: "inception_4b/5x5_reduce" -} -layer { - name: "inception_4b/5x5" - type: "Convolution" - bottom: "inception_4b/5x5_reduce" - top: "inception_4b/5x5" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 64 - pad: 2 - kernel_size: 5 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4b/relu_5x5" - type: "ReLU" - bottom: "inception_4b/5x5" - top: "inception_4b/5x5" -} -layer { - name: "inception_4b/pool" - type: "Pooling" - bottom: "inception_4a/output" - top: "inception_4b/pool" - pooling_param { - pool: MAX - kernel_size: 3 - stride: 1 - pad: 1 - } -} -layer { - name: "inception_4b/pool_proj" - type: "Convolution" - bottom: "inception_4b/pool" - top: "inception_4b/pool_proj" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 64 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4b/relu_pool_proj" - type: "ReLU" - bottom: "inception_4b/pool_proj" - top: "inception_4b/pool_proj" -} -layer { - name: "inception_4b/output" - type: "Concat" - bottom: "inception_4b/1x1" - bottom: "inception_4b/3x3" - bottom: "inception_4b/5x5" - bottom: "inception_4b/pool_proj" - top: "inception_4b/output" -} -layer { - name: "inception_4c/1x1" - type: "Convolution" - bottom: "inception_4b/output" - top: "inception_4c/1x1" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 128 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4c/relu_1x1" - type: "ReLU" - bottom: "inception_4c/1x1" - top: "inception_4c/1x1" -} -layer { - name: "inception_4c/3x3_reduce" - type: "Convolution" - bottom: "inception_4b/output" - top: "inception_4c/3x3_reduce" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 128 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4c/relu_3x3_reduce" - type: "ReLU" - bottom: "inception_4c/3x3_reduce" - top: "inception_4c/3x3_reduce" -} -layer { - name: "inception_4c/3x3" - type: "Convolution" - bottom: "inception_4c/3x3_reduce" - top: "inception_4c/3x3" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 256 - pad: 1 - kernel_size: 3 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4c/relu_3x3" - type: "ReLU" - bottom: "inception_4c/3x3" - top: "inception_4c/3x3" -} -layer { - name: "inception_4c/5x5_reduce" - type: "Convolution" - bottom: "inception_4b/output" - top: "inception_4c/5x5_reduce" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 24 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4c/relu_5x5_reduce" - type: "ReLU" - bottom: "inception_4c/5x5_reduce" - top: "inception_4c/5x5_reduce" -} -layer { - name: "inception_4c/5x5" - type: "Convolution" - bottom: "inception_4c/5x5_reduce" - top: "inception_4c/5x5" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 64 - pad: 2 - kernel_size: 5 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4c/relu_5x5" - type: "ReLU" - bottom: "inception_4c/5x5" - top: "inception_4c/5x5" -} -layer { - name: "inception_4c/pool" - type: "Pooling" - bottom: "inception_4b/output" - top: "inception_4c/pool" - pooling_param { - pool: MAX - kernel_size: 3 - stride: 1 - pad: 1 - } -} -layer { - name: "inception_4c/pool_proj" - type: "Convolution" - bottom: "inception_4c/pool" - top: "inception_4c/pool_proj" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 64 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4c/relu_pool_proj" - type: "ReLU" - bottom: "inception_4c/pool_proj" - top: "inception_4c/pool_proj" -} -layer { - name: "inception_4c/output" - type: "Concat" - bottom: "inception_4c/1x1" - bottom: "inception_4c/3x3" - bottom: "inception_4c/5x5" - bottom: "inception_4c/pool_proj" - top: "inception_4c/output" -} -layer { - name: "inception_4d/1x1" - type: "Convolution" - bottom: "inception_4c/output" - top: "inception_4d/1x1" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 112 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4d/relu_1x1" - type: "ReLU" - bottom: "inception_4d/1x1" - top: "inception_4d/1x1" -} -layer { - name: "inception_4d/3x3_reduce" - type: "Convolution" - bottom: "inception_4c/output" - top: "inception_4d/3x3_reduce" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 144 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4d/relu_3x3_reduce" - type: "ReLU" - bottom: "inception_4d/3x3_reduce" - top: "inception_4d/3x3_reduce" -} -layer { - name: "inception_4d/3x3" - type: "Convolution" - bottom: "inception_4d/3x3_reduce" - top: "inception_4d/3x3" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 288 - pad: 1 - kernel_size: 3 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4d/relu_3x3" - type: "ReLU" - bottom: "inception_4d/3x3" - top: "inception_4d/3x3" -} -layer { - name: "inception_4d/5x5_reduce" - type: "Convolution" - bottom: "inception_4c/output" - top: "inception_4d/5x5_reduce" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 32 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4d/relu_5x5_reduce" - type: "ReLU" - bottom: "inception_4d/5x5_reduce" - top: "inception_4d/5x5_reduce" -} -layer { - name: "inception_4d/5x5" - type: "Convolution" - bottom: "inception_4d/5x5_reduce" - top: "inception_4d/5x5" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 64 - pad: 2 - kernel_size: 5 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4d/relu_5x5" - type: "ReLU" - bottom: "inception_4d/5x5" - top: "inception_4d/5x5" -} -layer { - name: "inception_4d/pool" - type: "Pooling" - bottom: "inception_4c/output" - top: "inception_4d/pool" - pooling_param { - pool: MAX - kernel_size: 3 - stride: 1 - pad: 1 - } -} -layer { - name: "inception_4d/pool_proj" - type: "Convolution" - bottom: "inception_4d/pool" - top: "inception_4d/pool_proj" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 64 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4d/relu_pool_proj" - type: "ReLU" - bottom: "inception_4d/pool_proj" - top: "inception_4d/pool_proj" -} -layer { - name: "inception_4d/output" - type: "Concat" - bottom: "inception_4d/1x1" - bottom: "inception_4d/3x3" - bottom: "inception_4d/5x5" - bottom: "inception_4d/pool_proj" - top: "inception_4d/output" -} -#layer { -# name: "loss2/ave_pool" -# type: "Pooling" -# bottom: "inception_4d/output" -# top: "loss2/ave_pool" -# pooling_param { -# pool: AVE -# kernel_size: 5 -# stride: 3 -# } -#} -#layer { -# name: "loss2/conv" -# type: "Convolution" -# bottom: "loss2/ave_pool" -# top: "loss2/conv" -# param { -# lr_mult: 1 -# decay_mult: 1 -# } -# param { -# lr_mult: 2 -# decay_mult: 0 -# } -# convolution_param { -# num_output: 128 -# kernel_size: 1 -# weight_filler { -# type: "xavier" -# } -# bias_filler { -# type: "constant" -# value: 0.2 -# } -# } -#} -#layer { -# name: "loss2/relu_conv" -# type: "ReLU" -# bottom: "loss2/conv" -# top: "loss2/conv" -#} -#layer { -# name: "loss2/fc" -# type: "InnerProduct" -# bottom: "loss2/conv" -# top: "loss2/fc" -# param { -# lr_mult: 1 -# decay_mult: 1 -# } -# param { -# lr_mult: 2 -# decay_mult: 0 -# } -# inner_product_param { -# num_output: 1024 -# weight_filler { -# type: "xavier" -# } -# bias_filler { -# type: "constant" -# value: 0.2 -# } -# } -#} -#layer { -# name: "loss2/relu_fc" -# type: "ReLU" -# bottom: "loss2/fc" -# top: "loss2/fc" -#} -#layer { -# name: "loss2/drop_fc" -# type: "Dropout" -# bottom: "loss2/fc" -# top: "loss2/fc" -# dropout_param { -# dropout_ratio: 0.7 -# } -#} -#layer { -# name: "loss2/classifier" -# type: "InnerProduct" -# bottom: "loss2/fc" -# top: "loss2/classifier" -# param { -# lr_mult: 1 -# decay_mult: 1 -# } -# param { -# lr_mult: 2 -# decay_mult: 0 -# } -# inner_product_param { -# num_output: 1000 -# weight_filler { -# type: "xavier" -# } -# bias_filler { -# type: "constant" -# value: 0 -# } -# } -#} -#layer { -# name: "loss2/loss" -# type: "SoftmaxWithLoss" -# bottom: "loss2/classifier" -# bottom: "label" -# top: "loss2/loss1" -# loss_weight: 0.3 -#} -layer { - name: "inception_4e/1x1" - type: "Convolution" - bottom: "inception_4d/output" - top: "inception_4e/1x1" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 256 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4e/relu_1x1" - type: "ReLU" - bottom: "inception_4e/1x1" - top: "inception_4e/1x1" -} -layer { - name: "inception_4e/3x3_reduce" - type: "Convolution" - bottom: "inception_4d/output" - top: "inception_4e/3x3_reduce" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 160 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4e/relu_3x3_reduce" - type: "ReLU" - bottom: "inception_4e/3x3_reduce" - top: "inception_4e/3x3_reduce" -} -layer { - name: "inception_4e/3x3" - type: "Convolution" - bottom: "inception_4e/3x3_reduce" - top: "inception_4e/3x3" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 320 - pad: 1 - kernel_size: 3 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4e/relu_3x3" - type: "ReLU" - bottom: "inception_4e/3x3" - top: "inception_4e/3x3" -} -layer { - name: "inception_4e/5x5_reduce" - type: "Convolution" - bottom: "inception_4d/output" - top: "inception_4e/5x5_reduce" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 32 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4e/relu_5x5_reduce" - type: "ReLU" - bottom: "inception_4e/5x5_reduce" - top: "inception_4e/5x5_reduce" -} -layer { - name: "inception_4e/5x5" - type: "Convolution" - bottom: "inception_4e/5x5_reduce" - top: "inception_4e/5x5" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 128 - pad: 2 - kernel_size: 5 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4e/relu_5x5" - type: "ReLU" - bottom: "inception_4e/5x5" - top: "inception_4e/5x5" -} -layer { - name: "inception_4e/pool" - type: "Pooling" - bottom: "inception_4d/output" - top: "inception_4e/pool" - pooling_param { - pool: MAX - kernel_size: 3 - stride: 1 - pad: 1 - } -} -layer { - name: "inception_4e/pool_proj" - type: "Convolution" - bottom: "inception_4e/pool" - top: "inception_4e/pool_proj" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 128 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_4e/relu_pool_proj" - type: "ReLU" - bottom: "inception_4e/pool_proj" - top: "inception_4e/pool_proj" -} -layer { - name: "inception_4e/output" - type: "Concat" - bottom: "inception_4e/1x1" - bottom: "inception_4e/3x3" - bottom: "inception_4e/5x5" - bottom: "inception_4e/pool_proj" - top: "inception_4e/output" -} -layer { - name: "pool4/3x3_s2" - type: "Pooling" - bottom: "inception_4e/output" - top: "pool4/3x3_s2" - pooling_param { - pool: MAX - kernel_size: 3 - stride: 2 - } -} -layer { - name: "inception_5a/1x1" - type: "Convolution" - bottom: "pool4/3x3_s2" - top: "inception_5a/1x1" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 256 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_5a/relu_1x1" - type: "ReLU" - bottom: "inception_5a/1x1" - top: "inception_5a/1x1" -} -layer { - name: "inception_5a/3x3_reduce" - type: "Convolution" - bottom: "pool4/3x3_s2" - top: "inception_5a/3x3_reduce" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 160 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_5a/relu_3x3_reduce" - type: "ReLU" - bottom: "inception_5a/3x3_reduce" - top: "inception_5a/3x3_reduce" -} -layer { - name: "inception_5a/3x3" - type: "Convolution" - bottom: "inception_5a/3x3_reduce" - top: "inception_5a/3x3" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 320 - pad: 1 - kernel_size: 3 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_5a/relu_3x3" - type: "ReLU" - bottom: "inception_5a/3x3" - top: "inception_5a/3x3" -} -layer { - name: "inception_5a/5x5_reduce" - type: "Convolution" - bottom: "pool4/3x3_s2" - top: "inception_5a/5x5_reduce" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 32 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_5a/relu_5x5_reduce" - type: "ReLU" - bottom: "inception_5a/5x5_reduce" - top: "inception_5a/5x5_reduce" -} -layer { - name: "inception_5a/5x5" - type: "Convolution" - bottom: "inception_5a/5x5_reduce" - top: "inception_5a/5x5" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 128 - pad: 2 - kernel_size: 5 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_5a/relu_5x5" - type: "ReLU" - bottom: "inception_5a/5x5" - top: "inception_5a/5x5" -} -layer { - name: "inception_5a/pool" - type: "Pooling" - bottom: "pool4/3x3_s2" - top: "inception_5a/pool" - pooling_param { - pool: MAX - kernel_size: 3 - stride: 1 - pad: 1 - } -} -layer { - name: "inception_5a/pool_proj" - type: "Convolution" - bottom: "inception_5a/pool" - top: "inception_5a/pool_proj" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 128 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_5a/relu_pool_proj" - type: "ReLU" - bottom: "inception_5a/pool_proj" - top: "inception_5a/pool_proj" -} -layer { - name: "inception_5a/output" - type: "Concat" - bottom: "inception_5a/1x1" - bottom: "inception_5a/3x3" - bottom: "inception_5a/5x5" - bottom: "inception_5a/pool_proj" - top: "inception_5a/output" -} -layer { - name: "inception_5b/1x1" - type: "Convolution" - bottom: "inception_5a/output" - top: "inception_5b/1x1" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 384 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_5b/relu_1x1" - type: "ReLU" - bottom: "inception_5b/1x1" - top: "inception_5b/1x1" -} -layer { - name: "inception_5b/3x3_reduce" - type: "Convolution" - bottom: "inception_5a/output" - top: "inception_5b/3x3_reduce" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 192 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_5b/relu_3x3_reduce" - type: "ReLU" - bottom: "inception_5b/3x3_reduce" - top: "inception_5b/3x3_reduce" -} -layer { - name: "inception_5b/3x3" - type: "Convolution" - bottom: "inception_5b/3x3_reduce" - top: "inception_5b/3x3" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 384 - pad: 1 - kernel_size: 3 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_5b/relu_3x3" - type: "ReLU" - bottom: "inception_5b/3x3" - top: "inception_5b/3x3" -} -layer { - name: "inception_5b/5x5_reduce" - type: "Convolution" - bottom: "inception_5a/output" - top: "inception_5b/5x5_reduce" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 48 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_5b/relu_5x5_reduce" - type: "ReLU" - bottom: "inception_5b/5x5_reduce" - top: "inception_5b/5x5_reduce" -} -layer { - name: "inception_5b/5x5" - type: "Convolution" - bottom: "inception_5b/5x5_reduce" - top: "inception_5b/5x5" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 128 - pad: 2 - kernel_size: 5 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_5b/relu_5x5" - type: "ReLU" - bottom: "inception_5b/5x5" - top: "inception_5b/5x5" -} -layer { - name: "inception_5b/pool" - type: "Pooling" - bottom: "inception_5a/output" - top: "inception_5b/pool" - pooling_param { - pool: MAX - kernel_size: 3 - stride: 1 - pad: 1 - } -} -layer { - name: "inception_5b/pool_proj" - type: "Convolution" - bottom: "inception_5b/pool" - top: "inception_5b/pool_proj" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - convolution_param { - num_output: 128 - kernel_size: 1 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0.2 - } - } -} -layer { - name: "inception_5b/relu_pool_proj" - type: "ReLU" - bottom: "inception_5b/pool_proj" - top: "inception_5b/pool_proj" -} -layer { - name: "inception_5b/output" - type: "Concat" - bottom: "inception_5b/1x1" - bottom: "inception_5b/3x3" - bottom: "inception_5b/5x5" - bottom: "inception_5b/pool_proj" - top: "inception_5b/output" -} -layer { - name: "pool5/7x7_s1" - type: "Pooling" - bottom: "inception_5b/output" - top: "pool5/7x7_s1" - pooling_param { - pool: AVE - kernel_size: 7 - stride: 1 - } -} -layer { - name: "pool5/drop_7x7_s1" - type: "Dropout" - bottom: "pool5/7x7_s1" - top: "pool5/7x7_s1" - dropout_param { - dropout_ratio: 0.4 - } -} -layer { - name: "loss3/classifier" - type: "InnerProduct" - bottom: "pool5/7x7_s1" - top: "loss3/classifier" - param { - lr_mult: 1 - decay_mult: 1 - } - param { - lr_mult: 2 - decay_mult: 0 - } - inner_product_param { - num_output: 1000 - weight_filler { - type: "xavier" - } - bias_filler { - type: "constant" - value: 0 - } - } -} -layer { - name: "loss3/loss3" - type: "SoftmaxWithLoss" - bottom: "loss3/classifier" - bottom: "label" - top: "loss3/loss3" - loss_weight: 1 -} diff --git a/benchmark/caffe/image/run.sh b/benchmark/caffe/image/run.sh deleted file mode 100755 index aa9ac20ca5..0000000000 --- a/benchmark/caffe/image/run.sh +++ /dev/null @@ -1,30 +0,0 @@ -set -e - -function test() { - cfg=$1 - batch=$2 - prefix=$3 - sed -i "/input: \"data\"/{n;s/^input_dim.*/input_dim: $batch/g}" $cfg - sed -i "/input: \"label\"/{n;s/^input_dim.*/input_dim: $batch/g}" $cfg - caffe time --model=$cfg --iterations=50 --gpu 0 > logs/$prefix-1gpu-batch${batch}.log 2>&1 -} - -if [ ! -d "logs" ]; then - mkdir logs -fi - -# alexnet -test alexnet.prototxt 64 alexnet -test alexnet.prototxt 128 alexnet -test alexnet.prototxt 256 alexnet -test alexnet.prototxt 512 alexnet - -# googlenet -test googlenet.prototxt 64 googlenet -test googlenet.prototxt 128 googlenet - -# small net -test smallnet_mnist_cifar.prototxt 64 smallnet -test smallnet_mnist_cifar.prototxt 128 smallnet -test smallnet_mnist_cifar.prototxt 256 smallnet -test smallnet_mnist_cifar.prototxt 512 smallnet diff --git a/benchmark/caffe/image/run_multi.sh b/benchmark/caffe/image/run_multi.sh deleted file mode 100755 index 9a0a71bc18..0000000000 --- a/benchmark/caffe/image/run_multi.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -set -e - -function test() { - cfg=$1 - batch=$2 - prefix=$3 - batch_per_gpu=`expr ${batch} / 4` - sed -i "/input: \"data\"/{n;s/^input_dim.*/input_dim: ${batch_per_gpu}/g}" $cfg - sed -i "/input: \"label\"/{n;s/^input_dim.*/input_dim: ${batch_per_gpu}/g}" $cfg - sed -i "1c\net : \"${cfg}\"" solver.prototxt - caffe train --solver=solver.prototxt -gpu 0,1,2,3 > logs/${prefix}-4gpu-batch${batch}.log 2>&1 -} - -if [ ! -d "logs" ]; then - mkdir logs -fi - -# alexnet -test alexnet.prototxt 512 alexnet -test alexnet.prototxt 1024 alexnet - -# googlnet -test googlenet.prototxt 512 googlenet diff --git a/benchmark/caffe/image/smallnet_mnist_cifar.prototxt b/benchmark/caffe/image/smallnet_mnist_cifar.prototxt deleted file mode 100644 index 3cb0e32bbf..0000000000 --- a/benchmark/caffe/image/smallnet_mnist_cifar.prototxt +++ /dev/null @@ -1,198 +0,0 @@ -name: "mnist/cifar" -input: "data" -input_dim: 128 -input_dim: 3 -input_dim: 32 -input_dim: 32 -input: "label" -input_dim: 128 -input_dim: 1 -input_dim: 1 -input_dim: 1 -layer { - name: "conv1" - type: "Convolution" - bottom: "data" - top: "conv1" - param { - lr_mult: 1 - } - param { - lr_mult: 2 - } - convolution_param { - num_output: 32 - pad: 2 - kernel_size: 5 - stride: 1 - weight_filler { - type: "gaussian" - std: 0.0001 - } - bias_filler { - type: "constant" - } - } -} -layer { - name: "pool1" - type: "Pooling" - bottom: "conv1" - top: "pool1" - pooling_param { - pool: MAX - kernel_size: 3 - stride: 2 - } -} -layer { - name: "relu1" - type: "ReLU" - bottom: "pool1" - top: "pool1" -} -layer { - name: "conv2" - type: "Convolution" - bottom: "pool1" - top: "conv2" - param { - lr_mult: 1 - } - param { - lr_mult: 2 - } - convolution_param { - num_output: 32 - pad: 2 - kernel_size: 5 - stride: 1 - weight_filler { - type: "gaussian" - std: 0.01 - } - bias_filler { - type: "constant" - } - } -} -layer { - name: "relu2" - type: "ReLU" - bottom: "conv2" - top: "conv2" -} -layer { - name: "pool2" - type: "Pooling" - bottom: "conv2" - top: "pool2" - pooling_param { - pool: AVE - kernel_size: 3 - stride: 2 - } -} -layer { - name: "conv3" - type: "Convolution" - bottom: "pool2" - top: "conv3" - param { - lr_mult: 1 - } - param { - lr_mult: 2 - } - convolution_param { - num_output: 64 - pad: 2 - kernel_size: 5 - stride: 1 - weight_filler { - type: "gaussian" - std: 0.01 - } - bias_filler { - type: "constant" - } - } -} -layer { - name: "relu3" - type: "ReLU" - bottom: "conv3" - top: "conv3" -} -layer { - name: "pool3" - type: "Pooling" - bottom: "conv3" - top: "pool3" - pooling_param { - pool: AVE - kernel_size: 3 - stride: 2 - } -} -layer { - name: "ip1" - type: "InnerProduct" - bottom: "pool3" - top: "ip1" - param { - lr_mult: 1 - } - param { - lr_mult: 2 - } - inner_product_param { - num_output: 64 - weight_filler { - type: "gaussian" - std: 0.1 - } - bias_filler { - type: "constant" - } - } -} -layer { - name: "ip2" - type: "InnerProduct" - bottom: "ip1" - top: "ip2" - param { - lr_mult: 1 - } - param { - lr_mult: 2 - } - inner_product_param { - num_output: 10 - weight_filler { - type: "gaussian" - std: 0.1 - } - bias_filler { - type: "constant" - } - } -} -layer { - name: "accuracy" - type: "Accuracy" - bottom: "ip2" - bottom: "label" - top: "accuracy" - include { - phase: TEST - } -} -layer { - name: "loss" - type: "SoftmaxWithLoss" - bottom: "ip2" - bottom: "label" - top: "loss" -} diff --git a/benchmark/caffe/image/solver.prototxt b/benchmark/caffe/image/solver.prototxt deleted file mode 100644 index 61c10284e6..0000000000 --- a/benchmark/caffe/image/solver.prototxt +++ /dev/null @@ -1,10 +0,0 @@ -net: "alexnet.prototxt" -base_lr: 0.01 -lr_policy: "fixed" -display: 20 -max_iter: 200 -momentum: 0.9 -weight_decay: 0.0005 -snapshot: 10000 -snapshot_prefix: "models/caffe_alexnet_train" -solver_mode: GPU diff --git a/benchmark/figs/alexnet-4gpu.png b/benchmark/figs/alexnet-4gpu.png deleted file mode 100644 index 28b95a44508f0ee7ad270c9ccdf8659009406b03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 83783 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

BVEdIYT((7yTJPa{F@IiRM%B*h3?<@x0Pp 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 scQ!ljL;5(7c*=#Vq*$0OxPlb^rhX diff --git a/benchmark/figs/alexnet-cpu-train.png b/benchmark/figs/alexnet-cpu-train.png deleted file mode 100644 index b3200bbc049a9d75857fb5692902d7b475aa8f68..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16013 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 diff --git a/benchmark/figs/googlenet-4gpu.png b/benchmark/figs/googlenet-4gpu.png deleted file mode 100644 index 9b5331f05a3e54cacf949f10b6603bf627a6d106..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 83784 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
  • KN~uz($~AY2kVYi+q0yoBG2Wf9%(X?}Mxowf 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 LU-rvA)0h7Ts6}tF diff --git a/benchmark/figs/googlenet-cpu-infer.png b/benchmark/figs/googlenet-cpu-infer.png deleted file mode 100644 index 19478d433bae651f4506153ded11a96d5137b409..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14391 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�}E!PGiA&b$JsI0M+d+Sj+{6nfFh;*8Xn{EYzGY7u{R)uI(!Bubfgtg^E& z&1ga{*(Y$O+FL9nIQy9!vRq+qX90nx2Kza9s!8+ z04+M;4mmmjO=x&!$uxHMA9u$u7YT7e*6Wex%SD_W5=0UAU0+YDF}~aT+DMU%@(^^M zyD3OMdTD6soyZHaQR&|1LERnkRN;~C>EZ>+>By@bxfL>2c;zrMUChkF^Y)}<1He7( zB0f>ZYoLO$&!80%UMYzl-3oeu8i6fdW%5O`W~)xVhKMy?e;vLU>}mW=Sf%;o1#@n+ z2*=3apn;xldym)iITf$cPf<(xl!1M@3ujLmIl6Z*=~}H7GkALX#vvES`B&H2qA5lT zUS8#Azf<5A-!HVIWiLh_wPd;XGD1y}Z?Wrwc0$Qm^-*3_xVJCYOtz}jl}LdojNLX2 z9$SELfWkIMzi=<+BOfff*Icmay54Y4spLk>QuRSl*8D-va+9s%%#fnc?-?uo-SFk)NuX{&11N-8U{U8O+-suI}fodo?GU?gM zjxt_RRHOVPOO`AH%Y5gskscFzkB_upPS6#fx-OP? zdFK)=zrkWxg@^7f4|MoCoP4+o?+~r}y7Rd9db|LeA7c-rk-G-Ehm$xNnZDMtJ->LE zpT+f)C2&$65lDI3NdK6+)>Ru%CVK+Zo~AeR8EinKW@J35)_yirHqg%xO29# zC;JLD_(IM|gU z$5h8NFpE5kFxw2W6B|r2NIHw~r&T;PFPyi)X0Y|5hkA-9RRvI&)fzl0;P!AeohK@e z#utNB2`2@~A;l44nV$yI#7uVG#TZwP&OKm~JlSHbx#a14g60C_N{lpy;g<>fH;XYg zT%%EDcb^1o^R2-{?VSqq1IuQgQUMT{Izo~tM}_!F*7NCeH);os_D^)m5bG-4vomtnm3!`KZ-n*XcYh-8d?A!$Bj zxY9sdXwpoM77W#XM=w+Pgo~3n<9!>K_Gg!akxGCxr2h91pF|dIL?}uYjqjzk#O}%V z8;~Ysz5@$8=e;ETRf0zG6LiVH9H0aH0FpbMIhf)q(iHto_s4I-=;|C;jUnrbdB50q zmKI#vZ!`GCc7Npk!QYt{e+RJ@y$m*(p#>Ej%;SGep-i>Uj9F}N>`#pUGhFrG7X(ZC zG?gFo@egZfC=lD&kx827tRL9ruW>pe00ZzbYW2JdQQ-c89f*_niTDTPq!JJ_3MBOY zTMwI|wJ-o<{PW+*Rb+cRCWaSJ0A7rJ@|uKVzM++G$@nbj-)5VJ84da!%m1f2{$Cb2 zm*mlt`&0N4BK#y)iCVx+*hvbV&yu%D!~c^>q0qwL9{*X4na}nPbLF8i5v9%`4b|7T z4yk!bM)S6%4h8&pjEbCfT5ajQ3$v__n?mJ-r9Vo{{MIJt)nXLiYY=hlK42vk$X`!f z2z_ggbYdABYL@(=3{l#--NyBso+U?GY-kNhGk6u*M=|{T^M8xY|J@wL65BOgaqxovVY4-9#dCprqd2t6bJC? zw_;lq_mPQQ@0f6L6((v(Trxlz$9VkdhNy(`V_0usU@A)C^WeHgV5l6GiGLP)48td%8@gzOF= z73~ou2tQ{nz$p&dJb?%+%yd_-FSrn+Amk+P@SB#Z~N^=8>iIvz58@KQh&vptTC*Xk0x}rJx z2h>Zd^8}3SK-R0>+UXAJ3Ndz=SSgy%M#Y32o*0 zv!^IMw%N~d1K8zHg2lO=LP9UQx@IT*`O0#}TGQiA=dd?m>!N6(V*UO1wnGvM2@)S$ zzp%nvLd1f^XO&B1^k-+@a1~89u0U5=nSvqa_Q+fdy} zX}%<7zx2K%fbL7*&d$!Kk!vNaVObpn;|>1I^-_VB6Gcv~_AHJS8INma@?o;rgp0Vf zdG1;Y|Ce-l$2?yJH*mtG)M}A_p_xEJo{Eppsa6Yg0H$4pnJXqIJx=$Ym+!cPJ0Hi8 z#Jusvr@Q`ab);5q21*t*wukl~mS_ zy>}TG`*{Q}W43N(5ij|r!?SB;_QDt^dh+f|9)lU{c4=%6qF+5@cy2GtX{MO5uGaSk znl)X;OIiSB95M~Djlk!B7C zbE&%>OzXm~o1wiPW5Ku)|NISVYklJ>MQr1lXrkfwy=0=gb(*-yA#p!{mHLk`u_MXC zX^~_)K|&H_schKEr@=IZVb<8MZ$tk>9|#d!3EAGvRWVy7HHZ{gVH~*5ORAT99A4Ed zMRgw<(W zrOG#+)dfJEdYT!mBwW11*1AY*GKLvHfsgejcnvJi{V}U1j4Kp(Q54)-sHX<*I<{{= z2&nLLPqD|F-6crb!tF}7lYoq+RRGa7|jejpHq@@Pa> zLUXCpCo@{5sOOUT_N2X|~7lkZjkExH+OJW(vGnkEzotBhM3skst zf+@St)cE6vECWcP3B)(Hf+96hVb=nPKy3Mso)^J6KO4Dvu3|YL7R2XqXESjS<46&~ z^8}s@tpTp5KxO5h$JCm~&?Bt52t3BI^0md!`ArE_DoZEt>q*IjX zj>i3`iubHFwkI!b<7JLPesy=mi1E_K=XAGV2U?s#<1>u7q)4tEwy7ZXOBJ7_41oM< zWOOi027Z2eV`5Ka+o8z=eC&O@MI~CN)@*xxb7Rk<4848%Pqs>-nY&GQ>sgJf6`1kf znbX&E(xox6&FAn*?E-_EI`82IP7R#cuhW8?SvSbRiW%sDJPu~5%FF-q2Ug^0$b0cP zV#5l#72u0e<6@S=vUhxCsnI0*0!%JcNw$2C$ei^s_0sRjn5?n&0{FCy%ekB7^U zcvviu7xl#t8EdQ-IXg6)I9orY8Oeuc*PnG5yQ>4p4N zh^&d_@xFMfCrbOWnQXC3gE70xN_eh9ex@rY_l4J>ef#2Hn28;TQvN1N(T>*O?7ySt zO^Sla_xhA2KKr))m#pw5rbRv&?I&3*HRWu1`wjw}+k8vr#HBFzm#F_wi7eis(?y2k z5?D*T5&MOI!`_3zJPsflCDC3E8cI7du^$H5Rt)pDU|uVIoIL*Hv%@?TB2z_Lf$ zVfVN`j-56Bn^z{ggGIDk<@*RnsN3CfPXyl^ZG0^_swnAYC#_90a$f95D{W zB=Q`AA&j(;A5Dx8ruRwyekQXXk^Ck^>G_uc*ctPUuc=BxxU(~}2l4tPmQA<*$?`Nc z@VL)6em}Xw8hgE?`+(mMJ91^~qaLRn#3g%>K@TjrO#@0%BuFD04x2dMYk1YM6s4KCV zTM_$P6hDyAHT=e{%KqQ7;|MhY|6nxWX!imOnvbY!N5m9wkIcKlD|w4@A{Mb7vg@Mh_ezeWdNxi zyR4EUj{dVO*QANF!hhUD;;ifOJfezoifv5nxd)7Y&eiRgFo|YG4UkJ4IWc|rT|sQl z-|yo#vf4>SwHTimS2^O=-(Bn}HNy{biTM(nB6wTlz)&t`FxB^Fye94B9TS_CHj#3Q zxb=69Q)zr!VpA?|T%3*|7TJD`o(8i9e4h>cI`q>fF6Q!xS_KlfzTZ3Fi*!0*Wc4DC zqGYouCvofVN%60mEY7;1wQoinnXcq-o$^19A|BIpjTp7R#mK<5KGRBWl@ysf9ePa7 z%Eb7})l+^CZ^{H0(Xrt#%LQT*h~dHYFsEgGCxVjFdt>1XYSY8AW z!^T7nxM{GmV^|&cT?!YZp*q}+ZW7EjHHv!d$h7~+}`G_ zj)JQ68NNNW^6l_z+98I5L9X{!D)$i3lHl}O^0vZcy#~>9V_pNADaGglL2Zo)hWLth z$3m$Iqbq8=+2agNZ#;K}2*HF#z;jb=?2ccs z+07P&obpgoXSa$*PBC8dJ=G3%Y_LfwWzP?5uAY)0hJsN^$nT*R5k>0U`IkO4Xo=ML zx5|j+?g;v%->LxdUtGv?eB6E>WLgW_#Ai%9<@9K;mg;+YH6B$1IAnpMW_YK4P!b+S zK7`Gp)*Ck@`ZyTv`xl2}S+AhGDaCd>B`ofS<#lo^>J0wq&0V}} zLoa5w6t%n5Nt4PYPPW57)h1iLflswmtpvJgb|EI8b&EBQCrq|qsB32&rS*+JUWUmuNn&x@{px3TeHFDp!S-A z{Ofa!X5l=N`J*B}eex&ah6-kp;GvOuMeE>}5S&F2y|ucX`;z=2mS| z->rNu_aSOxZ?C@s;kny4mCTH%%dZOHV`Cl(4Mv>#PXX#IG+}=9$yYrr3PK==&|4}C V;vVS}|0nB^;vF^F!rLZK{trLdlsW(a diff --git a/benchmark/figs/resnet-cpu-train.png b/benchmark/figs/resnet-cpu-train.png deleted file mode 100644 index 96746b1759fa17d25ac5f40ed3678e16086364ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17991 zcmc(H2UJwevMwr!fP#Vuf+R@-A_5AMlajOKG~|rr97d7|h#(osStLr%BLWfylnj!F zoWl@iU>M#Wl%xLlzWgm)YjHkN=mAsp<$t>+TPwC5EUOE4}m~rWn~u@791TN z6B83ZefpG?l(f9OJT^9_q@gMLw+S;nDtlTIRl?#9%=cFwE6wv4QpSE8Q zQ@?Ki_Acljt*P$8%A$Z^96L!(M+^*-Yv}7-Cj(3f1LLue)MF79H@)=+Jfm4jhcwMy z)e7-C%1O;dN*VRYxQ6TGh1$~j$@@#DwL|@re5&rlwdBRf6L9yF$3B>xBInPY*Y~|M zKdgH_;##{Kbp(b8|K#zMYuns}uDxty(?;<^t2;ZW=hOG*)zUjXA%^!(aLi*BlG z)~)sG8~Io3aleL-4ma!(y7htM%1YAWpRXFySmto6$Cw94i$yxBt!MMHak0qy40eW) zN4B~?*%s(uIPSvYWKZ3l9b<4sWZxN*-Pn5aSm?fP>Mhn-*9!0Qd3a5noPR<9STVln zn-;?a=-$5dvu0`xf)*KU3R!0=KI#lxw+eY#QHN?do zv}St`dfw|1i&9=k?Kt>A;z7E)>ppLWFV?08vLV^c4q7cyw~1f~8*`<`!tVsKjt9!! zv2(-FspXF}+Ws5l_vU+rBvB?NB)v)*2h+}w=om!7@g1-(GA6oJbE(m7dBWQ^XYyFd zJt%?JerICyl^}cF1gPUB!lRR}?}28<9$;XY?e z^*Z(FIQLzYe<7}1X{Qny*j2Ke`9VkHhmF*DSt<0bOYM}9HJujmTjRQkP5Q6z`{=qR zxZ8ma&Z(VB3IP^CAV{h4sG5oI>ubrXTSTtAK~f**i$<+kxfYw9XI0vEJHk&H~?B>05QlLgn# z$uET-dmKO8%)?)9Jj8QVlqr|?8kIj#_0!5wEd||3wY69hRFf$Ws4Pw1sLBf;BxFr5 zw9Y+Z{h*aWUmoLaG+_&7`tcUf2SWd&t8L+t2t+%ZZjwMD`v@C>@^&l)!BXcFT|2vU zs^98Pb{!-3q^zcqMufMeMXa@5xKJlPh?M(@5PTBvG8dgV=o-l?6N zTV*ga7hgxE!da%|*i6(-a>bhyNDJ474c(WM+MVjaLs9EpgR+OwHVfI+)oGprMzztn zt6#ls877bIdp}Xetsyna?S1&*J+np@HbY<*itHGxBc?)?p-_{>bk_Uhu6YHD6#FrW zJB*VTwRnZ%yrXtq9)d=)UO)06l)N?A-%hYE7U5PIN%39B)UmAoa- z;A3f$1QPX9q07lHwm(!4>qt5bjMO}W$ceJrBn_wHFBd^En?HS+w)t?!H2 zUuq0{v1kCISntyJDH0}uMm6JH`rZhO$)H~b6Kpp27yG0Nc0Q275vj~}YTU*gboFR@ z>vhmy=eI0*Uy0~2TUCQZ2+Y%mIQY8ff)=tPH&kCxVt2B8v`&BV9512yyRFb7%i~zR zn%i#xB$$!POMZrh9uB@g41_i03TCZMjyi|#8)eXNI?=@|$GEmyDK%=9WJK3bnTaA za7Gbn)mV#=PrbYnX*J&IfSlihYoJ;Q{ zzmrHr&nI+4AZd7_BMP5My){hFm9R+J0GwVIM}pWCubd1W4wdOiTYYWXfA_GrSD|VL z96=#}(!EK6-H2ba&<1Ug9i8q1@g$dgqYtq~x79N_S3 z5xi<0huBKW33NI%jv`+ij7O?2_S)$+uCEOC%^d6&!nDY=7mGg;$%^KB$XX22mz65F zARO@(1;%(FeW3;Fu?2_)53A%V3l3Yz<(QrzmiDTi)s;HdPRf3%s`w0vT@_ z5!inV1>p-98(dwSI@7ByZB$Ae(vK8T)1&fBc^XQQ(oo!jPUI*7(x>~htxBP+xK66%x(PH822 zx{&uoS*Keo9Q0@RTY9iRUl=crHvtrMN>7n%UVLZ@Leqz0seR+I5>__)NHa~18RF?g8-Wqo}22#bA0eL z)V?vkYP7j=c>L3{WLm{Jsbu8I{IMvw#hB@w#f9Nm*j5)S9-0(YzkmloK2#NK%dcKz zX32TwE)2H#Dz%{*wu8H*iSqmzw#;6A5~6GNK>!I#uOx$ug@`u!chkaPGwwJ-YHe(Q z@sih^z3I@rdS=S2C&t?xM4(36$?d-#l0*fliC((Blm3Kbg3Z6!}R zuF%H&TxN-UjX8{6aU6OtC;UL4Mnp?f`hKgphP3$%vH&rFO@TAv{U`;7??h+3=wBjQ z9j^Z7W-MW25zLMH)grJ;afnHXx(MD;14fzxgJzLEXs4m6HpA2oGv`o6|YNT;RYqq@#g58*c3vuKZ!s6 zY=!6v5{Isq#J|Q_T>L_SS?$KOfTsi=8gDU!b!7`UtRXWNQY?EBL5PN3jq3-kQO2mD zdN&`3vYJhMQ&blTGtZ< zZ%lhtB(dpf$T~1LUSM0#@3!}ZKXLK7J6Qnpm}{T_ zvl)l0>71OVyy~U&y*_1`oIJ%C8>aaEyMts# z&noQV7f$bQ`ohU)OJwZ<=5{hNzk&<&Q;g5fPc;yERZ5?@y zCr-t2?+f>zBfYbI_!v6gsV%W^;*e>UlfV)Zx6{TumU1r?>fZ0>xi#u<1FpL@My{aZ z@}`BP7iIg9mGs`W48qlgCP9#GfuI%K&(-msA!P^YUQkMdQd{satHh0KficV@%_UOxmM^iSatbgi3c>eSfT)NZF3fLw z;cc0T!chq0`V!XFKx+5yjcBQan9$vg!ntxe!b0M_!t~2A*)1L{nq?v1bXXEwx(@4# z!Acp7v`=O-$M9#0Y&W;w-(v6%5i12w|IDYAtsu zs^aL@pyan~n;~HxRjuWY%%c3%-{hI;Ez7b!b(LqE_6?B`;Xgi(5#|JapaAU(i1vxr z_0@`_S&am^6;Ds~5Tst`6I4X9w32Yj!K4evk-IUWM~V#a+qXzCf? zt$W{Fvlv`kr@iu2JV2tayRs?k1hEPHHgj2P^t$cK81?&r(d=V6$SE3Ao% zg}jNo5Yt$xM3A`R`=4i}&S#u9wVfl!d1=`ZOod@hNBy3j(FddC&GUD$N$_*0vq4)R zIyb6X%WjQa#!I22=EWb86n46G+U3r6XoBu`BmpDAMYM=uLN@Hk$fFtXrKTv%$ERXn zU*ICVIvCmZQ!-Vt3g2W*OXbzMWxmix)AS@&j=d(a~$-@TDLUo;hJ zQJ25vc5yw0=UEbG-3^o87J`qii4QJ8Cuv=|XQf0VcAs1}++Kuc&LiW4LB-hzTS{J< z#lus*bIo)klJ`X}^iS0IME8lErv$SKTHp??-pAFaZNh9jwLRe(bhQZxtQ*}!o1s>k zsazRL7jQ`*lb@m_FSvc9QpI&7%v%pKn{ed{8WtA8<%#yoQ%%v1w7h6kzR_$$u2VHL zu=2kT-&Z04qQp|d;{F=ndcqYsbbs=@!!y;?-R+8|OZmw6@jwVLm{ZFmVPc70e4WD=~$~MKI;GtOt7~qGTkT6W5T}+_&M{z$KvX?|ByVIb_ZANd&)>)6^ z?F}}FX6LTZSdjDDo4OEpZP-Z|KR%i(9*3@56Ae4R1h{Ldi!v);@ zA)EBsVhGUk@xK*vWv*rOD2CqvwBcl@W!gZ$*c<{`sE*(E%lcP`GAh+e`@ni)G&r)`>^})=ti6?x+ee=COW2Eepl&t-O1XG z{?spm2m&PVFS`LC(0czIFLL3p=0(F}ZheFQtFyf%gm~FFx?}%@5$zgt?BAwaN8|a^ zpfq1;D2(LYHNq+U{|NH`%(9r4BZZk}ARNws`$L)Eee@uL0d+@VvppJE2IP=RDWBp- z+nS8LOi3M}61N$B=sf69n#FL=M-7>5*s;rIiNQFQB+H)u1W$PJv30ua-prIWx@ve; zm)E?^ZY0y1s$&hpovayR&>}B)_0q=2zdy{ly;?CN`;xHVpB^ zz_&6B)G8$O{r!bVd$|?4_7hn1inI|4+XZE2v%}JLpWwL2TDO|$>ynh^R?DjWN;;C1 zbZ!kCSOf5~ZfV|@ww`jXca;IFp66|zP|ltj_|wimKR!z#7uv9n^d5Sm4jd7<8K84E zE2705ABvY*5IuWNCNduv+Jf*9;xHt2E!$3^ETk~*O3uYZ5S>eTb6mmTAdKMPa6H`G z$Ka-P*dB-Oje_6hiOQuWzZy(3sJX z1Tu&A_M}o3nMJxCAv<6358PdJ{8F1T^tQKH6xxi4MR?^fPCPTY{fZrA0H_k+PHc;k2jVPI1>-2aIY*!5Nq|?1y`}qiH=H3S&b(|m(fpZ6$-1mN5MH&b=eur-`{{) zjaomJTQDhzz_$rDFYVI?aY>c7*anP2nC~{dB^fl{*)Q#@xH-b07wFhg(5=_L=Dwt5svar(r3wjVeerCw(3WMq{pt9A z)(j-3sM9b~PYR_x^u{^Ez#LTiF8-bjrOPSgX`rP2$SZOH)7e@X?M>&r^ zc_*pl!4_-_(8U6{`o0_COCR%b;5!=me1;VZPG!kgeEMXHv5x{uGt;qI*UP z&}Kbex%$KOVD;cyrlp*#sJFSX^4FrzErndl7ZWNcTK;INz`h;XDR+auL{WR#aoeMx zOugPx8%HxFfMYwhskUV`HL`)24IJR`Zj?c|d8F$LWA$58O1V=S%x@o)) z8{?%upRmaw!w5hV!>oh?GkCuaNI?cE|?2$O9YB_ud@l?~E)QPB%^|LCgmUUV>o zHKfcDaY>;oiK8;TcSy}KbnnogOVadp+aWBxwT&Pa9v%yCgXn^(9s|8W-HRsqxa1;~d4^<>TB6Maqz=;lW3$sMrI!T9~7qq?@tw{=Ib37|qKxk1-5oZ6D+ zO)X1PCSC97y>evztzqQ(fu@W|<>LuFZ?h9B0M-e(o&}xRDRT4zK zpGP%A7ojpo?0U*Rns-?g#%}sW1Vz9Tp7}!QrFifS#D?wFYfzd($C zP<2!xn~iby2dJ09!-dk3x;bNNmEK9$B^3DG@35ehs*a5C9~y{N)fcx$boX@b2+_Pw zal2SwQpZRe&N~3He=Rg%ydU{TCG}x3L`_&lLEa18?tDJOqp`Jx$n$dKX|;fcWkn`x z_vQx!7hya4iyd!1O8#mR;$utM%|cEyb~r!}N+~RNZB*MHv3douuuHzi6gP~xg^k|W z3!BE+vqYzrALK1x3ORJjb^gbHCdi2}FpsI(K+nv7{ZcPNK{_r#!;e~8M zPl&~l50qQBTiv;Vb-;e<@t!r2t>+*hIry9B+Xvamt2o@8bVO zvI!KlTT?9I?txtdKt@Okpn6)or2s^<<@ME)@vE*Dy5j+3`HoVOuhjsM~>~b|2W)d#sGzofJ78q|Hk=KuP_LLawbkIL9;}+25XMg z8>bDb)Me8ApU1_LVFWbTr~b?e?v1l>MOSVs!9iqF?tmFTkk;9jIRBrHjcE9wH!#6z zZ~(zKd@_qx^J6abFm8*+O9wc5sNa2O3q65B8+%7PTw7hKxQa&jCIyM9nmvc@=s%Bz zUj!0iw?!|*l&XGs<*Odui!e+Z+bmh+jt=3}suvQptNQ}{Rh|6Vq$OP9s3nHfYO_PZ z^<;m%PQtq^ofP?H;t(i(W(Hr727lQ1xx059v7i3($xJU-7_c>819FRF+i|9%&18(z zGpxAbnc~!J-b34B6@J5WD>4J3^WKMwUb#8ViSzdfGrt^vAgK(js!W(rm{|`)S1d$d zdsLYlhfT(YIZCIc?bxiN+d9QG`f0XX&2B<>S)-g28=3ZUBH>@~zL&hYN_XXP@Mb8T z&(S>|d$u+A*YVi8{VGU;z=qoW{%b6HT(g_aZL`It8N)*NhE}&K^{nYeZp&Scb=!VC zMRK>Oz~7TIfgPu)MZ?p!GB{4@v)&|WCHt!ujiV3f($fQ2k$HhclVWp{)1F_>$z}^V zDHq%rQNu$=2j@h~u>TuUy3=C@Ad`!!o3q!_xtix>#W^PH^nT_wB-ER`fnHB z{V-Yln`Q=;_yFTr1UAeHzIs5jJsFz&0ZlN?ET|9N{v{L4t4%Sabn7Dwla28{h-|Uj z5#=JUzjd)q&}n9X-P(^fh)3s%Kuh zPJfWdCL_VAaaKD$xJK{eJ_;?r&xj=oWo5jAbq%wckO;G|Z-WG~^07;1V=wxZ)O|vu z^(YzSULaV@jj4^~#H~v2WMTpcEd>rD!RtCeXl3d?40{t#{ZUf(>h4(`>VnR_fr4H7 z&}3WLk1;?ia4q488~S0;vWxT){471qGh~rZKU6O{Ymur0O~94LNE(cD{#6U6h|pO@ z7hDZJ@?~Kcc@A8CL_pIyKuj9dS*`q7-plGAU6bIVf-PLH_p9qiwYLXV*H~AbmQ9OZU;3{9a-LdlBZ}ith(+h(K7s)!z&b(EQ&k zXFx&X-{-kQ1XjDdInLhz@Z+h?r*>2PwENUJ;@`zzQ%A}r=DJQx%U(UZ$}a#YKK}`N zIdC|0dZcg~JQ*!P4hQ%8KmWuWjk`JaPu$TX%&~r}oaG(|L(KOgpXdOX|Ga>z|EJA> zLVjw$8B%f~LA5e?xT&GFpYJTPPNU2Bry}TqTE8D_5Sc*L9lccpPtdE)zc54J<9bIM ztRld}KQ9qJH27x`!Sd71r{_Yq85?nbw`8tGz3I1Ofd5AywFeiU9Sxnb9S}#qPi~k9 z4@}|QV*6KNjt&ObM7LB`%aZ?#F2C|H5`IHr^DJ}zi5Ihi<4#}}GXnoOTmsUsQ@(W( zl(zqmQy%l%a$ccb<@P@*;h#882|9k3a;Iqru-gA6YyJsR|9{z?^|X9)-`~9d zed{2f0)xP+L;}hdvbbepbrWBNb-S0KHT%mG%!)-wX+n`?Taq~R{Piw>xIQPl$3cbw zwSG}tzEe(xNSO%_HC5qK3_Pw2YVA`K-%H=_zKtw~bwQ=x8?e`Nh4O3(;i#c8n znGu%a>>F~BCg6YfKU+UCq`%%StC@F78hx^ixF7A^8i<;31WZ^sb{ zvVrV+L~Mykmmeyxb0%a>8q1&T%GN&{V1vor{MZt$QO<8ZyCNO3Db3JqTBJP|BS1ZU zcVu1C%HLE5+Bk+e7D5@^yGsKcq5XNZ^iQ4)lUi)=@q!x%s6w8mtV@_4Va+a!GiHF| zxJk0v_Eg{r$NMH!_RXOj1Z2NhH*V z*hlvs6VZwPovH8AH=YaS7MefiH*%evWcc9i>Dd(U`wDDL8asNczZz9* z3UStgG9hzqII4odK5m4F9g!+xVB9%5lSFt(W&F36Daiwc6V=q~nW>-z`yjFvd1gW6 zgA_WwV#2SXmB!DSxqt()hU^_)oo}T!!cRwm<1p5j^NZ>qO5tX5^*+F9nv_?ER}rLp z%94IXVK&ySPUQ*LbMPDAnfb|u_mcZ-WO#@pWNlKfnTbEDQ_n;~du*4)emqTW-W$w{mnWk;-cnVa{U)o*uGa5fSI$) z%)Z7SSGbsCJqx>4KF2U3_NAgV_}Gq%yl=EI<^dt2#?}Tdal?s+cF~bQjT0Wqoikcj zNHvzqD|<6D^G!PTH13h(Xi!))h(3>%r`0cxMvs$?g*wfjhcoukV3hP=m|91YTE~#KErW(SS#}bg{8<|db<*`;c~a&w(e@_E6=eD`NqxiO}0bC;O6&51vAHm_JLXDtndeSl`@(? zp9*Z~-5ARdbu;YW z%h+x>Py)ZF+O%HSUOa&{$Xx;n%8VODh%*a}u)@t6{Ptzp9(vR?7;{DOFr@k*6t53Y zX(odYx50@vxTr%umaVo_u%yai$X8 z^b#zs&bdj;gze%du1QLxP2(9wH=10w5z{JLX_we9=PfXiagw!R`w16|N}MH(B_QUV zlUddD9)l$Q_?~@dt>yy;~9tOwoCz3{M7JIWb;XJ;NPR66}T zS4QC7^|TaS#Z7sDz?p;v>e42Y1+Z)t#wzgW*V%0lE7L^J2iJY~F=;SJ2I|VRx6Ed6 z-yS)v3>0U(Uzm@;^SQ7xG0F;jndJ7NmM1RQLWea0uMu+{M-=1UJ6E$_nlELI^iN*w zE$a{{aPaq~+xO(1Bq=C;9-y#S5FJk~Bq=bKUshMUxcg-zLK;+U# zti6pnzf{bBOkWp;=R^2wvj6cwKug$ysi!kcq2MV#>RUP_Nq_08 z&;$}%CQ+jSQF?LwX26%YAq$Z|A^uO<_&*0H#Sc8G!)i*>AMMEaU;O*5NoX~Dt8?#Z zquZJa^?8DF#!Kg_f$&TA0~MfVEd^auD7_3LE)$gJUi#(4H72(@HRaEHFUWP%I zKOwhCQ)2;AC}~;y+77Ev?)l%wA-)|t)YUj^PwhYO?V53HvOV+C=+mu!e`6p@p8WC$ zBMFl3H;r_*qMrY9phJ_w`Cm@%z!Jjw4-7<;#o{31dl6fEc$XmYFNtOfO>rbdb8RN6 zm;VtPb^VH42>a383%~3exYe906~nDV(2EtHd;ZU~u)R>Bm7X)bC(Iyp@5t#lyU<7* z3xf)FWvqO3_B(#doQJf?^1om^@gK7Ee~hh}ZIciP86&9A0hnQoHbcUcT^slpR$h`h z!el=4n;yrLZ_oe8F=FKCn(c1z`&s^bb&-WO%7UFb5l&xjj6Dqi{3wV;2}T*1t#mEA zQi(N@r)J3co)XTprqx;h^=qGK^(c{XQzyQ8Q^(s?vI4O-pVjVHgqp>HBiD&HO%A!a z4ER?Vj{8lSU&o=Poud@H)e*fz+B$APOgXcoPd;2Y-Bx#QZuXGk>DN13A8y@wh2MN( zjM4Xs=is%?woT`Qy~b*k`~hf&6ODK(#_*Jl+OUiC=;yllCsuad*YrO4QEl3fn!Ns& zbt$!7J=Z$j8#g-9NoNZrc(T)UKI)3dVy4a8Q$ucWPI&b*rN260$JL=mGc*txyw%~m z)z%IwQXwTEVSo_?u{z~=@44Zu+6omn-)9SYAT+5^HeC#finzgvW5=gj#10MHpE-9* zsz-P}{28}wqB2Icx@FTI_C3@#PsL0$U}JXsytl(QnMGiMA9#vY9_xJKB=x&NxU7IweOC1f;$=bz&I@E`DSo36W5zN?~s(8SU@P3pTp;2(FX=!5nsy- zs<2X%M2R(?+{M7A)3+Kf`I5%LzCQB^uFSU zF?CqBn{K#I1bgP20DNr%(VQ#@a9$;P(GSN5)`lNW0G8FZfWMVgGcAGU>%UI$`)glB z?oX$lKV5WmwrJ7G$b#nBkBYrNJXb4>jy7PAWUDdpqwQ1|g3D*R{1}@_!VeIxHq99Z z>1P<6LWcd~CuBcd04%1HXTB{&-&_y_CPBx^uh{@#Yy!^=@I6E5EG-(j&p`R1EjlvM ziu^za*o@V=b*D6|=m>Qf_vZUc0ZEE`rXIl+AfirZJq@k+12@7yxyxB0ix;m~yOci#D@<_lmI_mYq6 z$@U-EkNz8|dP>0UeCO_mccFLJXo~qk3=jS;2Q2Quz1;2i{>HiJ-L*N;>w@Ip^+K{k%B26REb_k{EdMN5jG7kP zOsddT6`JJ#JGS7H!vg*r;RNpAh5Uo-UX5C%6n`H|Zp_}GCdXEL2FF^(d}1;QIL(Gz zr?Gr+juBjhGA_9zi4d5n!^`o%Kke*JG>rBGS1#UufP8*buo-;i-NsrciGybXWV&{k zr@Q<)Sy$*dd@vph{AwDSTDQl#th4YyKE7GP++1h;^*24}M zUL;_jbeQwv>X)kqq3^XKBiu0D5xoRPs($Qlb|I{IV+UR9U)V}-6uv!KPrw#CfZxk2pBTe zcHKae)-L+e?3V*;6j|X{gPX~CqCZ{`k5bKQ6_k<%^KN1O=KL*|yi_xiyl z%uzLVq0Kqt-egBJ_RFI22S;2l2cI>$HNwFv#=fonQ(U%H(_Q3tyBjlR6f*_F<#eAs z*leFv`c-Masv68Z$P0A2cqpK_7V@R*(@>Y8v+a#o5?6T1vWxl!M?S^%Q-NIMRVyJ! zWWITAKg3lTl@)l`G0CZWItAIgXTFW~8}NH?`@XI6#xr*ODyB_eLv?Gt87Z7RrP_%?Mesg3y2#FBo7|1pAjP?zlK%lg^OCKuWzo_+DcdB z8YIGB?Gl|DP{M638mYhMb?lUP96MhiF*^S2z+x@vhaBHbGSy2~JXN%5ZinLwS4iq_ zg)NPNhu+Ueh%Z#RlICKizS^1><~iAXf8Cf5ZQ%H)nbn!fq&N@Ba;DA(GSBp}!26pA zHkfU!a}Io!@XTe~L~fNWISqTWNsO9l;H=X&@ZUV|OccdMfg8GvCvja*+D-_3A#%d4 VTumpypC`ePdLsY0NL1hN{{R#w^I!k~ diff --git a/benchmark/figs/rnn_lstm_4gpus.png b/benchmark/figs/rnn_lstm_4gpus.png deleted file mode 100644 index 973ce2fa5f65e9681c972d4f5bd5776b5c4aa264..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 73243 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%0AGXeR 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 H_5J=I8vUTY diff --git a/benchmark/figs/rnn_lstm_cls.png b/benchmark/figs/rnn_lstm_cls.png deleted file mode 100644 index 26d05cac11aa7ae8cdfbcd8c4401f6547a9404f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117634 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

  • mxd5FP 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 IFWwINKRB8BQ2+n{ diff --git a/benchmark/fluid/Dockerfile b/benchmark/fluid/Dockerfile deleted file mode 100644 index 81ea870050..0000000000 --- a/benchmark/fluid/Dockerfile +++ /dev/null @@ -1,30 +0,0 @@ -FROM nvidia/cuda:9.0-cudnn7-devel-ubuntu16.04 - -# Use UBUNTU_MIRROR can speed up apt-get speed. -# ARG UBUNTU_MIRROR -# RUN /bin/bash -c 'if [[ -n ${UBUNTU_MIRROR} ]]; then sed -i 's#http://archive.ubuntu.com/ubuntu#${UBUNTU_MIRROR}#g' /etc/apt/sources.list; fi' - -RUN apt-get update && apt-get install -y python python-pip iputils-ping libgtk2.0-dev wget vim net-tools iftop python-opencv -RUN ln -s /usr/lib/x86_64-linux-gnu/libcudnn.so.7 /usr/lib/libcudnn.so && ln -s /usr/lib/x86_64-linux-gnu/libnccl.so.2 /usr/lib/libnccl.so - -# IMPORTANT: -# Add "ENV http_proxy=http://ip:port" if your download is slow, and don't forget to unset it at runtime. -# exmaple: unset http_proxy && unset https_proxy && python fluid_benchmark.py ... - - -RUN pip install -U pip -RUN pip install -U kubernetes paddlepaddle - -RUN pip uninstall -y paddlepaddle && mkdir /workspace - -ADD https://raw.githubusercontent.com/PaddlePaddle/cloud/develop/docker/paddle_k8s /usr/bin -ADD https://raw.githubusercontent.com/PaddlePaddle/cloud/develop/docker/k8s_tools.py /root -RUN chmod +x /usr/bin/paddle_k8s - -ADD *.whl / -RUN pip install /*.whl && rm -f /*.whl - -ENV LD_LIBRARY_PATH=/usr/local/lib -ADD fluid_benchmark.py recordio_converter.py args.py recordio_converter.py run.sh run_fluid_benchmark.sh imagenet_reader.py /workspace/ -ADD models/ /workspace/models/ - diff --git a/benchmark/fluid/README.md b/benchmark/fluid/README.md deleted file mode 100644 index 0a18d9fbd9..0000000000 --- a/benchmark/fluid/README.md +++ /dev/null @@ -1,99 +0,0 @@ -# Fluid Benchmark - -This directory contains several models configurations and tools that used to run -Fluid benchmarks for local and distributed training. - - -## Run the Benchmark - -To start, run the following command to get the full help message: - -```bash -python fluid_benchmark.py --help -``` - -Currently supported `--model` argument include: - -* mnist -* resnet - * you can chose to use different dataset using `--data_set cifar10` or - `--data_set flowers`. -* vgg -* stacked_dynamic_lstm -* machine_translation - -* Run the following command to start a benchmark job locally: - ```bash - python fluid_benchmark.py --model mnist --device GPU - ``` - You can choose to use GPU/CPU training. With GPU training, you can specify - `--gpus ` to run multi GPU training. - You can set async mode parameter server. With async mode, you can specify - `--async_mode` to train model asynchronous. -* Run distributed training with parameter servers: - * see [run_fluid_benchmark.sh](https://github.com/PaddlePaddle/Paddle/blob/develop/benchmark/fluid/run_fluid_benchmark.sh) as an example. - * start parameter servers: - ```bash - PADDLE_TRAINING_ROLE=PSERVER PADDLE_PSERVER_PORT=7164 PADDLE_PSERVER_IPS=127.0.0.1 PADDLE_TRAINERS=1 PADDLE_CURRENT_IP=127.0.0.1 PADDLE_TRAINER_ID=0 python fluid_benchmark.py --model mnist --device GPU --update_method pserver - sleep 15 - ``` - * start trainers: - ```bash - PADDLE_TRAINING_ROLE=TRAINER PADDLE_PSERVER_PORT=7164 PADDLE_PSERVER_IPS=127.0.0.1 PADDLE_TRAINERS=1 PADDLE_CURRENT_IP=127.0.0.1 PADDLE_TRAINER_ID=0 python fluid_benchmark.py --model mnist --device GPU --update_method pserver - ``` -* Run distributed training using NCCL2 - ```bash - PADDLE_PSERVER_PORT=7164 PADDLE_TRAINER_IPS=192.168.0.2,192.168.0.3 PADDLE_CURRENT_IP=127.0.0.1 PADDLE_TRAINER_ID=0 python fluid_benchmark.py --model mnist --device GPU --update_method nccl2 - ``` - -## Prepare the RecordIO file to Achieve Better Performance - -Run the following command will generate RecordIO files like "mnist.recordio" under the path -and batch_size you choose, you can use batch_size=1 so that later reader can change the batch_size -at any time using `fluid.batch`. - -```bash -python -c 'from recordio_converter import *; prepare_mnist("data", 1)' -``` - -## Run Distributed Benchmark on Kubernetes Cluster - -You may need to build a Docker image before submitting a cluster job onto Kubernetes, or you will -have to start all those processes manually on each node, which is not recommended. - -To build the Docker image, you need to choose a paddle "whl" package to run with, you may either -download it from -http://www.paddlepaddle.org/docs/develop/documentation/zh/build_and_install/pip_install_en.html or -build it by your own. Once you've got the "whl" package, put it under the current directory and run: - -```bash -docker build -t [your docker image name]:[your docker image tag] . -``` - -Then push the image to a Docker registry that your Kubernetes cluster can reach. - -We provide a script `kube_gen_job.py` to generate Kubernetes yaml files to submit -distributed benchmark jobs to your cluster. To generate a job yaml, just run: - -```bash -python kube_gen_job.py --jobname myjob --pscpu 4 --cpu 8 --gpu 8 --psmemory 20 --memory 40 --pservers 4 --trainers 4 --entry "python fluid_benchmark.py --model mnist --gpus 8 --device GPU --update_method pserver " --disttype pserver -``` - -Then the yaml files are generated under directory `myjob`, you can run: - -```bash -kubectl create -f myjob/ -``` - -The job shall start. - - -## Notes for Run Fluid Distributed with NCCL2 and RDMA - -Before running NCCL2 distributed jobs, please check that whether your node has multiple network -interfaces, try to add the environment variable `export NCCL_SOCKET_IFNAME=eth0` to use your actual -network device. - -To run high-performance distributed training, you must prepare your hardware environment to be -able to run RDMA enabled network communication, please check out [this](https://github.com/PaddlePaddle/Paddle/blob/develop/doc/fluid/howto/cluster/nccl2_rdma_training.md) -note for details. diff --git a/benchmark/fluid/args.py b/benchmark/fluid/args.py deleted file mode 100644 index ff616ddbb2..0000000000 --- a/benchmark/fluid/args.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import argparse - -__all__ = ['parse_args', ] - -BENCHMARK_MODELS = [ - "machine_translation", "resnet", "se_resnext", "vgg", "mnist", - "stacked_dynamic_lstm", "resnet_with_preprocess" -] - - -def parse_args(): - parser = argparse.ArgumentParser('Fluid model benchmarks.') - parser.add_argument( - '--model', - type=str, - choices=BENCHMARK_MODELS, - default='resnet', - help='The model to run benchmark with.') - parser.add_argument( - '--batch_size', type=int, default=32, help='The minibatch size.') - # args related to learning rate - parser.add_argument( - '--learning_rate', type=float, default=0.001, help='The learning rate.') - # TODO(wuyi): add "--use_fake_data" option back. - parser.add_argument( - '--skip_batch_num', - type=int, - default=5, - help='The first num of minibatch num to skip, for better performance test' - ) - parser.add_argument( - '--iterations', type=int, default=80, help='The number of minibatches.') - parser.add_argument( - '--pass_num', type=int, default=100, help='The number of passes.') - parser.add_argument( - '--data_format', - type=str, - default='NCHW', - choices=['NCHW', 'NHWC'], - help='The data data_format, now only support NCHW.') - parser.add_argument( - '--device', - type=str, - default='GPU', - choices=['CPU', 'GPU'], - help='The device type.') - parser.add_argument( - '--gpus', - type=int, - default=1, - help='If gpus > 1, will use ParallelExecutor to run, else use Executor.') - # this option is available only for vgg and resnet. - parser.add_argument( - '--cpus', - type=int, - default=1, - help='If cpus > 1, will set ParallelExecutor to use multiple threads.') - parser.add_argument( - '--data_set', - type=str, - default='flowers', - choices=['cifar10', 'flowers', 'imagenet'], - help='Optional dataset for benchmark.') - parser.add_argument( - '--infer_only', action='store_true', help='If set, run forward only.') - parser.add_argument( - '--use_cprof', action='store_true', help='If set, use cProfile.') - parser.add_argument( - '--use_nvprof', - action='store_true', - help='If set, use nvprof for CUDA.') - parser.add_argument( - '--no_test', - action='store_true', - help='If set, do not test the testset during training.') - parser.add_argument( - '--memory_optimize', - action='store_true', - help='If set, optimize runtime memory before start.') - parser.add_argument( - '--use_fake_data', - action='store_true', - help='If set ommit the actual read data operators.') - parser.add_argument( - '--profile', action='store_true', help='If set, profile a few steps.') - parser.add_argument( - '--update_method', - type=str, - default='local', - choices=['local', 'pserver', 'nccl2'], - help='Choose parameter update method, can be local, pserver, nccl2.') - parser.add_argument( - '--no_split_var', - action='store_true', - default=False, - help='Whether split variables into blocks when update_method is pserver') - parser.add_argument( - '--async_mode', - action='store_true', - default=False, - help='Whether start pserver in async mode to support ASGD') - parser.add_argument( - '--use_reader_op', - action='store_true', - help='Whether to use reader op, and must specify the data path if set this to true.' - ) - parser.add_argument( - '--data_path', - type=str, - default="", - help='Directory that contains all the training recordio files.') - parser.add_argument( - '--test_data_path', - type=str, - default="", - help='Directory that contains all the test data (NOT recordio).') - parser.add_argument( - '--use_inference_transpiler', - action='store_true', - help='If set, use inference transpiler to optimize the program.') - parser.add_argument( - '--no_random', - action='store_true', - help='If set, keep the random seed and do not shuffle the data.') - parser.add_argument( - '--reduce_strategy', - type=str, - choices=['reduce', 'all_reduce'], - default='all_reduce', - help='Specify the reduce strategy, can be reduce, all_reduce') - parser.add_argument( - '--fuse_broadcast_op', - action='store_true', - help='If set, would fuse multiple broadcast operators into one fused_broadcast operator.' - ) - args = parser.parse_args() - return args diff --git a/benchmark/fluid/check_env.sh b/benchmark/fluid/check_env.sh deleted file mode 100755 index af16b84ca8..0000000000 --- a/benchmark/fluid/check_env.sh +++ /dev/null @@ -1,261 +0,0 @@ -#!/bin/bash - -if [ "`uname -s`" != "Linux" ]; then - echo "Current scenario only support in Linux yet!" - exit 0 -fi - -echo "========================= Hardware Information =========================" -sockets=`grep 'physical id' /proc/cpuinfo | sort -u | wc -l` -cores_per_socket=`grep 'core id' /proc/cpuinfo | sort -u | wc -l` -ht=`lscpu |grep "per core" |awk -F':' '{print $2}'|xargs` -physical_cores=$((sockets * cores_per_socket)) -virtual_cores=`grep 'processor' /proc/cpuinfo | sort -u | wc -l` -numa_nodes=`lscpu |grep "NUMA node(s)"|awk -F':' '{print $2}'|xargs` -echo "CPU Name : `cat /proc/cpuinfo |grep -i "model name" |uniq |awk -F ':' '{print $2}'|xargs`" -echo "CPU Family : `lscpu |grep \"CPU family\" |awk -F':' '{print $2}'|xargs`" -echo "Socket Number : $sockets" -echo "Cores Per Socket : $cores_per_socket" -echo "Total Physical Cores : $physical_cores" -echo "Total Virtual Cores : $virtual_cores" -if [ $ht -eq 1 ]; then - echo "Hyper Threading : OFF" - if [ $physical_cores -ne $virtual_cores ]; then - echo "Error: HT logical error" - fi -else - echo "Hyper Threading : ON" - if [ $physical_cores -ge $virtual_cores ]; then - echo "Error: HT logical error" - fi -fi -echo "NUMA Nodes : $numa_nodes" -if [ $numa_nodes -lt $sockets ]; then - echo "Warning: NUMA node is not enough for the best performance,\ - at least $sockets" -fi - -echo "-------------------------- Memory Information --------------------------" -# dmidecode support start from 2.11 -dmi_ver=`dmidecode --version|awk -F '.' '{print $1}'|xargs` -if [ $dmi_ver -lt 2 ]; then - echo "Error: dmidecode unknown or version is too old" - exit 0 -fi -if [ `dmidecode | grep -ic "Permission denied"` -ne 0 ]; then - echo "Error: need root to run dmidecode" - exit 0 -fi -max_dimms=0 -num_dimms_installed=0 -for dimm_id in `dmidecode |grep Locator|sort -u | awk -F ':' '{print $2}'`; do - num_refered=`dmidecode |grep -wc "$dimm_id"` - # the actual dimm id should be refered only once - if [ $num_refered -eq 1 ]; then - num_unknown=`dmidecode | awk '/'$dimm_id'/ {s=1; f=0}; - /Unknown/ {f=1}; - /Manufacturer/ {if (s==1) {print f; exit 0;}};'` - if [ $num_unknown -eq 0 ]; then - dimms_installed="$dimms_installed \n $dimm_id" - ((num_dimms_installed++)) - else - dimms_uninstalled="$dimms_uninstalled \n $dimm_id" - fi - ((max_dimms++)) - fi -done -echo "Installed DIMM number : $num_dimms_installed" -num_dimms_mapped=`dmidecode | grep "Memory Device Mapped" | wc -l` -if [ $num_dimms_installed -ne $num_dimms_mapped ]; then - echo "Error: The installed DIMMs number does ont match the mapped memory device: $num_dimms_mapped" -fi -num_clock_configed=`dmidecode | grep -i "Configured Clock Speed" |grep -ic "Hz"` -if [ $num_dimms_installed -ne $num_clock_configed ]; then - echo "Error: The installed DIMMs number does ont match configured clocks: $num_clock_configed" -fi -echo -e "Installed DIMMs Locator: $dimms_installed" -echo -e "Not installed DIMMs : $dimms_uninstalled" -max_dimm_slots=`dmidecode | grep -c "Bank Locator"` -echo "DIMMs max slots : $max_dimm_slots" -if [ $max_dimms -ne $max_dimm_slots ]; then - echo "Error: The max dimm slots do not match the max dimms: $max_dimms" -fi -free_ver_main=`free -V|awk -F ' ' '{print $NF}'|awk -F '.' '{print $1}'` -free_ver_sub=`free -V|awk -F ' ' '{print $NF}'|awk -F '.' '{print $2}'` -if [ $free_ver_main -lt 3 ] || [ $free_ver_sub -lt 3 ]; then - mem_sz=`free |grep -i mem |awk -F' ' '{print $2}'|xargs` - swap_sz=`free |grep -i swap |awk -F' ' '{print $2}'|xargs` - total_sz=`free -t |grep -i total |tail -n 1| awk -F' ' '{print $2}'|xargs` - mem_sz="`awk 'BEGIN{printf "%.1f\n",('$mem_sz'/1024/1024)}'` GB" - swap_sz="`awk 'BEGIN{printf "%.1f\n",('$swap_sz'/1024/1024)}'` GB" - total_sz="`awk 'BEGIN{printf "%.1f\n",('$total_sz'/1024/1024)}'` GB" -else - mem_sz=`free -h |grep -i mem |awk -F' ' '{print $2}'|xargs` - swap_sz=`free -h |grep -i swap |awk -F' ' '{print $2}'|xargs` - total_sz=`free -th |grep -i total |tail -n 1| awk -F' ' '{print $2}'|xargs` -fi -echo "Memory Size : $mem_sz" -echo "Swap Memory Size : $swap_sz" -echo "Total Memory Size : $total_sz" -echo "Max Memory Capacity : `dmidecode |grep -i \"maximum capacity\"|sort -u|awk -F':' '{print $2}'|xargs`" -# DIMMs fequency -clock_speeds=`dmidecode | grep -i "Configured Clock Speed" | grep -i "Hz" |sort -u | awk -F':' '{print $2}'|xargs` -echo "Configed Clock Speed : $clock_speeds" -num_clock_type=`dmidecode | grep -i "Configured Clock Speed" | grep -i "Hz" |sort -u | wc -l` -if [ $num_clock_type -ne 1 ]; then - echo "Warning: Have more than 1 speed type, all DIMMs should have same fequency: $clock_speeds" -fi - -echo "-------------------------- Turbo Information --------------------------" -scaling_drive=`cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver` -echo "Scaling Driver : $scaling_drive" -if [ $scaling_drive == "intel_pstate" ] && [ -e /sys/devices/system/cpu/intel_pstate/no_turbo ]; then - turbo=`cat /sys/devices/system/cpu/intel_pstate/no_turbo` - if [ $turbo -eq 1 ]; then - echo "Turbo Status : OFF" - else - echo "Turbo Status : ON" - fi -else - echo "Warning: Scaling driver is not intel_pstarte, maybe should enable it in BIOS" - echo "Turbo Status : Unknown" -fi -# cpu frequency -num_max_freq=`cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq| sort -u |wc -l` -num_min_freq=`cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_min_freq| sort -u |wc -l` -if [ $num_max_freq -ne 1 ]; then - echo "Error: the max_frequency of all CPU should be equal" -fi -if [ $num_min_freq -ne 1 ]; then - echo "Error: the min_frequency of all CPU should be equal" -fi -max_freq=`cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq| uniq|xargs` # kHz -max_freq=`awk 'BEGIN{printf "%.2f",('$max_freq' / 1000000)}'` # GHz -min_freq=`cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_min_freq| uniq|xargs` # kHz -min_freq=`awk 'BEGIN{printf "%.2f",('$min_freq' / 1000000)}'` # GHz -echo "CPU Max Frequency : $max_freq GHz" -echo "CPU Min Frequency : $min_freq GHz" -# cpu governor -num_governor=`cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor| sort -u |wc -l` -if [ $num_governor -ne 1 ]; then - echo "Error: the governor of all CPU should be the same" -fi -governor=`cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor| sort -u |uniq` -echo "CPU Freq Governor : $governor" - - -echo "========================= Software Information =========================" -echo "BIOS Release Date : `dmidecode | grep "Release Date"|awk -F ':' '{print $2}'|xargs`" -echo "OS Version : `cat /etc/redhat-release`" -echo "Kernel Release Version : `uname -r`" -echo "Kernel Patch Version : `uname -v`" -echo "GCC Version :`gcc --version | head -n 1|awk -F '\\\(GCC\\\)' '{print $2}'`" -if command -v cmake >/dev/null 2>&1; then - cmake_ver=`cmake --version | head -n 1 | awk -F 'version' '{print $2}'` -else - cmake_ver=" Not installed" -fi -echo "CMake Version :$cmake_ver" -echo "------------------ Environment Variables Information -------------------" -kmp_affinity=`env | grep KMP_AFFINITY` -omp_dynamic=`env | grep OMP_DYNAMIC` -omp_nested=`env | grep OMP_NESTED` -omp_num_threads=`env | grep OMP_NUM_THREADS` -mkl_num_threads=`env | grep MKL_NUM_THREADS` -mkl_dynamic=`env | grep MKL_DYNAMIC` -if [ ! $kmp_affinity ]; then kmp_affinity="unset"; fi -if [ ! $omp_dynamic ]; then omp_dynamic="unset"; fi -if [ ! $omp_nested ]; then omp_nested="unset"; fi -if [ ! $omp_num_threads ]; then omp_num_threads="unset"; fi -if [ ! $mkl_num_threads ]; then mkl_num_threads="unset"; fi -if [ ! $mkl_dynamic ]; then mkl_dynamic="unset"; fi -echo "KMP_AFFINITY : $kmp_affinity" -echo "OMP_DYNAMIC : $omp_dynamic" -echo "OMP_NESTED : $omp_nested" -echo "OMP_NUM_THREADS : $omp_num_threads" -echo "MKL_NUM_THREADS : $mkl_num_threads" -echo "MKL_DYNAMIC : $mkl_dynamic" -# Check if any MKL related libraries have been installed in LD_LIBRARY_PATH -for path in `echo $LD_LIBRARY_PATH | awk -F ':' '{for(i=1;i<=NF;++i)print $i}'`; do - mkldnn_found=`find $path -name "libmkldnn.so"` - if [ "$mkldnn_found" ]; then - echo "Found MKL-DNN : $mkldnn_found" - fi - mklml_found=`find $path -name "libmklml_intel.so"` - if [ "$mklml_found" ]; then - echo "Found MKLML : $mklml_found" - fi - iomp_found=`find $path -name "libiomp5.so"` - if [ "$iomp_found" ]; then - echo "Found IOMP : $iomp_found" - fi -done - -# dump all details for fully check -lscpu > lscpu.dump -dmidecode > dmidecode.dump - -# The expected result would be like: -# ========================= Hardware Information ========================= -# CPU Name : Intel(R) Xeon(R) Gold 6148M CPU @ 2.40GHz -# CPU Family : 6 -# Socket Number : 2 -# Cores Per Socket : 20 -# Total Physical Cores : 40 -# Total Virtual Cores : 40 -# Hyper Threading : OFF -# NUMA Nodes : 2 -# -------------------------- Memory Information -------------------------- -# Installed DIMM number : 12 -# Installed DIMMs Locator: -# CPU1_DIMM_A1 -# CPU1_DIMM_B1 -# CPU1_DIMM_C1 -# CPU1_DIMM_D1 -# CPU1_DIMM_E1 -# CPU1_DIMM_F1 -# CPU2_DIMM_A1 -# CPU2_DIMM_B1 -# CPU2_DIMM_C1 -# CPU2_DIMM_D1 -# CPU2_DIMM_E1 -# CPU2_DIMM_F1 -# Not installed DIMMs : -# CPU1_DIMM_A2 -# CPU1_DIMM_B2 -# CPU1_DIMM_C2 -# CPU1_DIMM_D2 -# CPU1_DIMM_E2 -# CPU1_DIMM_F2 -# CPU2_DIMM_A2 -# CPU2_DIMM_B2 -# CPU2_DIMM_C2 -# CPU2_DIMM_D2 -# CPU2_DIMM_E2 -# CPU2_DIMM_F2 -# DIMMs max slots : 24 -# Memory Size : 376G -# Swap Memory Size : 4.0G -# Total Memory Size : 380G -# Max Memory Capacity : 2304 GB -# Configed Clock Speed : 2666 MHz -# -------------------------- Turbo Information -------------------------- -# Scaling Driver : intel_pstate -# Turbo Status : ON -# CPU Max Frequency : 3.70 GHz -# CPU Min Frequency : 1.00 GHz -# CPU Freq Governor : performance -# ========================= Software Information ========================= -# BIOS Release Date : 03/10/2017 -# OS Version : CentOS Linux release 7.3.1611 (Core) -# Kernel Release Version : 3.10.0-514.el7.x86_64 -# Kernel Patch Version : #1 SMP Tue Nov 22 16:42:41 UTC 2016 -# GCC Version : 4.8.5 20150623 (Red Hat 4.8.5-11) -# CMake Version : 3.5.2 -# ------------------ Environment Variables Information ------------------- -# KMP_AFFINITY : unset -# OMP_DYNAMIC : unset -# OMP_NESTED : unset -# OMP_NUM_THREADS : unset -# MKL_NUM_THREADS : unset -# MKL_DYNAMIC : unset diff --git a/benchmark/fluid/fluid_benchmark.py b/benchmark/fluid/fluid_benchmark.py deleted file mode 100644 index df159a334e..0000000000 --- a/benchmark/fluid/fluid_benchmark.py +++ /dev/null @@ -1,369 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import argparse -import cProfile -import time -import os -import traceback - -import numpy as np - -import paddle.fluid as fluid -import paddle.fluid.core as core -import paddle.fluid.profiler as profiler -import paddle.fluid.transpiler.distribute_transpiler as distribute_transpiler - -from args import * - - -def append_nccl2_prepare(trainer_id, startup_prog): - if trainer_id >= 0: - # append gen_nccl_id at the end of startup program - trainer_id = int(os.getenv("PADDLE_TRAINER_ID")) - port = os.getenv("PADDLE_PSERVER_PORT") - worker_ips = os.getenv("PADDLE_TRAINER_IPS") - worker_endpoints = [] - for ip in worker_ips.split(","): - worker_endpoints.append(':'.join([ip, port])) - num_trainers = len(worker_endpoints) - current_endpoint = os.getenv("PADDLE_CURRENT_IP") + ":" + port - worker_endpoints.remove(current_endpoint) - - nccl_id_var = startup_prog.global_block().create_var( - name="NCCLID", - persistable=True, - type=fluid.core.VarDesc.VarType.RAW) - startup_prog.global_block().append_op( - type="gen_nccl_id", - inputs={}, - outputs={"NCCLID": nccl_id_var}, - attrs={ - "endpoint": current_endpoint, - "endpoint_list": worker_endpoints, - "trainer_id": trainer_id - }) - return nccl_id_var, num_trainers, trainer_id - else: - raise Exception("must set positive PADDLE_TRAINER_ID env variables for " - "nccl-based dist train.") - - -def dist_transpile(trainer_id, args, train_prog, startup_prog): - if trainer_id < 0: - return None, None - - # the port of all pservers, needed by both trainer and pserver - port = os.getenv("PADDLE_PSERVER_PORT", "6174") - # comma separated ips of all pservers, needed by trainer and - # pserver - pserver_ips = os.getenv("PADDLE_PSERVER_IPS", "") - eplist = [] - for ip in pserver_ips.split(","): - eplist.append(':'.join([ip, port])) - pserver_endpoints = ",".join(eplist) - # total number of workers/trainers in the job, needed by - # trainer and pserver - trainers = int(os.getenv("PADDLE_TRAINERS")) - # the IP of the local machine, needed by pserver only - current_endpoint = os.getenv("PADDLE_CURRENT_IP", "") + ":" + port - # the role, should be either PSERVER or TRAINER - training_role = os.getenv("PADDLE_TRAINING_ROLE") - - config = fluid.DistributeTranspilerConfig() - config.slice_var_up = not args.no_split_var - config.min_block_size = 1048576 - t = distribute_transpiler.DistributeTranspiler(config=config) - - t.transpile( - trainer_id, - # NOTE: *MUST* use train_prog, for we are using with guard to - # generate different program for train and test. - program=train_prog, - pservers=pserver_endpoints, - trainers=trainers, - sync_mode=not args.async_mode, - startup_program=startup_prog) - if training_role == "PSERVER": - pserver_program = t.get_pserver_program(current_endpoint) - pserver_startup_program = t.get_startup_program( - current_endpoint, pserver_program, startup_program=startup_prog) - return pserver_program, pserver_startup_program - elif training_role == "TRAINER": - train_program = t.get_trainer_program() - return train_program, startup_prog - else: - raise ValueError( - 'PADDLE_TRAINING_ROLE environment variable must be either TRAINER or PSERVER' - ) - - -def test_parallel(exe, test_args, args, test_prog, feeder): - acc_evaluators = [] - for i in xrange(len(test_args[2])): - acc_evaluators.append(fluid.metrics.Accuracy()) - - to_fetch = [v.name for v in test_args[2]] - if args.use_reader_op: - test_args[4].start() - while True: - try: - acc_rets = exe.run(fetch_list=to_fetch) - for i, e in enumerate(acc_evaluators): - e.update( - value=np.array(acc_rets[i]), weight=args.batch_size) - except fluid.core.EOFException as eof: - test_args[4].reset() - break - else: - for batch_id, data in enumerate(test_args[3]()): - acc_rets = exe.run(feed=feeder.feed(data), fetch_list=to_fetch) - for i, e in enumerate(acc_evaluators): - e.update(value=np.array(acc_rets[i]), weight=len(data)) - - return [e.eval() for e in acc_evaluators] - - -# NOTE: only need to benchmark using parallelexe -def train_parallel(train_args, test_args, args, train_prog, test_prog, - startup_prog, nccl_id_var, num_trainers, trainer_id): - over_all_start = time.time() - place = core.CPUPlace() if args.device == 'CPU' else core.CUDAPlace(0) - feeder = None - if not args.use_reader_op: - feed_var_list = [ - var for var in train_prog.global_block().vars.itervalues() - if var.is_data - ] - feeder = fluid.DataFeeder(feed_var_list, place) - # generate fake: - if args.use_fake_data: - for var in feed_var_list: - v = startup_prog.global_block()._clone_variable(var) - var.persistable = True - v.persistable = True - - real_shape = list(var.shape) - real_shape[0] = args.batch_size / args.gpus - startup_prog.global_block().append_op( - outputs={"Out": v}, - type="fill_constant", - attrs={"shape": real_shape, - "value": 1.0, - "dtype": var.dtype}) - - if nccl_id_var and trainer_id == 0: - #FIXME(wuyi): wait other trainer to start listening - time.sleep(30) - - startup_exe = fluid.Executor(place) - startup_exe.run(startup_prog) - strategy = fluid.ExecutionStrategy() - strategy.num_threads = args.cpus - strategy.allow_op_delay = False - build_strategy = fluid.BuildStrategy() - if args.reduce_strategy == "reduce": - build_strategy.reduce_strategy = fluid.BuildStrategy( - ).ReduceStrategy.Reduce - else: - build_strategy.reduce_strategy = fluid.BuildStrategy( - ).ReduceStrategy.AllReduce - - avg_loss = train_args[0] - - if args.update_method == "pserver": - # parameter server mode distributed training, merge - # gradients on local server, do not initialize - # ParallelExecutor with multi server all-reduce mode. - num_trainers = 1 - trainer_id = 0 - - exe = fluid.ParallelExecutor( - True, - avg_loss.name, - main_program=train_prog, - exec_strategy=strategy, - build_strategy=build_strategy, - num_trainers=num_trainers, - trainer_id=trainer_id) - - if not args.no_test: - if args.update_method == "pserver": - test_scope = None - else: - # NOTE: use an empty scope to avoid test exe using NCCLID - test_scope = fluid.Scope() - test_exe = fluid.ParallelExecutor( - True, main_program=test_prog, share_vars_from=exe) - - for pass_id in range(args.pass_num): - num_samples = 0 - iters = 0 - start_time = time.time() - if not args.use_reader_op: - reader_generator = train_args[3]() #train_reader - batch_id = 0 - data = None - if args.use_reader_op: - train_args[4].start() - while True: - if not args.use_reader_op: - data = next(reader_generator, None) - if data == None: - break - if args.profile and batch_id == 5: - profiler.start_profiler("All") - profiler.reset_profiler() - elif args.profile and batch_id == 10: - print("profiling total time: ", time.time() - start_time) - profiler.stop_profiler("total", "/tmp/profile_%d_pass%d" % - (trainer_id, pass_id)) - if iters == args.iterations: - reader_generator.close() - break - - if iters == args.skip_batch_num: - start_time = time.time() - num_samples = 0 - fetch_list = [avg_loss.name] - acc_name_list = [v.name for v in train_args[2]] - fetch_list.extend(acc_name_list) - - if args.use_fake_data or args.use_reader_op: - try: - fetch_ret = exe.run(fetch_list) - except fluid.core.EOFException as eof: - break - except fluid.core.EnforceNotMet as ex: - traceback.print_exc() - break - else: - fetch_ret = exe.run(fetch_list, feed=feeder.feed(data)) - if args.use_reader_op: - num_samples += args.batch_size * args.gpus - else: - num_samples += len(data) - - iters += 1 - if batch_id % 1 == 0: - fetched_data = [np.mean(np.array(d)) for d in fetch_ret] - print("Pass %d, batch %d, loss %s, accucacys: %s" % - (pass_id, batch_id, fetched_data[0], fetched_data[1:])) - batch_id += 1 - - print_train_time(start_time, time.time(), num_samples) - if args.use_reader_op: - train_args[4].reset() # reset reader handle - else: - del reader_generator - - if not args.no_test and test_args[2]: - test_feeder = None - if not args.use_reader_op: - test_feed_var_list = [ - var for var in test_prog.global_block().vars.itervalues() - if var.is_data - ] - test_feeder = fluid.DataFeeder(test_feed_var_list, place) - test_ret = test_parallel(test_exe, test_args, args, test_prog, - test_feeder) - print("Pass: %d, Test Accuracy: %s\n" % - (pass_id, [np.mean(np.array(v)) for v in test_ret])) - - print("total train time: ", time.time() - over_all_start) - - -def print_arguments(args): - vars(args)['use_nvprof'] = (vars(args)['use_nvprof'] and - vars(args)['device'] == 'GPU') - print('----------- Configuration Arguments -----------') - for arg, value in sorted(vars(args).iteritems()): - print('%s: %s' % (arg, value)) - print('------------------------------------------------') - - -def print_train_time(start_time, end_time, num_samples): - train_elapsed = end_time - start_time - examples_per_sec = num_samples / train_elapsed - print('\nTotal examples: %d, total time: %.5f, %.5f examples/sed\n' % - (num_samples, train_elapsed, examples_per_sec)) - - -def print_paddle_envs(): - print('----------- Configuration envs -----------') - for k in os.environ: - if "PADDLE_" in k: - print "ENV %s:%s" % (k, os.environ[k]) - print('------------------------------------------------') - - -def main(): - args = parse_args() - print_arguments(args) - print_paddle_envs() - if args.no_random: - fluid.default_startup_program().random_seed = 1 - - # the unique trainer id, starting from 0, needed by trainer - # only - nccl_id_var, num_trainers, trainer_id = ( - None, 1, int(os.getenv("PADDLE_TRAINER_ID", "0"))) - - if args.use_cprof: - pr = cProfile.Profile() - pr.enable() - - model_def = __import__("models.%s" % args.model, fromlist=["models"]) - - train_prog = fluid.Program() - test_prog = fluid.Program() - startup_prog = fluid.Program() - - train_args = list(model_def.get_model(args, True, train_prog, startup_prog)) - test_args = list(model_def.get_model(args, False, test_prog, startup_prog)) - - all_args = [train_args, test_args, args] - - if args.update_method == "pserver": - train_prog, startup_prog = dist_transpile(trainer_id, args, train_prog, - startup_prog) - if not train_prog: - raise Exception( - "Must configure correct environments to run dist train.") - all_args.extend([train_prog, test_prog, startup_prog]) - if args.gpus > 1 and os.getenv("PADDLE_TRAINING_ROLE") == "TRAINER": - all_args.extend([nccl_id_var, num_trainers, trainer_id]) - train_parallel(*all_args) - elif os.getenv("PADDLE_TRAINING_ROLE") == "PSERVER": - # start pserver with Executor - server_exe = fluid.Executor(fluid.CPUPlace()) - server_exe.run(startup_prog) - server_exe.run(train_prog) - exit(0) - - # for other update methods, use default programs - all_args.extend([train_prog, test_prog, startup_prog]) - - if args.update_method == "nccl2": - nccl_id_var, num_trainers, trainer_id = append_nccl2_prepare( - trainer_id, startup_prog) - - if args.device == "CPU": - raise Exception("Only support GPU perf with parallel exe") - all_args.extend([nccl_id_var, num_trainers, trainer_id]) - train_parallel(*all_args) - - -if __name__ == "__main__": - main() diff --git a/benchmark/fluid/imagenet_reader.py b/benchmark/fluid/imagenet_reader.py deleted file mode 100644 index a39485a61f..0000000000 --- a/benchmark/fluid/imagenet_reader.py +++ /dev/null @@ -1,344 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import math -import random -import functools -import numpy as np -from threading import Thread -import subprocess -import time - -from Queue import Queue -import paddle -from PIL import Image, ImageEnhance - -random.seed(0) - -DATA_DIM = 224 - -THREAD = int(os.getenv("PREPROCESS_THREADS", "10")) -BUF_SIZE = 5120 - -DATA_DIR = '/mnt/ImageNet' -TRAIN_LIST = '/mnt/ImageNet/train.txt' -TEST_LIST = '/mnt/ImageNet/val.txt' - -img_mean = np.array([0.485, 0.456, 0.406]).reshape((3, 1, 1)) -img_std = np.array([0.229, 0.224, 0.225]).reshape((3, 1, 1)) - - -def resize_short(img, target_size): - percent = float(target_size) / min(img.size[0], img.size[1]) - resized_width = int(round(img.size[0] * percent)) - resized_height = int(round(img.size[1] * percent)) - img = img.resize((resized_width, resized_height), Image.LANCZOS) - return img - - -def crop_image(img, target_size, center): - width, height = img.size - size = target_size - if center == True: - w_start = (width - size) / 2 - h_start = (height - size) / 2 - else: - w_start = random.randint(0, width - size) - h_start = random.randint(0, height - size) - w_end = w_start + size - h_end = h_start + size - img = img.crop((w_start, h_start, w_end, h_end)) - return img - - -def random_crop(img, size, scale=[0.08, 1.0], ratio=[3. / 4., 4. / 3.]): - aspect_ratio = math.sqrt(random.uniform(*ratio)) - w = 1. * aspect_ratio - h = 1. / aspect_ratio - - bound = min((float(img.size[0]) / img.size[1]) / (w**2), - (float(img.size[1]) / img.size[0]) / (h**2)) - scale_max = min(scale[1], bound) - scale_min = min(scale[0], bound) - - target_area = img.size[0] * img.size[1] * random.uniform(scale_min, - scale_max) - target_size = math.sqrt(target_area) - w = int(target_size * w) - h = int(target_size * h) - - i = random.randint(0, img.size[0] - w) - j = random.randint(0, img.size[1] - h) - - img = img.crop((i, j, i + w, j + h)) - img = img.resize((size, size), Image.LANCZOS) - return img - - -def rotate_image(img): - angle = random.randint(-10, 10) - img = img.rotate(angle) - return img - - -def distort_color(img): - def random_brightness(img, lower=0.5, upper=1.5): - e = random.uniform(lower, upper) - return ImageEnhance.Brightness(img).enhance(e) - - def random_contrast(img, lower=0.5, upper=1.5): - e = random.uniform(lower, upper) - return ImageEnhance.Contrast(img).enhance(e) - - def random_color(img, lower=0.5, upper=1.5): - e = random.uniform(lower, upper) - return ImageEnhance.Color(img).enhance(e) - - ops = [random_brightness, random_contrast, random_color] - random.shuffle(ops) - - img = ops[0](img) - img = ops[1](img) - img = ops[2](img) - - return img - - -def process_image(sample, mode, color_jitter, rotate): - img_path = sample[0] - - img = Image.open(img_path) - if mode == 'train': - if rotate: img = rotate_image(img) - img = random_crop(img, DATA_DIM) - else: - img = resize_short(img, target_size=256) - img = crop_image(img, target_size=DATA_DIM, center=True) - if mode == 'train': - if color_jitter: - img = distort_color(img) - if random.randint(0, 1) == 1: - img = img.transpose(Image.FLIP_LEFT_RIGHT) - - if img.mode != 'RGB': - img = img.convert('RGB') - - img = np.array(img).astype('float32').transpose((2, 0, 1)) / 255 - img -= img_mean - img /= img_std - - if mode == 'train' or mode == 'val': - return img, sample[1] - elif mode == 'test': - return [img] - - -class XmapEndSignal(): - pass - - -def xmap_readers(mapper, - reader, - process_num, - buffer_size, - order=False, - print_queue_state=True): - end = XmapEndSignal() - - # define a worker to read samples from reader to in_queue - def read_worker(reader, in_queue): - for i in reader(): - in_queue.put(i) - in_queue.put(end) - - # define a worker to read samples from reader to in_queue with order flag - def order_read_worker(reader, in_queue, file_queue): - in_order = 0 - for i in reader(): - in_queue.put((in_order, i)) - in_order += 1 - in_queue.put(end) - - # define a worker to handle samples from in_queue by mapper - # and put mapped samples into out_queue - def handle_worker(in_queue, out_queue, mapper): - sample = in_queue.get() - while not isinstance(sample, XmapEndSignal): - r = mapper(sample) - out_queue.put(r) - sample = in_queue.get() - in_queue.put(end) - out_queue.put(end) - - # define a worker to handle samples from in_queue by mapper - # and put mapped samples into out_queue by order - def order_handle_worker(in_queue, out_queue, mapper, out_order): - ins = in_queue.get() - while not isinstance(ins, XmapEndSignal): - order, sample = ins - r = mapper(sample) - while order != out_order[0]: - pass - out_queue.put(r) - out_order[0] += 1 - ins = in_queue.get() - in_queue.put(end) - out_queue.put(end) - - def xreader(): - file_queue = Queue() - in_queue = Queue(buffer_size) - out_queue = Queue(buffer_size) - out_order = [0] - # start a read worker in a thread - target = order_read_worker if order else read_worker - t = Thread(target=target, args=(reader, in_queue)) - t.daemon = True - t.start() - # start several handle_workers - target = order_handle_worker if order else handle_worker - args = (in_queue, out_queue, mapper, out_order) if order else ( - in_queue, out_queue, mapper) - workers = [] - for i in xrange(process_num): - worker = Thread(target=target, args=args) - worker.daemon = True - workers.append(worker) - for w in workers: - w.start() - - sample = out_queue.get() - start_t = time.time() - while not isinstance(sample, XmapEndSignal): - yield sample - sample = out_queue.get() - if time.time() - start_t > 3: - if print_queue_state: - print("queue sizes: ", in_queue.qsize(), out_queue.qsize()) - start_t = time.time() - finish = 1 - while finish < process_num: - sample = out_queue.get() - if isinstance(sample, XmapEndSignal): - finish += 1 - else: - yield sample - - return xreader - - -def _reader_creator(file_list, - mode, - shuffle=False, - color_jitter=False, - rotate=False, - xmap=True): - def reader(): - with open(file_list) as flist: - full_lines = [line.strip() for line in flist] - if shuffle: - random.shuffle(full_lines) - if mode == 'train': - trainer_id = int(os.getenv("PADDLE_TRAINER_ID")) - trainer_count = int(os.getenv("PADDLE_TRAINERS")) - per_node_lines = len(full_lines) / trainer_count - lines = full_lines[trainer_id * per_node_lines:(trainer_id + 1) - * per_node_lines] - print( - "read images from %d, length: %d, lines length: %d, total: %d" - % (trainer_id * per_node_lines, per_node_lines, len(lines), - len(full_lines))) - else: - lines = full_lines - - for line in lines: - if mode == 'train': - img_path, label = line.split() - img_path = img_path.replace("JPEG", "jpeg") - img_path = os.path.join(DATA_DIR, "train", img_path) - yield (img_path, int(label)) - elif mode == 'val': - img_path, label = line.split() - img_path = img_path.replace("JPEG", "jpeg") - img_path = os.path.join(DATA_DIR, "val", img_path) - yield (img_path, int(label)) - elif mode == 'test': - img_path = os.path.join(DATA_DIR, line) - yield [img_path] - - mapper = functools.partial( - process_image, mode=mode, color_jitter=color_jitter, rotate=rotate) - - return paddle.reader.xmap_readers(mapper, reader, THREAD, BUF_SIZE) - - -def load_raw_image_uint8(sample): - img_arr = np.array(Image.open(sample[0])).astype('int64') - return img_arr, int(sample[1]) - - -def train_raw(file_list=TRAIN_LIST, shuffle=True): - def reader(): - with open(file_list) as flist: - full_lines = [line.strip() for line in flist] - if shuffle: - random.shuffle(full_lines) - - trainer_id = int(os.getenv("PADDLE_TRAINER_ID")) - trainer_count = int(os.getenv("PADDLE_TRAINERS")) - per_node_lines = len(full_lines) / trainer_count - lines = full_lines[trainer_id * per_node_lines:(trainer_id + 1) * - per_node_lines] - print("read images from %d, length: %d, lines length: %d, total: %d" - % (trainer_id * per_node_lines, per_node_lines, len(lines), - len(full_lines))) - - for line in lines: - img_path, label = line.split() - img_path = img_path.replace("JPEG", "jpeg") - img_path = os.path.join(DATA_DIR, "train", img_path) - yield (img_path, int(label)) - - return paddle.reader.xmap_readers(load_raw_image_uint8, reader, THREAD, - BUF_SIZE) - - -def train(file_list=TRAIN_LIST, xmap=True): - return _reader_creator( - file_list, - 'train', - shuffle=True, - color_jitter=False, - rotate=False, - xmap=xmap) - - -def val(file_list=TEST_LIST, xmap=True): - return _reader_creator(file_list, 'val', shuffle=False, xmap=xmap) - - -def test(file_list=TEST_LIST): - return _reader_creator(file_list, 'test', shuffle=False) - - -if __name__ == "__main__": - c = 0 - start_t = time.time() - for d in train()(): - c += 1 - if c >= 10000: - break - spent = time.time() - start_t - print("read 10000 speed: ", 10000 / spent, spent) diff --git a/benchmark/fluid/kube_gen_job.py b/benchmark/fluid/kube_gen_job.py deleted file mode 100644 index c1f22f1bfa..0000000000 --- a/benchmark/fluid/kube_gen_job.py +++ /dev/null @@ -1,210 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import yaml -import copy -import argparse -import random -import os -import copy -from kube_templates import pserver, trainer, envs - - -def parse_args(): - parser = argparse.ArgumentParser(description='Generate dist job yamls.') - - parser.add_argument( - '--jobname', default="paddlejob", help='unique job name') - parser.add_argument( - '--cpu', default=1, type=int, help='CPU cores per trainer node') - parser.add_argument( - '--pscpu', default=1, type=int, help='CPU cores per pserver node') - parser.add_argument( - '--gpu', default=0, type=int, help='num of GPUs per node') - parser.add_argument( - '--image', - default="bootstrapper:5000/fluid_benchmark:gpu", - help='num of GPUs per node') - parser.add_argument( - '--pservers', default=1, type=int, help='num of pservers') - parser.add_argument( - '--trainers', default=1, type=int, help='num of trainers') - parser.add_argument('--memory', default=1, type=int, help='trainer memory') - parser.add_argument( - '--psmemory', default=1, type=int, help='pserver memory') - parser.add_argument( - '--port', default=30236, type=int, help='num of trainers') - parser.add_argument( - '--entry', default="python train.py", help='command to run') - parser.add_argument( - '--fluid', default=1, type=int, help='whether is fluid job') - parser.add_argument( - '--rdma', action='store_true', help='whether mount rdma libs') - parser.add_argument( - '--disttype', - default="pserver", - type=str, - choices=['pserver', 'nccl2', 'local'], - help='pserver or nccl2 or local') - - args = parser.parse_args() - return args - - -def gen_job(): - ps = pserver - tn = trainer - args = parse_args() - - ps_container = ps["spec"]["template"]["spec"]["containers"][0] - tn_container = tn["spec"]["template"]["spec"]["containers"][0] - - if args.fluid == 1: - ps_container["command"] = \ - ["paddle_k8s", "start_fluid"] - tn_container["command"] = \ - ["paddle_k8s", "start_fluid"] - ps["metadata"]["name"] = args.jobname + "-pserver" - ps["spec"]["template"]["metadata"]["labels"][ - "paddle-job-pserver"] = args.jobname - tn["metadata"]["name"] = args.jobname + "-trainer" - tn["spec"]["template"]["metadata"]["labels"]["paddle-job"] = args.jobname - - ps_container["image"] = args.image - tn_container["image"] = args.image - - ps_container["resources"]["requests"]["cpu"] = str(args.pscpu) - ps_container["resources"]["requests"]["memory"] = str(args.psmemory) + "Gi" - ps_container["resources"]["limits"]["cpu"] = str(args.pscpu) - ps_container["resources"]["limits"]["memory"] = str(args.psmemory) + "Gi" - - tn_container["resources"]["requests"]["cpu"] = str(args.cpu) - tn_container["resources"]["requests"]["memory"] = str(args.memory) + "Gi" - tn_container["resources"]["limits"]["cpu"] = str(args.cpu) - tn_container["resources"]["limits"]["memory"] = str(args.memory) + "Gi" - if args.gpu > 0: - tn_container["resources"]["requests"][ - "alpha.kubernetes.io/nvidia-gpu"] = str(args.gpu) - tn_container["resources"]["limits"][ - "alpha.kubernetes.io/nvidia-gpu"] = str(args.gpu) - - ps["spec"]["replicas"] = int(args.pservers) - tn["spec"]["parallelism"] = int(args.trainers) - tn["spec"]["completions"] = int(args.trainers) - ps_container["ports"][0]["name"] = "jobport-" + str(args.port) - ps_container["ports"][0]["containerPort"] = args.port - spreadport = random.randint(40000, 60000) - tn_container["ports"][0]["name"] = "spr-" + str(spreadport) - tn_container["ports"][0]["containerPort"] = spreadport - - envs.append({"name": "PADDLE_JOB_NAME", "value": args.jobname}) - envs.append({"name": "PADDLE_TRAINERS", "value": str(args.trainers)}) - envs.append({"name": "PADDLE_PSERVERS", "value": str(args.pservers)}) - envs.append({"name": "ENTRY", "value": args.entry}) - envs.append({"name": "PADDLE_PSERVER_PORT", "value": str(args.port)}) - # NOTE: these directories below are cluster specific, please modify - # this settings before you run on your own cluster. - envs.append({ - "name": "LD_LIBRARY_PATH", - "value": - "/usr/local/lib:/usr/local/nvidia/lib64:/usr/local/rdma/lib64:/usr/lib64/mlnx_ofed/valgrind" - }) - - volumes = [{ - "name": "nvidia-driver", - "hostPath": { - "path": "/usr/local/nvidia/lib64" - } - }] - volumeMounts = [{ - "mountPath": "/usr/local/nvidia/lib64", - "name": "nvidia-driver" - }] - - if args.rdma: - volumes.extend([{ - "name": "ibetc", - "hostPath": { - "path": "/etc/libibverbs.d" - } - }, { - "name": "iblibs", - "hostPath": { - "path": "/usr/local/rdma" - } - }, { - "name": "valgrind", - "hostPath": { - "path": "/usr/lib64/mlnx_ofed/valgrind" - } - }]) - volumeMounts.extend([{ - "mountPath": "/etc/libibverbs.d", - "name": "ibetc" - }, { - "mountPath": "/usr/local/rdma", - "name": "iblibs" - }, { - "mountPath": "/usr/lib64/mlnx_ofed/valgrind", - "name": "valgrind" - }]) - # append shm for NCCL2 - volumes.append({"name": "dshm", "emptyDir": {"medium": "Memory"}}) - volumeMounts.append({"mountPath": "/dev/shm", "name": "dshm"}) - - # add ceph volumes - volumes.append({ - "name": "ceph-data", - "cephfs": { - "monitors": ["192.168.16.23:6789"], - "secretRef": { - "name": "ceph-secret" - }, - "user": "admin", - } - }) - volumeMounts.append({"mountPath": "/mnt/data", "name": "ceph-data"}) - - tn["spec"]["template"]["spec"]["volumes"] = volumes - tn_container["volumeMounts"] = volumeMounts - - ps_container["env"] = copy.deepcopy(envs) - ps_container["env"].append({ - "name": "PADDLE_TRAINING_ROLE", - "value": "PSERVER" - }) - tn_container["env"] = envs - if args.disttype == "pserver": - tn_container["env"].append({ - "name": "PADDLE_TRAINING_ROLE", - "value": "TRAINER" - }) - elif args.disttype == "nccl2" or args.disttype == "local": - # NCCL2 have no training role, set to plain WORKER - tn_container["env"].append({ - "name": "PADDLE_TRAINING_ROLE", - "value": "WORKER" - }) - - os.mkdir(args.jobname) - if args.disttype == "pserver": - with open("%s/pserver.yaml" % args.jobname, "w") as fn: - yaml.dump(ps, fn) - - with open("%s/trainer.yaml" % args.jobname, "w") as fn: - yaml.dump(tn, fn) - - -if __name__ == "__main__": - gen_job() diff --git a/benchmark/fluid/kube_templates/__init__.py b/benchmark/fluid/kube_templates/__init__.py deleted file mode 100644 index 2d09d940a5..0000000000 --- a/benchmark/fluid/kube_templates/__init__.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from pserver import pserver -from trainer import trainer - -__all__ = ["pserver", "trainer", "envs"] - -envs = [ - # envs that don't need to change - { - "name": "GLOG_v", - "value": "0" - }, - { - "name": "GLOG_logtostderr", - "value": "1" - }, - { - "name": "TOPOLOGY", - "value": "" - }, - { - "name": "TRAINER_PACKAGE", - "value": "/workspace" - }, - { - "name": "PADDLE_INIT_NICS", - "value": "eth2" - }, - { - "name": "NAMESPACE", - "valueFrom": { - "fieldRef": { - "fieldPath": "metadata.namespace" - } - } - }, - { - "name": "POD_IP", - "valueFrom": { - "fieldRef": { - "fieldPath": "status.podIP" - } - } - }, - { - "name": "PADDLE_CURRENT_IP", - "valueFrom": { - "fieldRef": { - "fieldPath": "status.podIP" - } - } - } -] diff --git a/benchmark/fluid/kube_templates/pserver.py b/benchmark/fluid/kube_templates/pserver.py deleted file mode 100644 index b54982c806..0000000000 --- a/benchmark/fluid/kube_templates/pserver.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -pserver = { - "apiVersion": "extensions/v1beta1", - "kind": "ReplicaSet", - "metadata": { - "name": "jobname-pserver" - }, - "spec": { - "replicas": 1, - "template": { - "metadata": { - "labels": { - "paddle-job-pserver": "jobname" - } - }, - "spec": { - "hostNetwork": True, - "imagePullSecrets": [{ - "name": "job-registry-secret" - }], - "containers": [{ - "name": "pserver", - "image": "", - "imagePullPolicy": "Always", - "ports": [{ - "name": "jobport-1", - "containerPort": 1 - }], - "env": [], - "command": ["paddle_k8s", "start_pserver"], - "resources": { - "requests": { - "memory": "10Gi", - "cpu": "4" - }, - "limits": { - "memory": "10Gi", - "cpu": "4" - } - } - }] - } - } - } -} diff --git a/benchmark/fluid/kube_templates/trainer.py b/benchmark/fluid/kube_templates/trainer.py deleted file mode 100644 index b915d31e37..0000000000 --- a/benchmark/fluid/kube_templates/trainer.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -trainer = { - "apiVersion": "batch/v1", - "kind": "Job", - "metadata": { - "name": "jobname-pserver" - }, - "spec": { - "parallelism": 4, - "completions": 4, - "template": { - "metadata": { - "labels": { - "paddle-job": "jobname" - } - }, - "spec": { - "hostNetwork": True, - "imagePullSecrets": [{ - "name": "job-registry-secret" - }], - "restartPolicy": "Never", - "containers": [{ - "name": "trainer", - "image": "", - "imagePullPolicy": "Always", - # to let container set rlimit - "securityContext": { - "privileged": True - # TODO(wuyi): use below specific cap instead of privileged, - # using privileged will cause all GPU device are visible - # in the container. - # "capabilities": { - # "add": ["SYS_RESOURCE"] - # } - }, - "ports": [{ - "name": "jobport-1", - "containerPort": 1 - }], - "env": [], - "command": ["paddle_k8s", "start_trainer", "v2"], - "resources": { - "requests": { - "memory": "10Gi", - "cpu": "4", - }, - "limits": { - "memory": "10Gi", - "cpu": "4", - } - } - }] - } - } - } -} diff --git a/benchmark/fluid/models/__init__.py b/benchmark/fluid/models/__init__.py deleted file mode 100644 index 1b8f63c707..0000000000 --- a/benchmark/fluid/models/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -__all__ = [ - "machine_translation", "resnet", "vgg", "mnist", "stacked_dynamic_lstm", - "resnet_with_preprocess" -] diff --git a/benchmark/fluid/models/machine_translation.py b/benchmark/fluid/models/machine_translation.py deleted file mode 100644 index 18163c35d6..0000000000 --- a/benchmark/fluid/models/machine_translation.py +++ /dev/null @@ -1,217 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""seq2seq model for fluid.""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import numpy as np -import argparse -import time -import distutils.util - -import paddle -import paddle.fluid as fluid -import paddle.fluid.core as core -import paddle.fluid.framework as framework -from paddle.fluid.executor import Executor - - -def lstm_step(x_t, hidden_t_prev, cell_t_prev, size): - def linear(inputs): - return fluid.layers.fc(input=inputs, size=size, bias_attr=True) - - forget_gate = fluid.layers.sigmoid(x=linear([hidden_t_prev, x_t])) - input_gate = fluid.layers.sigmoid(x=linear([hidden_t_prev, x_t])) - output_gate = fluid.layers.sigmoid(x=linear([hidden_t_prev, x_t])) - cell_tilde = fluid.layers.tanh(x=linear([hidden_t_prev, x_t])) - - cell_t = fluid.layers.sums(input=[ - fluid.layers.elementwise_mul( - x=forget_gate, y=cell_t_prev), fluid.layers.elementwise_mul( - x=input_gate, y=cell_tilde) - ]) - - hidden_t = fluid.layers.elementwise_mul( - x=output_gate, y=fluid.layers.tanh(x=cell_t)) - - return hidden_t, cell_t - - -def seq_to_seq_net(embedding_dim, encoder_size, decoder_size, source_dict_dim, - target_dict_dim, is_generating, beam_size, max_length): - """Construct a seq2seq network.""" - - def bi_lstm_encoder(input_seq, gate_size): - # Linear transformation part for input gate, output gate, forget gate - # and cell activation vectors need be done outside of dynamic_lstm. - # So the output size is 4 times of gate_size. - input_forward_proj = fluid.layers.fc(input=input_seq, - size=gate_size * 4, - act=None, - bias_attr=False) - forward, _ = fluid.layers.dynamic_lstm( - input=input_forward_proj, size=gate_size * 4, use_peepholes=False) - input_reversed_proj = fluid.layers.fc(input=input_seq, - size=gate_size * 4, - act=None, - bias_attr=False) - reversed, _ = fluid.layers.dynamic_lstm( - input=input_reversed_proj, - size=gate_size * 4, - is_reverse=True, - use_peepholes=False) - return forward, reversed - - src_word_idx = fluid.layers.data( - name='source_sequence', shape=[1], dtype='int64', lod_level=1) - - src_embedding = fluid.layers.embedding( - input=src_word_idx, - size=[source_dict_dim, embedding_dim], - dtype='float32') - - src_forward, src_reversed = bi_lstm_encoder( - input_seq=src_embedding, gate_size=encoder_size) - - encoded_vector = fluid.layers.concat( - input=[src_forward, src_reversed], axis=1) - - encoded_proj = fluid.layers.fc(input=encoded_vector, - size=decoder_size, - bias_attr=False) - - backward_first = fluid.layers.sequence_pool( - input=src_reversed, pool_type='first') - - decoder_boot = fluid.layers.fc(input=backward_first, - size=decoder_size, - bias_attr=False, - act='tanh') - - def lstm_decoder_with_attention(target_embedding, encoder_vec, encoder_proj, - decoder_boot, decoder_size): - def simple_attention(encoder_vec, encoder_proj, decoder_state): - decoder_state_proj = fluid.layers.fc(input=decoder_state, - size=decoder_size, - bias_attr=False) - decoder_state_expand = fluid.layers.sequence_expand( - x=decoder_state_proj, y=encoder_proj) - concated = fluid.layers.concat( - input=[encoder_proj, decoder_state_expand], axis=1) - attention_weights = fluid.layers.fc(input=concated, - size=1, - act='tanh', - bias_attr=False) - attention_weights = fluid.layers.sequence_softmax( - input=attention_weights) - weigths_reshape = fluid.layers.reshape( - x=attention_weights, shape=[-1]) - scaled = fluid.layers.elementwise_mul( - x=encoder_vec, y=weigths_reshape, axis=0) - context = fluid.layers.sequence_pool(input=scaled, pool_type='sum') - return context - - rnn = fluid.layers.DynamicRNN() - - cell_init = fluid.layers.fill_constant_batch_size_like( - input=decoder_boot, - value=0.0, - shape=[-1, decoder_size], - dtype='float32') - cell_init.stop_gradient = False - - with rnn.block(): - current_word = rnn.step_input(target_embedding) - encoder_vec = rnn.static_input(encoder_vec) - encoder_proj = rnn.static_input(encoder_proj) - hidden_mem = rnn.memory(init=decoder_boot, need_reorder=True) - cell_mem = rnn.memory(init=cell_init) - context = simple_attention(encoder_vec, encoder_proj, hidden_mem) - decoder_inputs = fluid.layers.concat( - input=[context, current_word], axis=1) - h, c = lstm_step(decoder_inputs, hidden_mem, cell_mem, decoder_size) - rnn.update_memory(hidden_mem, h) - rnn.update_memory(cell_mem, c) - out = fluid.layers.fc(input=h, - size=target_dict_dim, - bias_attr=True, - act='softmax') - rnn.output(out) - return rnn() - - if not is_generating: - trg_word_idx = fluid.layers.data( - name='target_sequence', shape=[1], dtype='int64', lod_level=1) - - trg_embedding = fluid.layers.embedding( - input=trg_word_idx, - size=[target_dict_dim, embedding_dim], - dtype='float32') - - prediction = lstm_decoder_with_attention(trg_embedding, encoded_vector, - encoded_proj, decoder_boot, - decoder_size) - label = fluid.layers.data( - name='label_sequence', shape=[1], dtype='int64', lod_level=1) - cost = fluid.layers.cross_entropy(input=prediction, label=label) - avg_cost = fluid.layers.mean(x=cost) - - feeding_list = ["source_sequence", "target_sequence", "label_sequence"] - - return avg_cost, feeding_list - - -def lodtensor_to_ndarray(lod_tensor): - dims = lod_tensor.get_dims() - ndarray = np.zeros(shape=dims).astype('float32') - for i in xrange(np.product(dims)): - ndarray.ravel()[i] = lod_tensor.get_float_element(i) - return ndarray - - -def get_model(args, is_train, main_prog, startup_prog): - if args.use_reader_op: - raise Exception("machine_translation do not support reader op for now.") - embedding_dim = 512 - encoder_size = 512 - decoder_size = 512 - dict_size = 30000 - beam_size = 3 - max_length = 250 - - with fluid.program_guard(main_prog, startup_prog): - with fluid.unique_name.guard(): - avg_cost, feeding_list = seq_to_seq_net( - embedding_dim, - encoder_size, - decoder_size, - dict_size, - dict_size, - False, - beam_size=beam_size, - max_length=max_length) - if is_train: - optimizer = fluid.optimizer.Adam(learning_rate=args.learning_rate) - optimizer.minimize(avg_cost) - - batch_generator = paddle.batch( - paddle.reader.shuffle( - paddle.dataset.wmt14.train(dict_size) - if is_train else paddle.dataset.wmt14.test(dict_size), - buf_size=1000), - batch_size=args.batch_size * args.gpus) - - return avg_cost, optimizer, [], batch_generator, None diff --git a/benchmark/fluid/models/mnist.py b/benchmark/fluid/models/mnist.py deleted file mode 100644 index f123e07fb7..0000000000 --- a/benchmark/fluid/models/mnist.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import numpy as np -import argparse -import time -import cProfile -import os - -import paddle -import paddle.fluid as fluid -import paddle.fluid.profiler as profiler - -SEED = 1 -DTYPE = "float32" - -# random seed must set before configuring the network. -# fluid.default_startup_program().random_seed = SEED - - -def cnn_model(data): - conv_pool_1 = fluid.nets.simple_img_conv_pool( - input=data, - filter_size=5, - num_filters=20, - pool_size=2, - pool_stride=2, - act="relu") - conv_pool_2 = fluid.nets.simple_img_conv_pool( - input=conv_pool_1, - filter_size=5, - num_filters=50, - pool_size=2, - pool_stride=2, - act="relu") - - # TODO(dzhwinter) : refine the initializer and random seed settting - SIZE = 10 - input_shape = conv_pool_2.shape - param_shape = [reduce(lambda a, b: a * b, input_shape[1:], 1)] + [SIZE] - scale = (2.0 / (param_shape[0]**2 * SIZE))**0.5 - - predict = fluid.layers.fc( - input=conv_pool_2, - size=SIZE, - act="softmax", - param_attr=fluid.param_attr.ParamAttr( - initializer=fluid.initializer.NormalInitializer( - loc=0.0, scale=scale))) - return predict - - -def get_model(args, is_train, main_prog, startup_prog): - # NOTE: mnist is small, we don't implement data sharding yet. - opt = None - data_file_handle = None - with fluid.program_guard(main_prog, startup_prog): - if args.use_reader_op: - filelist = [ - os.path.join(args.data_path, f) - for f in os.listdir(args.data_path) - ] - data_file_handle = fluid.layers.open_files( - filenames=filelist, - shapes=[[-1, 1, 28, 28], (-1, 1)], - lod_levels=[0, 0], - dtypes=["float32", "int64"], - thread_num=1, - pass_num=1) - data_file = fluid.layers.double_buffer( - fluid.layers.batch( - data_file_handle, batch_size=args.batch_size)) - with fluid.unique_name.guard(): - if args.use_reader_op: - input, label = fluid.layers.read_file(data_file) - else: - images = fluid.layers.data( - name='pixel', shape=[1, 28, 28], dtype='float32') - label = fluid.layers.data( - name='label', shape=[1], dtype='int64') - - predict = cnn_model(images) - cost = fluid.layers.cross_entropy(input=predict, label=label) - avg_cost = fluid.layers.mean(x=cost) - # Evaluator - batch_acc = fluid.layers.accuracy(input=predict, label=label) - # Optimization - if is_train: - opt = fluid.optimizer.AdamOptimizer( - learning_rate=0.001, beta1=0.9, beta2=0.999) - opt.minimize(avg_cost) - if args.memory_optimize: - fluid.memory_optimize(main_prog) - - # Reader - if is_train: - reader = paddle.dataset.mnist.train() - else: - reader = paddle.dataset.mnist.test() - batched_reader = paddle.batch( - reader, batch_size=args.batch_size * args.gpus) - return avg_cost, opt, [batch_acc], batched_reader, data_file_handle diff --git a/benchmark/fluid/models/resnet.py b/benchmark/fluid/models/resnet.py deleted file mode 100644 index f692e7722a..0000000000 --- a/benchmark/fluid/models/resnet.py +++ /dev/null @@ -1,237 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import functools -import numpy as np -import time -import os -import math - -import cProfile, pstats, StringIO - -import paddle -import paddle.fluid as fluid -import paddle.fluid.core as core -import paddle.fluid.profiler as profiler -from imagenet_reader import train, val - -train_parameters = { - "input_size": [3, 224, 224], - "input_mean": [0.485, 0.456, 0.406], - "input_std": [0.229, 0.224, 0.225], - "learning_strategy": { - "name": "piecewise_decay", - "batch_size": 256, - "epochs": [30, 60, 90], - "steps": [0.1, 0.01, 0.001, 0.0001] - } -} - - -class ResNet(): - def __init__(self, layers=50, is_train=True): - self.params = train_parameters - self.layers = layers - self.is_train = is_train - - def net(self, input, class_dim=1000): - layers = self.layers - supported_layers = [50, 101, 152] - assert layers in supported_layers, \ - "supported layers are {} but input layer is {}".format(supported_layers, layers) - - if layers == 50: - depth = [3, 4, 6, 3] - elif layers == 101: - depth = [3, 4, 23, 3] - elif layers == 152: - depth = [3, 8, 36, 3] - num_filters = [64, 128, 256, 512] - - conv = self.conv_bn_layer( - input=input, num_filters=64, filter_size=7, stride=2, act='relu') - conv = fluid.layers.pool2d( - input=conv, - pool_size=3, - pool_stride=2, - pool_padding=1, - pool_type='max') - - for block in range(len(depth)): - for i in range(depth[block]): - conv = self.bottleneck_block( - input=conv, - num_filters=num_filters[block], - stride=2 if i == 0 and block != 0 else 1) - - pool = fluid.layers.pool2d( - input=conv, pool_size=7, pool_type='avg', global_pooling=True) - stdv = 1.0 / math.sqrt(pool.shape[1] * 1.0) - out = fluid.layers.fc(input=pool, - size=class_dim, - act='softmax', - param_attr=fluid.param_attr.ParamAttr( - initializer=fluid.initializer.Uniform(-stdv, - stdv))) - return out - - def conv_bn_layer(self, - input, - num_filters, - filter_size, - stride=1, - groups=1, - act=None): - conv = fluid.layers.conv2d( - input=input, - num_filters=num_filters, - filter_size=filter_size, - stride=stride, - padding=(filter_size - 1) // 2, - groups=groups, - act=None, - bias_attr=False) - return fluid.layers.batch_norm( - input=conv, act=act, is_test=not self.is_train) - - def shortcut(self, input, ch_out, stride): - ch_in = input.shape[1] - if ch_in != ch_out or stride != 1: - return self.conv_bn_layer(input, ch_out, 1, stride) - else: - return input - - def bottleneck_block(self, input, num_filters, stride): - conv0 = self.conv_bn_layer( - input=input, num_filters=num_filters, filter_size=1, act='relu') - conv1 = self.conv_bn_layer( - input=conv0, - num_filters=num_filters, - filter_size=3, - stride=stride, - act='relu') - conv2 = self.conv_bn_layer( - input=conv1, num_filters=num_filters * 4, filter_size=1, act=None) - - short = self.shortcut(input, num_filters * 4, stride) - - return fluid.layers.elementwise_add(x=short, y=conv2, act='relu') - - -def _model_reader_dshape_classdim(args, is_train): - model = None - reader = None - if args.data_set == "flowers": - class_dim = 102 - if args.data_format == 'NCHW': - dshape = [3, 224, 224] - else: - dshape = [224, 224, 3] - if is_train: - reader = paddle.dataset.flowers.train() - else: - reader = paddle.dataset.flowers.test() - elif args.data_set == "imagenet": - class_dim = 1000 - if args.data_format == 'NCHW': - dshape = [3, 224, 224] - else: - dshape = [224, 224, 3] - if not args.data_path: - raise Exception( - "Must specify --data_path when training with imagenet") - if not args.use_reader_op: - if is_train: - reader = train() - else: - reader = val() - else: - if is_train: - reader = train(xmap=False) - else: - reader = val(xmap=False) - return reader, dshape, class_dim - - -def get_model(args, is_train, main_prog, startup_prog): - reader, dshape, class_dim = _model_reader_dshape_classdim(args, is_train) - - pyreader = None - trainer_count = int(os.getenv("PADDLE_TRAINERS")) - with fluid.program_guard(main_prog, startup_prog): - with fluid.unique_name.guard(): - if args.use_reader_op: - pyreader = fluid.layers.py_reader( - capacity=args.batch_size * args.gpus, - shapes=([-1] + dshape, (-1, 1)), - dtypes=('float32', 'int64'), - name="train_reader" if is_train else "test_reader", - use_double_buffer=True) - input, label = fluid.layers.read_file(pyreader) - else: - input = fluid.layers.data( - name='data', shape=dshape, dtype='float32') - label = fluid.layers.data( - name='label', shape=[1], dtype='int64') - - model = ResNet(is_train=is_train) - predict = model.net(input, class_dim=class_dim) - cost = fluid.layers.cross_entropy(input=predict, label=label) - avg_cost = fluid.layers.mean(x=cost) - - batch_acc1 = fluid.layers.accuracy(input=predict, label=label, k=1) - batch_acc5 = fluid.layers.accuracy(input=predict, label=label, k=5) - - # configure optimize - optimizer = None - if is_train: - total_images = 1281167 / trainer_count - - step = int(total_images / (args.batch_size * args.gpus) + 1) - epochs = [30, 60, 90] - bd = [step * e for e in epochs] - base_lr = args.learning_rate - lr = [] - lr = [base_lr * (0.1**i) for i in range(len(bd) + 1)] - optimizer = fluid.optimizer.Momentum( - learning_rate=fluid.layers.piecewise_decay( - boundaries=bd, values=lr), - momentum=0.9, - regularization=fluid.regularizer.L2Decay(1e-4)) - optimizer.minimize(avg_cost) - - if args.memory_optimize: - fluid.memory_optimize(main_prog) - - # config readers - if not args.use_reader_op: - batched_reader = paddle.batch( - reader if args.no_random else paddle.reader.shuffle( - reader, buf_size=5120), - batch_size=args.batch_size * args.gpus, - drop_last=True) - else: - batched_reader = None - pyreader.decorate_paddle_reader( - paddle.batch( - reader if args.no_random else paddle.reader.shuffle( - reader, buf_size=5120), - batch_size=args.batch_size)) - - return avg_cost, optimizer, [batch_acc1, - batch_acc5], batched_reader, pyreader diff --git a/benchmark/fluid/models/resnet_with_preprocess.py b/benchmark/fluid/models/resnet_with_preprocess.py deleted file mode 100644 index e996c9a704..0000000000 --- a/benchmark/fluid/models/resnet_with_preprocess.py +++ /dev/null @@ -1,263 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import functools -import numpy as np -import time -import os - -import cProfile, pstats, StringIO - -import paddle -import paddle.fluid as fluid -import paddle.fluid.core as core -import paddle.fluid.profiler as profiler -# from recordio_converter import imagenet_train, imagenet_test -from imagenet_reader import train_raw, val - - -def conv_bn_layer(input, - ch_out, - filter_size, - stride, - padding, - act='relu', - is_train=True): - conv1 = fluid.layers.conv2d( - input=input, - filter_size=filter_size, - num_filters=ch_out, - stride=stride, - padding=padding, - act=None, - bias_attr=False) - return fluid.layers.batch_norm(input=conv1, act=act, is_test=not is_train) - - -def shortcut(input, ch_out, stride, is_train=True): - ch_in = input.shape[1] # if args.data_format == 'NCHW' else input.shape[-1] - if ch_in != ch_out: - return conv_bn_layer( - input, ch_out, 1, stride, 0, None, is_train=is_train) - else: - return input - - -def basicblock(input, ch_out, stride, is_train=True): - short = shortcut(input, ch_out, stride, is_train=is_train) - conv1 = conv_bn_layer(input, ch_out, 3, stride, 1, is_train=is_train) - conv2 = conv_bn_layer(conv1, ch_out, 3, 1, 1, act=None, is_train=is_train) - return fluid.layers.elementwise_add(x=short, y=conv2, act='relu') - - -def bottleneck(input, ch_out, stride, is_train=True): - short = shortcut(input, ch_out * 4, stride, is_train=is_train) - conv1 = conv_bn_layer(input, ch_out, 1, stride, 0, is_train=is_train) - conv2 = conv_bn_layer(conv1, ch_out, 3, 1, 1, is_train=is_train) - conv3 = conv_bn_layer( - conv2, ch_out * 4, 1, 1, 0, act=None, is_train=is_train) - return fluid.layers.elementwise_add(x=short, y=conv3, act='relu') - - -def layer_warp(block_func, input, ch_out, count, stride): - res_out = block_func(input, ch_out, stride) - for i in range(1, count): - res_out = block_func(res_out, ch_out, 1) - return res_out - - -def resnet_imagenet(input, - class_dim, - depth=50, - data_format='NCHW', - is_train=True): - - cfg = { - 18: ([2, 2, 2, 1], basicblock), - 34: ([3, 4, 6, 3], basicblock), - 50: ([3, 4, 6, 3], bottleneck), - 101: ([3, 4, 23, 3], bottleneck), - 152: ([3, 8, 36, 3], bottleneck) - } - stages, block_func = cfg[depth] - conv1 = conv_bn_layer(input, ch_out=64, filter_size=7, stride=2, padding=3) - pool1 = fluid.layers.pool2d( - input=conv1, pool_type='avg', pool_size=3, pool_stride=2) - res1 = layer_warp(block_func, pool1, 64, stages[0], 1) - res2 = layer_warp(block_func, res1, 128, stages[1], 2) - res3 = layer_warp(block_func, res2, 256, stages[2], 2) - res4 = layer_warp(block_func, res3, 512, stages[3], 2) - pool2 = fluid.layers.pool2d( - input=res4, - pool_size=7, - pool_type='avg', - pool_stride=1, - global_pooling=True) - out = fluid.layers.fc(input=pool2, size=class_dim, act='softmax') - return out - - -def resnet_cifar10(input, class_dim, depth=32, data_format='NCHW'): - assert (depth - 2) % 6 == 0 - - n = (depth - 2) // 6 - - conv1 = conv_bn_layer( - input=input, ch_out=16, filter_size=3, stride=1, padding=1) - res1 = layer_warp(basicblock, conv1, 16, n, 1) - res2 = layer_warp(basicblock, res1, 32, n, 2) - res3 = layer_warp(basicblock, res2, 64, n, 2) - pool = fluid.layers.pool2d( - input=res3, pool_size=8, pool_type='avg', pool_stride=1) - out = fluid.layers.fc(input=pool, size=class_dim, act='softmax') - return out - - -def _model_reader_dshape_classdim(args, is_train): - model = resnet_cifar10 - reader = None - if args.data_set == "cifar10": - class_dim = 10 - if args.data_format == 'NCHW': - dshape = [3, 32, 32] - else: - dshape = [32, 32, 3] - model = resnet_cifar10 - if is_train: - reader = paddle.dataset.cifar.train10() - else: - reader = paddle.dataset.cifar.test10() - elif args.data_set == "flowers": - class_dim = 102 - if args.data_format == 'NCHW': - dshape = [3, 224, 224] - else: - dshape = [224, 224, 3] - model = resnet_imagenet - if is_train: - reader = paddle.dataset.flowers.train() - else: - reader = paddle.dataset.flowers.test() - elif args.data_set == "imagenet": - class_dim = 1000 - if args.data_format == 'NCHW': - dshape = [3, 224, 224] - else: - dshape = [224, 224, 3] - model = resnet_imagenet - if not args.data_path: - raise Exception( - "Must specify --data_path when training with imagenet") - if not args.use_reader_op: - if is_train: - reader = train_raw() - else: - reader = val() - else: - if is_train: - reader = train_raw() - else: - reader = val(xmap=False) - return model, reader, dshape, class_dim - - -def get_model(args, is_train, main_prog, startup_prog): - model, reader, dshape, class_dim = _model_reader_dshape_classdim(args, - is_train) - - pyreader = None - trainer_count = int(os.getenv("PADDLE_TRAINERS")) - with fluid.program_guard(main_prog, startup_prog): - with fluid.unique_name.guard(): - if args.use_reader_op: - pyreader = fluid.layers.py_reader( - capacity=args.batch_size * args.gpus, - shapes=([-1] + dshape, (-1, 1)), - dtypes=('uint8', 'int64'), - name="train_reader" if is_train else "test_reader", - use_double_buffer=True) - input, label = fluid.layers.read_file(pyreader) - else: - input = fluid.layers.data( - name='data', shape=dshape, dtype='uint8') - label = fluid.layers.data( - name='label', shape=[1], dtype='int64') - - # add imagenet preprocessors - random_crop = fluid.layers.random_crop(input, dshape) - casted = fluid.layers.cast(random_crop, 'float32') - # input is HWC - trans = fluid.layers.transpose(casted, [0, 3, 1, 2]) / 255.0 - img_mean = fluid.layers.tensor.assign( - np.array([0.485, 0.456, 0.406]).astype('float32').reshape((3, 1, - 1))) - img_std = fluid.layers.tensor.assign( - np.array([0.229, 0.224, 0.225]).astype('float32').reshape((3, 1, - 1))) - h1 = fluid.layers.elementwise_sub(trans, img_mean, axis=1) - h2 = fluid.layers.elementwise_div(h1, img_std, axis=1) - - # pre_out = (trans - img_mean) / img_std - - predict = model(h2, class_dim, is_train=is_train) - cost = fluid.layers.cross_entropy(input=predict, label=label) - avg_cost = fluid.layers.mean(x=cost) - - batch_acc1 = fluid.layers.accuracy(input=predict, label=label, k=1) - batch_acc5 = fluid.layers.accuracy(input=predict, label=label, k=5) - - # configure optimize - optimizer = None - if is_train: - total_images = 1281167 / trainer_count - - step = int(total_images / args.batch_size + 1) - epochs = [30, 60, 80, 90] - bd = [step * e for e in epochs] - base_lr = args.learning_rate - lr = [] - lr = [base_lr * (0.1**i) for i in range(len(bd) + 1)] - optimizer = fluid.optimizer.Momentum( - learning_rate=base_lr, - #learning_rate=fluid.layers.piecewise_decay( - # boundaries=bd, values=lr), - momentum=0.9, - regularization=fluid.regularizer.L2Decay(1e-4)) - optimizer.minimize(avg_cost) - - if args.memory_optimize: - fluid.memory_optimize(main_prog) - - # config readers - if not args.use_reader_op: - batched_reader = paddle.batch( - reader if args.no_random else paddle.reader.shuffle( - reader, buf_size=5120), - batch_size=args.batch_size * args.gpus, - drop_last=True) - else: - batched_reader = None - pyreader.decorate_paddle_reader( - paddle.batch( - # reader if args.no_random else paddle.reader.shuffle( - # reader, buf_size=5120), - reader, - batch_size=args.batch_size)) - - return avg_cost, optimizer, [batch_acc1, - batch_acc5], batched_reader, pyreader diff --git a/benchmark/fluid/models/se_resnext.py b/benchmark/fluid/models/se_resnext.py deleted file mode 100644 index 7fbb83c2ec..0000000000 --- a/benchmark/fluid/models/se_resnext.py +++ /dev/null @@ -1,280 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import paddle -import paddle.fluid as fluid -import math -import os -from imagenet_reader import train, val - -__all__ = [ - "SE_ResNeXt", "SE_ResNeXt50_32x4d", "SE_ResNeXt101_32x4d", - "SE_ResNeXt152_32x4d", "get_model" -] - -train_parameters = { - "input_size": [3, 224, 224], - "input_mean": [0.485, 0.456, 0.406], - "input_std": [0.229, 0.224, 0.225], - "learning_strategy": { - "name": "piecewise_decay", - "batch_size": 256, - "epochs": [30, 60, 90], - "steps": [0.1, 0.01, 0.001, 0.0001] - } -} - - -class SE_ResNeXt(): - def __init__(self, layers=50, is_train=True): - self.params = train_parameters - self.layers = layers - self.is_train = is_train - - def net(self, input, class_dim=1000): - layers = self.layers - supported_layers = [50, 101, 152] - assert layers in supported_layers, \ - "supported layers are {} but input layer is {}".format(supported_layers, layers) - if layers == 50: - cardinality = 32 - reduction_ratio = 16 - depth = [3, 4, 6, 3] - num_filters = [128, 256, 512, 1024] - - conv = self.conv_bn_layer( - input=input, - num_filters=64, - filter_size=7, - stride=2, - act='relu') - conv = fluid.layers.pool2d( - input=conv, - pool_size=3, - pool_stride=2, - pool_padding=1, - pool_type='max') - elif layers == 101: - cardinality = 32 - reduction_ratio = 16 - depth = [3, 4, 23, 3] - num_filters = [128, 256, 512, 1024] - - conv = self.conv_bn_layer( - input=input, - num_filters=64, - filter_size=7, - stride=2, - act='relu') - conv = fluid.layers.pool2d( - input=conv, - pool_size=3, - pool_stride=2, - pool_padding=1, - pool_type='max') - elif layers == 152: - cardinality = 64 - reduction_ratio = 16 - depth = [3, 8, 36, 3] - num_filters = [128, 256, 512, 1024] - - conv = self.conv_bn_layer( - input=input, - num_filters=64, - filter_size=3, - stride=2, - act='relu') - conv = self.conv_bn_layer( - input=conv, num_filters=64, filter_size=3, stride=1, act='relu') - conv = self.conv_bn_layer( - input=conv, - num_filters=128, - filter_size=3, - stride=1, - act='relu') - conv = fluid.layers.pool2d( - input=conv, pool_size=3, pool_stride=2, pool_padding=1, \ - pool_type='max') - - for block in range(len(depth)): - for i in range(depth[block]): - conv = self.bottleneck_block( - input=conv, - num_filters=num_filters[block], - stride=2 if i == 0 and block != 0 else 1, - cardinality=cardinality, - reduction_ratio=reduction_ratio) - - pool = fluid.layers.pool2d( - input=conv, pool_size=7, pool_type='avg', global_pooling=True) - drop = fluid.layers.dropout(x=pool, dropout_prob=0.5) - stdv = 1.0 / math.sqrt(drop.shape[1] * 1.0) - out = fluid.layers.fc(input=drop, - size=class_dim, - act='softmax', - param_attr=fluid.param_attr.ParamAttr( - initializer=fluid.initializer.Uniform(-stdv, - stdv))) - return out - - def shortcut(self, input, ch_out, stride): - ch_in = input.shape[1] - if ch_in != ch_out or stride != 1: - filter_size = 1 - return self.conv_bn_layer(input, ch_out, filter_size, stride) - else: - return input - - def bottleneck_block(self, input, num_filters, stride, cardinality, - reduction_ratio): - conv0 = self.conv_bn_layer( - input=input, num_filters=num_filters, filter_size=1, act='relu') - conv1 = self.conv_bn_layer( - input=conv0, - num_filters=num_filters, - filter_size=3, - stride=stride, - groups=cardinality, - act='relu') - conv2 = self.conv_bn_layer( - input=conv1, num_filters=num_filters * 2, filter_size=1, act=None) - scale = self.squeeze_excitation( - input=conv2, - num_channels=num_filters * 2, - reduction_ratio=reduction_ratio) - - short = self.shortcut(input, num_filters * 2, stride) - - return fluid.layers.elementwise_add(x=short, y=scale, act='relu') - - def conv_bn_layer(self, - input, - num_filters, - filter_size, - stride=1, - groups=1, - act=None): - conv = fluid.layers.conv2d( - input=input, - num_filters=num_filters, - filter_size=filter_size, - stride=stride, - padding=(filter_size - 1) / 2, - groups=groups, - act=None, - bias_attr=False) - return fluid.layers.batch_norm( - input=conv, act=act, is_test=not self.is_train) - - def squeeze_excitation(self, input, num_channels, reduction_ratio): - pool = fluid.layers.pool2d( - input=input, pool_size=0, pool_type='avg', global_pooling=True) - stdv = 1.0 / math.sqrt(pool.shape[1] * 1.0) - squeeze = fluid.layers.fc(input=pool, - size=num_channels / reduction_ratio, - act='relu', - param_attr=fluid.param_attr.ParamAttr( - initializer=fluid.initializer.Uniform( - -stdv, stdv))) - stdv = 1.0 / math.sqrt(squeeze.shape[1] * 1.0) - excitation = fluid.layers.fc(input=squeeze, - size=num_channels, - act='sigmoid', - param_attr=fluid.param_attr.ParamAttr( - initializer=fluid.initializer.Uniform( - -stdv, stdv))) - scale = fluid.layers.elementwise_mul(x=input, y=excitation, axis=0) - return scale - - -def SE_ResNeXt50_32x4d(): - model = SE_ResNeXt(layers=50) - return model - - -def SE_ResNeXt101_32x4d(): - model = SE_ResNeXt(layers=101) - return model - - -def SE_ResNeXt152_32x4d(): - model = SE_ResNeXt(layers=152) - return model - - -def get_model(args, is_train, main_prog, startup_prog): - model = SE_ResNeXt(layers=50) - batched_reader = None - pyreader = None - trainer_count = int(os.getenv("PADDLE_TRAINERS")) - dshape = train_parameters["input_size"] - - with fluid.program_guard(main_prog, startup_prog): - with fluid.unique_name.guard(): - if args.use_reader_op: - pyreader = fluid.layers.py_reader( - capacity=10, - shapes=([-1] + dshape, (-1, 1)), - dtypes=('float32', 'int64'), - name="train_reader" if is_train else "test_reader", - use_double_buffer=True) - input, label = fluid.layers.read_file(pyreader) - else: - input = fluid.layers.data( - name='data', shape=dshape, dtype='float32') - label = fluid.layers.data( - name='label', shape=[1], dtype='int64') - - out = model.net(input=input) - cost = fluid.layers.cross_entropy(input=out, label=label) - avg_cost = fluid.layers.mean(x=cost) - acc_top1 = fluid.layers.accuracy(input=out, label=label, k=1) - acc_top5 = fluid.layers.accuracy(input=out, label=label, k=5) - - optimizer = None - if is_train: - total_images = 1281167 / trainer_count - - step = int(total_images / args.batch_size + 1) - epochs = [40, 80, 100] - bd = [step * e for e in epochs] - base_lr = args.learning_rate - lr = [] - lr = [base_lr * (0.1**i) for i in range(len(bd) + 1)] - optimizer = fluid.optimizer.Momentum( - # learning_rate=base_lr, - learning_rate=fluid.layers.piecewise_decay( - boundaries=bd, values=lr), - momentum=0.9, - regularization=fluid.regularizer.L2Decay(1e-4)) - optimizer.minimize(avg_cost) - - if args.memory_optimize: - fluid.memory_optimize(main_prog) - - # config readers - if is_train: - reader = train() - else: - reader = val() - - if not args.use_reader_op: - batched_reader = paddle.batch( - reader, batch_size=args.batch_size * args.gpus, drop_last=True) - else: - pyreader.decorate_paddle_reader( - paddle.batch( - reader, batch_size=args.batch_size)) - - return avg_cost, optimizer, [acc_top1, acc_top5], batched_reader, pyreader diff --git a/benchmark/fluid/models/stacked_dynamic_lstm.py b/benchmark/fluid/models/stacked_dynamic_lstm.py deleted file mode 100644 index f23bb59de9..0000000000 --- a/benchmark/fluid/models/stacked_dynamic_lstm.py +++ /dev/null @@ -1,130 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import argparse -import cPickle -import os -import random -import time - -import numpy -import paddle -import paddle.dataset.imdb as imdb -import paddle.fluid as fluid -import paddle.fluid.profiler as profiler - -word_dict = imdb.word_dict() - - -def crop_sentence(reader, crop_size): - unk_value = word_dict[''] - - def __impl__(): - for item in reader(): - if len([x for x in item[0] if x != unk_value]) < crop_size: - yield item - - return __impl__ - - -def lstm_net(sentence, lstm_size): - sentence = fluid.layers.fc(input=sentence, size=lstm_size, act='tanh') - - rnn = fluid.layers.DynamicRNN() - with rnn.block(): - word = rnn.step_input(sentence) - prev_hidden = rnn.memory(value=0.0, shape=[lstm_size]) - prev_cell = rnn.memory(value=0.0, shape=[lstm_size]) - - def gate_common( - ipt, - hidden, - size, ): - gate0 = fluid.layers.fc(input=ipt, size=size, bias_attr=True) - gate1 = fluid.layers.fc(input=hidden, size=size, bias_attr=False) - gate = fluid.layers.sums(input=[gate0, gate1]) - return gate - - forget_gate = fluid.layers.sigmoid( - x=gate_common(word, prev_hidden, lstm_size)) - input_gate = fluid.layers.sigmoid( - x=gate_common(word, prev_hidden, lstm_size)) - output_gate = fluid.layers.sigmoid( - x=gate_common(word, prev_hidden, lstm_size)) - cell_gate = fluid.layers.tanh( - x=gate_common(word, prev_hidden, lstm_size)) - - cell = fluid.layers.sums(input=[ - fluid.layers.elementwise_mul( - x=forget_gate, y=prev_cell), fluid.layers.elementwise_mul( - x=input_gate, y=cell_gate) - ]) - - hidden = fluid.layers.elementwise_mul( - x=output_gate, y=fluid.layers.tanh(x=cell)) - - rnn.update_memory(prev_cell, cell) - rnn.update_memory(prev_hidden, hidden) - rnn.output(hidden) - - last = fluid.layers.sequence_pool(rnn(), 'last') - logit = fluid.layers.fc(input=last, size=2, act='softmax') - return logit - - -def get_model(args, is_train, main_prog, startup_prog): - if args.use_reader_op: - raise Exception( - "stacked_dynamic_lstm do not support reader op for now.") - lstm_size = 512 - emb_dim = 512 - crop_size = 1500 - - with fluid.program_guard(main_prog, startup_prog): - with fluid.unique_name.guard(): - data = fluid.layers.data( - name="words", shape=[1], lod_level=1, dtype='int64') - sentence = fluid.layers.embedding( - input=data, size=[len(word_dict), emb_dim]) - logit = lstm_net(sentence, lstm_size) - loss = fluid.layers.cross_entropy( - input=logit, - label=fluid.layers.data( - name='label', shape=[1], dtype='int64')) - loss = fluid.layers.mean(x=loss) - - # add acc - batch_size_tensor = fluid.layers.create_tensor(dtype='int64') - batch_acc = fluid.layers.accuracy(input=logit, label=fluid.layers.data(name='label', \ - shape=[1], dtype='int64'), total=batch_size_tensor) - - if is_train: - adam = fluid.optimizer.Adam() - adam.minimize(loss) - - if is_train: - reader = crop_sentence(imdb.train(word_dict), crop_size) - else: - reader = crop_sentence(imdb.test(word_dict), crop_size) - - batched_reader = paddle.batch( - paddle.reader.shuffle( - reader, buf_size=25000), - batch_size=args.batch_size * args.gpus) - - return loss, adam, [batch_acc], batched_reader, None diff --git a/benchmark/fluid/models/vgg.py b/benchmark/fluid/models/vgg.py deleted file mode 100644 index cf9708d500..0000000000 --- a/benchmark/fluid/models/vgg.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""VGG16 benchmark in Fluid""" -from __future__ import print_function - -import sys -import time -import numpy as np -import paddle -import paddle.fluid as fluid -import paddle.fluid.core as core -import argparse -import functools -import os - - -def vgg16_bn_drop(input, is_train=True): - def conv_block(input, num_filter, groups, dropouts): - return fluid.nets.img_conv_group( - input=input, - pool_size=2, - pool_stride=2, - conv_num_filter=[num_filter] * groups, - conv_filter_size=3, - conv_act='relu', - conv_with_batchnorm=True, - conv_batchnorm_drop_rate=dropouts, - pool_type='max') - - conv1 = conv_block(input, 64, 2, [0.3, 0]) - conv2 = conv_block(conv1, 128, 2, [0.4, 0]) - conv3 = conv_block(conv2, 256, 3, [0.4, 0.4, 0]) - conv4 = conv_block(conv3, 512, 3, [0.4, 0.4, 0]) - conv5 = conv_block(conv4, 512, 3, [0.4, 0.4, 0]) - - drop = fluid.layers.dropout(x=conv5, dropout_prob=0.5) - fc1 = fluid.layers.fc(input=drop, size=512, act=None) - bn = fluid.layers.batch_norm(input=fc1, act='relu', is_test=not is_train) - drop2 = fluid.layers.dropout(x=bn, dropout_prob=0.5) - fc2 = fluid.layers.fc(input=drop2, size=512, act=None) - return fc2 - - -def get_model(args, is_train, main_prog, startup_prog): - if args.data_set == "cifar10": - classdim = 10 - if args.data_format == 'NCHW': - data_shape = [3, 32, 32] - else: - data_shape = [32, 32, 3] - else: - classdim = 102 - if args.data_format == 'NCHW': - data_shape = [3, 224, 224] - else: - data_shape = [224, 224, 3] - filelist = [ - os.path.join(args.data_path, f) for f in os.listdir(args.data_path) - ] - with fluid.program_guard(main_prog, startup_prog): - if args.use_reader_op: - data_file_handle = fluid.layers.open_files( - filenames=filelist, - shapes=[[-1] + data_shape, (-1, 1)], - lod_levels=[0, 0], - dtypes=["float32", "int64"], - thread_num=1, - pass_num=1) - data_file = fluid.layers.double_buffer( - fluid.layers.batch( - data_file_handle, batch_size=args.batch_size)) - with fluid.unique_name.guard(): - if args.use_reader_op: - images, label = fluid.layers.read_file(data_file) - else: - images = fluid.layers.data( - name='data', shape=data_shape, dtype='float32') - label = fluid.layers.data( - name='label', shape=[1], dtype='int64') - # Train program - net = vgg16_bn_drop(images, is_train=is_train) - predict = fluid.layers.fc(input=net, size=classdim, act='softmax') - cost = fluid.layers.cross_entropy(input=predict, label=label) - avg_cost = fluid.layers.mean(x=cost) - - # Evaluator - batch_size_tensor = fluid.layers.create_tensor(dtype='int64') - batch_acc = fluid.layers.accuracy( - input=predict, label=label, total=batch_size_tensor) - # Optimization - if is_train: - optimizer = fluid.optimizer.Adam( - learning_rate=args.learning_rate) - optimizer.minimize(avg_cost) - - # data reader - if is_train: - reader = paddle.dataset.cifar.train10() \ - if args.data_set == 'cifar10' else paddle.dataset.flowers.train() - else: - reader = paddle.dataset.cifar.test10() \ - if args.data_set == 'cifar10' else paddle.dataset.flowers.test() - - batched_reader = paddle.batch( - paddle.reader.shuffle( - reader, buf_size=5120), - batch_size=args.batch_size * args.gpus) - - return avg_cost, optimizer, [batch_acc], batched_reader, data_file_handle diff --git a/benchmark/fluid/recordio_converter.py b/benchmark/fluid/recordio_converter.py deleted file mode 100644 index f2dc39109b..0000000000 --- a/benchmark/fluid/recordio_converter.py +++ /dev/null @@ -1,164 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import random -import paddle -import paddle.fluid as fluid -import paddle.fluid.core as core -from paddle.dataset import mnist, cifar, flowers, image - - -def convert_2_recordio(py_reader, outfilepath, batch_size, shape_data, - shape_label): - num_batches = 0 - with fluid.program_guard(fluid.Program(), fluid.Program()): - reader = paddle.batch(py_reader(), batch_size=batch_size) - feeder = fluid.DataFeeder( - feed_list=[ # order is image and label - fluid.layers.data( - name='image', shape=shape_data), - fluid.layers.data( - name='label', shape=shape_label, dtype='int64'), - ], - place=fluid.CPUPlace()) - num_batches = fluid.recordio_writer.convert_reader_to_recordio_file( - outfilepath, reader, feeder) - return num_batches - - -def prepare_mnist(outpath, batch_size): - outfilepath = os.path.join(outpath, "mnist.recordio") - convert_2_recordio(mnist.train, outfilepath, batch_size, [784], [1]) - - -def prepare_cifar10(outpath, batch_size): - outfilepath = os.path.join(outpath, "cifar.recordio") - convert_2_recordio(cifar.train10, outfilepath, batch_size, [3, 32, 32], [1]) - - -def prepare_flowers(outpath, batch_size): - outfilepath = os.path.join(outpath, "flowers.recordio") - convert_2_recordio(flowers.train, outfilepath, batch_size, [3, 224, 224], - [1]) - - -def default_mapper(sample): - img, label = sample - img = image.simple_transform( - img, 256, 224, True, mean=[103.94, 116.78, 123.68]) - return img.flatten().astype('float32'), label - - -def imagenet_train(data_dir): - contents = os.listdir(data_dir) - if set(contents) != set( - ["train", "train.txt", "val", "val_set", "val.txt", "unzip.sh"]): - raise Exception("Imagenet data contents error!") - img2label = dict() - imgfilelist = [] - with open(os.path.join(data_dir, "train.txt")) as fn: - while 1: - l = fn.readline() - if not l: - break - img, lbl = l[:-1].split(" ") - img2label[img] = int(lbl) - imgfilelist.append(img) - # shuffle all, this is slow - random.shuffle(imgfilelist) - - def train_reader(): - for idx, imgfile in enumerate(imgfilelist): - data = image.load_image( - os.path.join(data_dir, "train", imgfile.lower())) - label = [img2label[imgfile], ] - yield [data, label] - - return paddle.reader.map_readers(default_mapper, train_reader) - - -def imagenet_test(data_dir): - contents = os.listdir(data_dir) - if set(contents) != set( - ["train", "train.txt", "val", "val_set", "val.txt", "unzip.sh"]): - raise Exception("Imagenet data contents error!") - img2label = dict() - imgfilelist = [] - with open(os.path.join(data_dir, "val.txt")) as fn: - while 1: - l = fn.readline() - if not l: - break - img, lbl = l[:-1].split(" ") - img2label[img] = int(lbl) - imgfilelist.append(img) - - def test_reader(): - for idx, imgfile in enumerate(imgfilelist): - base_path = os.path.join(data_dir, "val", imgfile.split(".")[0]) - image_path = ".".join([base_path, "jpeg"]) - data = image.load_image(image_path) - label = [img2label[imgfile], ] - yield [data, label] - - return paddle.reader.map_readers(default_mapper, test_reader) - - -# FIXME(wuyi): delete this when https://github.com/PaddlePaddle/Paddle/pull/11066 is merged -def convert_reader_to_recordio_files( - filename, - batch_per_file, - reader_creator, - feeder, - compressor=core.RecordIOWriter.Compressor.Snappy, - max_num_records=1000, - feed_order=None): - if feed_order is None: - feed_order = feeder.feed_names - f_name, f_ext = os.path.splitext(filename) - assert (f_ext == ".recordio") - - lines = [] - f_idx = 0 - counter = 0 - for idx, batch in enumerate(reader_creator()): - lines.append(batch) - if idx >= batch_per_file and idx % batch_per_file == 0: - filename = "%s-%05d%s" % (f_name, f_idx, f_ext) - with fluid.recordio_writer.create_recordio_writer( - filename, compressor, max_num_records) as writer: - for l in lines: - res = feeder.feed(l) - for each in feed_order: - writer.append_tensor(res[each]) - writer.complete_append_tensor() - counter += 1 - lines = [] - f_idx += 1 - print("written file: ", filename) - return counter - - -def prepare_imagenet(inpath, outpath, batch_size): - r = paddle.batch(imagenet_train(inpath), batch_size=batch_size) - feeder = fluid.DataFeeder( - feed_list=[ - fluid.layers.data( - name="image", shape=[3, 224, 224]), fluid.layers.data( - name="label", shape=[1], dtype='int64') - ], - place=fluid.CPUPlace()) - outpath = os.path.join(outpath, "imagenet.recordio") - convert_reader_to_recordio_files(outpath, 10000, r, feeder) diff --git a/benchmark/fluid/run.sh b/benchmark/fluid/run.sh deleted file mode 100755 index 5d9b2db871..0000000000 --- a/benchmark/fluid/run.sh +++ /dev/null @@ -1,109 +0,0 @@ -#!/bin/bash -# This script benchmarking the PaddlePaddle Fluid on -# single thread single GPU. - -mkdir -p logs -#export FLAGS_fraction_of_gpu_memory_to_use=0.0 -export CUDNN_PATH=/paddle/cudnn_v5 - -# disable openmp and mkl parallel -#https://github.com/PaddlePaddle/Paddle/issues/7199 -export MKL_NUM_THREADS=1 -export OMP_NUM_THREADS=1 -ht=`lscpu |grep "per core"|awk -F':' '{print $2}'|xargs` -if [ $ht -eq 1 ]; then # HT is OFF - if [ -z "$KMP_AFFINITY" ]; then - export KMP_AFFINITY="granularity=fine,compact,0,0" - fi - if [ -z "$OMP_DYNAMIC" ]; then - export OMP_DYNAMIC="FALSE" - fi -else # HT is ON - if [ -z "$KMP_AFFINITY" ]; then - export KMP_AFFINITY="granularity=fine,compact,1,0" - fi -fi -# disable multi-gpu if have more than one -export CUDA_VISIBLE_DEVICES=0 -export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH -export LD_LIBRARY_PATH=$CUDNN_PATH:$LD_LIBRARY_PATH - -# only query the gpu used -nohup stdbuf -oL nvidia-smi \ - --id=${CUDA_VISIBLE_DEVICES} \ - --query-gpu=timestamp \ - --query-compute-apps=pid,process_name,used_memory \ - --format=csv \ - --filename=mem.log \ - -l 1 & - -# mnist -# mnist gpu mnist 128 -FLAGS_benchmark=true stdbuf -oL python fluid_benchmark.py \ - --model=mnist \ - --device=GPU \ - --batch_size=128 \ - --skip_batch_num=5 \ - --iterations=500 \ - 2>&1 | tee -a logs/mnist_gpu_128.log - -# vgg16 -# gpu cifar10 128 -FLAGS_benchmark=true stdbuf -oL python fluid_benchmark.py \ - --model=vgg16 \ - --device=GPU \ - --batch_size=128 \ - --skip_batch_num=5 \ - --iterations=30 \ - 2>&1 | tee -a logs/vgg16_gpu_128.log - -# flowers gpu 128 -FLAGS_benchmark=true stdbuf -oL python fluid_benchmark.py \ - --model=vgg16 \ - --device=GPU \ - --batch_size=32 \ - --data_set=flowers \ - --skip_batch_num=5 \ - --iterations=30 \ - 2>&1 | tee -a logs/vgg16_gpu_flowers_32.log - -# resnet50 -# resnet50 gpu cifar10 128 -FLAGS_benchmark=true stdbuf -oL python fluid_benchmark.py \ - --model=resnet \ - --device=GPU \ - --batch_size=128 \ - --data_set=cifar10 \ - --skip_batch_num=5 \ - --iterations=30 \ - 2>&1 | tee -a logs/resnet50_gpu_128.log - -# resnet50 gpu flowers 64 -FLAGS_benchmark=true stdbuf -oL python fluid_benchmark.py \ - --model=resnet \ - --device=GPU \ - --batch_size=64 \ - --data_set=flowers \ - --skip_batch_num=5 \ - --iterations=30 \ - 2>&1 | tee -a logs/resnet50_gpu_flowers_64.log - -# lstm -# lstm gpu imdb 32 # tensorflow only support batch=32 -FLAGS_benchmark=true stdbuf -oL python fluid_benchmark.py \ - --model=stacked_dynamic_lstm \ - --device=GPU \ - --batch_size=32 \ - --skip_batch_num=5 \ - --iterations=30 \ - 2>&1 | tee -a logs/lstm_gpu_32.log - -# seq2seq -# seq2seq gpu wmb 128 -FLAGS_benchmark=true stdbuf -oL python fluid_benchmark.py \ - --model=machine_translation \ - --device=GPU \ - --batch_size=128 \ - --skip_batch_num=5 \ - --iterations=30 \ - 2>&1 | tee -a logs/lstm_gpu_128.log diff --git a/benchmark/fluid/run_fluid_benchmark.sh b/benchmark/fluid/run_fluid_benchmark.sh deleted file mode 100644 index 4309a3126c..0000000000 --- a/benchmark/fluid/run_fluid_benchmark.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -PADDLE_TRAINING_ROLE=PSERVER PADDLE_PSERVER_PORT=7164 PADDLE_PSERVER_IPS=127.0.0.1 PADDLE_TRAINERS=2 PADDLE_CURRENT_IP=127.0.0.1 PADDLE_TRAINER_ID=0 python fluid_benchmark.py --model resnet --device CPU --update_method pserver --iterations=10000 & - -sleep 15 - -CUDA_VISIBLE_DEVICES=0,1 PADDLE_TRAINING_ROLE=TRAINER PADDLE_PSERVER_PORT=7164 PADDLE_PSERVER_IPS=127.0.0.1 PADDLE_TRAINERS=2 PADDLE_CURRENT_IP=127.0.0.1 PADDLE_TRAINER_ID=0 python fluid_benchmark.py --model resnet --device GPU --update_method pserver --iterations=10000 --gpus 2 & - -CUDA_VISIBLE_DEVICES=2,3 PADDLE_TRAINING_ROLE=TRAINER PADDLE_PSERVER_PORT=7164 PADDLE_PSERVER_IPS=127.0.0.1 PADDLE_TRAINERS=2 PADDLE_CURRENT_IP=127.0.0.1 PADDLE_TRAINER_ID=1 python fluid_benchmark.py --model resnet --device GPU --update_method pserver --iterations=10000 --gpus 2 & diff --git a/benchmark/tensorflow/image/alexnet.py b/benchmark/tensorflow/image/alexnet.py deleted file mode 100644 index 95728b7a85..0000000000 --- a/benchmark/tensorflow/image/alexnet.py +++ /dev/null @@ -1,312 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from six.moves import xrange # pylint: disable=redefined-builtin -from datetime import datetime -import math -import time - -import tensorflow.python.platform -import tensorflow as tf - -FLAGS = tf.app.flags.FLAGS - -tf.app.flags.DEFINE_integer('batch_size', 128, """Batch size.""") -tf.app.flags.DEFINE_integer('num_batches', 100, """Number of batches to run.""") -tf.app.flags.DEFINE_boolean('forward_only', False, - """Only run the forward pass.""") -tf.app.flags.DEFINE_boolean('forward_backward_only', False, - """Only run the forward-forward pass.""") -tf.app.flags.DEFINE_string('data_format', 'NCHW', - """The data format for Convnet operations. - Can be either NHWC or NCHW. - """) -tf.app.flags.DEFINE_boolean('log_device_placement', False, - """Whether to log device placement.""") - - -def _conv(name, inpOp, nIn, nOut, kH, kW, dH, dW, padType, wd=0.0005): - with tf.name_scope(name) as scope: - kernel = tf.get_variable( - name + '_w', [kH, kW, nIn, nOut], - initializer=tf.truncated_normal_initializer( - stddev=0.01, dtype=tf.float32), - dtype=tf.float32) - - if wd is not None and wd > 0: - weight_decay = tf.mul(tf.nn.l2_loss(kernel), wd, name='weight_loss') - tf.add_to_collection('losses', weight_decay) - - if FLAGS.data_format == 'NCHW': - strides = [1, 1, dH, dW] - else: - strides = [1, dH, dW, 1] - conv = tf.nn.conv2d( - inpOp, - kernel, - strides, - padding=padType, - data_format=FLAGS.data_format) - - biases = tf.get_variable( - name=name + '_b', - shape=[nOut], - initializer=tf.constant_initializer( - value=0.0, dtype=tf.float32), - dtype=tf.float32) - - bias = tf.reshape( - tf.nn.bias_add( - conv, biases, data_format=FLAGS.data_format), - conv.get_shape()) - - conv1 = tf.nn.relu(bias, name=scope) - return conv1 - - -def _affine(name, inpOp, nIn, nOut, wd=0.0005, act=True, drop=None): - with tf.name_scope(name) as scope: - kernel = tf.get_variable( - name + '_w', [nIn, nOut], - initializer=tf.truncated_normal_initializer( - stddev=0.01, dtype=tf.float32), - dtype=tf.float32) - - if wd is not None and wd > 0: - weight_decay = tf.mul(tf.nn.l2_loss(kernel), wd, name='weight_loss') - tf.add_to_collection('losses', weight_decay) - - biases = tf.get_variable( - name + '_b', [nOut], - initializer=tf.constant_initializer( - value=0.0, dtype=tf.float32), - dtype=tf.float32, - trainable=True) - - affine1 = tf.nn.relu_layer(inpOp, kernel, biases, name=name) if act else \ - tf.matmul(inpOp, kernel) + biases - - output = tf.nn.dropout(affine1, drop) if drop else affine1 - - return output - - -def _mpool(name, inpOp, kH, kW, dH, dW): - if FLAGS.data_format == 'NCHW': - ksize = [1, 1, kH, kW] - strides = [1, 1, dH, dW] - else: - ksize = [1, kH, kW, 1] - strides = [1, dH, dW, 1] - return tf.nn.max_pool( - inpOp, - ksize=ksize, - strides=strides, - padding='VALID', - data_format=FLAGS.data_format, - name=name) - - -def _norm(name, l_input, lsize=4): - return tf.nn.lrn(l_input, - lsize, - bias=1.0, - alpha=0.001 / 9.0, - beta=0.75, - name=name) - - -def loss(logits, labels): - labels = tf.cast(labels, tf.int64) - cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits( - logits, labels, name='cross_entropy_per_example') - cross_entropy_mean = tf.reduce_mean(cross_entropy, name='cross_entropy') - tf.add_to_collection('losses', cross_entropy_mean) - - # The total loss is defined as the cross entropy loss plus all of the weight - # decay terms (L2 loss). - return tf.add_n(tf.get_collection('losses'), name='total_loss') - - -def get_incoming_shape(incoming): - """ Returns the incoming data shape """ - if isinstance(incoming, tf.Tensor): - return incoming.get_shape().as_list() - elif type(incoming) in [np.array, list, tuple]: - return np.shape(incoming) - else: - raise Exception("Invalid incoming layer.") - - -def inference(images): - conv1 = _conv('conv1', images, 3, 96, 11, 11, 4, 4, 'VALID') - pool1 = _mpool('pool1', conv1, 3, 3, 2, 2) - norm1 = _norm('norm1', pool1, lsize=5) - conv2 = _conv('conv2', norm1, 96, 256, 5, 5, 1, 1, 'SAME') - pool2 = _mpool('pool2', conv2, 3, 3, 2, 2) - norm2 = _norm('norm2', pool2, lsize=5) - conv3 = _conv('conv3', norm2, 256, 384, 3, 3, 1, 1, 'SAME') - conv4 = _conv('conv4', conv3, 384, 384, 3, 3, 1, 1, 'SAME') - conv5 = _conv('conv5', conv4, 384, 256, 3, 3, 1, 1, 'SAME') - pool5 = _mpool('pool5', conv5, 3, 3, 2, 2) - resh1 = tf.reshape(pool5, [-1, 256 * 6 * 6]) - affn1 = _affine('fc6', resh1, 256 * 6 * 6, 4096, 0.5) - affn2 = _affine('fc7', affn1, 4096, 4096, 0.5) - affn3 = _affine('fc8', affn2, 4096, 1000, wd=None, act=False) # last fc - - return affn3 - - -def time_tensorflow_run(session, target, info_string): - num_steps_burn_in = 10 - total_duration = 0.0 - total_duration_squared = 0.0 - if not isinstance(target, list): - target = [target] - target_op = tf.group(*target) - for i in xrange(FLAGS.num_batches + num_steps_burn_in): - start_time = time.time() - _ = session.run(target_op) - duration = time.time() - start_time - if i > num_steps_burn_in: - if not i % 10: - print('%s: step %d, duration = %.3f' % - (datetime.now(), i - num_steps_burn_in, duration)) - total_duration += duration - total_duration_squared += duration * duration - mn = total_duration / FLAGS.num_batches - vr = total_duration_squared / FLAGS.num_batches - mn * mn - sd = math.sqrt(vr) - print('%s: %s across %d steps, %.3f +/- %.3f sec / batch' % - (datetime.now(), info_string, FLAGS.num_batches, mn, sd)) - - -def _add_loss_summaries(total_loss): - """ - Generates moving average for all losses and associated summaries for - visualizing the performance of the network. - - Args: - total_loss: Total loss from loss(). - Returns: - loss_averages_op: op for generating moving averages of losses. - """ - # Compute the moving average of all individual losses and the total loss. - loss_averages = tf.train.ExponentialMovingAverage(0.9, name='avg') - losses = tf.get_collection('losses') - loss_averages_op = loss_averages.apply(losses + [total_loss]) - - # Attach a scalar summary to all individual losses and the total loss; do the - # same for the averaged version of the losses. - for l in losses + [total_loss]: - # Name each loss as '(raw)' and name the moving average version of the loss - # as the original loss name. - tf.scalar_summary(l.op.name + ' (raw)', l) - tf.scalar_summary(l.op.name, loss_averages.average(l)) - - return loss_averages_op - - -def run_benchmark(): - with tf.Graph().as_default(): - with tf.device('/gpu:0'): - # Generate some dummy images. - image_size = 224 - # Note that our padding definition is slightly different the cuda-convnet. - # In order to force the model to start with the same activations sizes, - # we add 3 to the image_size and employ VALID padding above. - if FLAGS.data_format == 'NCHW': - image_shape = [ - FLAGS.batch_size, 3, image_size + 3, image_size + 3 - ] - else: - image_shape = [ - FLAGS.batch_size, image_size + 3, image_size + 3, 3 - ] - images = tf.get_variable( - 'image', - image_shape, - initializer=tf.truncated_normal_initializer( - stddev=0.1, dtype=tf.float32), - dtype=tf.float32, - trainable=False) - - labels = tf.get_variable( - 'label', [FLAGS.batch_size], - initializer=tf.constant_initializer(1), - dtype=tf.int32, - trainable=False) - - # Build a Graph that computes the logits predictions from the - # inference model. - last_layer = inference(images) - - objective = loss(last_layer, labels) - # Compute the gradient with respect to all the parameters. - - # Compute gradients. - # opt = tf.train.GradientDescentOptimizer(0.001) - opt = tf.train.MomentumOptimizer(0.001, 0.9) - grads = opt.compute_gradients(objective) - global_step = tf.get_variable( - 'global_step', [], - initializer=tf.constant_initializer( - 0.0, dtype=tf.float32), - trainable=False, - dtype=tf.float32) - apply_gradient_op = opt.apply_gradients( - grads, global_step=global_step) - - # Track the moving averages of all trainable variables. - variable_averages = tf.train.ExponentialMovingAverage(0.9, - global_step) - variables_averages_op = variable_averages.apply( - tf.trainable_variables()) - - # Build an initialization operation. - init = tf.initialize_all_variables() - - # Start running operations on the Graph. - sess = tf.Session(config=tf.ConfigProto( - allow_soft_placement=True, - log_device_placement=FLAGS.log_device_placement)) - sess.run(init) - - run_forward = True - run_forward_backward = True - if FLAGS.forward_only and FLAGS.forward_backward_only: - raise ValueError("Cannot specify --forward_only and " - "--forward_backward_only at the same time.") - if FLAGS.forward_only: - run_forward_backward = False - elif FLAGS.forward_backward_only: - run_forward = False - - if run_forward: - time_tensorflow_run(sess, last_layer, "Forward") - - if run_forward_backward: - with tf.control_dependencies( - [apply_gradient_op, variables_averages_op]): - train_op = tf.no_op(name='train') - time_tensorflow_run(sess, [train_op, objective], - "Forward-backward") - - -def main(_): - run_benchmark() - - -if __name__ == '__main__': - tf.app.run() diff --git a/benchmark/tensorflow/image/alexnet_multi_gpu.py b/benchmark/tensorflow/image/alexnet_multi_gpu.py deleted file mode 100644 index 51dfe3f1cb..0000000000 --- a/benchmark/tensorflow/image/alexnet_multi_gpu.py +++ /dev/null @@ -1,379 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from six.moves import xrange # pylint: disable=redefined-builtin -from datetime import datetime -import math -import re -import time - -import tensorflow.python.platform -import tensorflow as tf - -FLAGS = tf.app.flags.FLAGS - -tf.app.flags.DEFINE_integer('batch_size', 64, """Batch size.""") -tf.app.flags.DEFINE_integer('num_batches', 100, """Number of batches to run.""") -tf.app.flags.DEFINE_string('data_format', 'NCHW', - """The data format for Convnet operations. - Can be either NHWC or NCHW. - """) - -tf.app.flags.DEFINE_string('train_dir', '/train_model', - """Directory where to write event logs """ - """and checkpoint.""") -tf.app.flags.DEFINE_integer('num_gpus', 4, """How many GPUs to use.""") -tf.app.flags.DEFINE_boolean('log_device_placement', False, - """Whether to log device placement.""") - -NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN = 50000 -NUM_EPOCHS_PER_DECAY = 50 -INITIAL_LEARNING_RATE = 0.1 -LEARNING_RATE_DECAY_FACTOR = 0.1 -TOWER_NAME = 'tower' - - -def _conv(name, inpOp, nIn, nOut, kH, kW, dH, dW, padType, wd=0.005): - with tf.name_scope(name) as scope: - kernel = tf.get_variable( - name + '_w', [kH, kW, nIn, nOut], - initializer=tf.truncated_normal_initializer( - stddev=0.01, dtype=tf.float32), - dtype=tf.float32) - - if wd is not None: - weight_decay = tf.mul(tf.nn.l2_loss(kernel), wd, name='weight_loss') - tf.add_to_collection('losses', weight_decay) - - if FLAGS.data_format == 'NCHW': - strides = [1, 1, dH, dW] - else: - strides = [1, dH, dW, 1] - conv = tf.nn.conv2d( - inpOp, - kernel, - strides, - padding=padType, - data_format=FLAGS.data_format) - - biases = tf.get_variable( - name=name + '_b', - shape=[nOut], - initializer=tf.constant_initializer( - value=0.0, dtype=tf.float32), - dtype=tf.float32) - - bias = tf.reshape( - tf.nn.bias_add( - conv, biases, data_format=FLAGS.data_format), - conv.get_shape()) - - conv1 = tf.nn.relu(bias, name=scope) - return conv1 - - -def _affine(name, inpOp, nIn, nOut, wd=0.005, act=True): - with tf.name_scope(name) as scope: - kernel = tf.get_variable( - name + '_w', [nIn, nOut], - initializer=tf.truncated_normal_initializer( - stddev=0.01, dtype=tf.float32), - dtype=tf.float32) - - if wd is not None: - weight_decay = tf.mul(tf.nn.l2_loss(kernel), wd, name='weight_loss') - tf.add_to_collection('losses', weight_decay) - - biases = tf.get_variable( - name + '_b', [nOut], - initializer=tf.constant_initializer( - value=0.0, dtype=tf.float32), - dtype=tf.float32, - trainable=True) - - affine1 = tf.nn.relu_layer(inpOp, kernel, biases, name=name) if act else \ - tf.matmul(inpOp, kernel) + biases - - return affine1 - - -def _mpool(name, inpOp, kH, kW, dH, dW): - if FLAGS.data_format == 'NCHW': - ksize = [1, 1, kH, kW] - strides = [1, 1, dH, dW] - else: - ksize = [1, kH, kW, 1] - strides = [1, dH, dW, 1] - return tf.nn.max_pool( - inpOp, - ksize=ksize, - strides=strides, - padding='VALID', - data_format=FLAGS.data_format, - name=name) - - -def _norm(name, l_input, lsize=4): - return tf.nn.lrn(l_input, - lsize, - bias=1.0, - alpha=0.001 / 9.0, - beta=0.75, - name=name) - - -def loss(logits, labels): - labels = tf.cast(labels, tf.int64) - cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits( - logits, labels, name='cross_entropy_per_example') - cross_entropy_mean = tf.reduce_mean(cross_entropy, name='cross_entropy') - tf.add_to_collection('losses', cross_entropy_mean) - - # The total loss is defined as the cross entropy loss plus all of the weight - # decay terms (L2 loss). - return tf.add_n(tf.get_collection('losses'), name='total_loss') - - -def get_incoming_shape(incoming): - """ Returns the incoming data shape """ - if isinstance(incoming, tf.Tensor): - return incoming.get_shape().as_list() - elif type(incoming) in [np.array, list, tuple]: - return np.shape(incoming) - else: - raise Exception("Invalid incoming layer.") - - -def inference(images): - conv1 = _conv('conv1', images, 3, 96, 11, 11, 4, 4, 'VALID') - pool1 = _mpool('pool1', conv1, 3, 3, 2, 2) - norm1 = _norm('norm1', pool1, lsize=5) - conv2 = _conv('conv2', norm1, 96, 256, 5, 5, 1, 1, 'SAME') - pool2 = _mpool('pool2', conv2, 3, 3, 2, 2) - norm2 = _norm('norm2', pool2, lsize=5) - conv3 = _conv('conv3', norm2, 256, 384, 3, 3, 1, 1, 'SAME') - conv4 = _conv('conv4', conv3, 384, 384, 3, 3, 1, 1, 'SAME') - conv5 = _conv('conv5', conv4, 384, 256, 3, 3, 1, 1, 'SAME') - pool5 = _mpool('pool5', conv5, 3, 3, 2, 2) - resh1 = tf.reshape(pool5, [-1, 256 * 6 * 6]) - affn1 = _affine('fc6', resh1, 256 * 6 * 6, 4096) - affn2 = _affine('fc7', affn1, 4096, 4096) - affn3 = _affine('fc8', affn2, 4096, 1000, wd=None, act=False) # last fc - - return affn3 - - -def tower_loss(scope): - """Calculate the total loss on a single tower running the model. - Args: - scope: unique prefix string identifying the tower, e.g. 'tower_0' - Returns: - Tensor of shape [] containing the total loss for a batch of data - """ - image_size = 224 - if FLAGS.data_format == 'NCHW': - image_shape = [FLAGS.batch_size, 3, image_size + 3, image_size + 3] - else: - image_shape = [FLAGS.batch_size, image_size + 3, image_size + 3, 3] - images = tf.get_variable( - 'image', - image_shape, - initializer=tf.truncated_normal_initializer( - stddev=0.1, dtype=tf.float32), - dtype=tf.float32, - trainable=False) - - labels = tf.get_variable( - 'label', [FLAGS.batch_size], - initializer=tf.constant_initializer(1), - dtype=tf.int32, - trainable=False) - - # Build a Graph that computes the logits predictions from the - # inference model. - last_layer = inference(images) - - # Build the portion of the Graph calculating the losses. Note that we will - # assemble the total_loss using a custom function below. - _ = loss(last_layer, labels) - - # Assemble all of the losses for the current tower only. - losses = tf.get_collection('losses', scope) - - # Calculate the total loss for the current tower. - total_loss = tf.add_n(losses, name='total_loss') - - # Compute the moving average of all individual losses and the total loss. - loss_averages = tf.train.ExponentialMovingAverage(0.9, name='avg') - loss_averages_op = loss_averages.apply(losses + [total_loss]) - - # Attach a scalar summary to all individual losses and the total loss; do the - # same for the averaged version of the losses. - for l in losses + [total_loss]: - # Remove 'tower_[0-9]/' from the name in case this is a multi-GPU training - # session. This helps the clarity of presentation on tensorboard. - loss_name = re.sub('%s_[0-9]*/' % TOWER_NAME, '', l.op.name) - # Name each loss as '(raw)' and name the moving average version of the loss - # as the original loss name. - tf.scalar_summary(loss_name + ' (raw)', l) - tf.scalar_summary(loss_name, loss_averages.average(l)) - - with tf.control_dependencies([loss_averages_op]): - total_loss = tf.identity(total_loss) - return total_loss - - -def average_gradients(tower_grads): - """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 individual gradients. The inner list is over the gradient - calculation for each tower. - Returns: - List of pairs of (gradient, variable) where the gradient has been averaged - across all towers. - """ - average_grads = [] - for grad_and_vars in zip(*tower_grads): - # Note that each grad_and_vars looks like the following: - # ((grad0_gpu0, var0_gpu0), ... , (grad0_gpuN, var0_gpuN)) - grads = [] - for g, _ in grad_and_vars: - # Add 0 dimension to the gradients to represent the tower. - expanded_g = tf.expand_dims(g, 0) - - # Append on a 'tower' dimension which we will average over below. - grads.append(expanded_g) - - # Average over the 'tower' dimension. - grad = tf.concat(0, grads) - grad = tf.reduce_mean(grad, 0) - - # Keep in mind that the Variables are redundant because they are shared - # across towers. So .. we will just return the first tower's pointer to - # the Variable. - v = grad_and_vars[0][1] - grad_and_var = (grad, v) - average_grads.append(grad_and_var) - return average_grads - - -def time_tensorflow_run(session, target): - num_steps_burn_in = 50 - total_duration = 0.0 - total_duration_squared = 0.0 - for i in xrange(FLAGS.num_batches + num_steps_burn_in): - start_time = time.time() - _, loss_value = session.run(target) - duration = time.time() - start_time - if i > num_steps_burn_in: - if not i % 10: - num_examples_per_step = FLAGS.batch_size * FLAGS.num_gpus - examples_per_sec = num_examples_per_step / duration - sec_per_batch = duration - - format_str = ( - '%s: step %d, loss = %.2f (%.1f examples/sec; %.3f ' - 'sec/batch batch_size = %d)') - print(format_str % - (datetime.now(), i - num_steps_burn_in, loss_value, - duration, sec_per_batch, num_examples_per_step)) - - total_duration += duration - total_duration_squared += duration * duration - - mn = total_duration / FLAGS.num_batches - vr = total_duration_squared / FLAGS.num_batches - mn * mn - sd = math.sqrt(vr) - print('%s: FwdBwd across %d steps, %.3f +/- %.3f sec / batch' % - (datetime.now(), FLAGS.num_batches, mn, sd)) - - -def run_benchmark(): - with tf.Graph().as_default(), tf.device('/cpu:0'): - # Create a variable to count the number of train() calls. This equals the - # number of batches processed * FLAGS.num_gpus. - global_step = tf.get_variable( - 'global_step', [], - initializer=tf.constant_initializer(0), - trainable=False) - - # Calculate the learning rate schedule. - num_batches_per_epoch = (NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN / - FLAGS.batch_size) - decay_steps = int(num_batches_per_epoch * NUM_EPOCHS_PER_DECAY) - - # Decay the learning rate exponentially based on the number of steps. - lr = tf.train.exponential_decay( - INITIAL_LEARNING_RATE, - global_step, - decay_steps, - LEARNING_RATE_DECAY_FACTOR, - staircase=True) - - # Create an optimizer that performs gradient descent. - opt = tf.train.MomentumOptimizer(lr, 0.9) - - # Calculate the gradients for each model tower. - tower_grads = [] - for i in xrange(FLAGS.num_gpus): - with tf.device('/gpu:%d' % i): - with tf.name_scope('%s_%d' % (TOWER_NAME, i)) as scope: - # Calculate the loss for one tower of the model. This function - # constructs the entire model but shares the variables across - # all towers. - loss = tower_loss(scope) - - # Reuse variables for the next tower. - tf.get_variable_scope().reuse_variables() - - # Retain the summaries from the final tower. - summaries = tf.get_collection(tf.GraphKeys.SUMMARIES, scope) - - # Calculate the gradients for the batch of data on this tower. - grads = opt.compute_gradients(loss) - - # Keep track of the gradients across all towers. - tower_grads.append(grads) - - # We must calculate the mean of each gradient. Note that this is the - # synchronization point across all towers. - grads = average_gradients(tower_grads) - - # Apply the gradients to adjust the shared variables. - apply_gradient_op = opt.apply_gradients(grads, global_step=global_step) - - # Group all updates to into a single train op. - train_op = tf.group(apply_gradient_op) - - # Build an initialization operation. - init = tf.initialize_all_variables() - - # Start running operations on the Graph. allow_soft_placement must be set to - # True to build towers on GPU, as some of the ops do not have GPU - # implementations. - sess = tf.Session(config=tf.ConfigProto( - allow_soft_placement=True, - log_device_placement=FLAGS.log_device_placement)) - sess.run(init) - time_tensorflow_run(sess, [train_op, loss]) - - -def main(_): - run_benchmark() - - -if __name__ == '__main__': - tf.app.run() diff --git a/benchmark/tensorflow/image/googlenet.py b/benchmark/tensorflow/image/googlenet.py deleted file mode 100644 index 37b2ba6911..0000000000 --- a/benchmark/tensorflow/image/googlenet.py +++ /dev/null @@ -1,325 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from six.moves import xrange -from datetime import datetime -import math -import time - -import tensorflow.python.platform -import tensorflow as tf - -FLAGS = tf.app.flags.FLAGS - -tf.app.flags.DEFINE_integer('batch_size', 128, """Batch size.""") -tf.app.flags.DEFINE_integer('num_batches', 100, """Number of batches to run.""") -tf.app.flags.DEFINE_boolean('forward_only', False, - """Only run the forward pass.""") -tf.app.flags.DEFINE_boolean('forward_backward_only', False, - """Only run the forward-forward pass.""") -tf.app.flags.DEFINE_string('data_format', 'NCHW', - """The data format for Convnet operations. - Can be either NHWC or NCHW. - """) -tf.app.flags.DEFINE_boolean('log_device_placement', False, - """Whether to log device placement.""") - -parameters = [] - -conv_counter = 1 -pool_counter = 1 -affine_counter = 1 - - -def _conv(inpOp, nIn, nOut, kH, kW, dH, dW, padType, wd=0.0005): - global conv_counter - global parameters - name = 'conv' + str(conv_counter) - conv_counter += 1 - with tf.name_scope(name) as scope: - kernel = tf.Variable( - tf.truncated_normal( - [kH, kW, nIn, nOut], dtype=tf.float32, stddev=1e-1), - name='weights') - - if wd is not None and wd > 0: - weight_decay = tf.mul(tf.nn.l2_loss(kernel), wd, name='weight_loss') - tf.add_to_collection('losses', weight_decay) - - if FLAGS.data_format == 'NCHW': - strides = [1, 1, dH, dW] - else: - strides = [1, dH, dW, 1] - conv = tf.nn.conv2d( - inpOp, - kernel, - strides, - padding=padType, - data_format=FLAGS.data_format) - biases = tf.Variable( - tf.constant( - 0.0, shape=[nOut], dtype=tf.float32), - trainable=True, - name='biases') - bias = tf.reshape( - tf.nn.bias_add( - conv, biases, data_format=FLAGS.data_format), - conv.get_shape()) - conv1 = tf.nn.relu(bias, name=scope) - parameters += [kernel, biases] - return conv1 - - -def _affine(inpOp, nIn, nOut, act=True, wd=0.0005): - global affine_counter - global parameters - name = 'affine' + str(affine_counter) - affine_counter += 1 - with tf.name_scope(name) as scope: - kernel = tf.Variable( - tf.truncated_normal( - [nIn, nOut], dtype=tf.float32, stddev=1e-1), - name='weights') - - if wd is not None and wd > 0: - weight_decay = tf.mul(tf.nn.l2_loss(kernel), wd, name='weight_loss') - tf.add_to_collection('losses', weight_decay) - - biases = tf.Variable( - tf.constant( - 0.0, shape=[nOut], dtype=tf.float32), - trainable=True, - name='biases') - affine1 = tf.nn.relu_layer( - inpOp, kernel, biases, - name=name) if act else tf.matmul(inpOp, kernel) + biases - parameters += [kernel, biases] - return affine1 - - -def _mpool(inpOp, kH, kW, dH, dW, padding): - global pool_counter - global parameters - name = 'pool' + str(pool_counter) - pool_counter += 1 - if FLAGS.data_format == 'NCHW': - ksize = [1, 1, kH, kW] - strides = [1, 1, dH, dW] - else: - ksize = [1, kH, kW, 1] - strides = [1, dH, dW, 1] - return tf.nn.max_pool( - inpOp, - ksize=ksize, - strides=strides, - padding=padding, - data_format=FLAGS.data_format, - name=name) - - -def _apool(inpOp, kH, kW, dH, dW, padding): - global pool_counter - global parameters - name = 'pool' + str(pool_counter) - pool_counter += 1 - if FLAGS.data_format == 'NCHW': - ksize = [1, 1, kH, kW] - strides = [1, 1, dH, dW] - else: - ksize = [1, kH, kW, 1] - strides = [1, dH, dW, 1] - return tf.nn.avg_pool( - inpOp, - ksize=ksize, - strides=strides, - padding=padding, - data_format=FLAGS.data_format, - name=name) - - -def _inception(inp, inSize, o1s, o2s1, o2s2, o3s1, o3s2, o4s1, o4s2): - conv1 = _conv(inp, inSize, o1s, 1, 1, 1, 1, 'VALID') - - conv3_ = _conv(inp, inSize, o2s1, 1, 1, 1, 1, 'VALID') - conv3 = _conv(conv3_, o2s1, o2s2, 3, 3, 1, 1, 'SAME') - - conv5_ = _conv(inp, inSize, o3s1, 1, 1, 1, 1, 'VALID') - conv5 = _conv(conv5_, o3s1, o3s2, 5, 5, 1, 1, 'SAME') - - pool_ = _mpool(inp, o4s1, o4s1, 1, 1, 'SAME') - pool = _conv(pool_, inSize, o4s2, 1, 1, 1, 1, 'VALID') - - if FLAGS.data_format == 'NCHW': - channel_dim = 1 - else: - channel_dim = 3 - incept = tf.concat(channel_dim, [conv1, conv3, conv5, pool]) - return incept - - -def loss(logits, labels): - batch_size = tf.size(labels) - labels = tf.expand_dims(labels, 1) - indices = tf.expand_dims(tf.range(0, batch_size, 1), 1) - concated = tf.concat(1, [indices, labels]) - onehot_labels = tf.sparse_to_dense(concated, - tf.pack([batch_size, 1000]), 1.0, 0.0) - cross_entropy = tf.nn.softmax_cross_entropy_with_logits( - logits, onehot_labels, name='xentropy') - loss = tf.reduce_mean(cross_entropy, name='xentropy_mean') - return loss - - -def inference(images): - # stage 1 - conv1 = _conv(images, 3, 64, 7, 7, 2, 2, 'SAME') - pool1 = _mpool(conv1, 3, 3, 2, 2, 'SAME') - # stage 2 - conv2 = _conv(pool1, 64, 64, 1, 1, 1, 1, 'VALID') - conv3 = _conv(conv2, 64, 192, 3, 3, 1, 1, 'SAME') - pool3 = _mpool(conv3, 3, 3, 2, 2, 'SAME') - - # stage 3 - incept3a = _inception(pool3, 192, 64, 96, 128, 16, 32, 3, 32) - incept3b = _inception(incept3a, 256, 128, 128, 192, 32, 96, 3, 64) - pool4 = _mpool(incept3b, 3, 3, 2, 2, 'SAME') - - # stage 4 - incept4a = _inception(pool4, 480, 192, 96, 208, 16, 48, 3, 64) - incept4b = _inception(incept4a, 512, 160, 112, 224, 24, 64, 3, 64) - incept4c = _inception(incept4b, 512, 128, 128, 256, 24, 64, 3, 64) - incept4d = _inception(incept4c, 512, 112, 144, 288, 32, 64, 3, 64) - incept4e = _inception(incept4d, 528, 256, 160, 320, 32, 128, 3, 128) - pool5 = _mpool(incept4e, 3, 3, 2, 2, 'SAME') - - # stage 5 - incept5a = _inception(pool5, 832, 256, 160, 320, 32, 128, 3, 128) - incept5b = _inception(incept5a, 832, 384, 192, 384, 48, 128, 3, 128) - pool6 = _apool(incept5b, 7, 7, 1, 1, 'VALID') - - # output 1 - resh1 = tf.reshape(pool6, [-1, 1024]) - drop = tf.nn.dropout(resh1, 0.4) - affn1 = _affine(resh1, 1024, 1000, act=False) - - return affn1 - - -def time_tensorflow_run(session, target, info_string): - num_steps_burn_in = 10 - total_duration = 0.0 - total_duration_squared = 0.0 - if not isinstance(target, list): - target = [target] - target_op = tf.group(*target) - for i in range(FLAGS.num_batches + num_steps_burn_in): - start_time = time.time() - _ = session.run(target_op) - duration = time.time() - start_time - if i > num_steps_burn_in: - if not i % 10: - print('%s: step %d, duration = %.3f' % - (datetime.now(), i - num_steps_burn_in, duration)) - total_duration += duration - total_duration_squared += duration * duration - mn = total_duration / FLAGS.num_batches - vr = total_duration_squared / FLAGS.num_batches - mn * mn - sd = math.sqrt(vr) - print('%s: %s across %d steps, %.3f +/- %.3f sec / batch' % - (datetime.now(), info_string, FLAGS.num_batches, mn, sd)) - - -def run_benchmark(): - global parameters - with tf.Graph().as_default(): - # Generate some dummy images. - image_size = 224 - if FLAGS.data_format == 'NCHW': - image_shape = [FLAGS.batch_size, 3, image_size, image_size] - else: - image_shape = [FLAGS.batch_size, image_size, image_size, 3] - - images = tf.get_variable( - 'image', - image_shape, - initializer=tf.truncated_normal_initializer( - stddev=0.1, dtype=tf.float32), - dtype=tf.float32, - trainable=False) - - labels = tf.get_variable( - 'label', [FLAGS.batch_size], - initializer=tf.constant_initializer(1), - dtype=tf.int32, - trainable=False) - - # Build a Graph that computes the logits predictions from the - # inference model. - last_layer = inference(images) - - objective = loss(last_layer, labels) - - # Compute gradients. - # opt = tf.train.GradientDescentOptimizer(0.001) - opt = tf.train.MomentumOptimizer(0.001, 0.9) - grads = opt.compute_gradients(objective) - global_step = tf.get_variable( - 'global_step', [], - initializer=tf.constant_initializer( - 0.0, dtype=tf.float32), - trainable=False, - dtype=tf.float32) - apply_gradient_op = opt.apply_gradients(grads, global_step=global_step) - - # Track the moving averages of all trainable variables. - variable_averages = tf.train.ExponentialMovingAverage(0.9, global_step) - variables_averages_op = variable_averages.apply(tf.trainable_variables( - )) - - # Build an initialization operation. - init = tf.initialize_all_variables() - - # Start running operations on the Graph. - sess = tf.Session(config=tf.ConfigProto( - allow_soft_placement=True, - log_device_placement=FLAGS.log_device_placement)) - sess.run(init) - - run_forward = True - run_forward_backward = True - if FLAGS.forward_only and FLAGS.forward_backward_only: - raise ValueError("Cannot specify --forward_only and " - "--forward_backward_only at the same time.") - if FLAGS.forward_only: - run_forward_backward = False - elif FLAGS.forward_backward_only: - run_forward = False - - if run_forward: - # Run the forward benchmark. - time_tensorflow_run(sess, last_layer, "Forward") - - if run_forward_backward: - with tf.control_dependencies( - [apply_gradient_op, variables_averages_op]): - train_op = tf.no_op(name='train') - time_tensorflow_run(sess, [train_op, objective], "Forward-backward") - - -def main(_): - run_benchmark() - - -if __name__ == '__main__': - tf.app.run() diff --git a/benchmark/tensorflow/image/googlenet_multi_gpu.py b/benchmark/tensorflow/image/googlenet_multi_gpu.py deleted file mode 100644 index 7179c5301c..0000000000 --- a/benchmark/tensorflow/image/googlenet_multi_gpu.py +++ /dev/null @@ -1,425 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from six.moves import xrange # pylint: disable=redefined-builtin -from datetime import datetime -import math -import re -import time - -import tensorflow.python.platform -import tensorflow as tf - -FLAGS = tf.app.flags.FLAGS - -tf.app.flags.DEFINE_integer('batch_size', 64, """Batch size.""") -tf.app.flags.DEFINE_integer('num_batches', 100, """Number of batches to run.""") -tf.app.flags.DEFINE_string('data_format', 'NCHW', - """The data format for Convnet operations. - Can be either NHWC or NCHW. - """) - -tf.app.flags.DEFINE_string('train_dir', '/train_model', - """Directory where to write event logs """ - """and checkpoint.""") -tf.app.flags.DEFINE_integer('num_gpus', 4, """How many GPUs to use.""") -tf.app.flags.DEFINE_boolean('log_device_placement', False, - """Whether to log device placement.""") - -NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN = 50000 -NUM_EPOCHS_PER_DECAY = 50 -INITIAL_LEARNING_RATE = 0.1 -LEARNING_RATE_DECAY_FACTOR = 0.1 -TOWER_NAME = 'tower' - - -def _conv(name, inpOp, nIn, nOut, kH, kW, dH, dW, padType, wd=0.005): - with tf.name_scope(name) as scope: - kernel = tf.get_variable( - name + '_w', [kH, kW, nIn, nOut], - initializer=tf.truncated_normal_initializer( - stddev=0.01, dtype=tf.float32), - dtype=tf.float32) - - if wd is not None: - weight_decay = tf.mul(tf.nn.l2_loss(kernel), wd, name='weight_loss') - tf.add_to_collection('losses', weight_decay) - - if FLAGS.data_format == 'NCHW': - strides = [1, 1, dH, dW] - else: - strides = [1, dH, dW, 1] - conv = tf.nn.conv2d( - inpOp, - kernel, - strides, - padding=padType, - data_format=FLAGS.data_format) - - biases = tf.get_variable( - name=name + '_b', - shape=[nOut], - initializer=tf.constant_initializer( - value=0.0, dtype=tf.float32), - dtype=tf.float32) - - bias = tf.reshape( - tf.nn.bias_add( - conv, biases, data_format=FLAGS.data_format), - conv.get_shape()) - - conv1 = tf.nn.relu(bias, name=scope) - return conv1 - - -def _affine(name, inpOp, nIn, nOut, wd=0.005, act=True): - with tf.name_scope(name) as scope: - kernel = tf.get_variable( - name + '_w', [nIn, nOut], - initializer=tf.truncated_normal_initializer( - stddev=0.01, dtype=tf.float32), - dtype=tf.float32) - - if wd is not None: - weight_decay = tf.mul(tf.nn.l2_loss(kernel), wd, name='weight_loss') - tf.add_to_collection('losses', weight_decay) - - biases = tf.get_variable( - name + '_b', [nOut], - initializer=tf.constant_initializer( - value=0.0, dtype=tf.float32), - dtype=tf.float32, - trainable=True) - - affine1 = tf.nn.relu_layer(inpOp, kernel, biases, name=name) if act else \ - tf.matmul(inpOp, kernel) + biases - - return affine1 - - -def _mpool(name, inpOp, kH, kW, dH, dW, padding): - if FLAGS.data_format == 'NCHW': - ksize = [1, 1, kH, kW] - strides = [1, 1, dH, dW] - else: - ksize = [1, kH, kW, 1] - strides = [1, dH, dW, 1] - return tf.nn.max_pool( - inpOp, - ksize=ksize, - strides=strides, - padding=padding, - data_format=FLAGS.data_format, - name=name) - - -def _apool(name, inpOp, kH, kW, dH, dW, padding): - if FLAGS.data_format == 'NCHW': - ksize = [1, 1, kH, kW] - strides = [1, 1, dH, dW] - else: - ksize = [1, kH, kW, 1] - strides = [1, dH, dW, 1] - return tf.nn.avg_pool( - inpOp, - ksize=ksize, - strides=strides, - padding=padding, - data_format=FLAGS.data_format, - name=name) - - -def loss(logits, labels): - labels = tf.cast(labels, tf.int64) - cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits( - logits, labels, name='cross_entropy_per_example') - cross_entropy_mean = tf.reduce_mean(cross_entropy, name='cross_entropy') - tf.add_to_collection('losses', cross_entropy_mean) - - # The total loss is defined as the cross entropy loss plus all of the weight - # decay terms (L2 loss). - return tf.add_n(tf.get_collection('losses'), name='total_loss') - - -def get_incoming_shape(incoming): - """ Returns the incoming data shape """ - if isinstance(incoming, tf.Tensor): - return incoming.get_shape().as_list() - elif type(incoming) in [np.array, list, tuple]: - return np.shape(incoming) - else: - raise Exception("Invalid incoming layer.") - - -def _inception(name, inp, inSize, o1s, o2s1, o2s2, o3s1, o3s2, o4s1, o4s2): - conv1 = _conv(name + '_1', inp, inSize, o1s, 1, 1, 1, 1, 'VALID') - - conv3_ = _conv(name + '_3r', inp, inSize, o2s1, 1, 1, 1, 1, 'VALID') - conv3 = _conv(name + '_3', conv3_, o2s1, o2s2, 3, 3, 1, 1, 'SAME') - - conv5_ = _conv(name + '_5r', inp, inSize, o3s1, 1, 1, 1, 1, 'VALID') - conv5 = _conv(name + '5', conv5_, o3s1, o3s2, 5, 5, 1, 1, 'SAME') - - pool_ = _mpool(name + 'pool', inp, o4s1, o4s1, 1, 1, 'SAME') - pool = _conv(name + 'proj', pool_, inSize, o4s2, 1, 1, 1, 1, 'VALID') - - if FLAGS.data_format == 'NCHW': - channel_dim = 1 - else: - channel_dim = 3 - incept = tf.concat(channel_dim, [conv1, conv3, conv5, pool]) - return incept - - -def inference(images): - # stage 1 - conv1 = _conv('conv1', images, 3, 64, 7, 7, 2, 2, 'SAME') - pool1 = _mpool('pool1', conv1, 3, 3, 2, 2, 'SAME') - - # stage 2 - conv2 = _conv('conv2', pool1, 64, 64, 1, 1, 1, 1, 'VALID') - conv3 = _conv('conv3', conv2, 64, 192, 3, 3, 1, 1, 'SAME') - pool3 = _mpool('pool3', conv3, 3, 3, 2, 2, 'SAME') - - # stage 3 - incept3a = _inception('ince3a', pool3, 192, 64, 96, 128, 16, 32, 3, 32) - incept3b = _inception('ince3b', incept3a, 256, 128, 128, 192, 32, 96, 3, 64) - pool4 = _mpool('pool4', incept3b, 3, 3, 2, 2, 'SAME') - - # stage 4 - incept4a = _inception('ince4a', pool4, 480, 192, 96, 208, 16, 48, 3, 64) - incept4b = _inception('ince4b', incept4a, 512, 160, 112, 224, 24, 64, 3, 64) - incept4c = _inception('ince4c', incept4b, 512, 128, 128, 256, 24, 64, 3, 64) - incept4d = _inception('ince4d', incept4c, 512, 112, 144, 288, 32, 64, 3, 64) - incept4e = _inception('ince4e', incept4d, 528, 256, 160, 320, 32, 128, 3, - 128) - pool5 = _mpool('pool5', incept4e, 3, 3, 2, 2, 'SAME') - - # stage 5 - incept5a = _inception('ince5a', pool5, 832, 256, 160, 320, 32, 128, 3, 128) - incept5b = _inception('ince5b', incept5a, 832, 384, 192, 384, 48, 128, 3, - 128) - pool6 = _apool('pool6', incept5b, 7, 7, 1, 1, 'VALID') - - # output 1 - resh1 = tf.reshape(pool6, [-1, 1024]) - drop = tf.nn.dropout(resh1, 0.4) - affn1 = _affine('fc_out', resh1, 1024, 1000, act=False) - - return affn1 - - -def tower_loss(scope): - """Calculate the total loss on a single tower running the model. - Args: - scope: unique prefix string identifying the tower, e.g. 'tower_0' - Returns: - Tensor of shape [] containing the total loss for a batch of data - """ - image_size = 224 - if FLAGS.data_format == 'NCHW': - image_shape = [FLAGS.batch_size, 3, image_size, image_size] - else: - image_shape = [FLAGS.batch_size, image_size, image_size, 3] - images = tf.get_variable( - 'image', - image_shape, - initializer=tf.truncated_normal_initializer( - stddev=0.1, dtype=tf.float32), - dtype=tf.float32, - trainable=False) - - labels = tf.get_variable( - 'label', [FLAGS.batch_size], - initializer=tf.constant_initializer(1), - dtype=tf.int32, - trainable=False) - - # Build a Graph that computes the logits predictions from the - # inference model. - last_layer = inference(images) - - # Build the portion of the Graph calculating the losses. Note that we will - # assemble the total_loss using a custom function below. - _ = loss(last_layer, labels) - - # Assemble all of the losses for the current tower only. - losses = tf.get_collection('losses', scope) - - # Calculate the total loss for the current tower. - total_loss = tf.add_n(losses, name='total_loss') - - # Compute the moving average of all individual losses and the total loss. - loss_averages = tf.train.ExponentialMovingAverage(0.9, name='avg') - loss_averages_op = loss_averages.apply(losses + [total_loss]) - - # Attach a scalar summary to all individual losses and the total loss; do the - # same for the averaged version of the losses. - for l in losses + [total_loss]: - # Remove 'tower_[0-9]/' from the name in case this is a multi-GPU training - # session. This helps the clarity of presentation on tensorboard. - loss_name = re.sub('%s_[0-9]*/' % TOWER_NAME, '', l.op.name) - # Name each loss as '(raw)' and name the moving average version of the loss - # as the original loss name. - tf.scalar_summary(loss_name + ' (raw)', l) - tf.scalar_summary(loss_name, loss_averages.average(l)) - - with tf.control_dependencies([loss_averages_op]): - total_loss = tf.identity(total_loss) - return total_loss - - -def average_gradients(tower_grads): - """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 individual gradients. The inner list is over the gradient - calculation for each tower. - Returns: - List of pairs of (gradient, variable) where the gradient has been averaged - across all towers. - """ - average_grads = [] - for grad_and_vars in zip(*tower_grads): - # Note that each grad_and_vars looks like the following: - # ((grad0_gpu0, var0_gpu0), ... , (grad0_gpuN, var0_gpuN)) - grads = [] - for g, _ in grad_and_vars: - # Add 0 dimension to the gradients to represent the tower. - expanded_g = tf.expand_dims(g, 0) - - # Append on a 'tower' dimension which we will average over below. - grads.append(expanded_g) - - # Average over the 'tower' dimension. - grad = tf.concat(0, grads) - grad = tf.reduce_mean(grad, 0) - - # Keep in mind that the Variables are redundant because they are shared - # across towers. So .. we will just return the first tower's pointer to - # the Variable. - v = grad_and_vars[0][1] - grad_and_var = (grad, v) - average_grads.append(grad_and_var) - return average_grads - - -def time_tensorflow_run(session, target): - num_steps_burn_in = 50 - total_duration = 0.0 - total_duration_squared = 0.0 - for i in xrange(FLAGS.num_batches + num_steps_burn_in): - start_time = time.time() - _, loss_value = session.run(target) - duration = time.time() - start_time - if i > num_steps_burn_in: - if not i % 10: - num_examples_per_step = FLAGS.batch_size * FLAGS.num_gpus - examples_per_sec = num_examples_per_step / duration - sec_per_batch = duration - - format_str = ( - '%s: step %d, loss = %.2f (%.1f examples/sec; %.3f ' - 'sec/batch batch_size = %d)') - print(format_str % - (datetime.now(), i - num_steps_burn_in, loss_value, - duration, sec_per_batch, num_examples_per_step)) - - total_duration += duration - total_duration_squared += duration * duration - - mn = total_duration / FLAGS.num_batches - vr = total_duration_squared / FLAGS.num_batches - mn * mn - sd = math.sqrt(vr) - print('%s: FwdBwd across %d steps, %.3f +/- %.3f sec / batch' % - (datetime.now(), FLAGS.num_batches, mn, sd)) - - -def run_benchmark(): - with tf.Graph().as_default(), tf.device('/cpu:0'): - # Create a variable to count the number of train() calls. This equals the - # number of batches processed * FLAGS.num_gpus. - global_step = tf.get_variable( - 'global_step', [], - initializer=tf.constant_initializer(0), - trainable=False) - - # Calculate the learning rate schedule. - num_batches_per_epoch = (NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN / - FLAGS.batch_size) - decay_steps = int(num_batches_per_epoch * NUM_EPOCHS_PER_DECAY) - - # Decay the learning rate exponentially based on the number of steps. - lr = tf.train.exponential_decay( - INITIAL_LEARNING_RATE, - global_step, - decay_steps, - LEARNING_RATE_DECAY_FACTOR, - staircase=True) - - # Create an optimizer that performs gradient descent. - opt = tf.train.MomentumOptimizer(lr, 0.9) - - # Calculate the gradients for each model tower. - tower_grads = [] - for i in xrange(FLAGS.num_gpus): - with tf.device('/gpu:%d' % i): - with tf.name_scope('%s_%d' % (TOWER_NAME, i)) as scope: - # Calculate the loss for one tower of the model. This function - # constructs the entire model but shares the variables across - # all towers. - loss = tower_loss(scope) - - # Reuse variables for the next tower. - tf.get_variable_scope().reuse_variables() - - # Retain the summaries from the final tower. - summaries = tf.get_collection(tf.GraphKeys.SUMMARIES, scope) - - # Calculate the gradients for the batch of data on this tower. - grads = opt.compute_gradients(loss) - - # Keep track of the gradients across all towers. - tower_grads.append(grads) - - # We must calculate the mean of each gradient. Note that this is the - # synchronization point across all towers. - grads = average_gradients(tower_grads) - - # Apply the gradients to adjust the shared variables. - apply_gradient_op = opt.apply_gradients(grads, global_step=global_step) - - # Group all updates to into a single train op. - train_op = tf.group(apply_gradient_op) - - # Build an initialization operation. - init = tf.initialize_all_variables() - - # Start running operations on the Graph. allow_soft_placement must be set to - # True to build towers on GPU, as some of the ops do not have GPU - # implementations. - sess = tf.Session(config=tf.ConfigProto( - allow_soft_placement=True, - log_device_placement=FLAGS.log_device_placement)) - sess.run(init) - time_tensorflow_run(sess, [train_op, loss]) - - -def main(_): - run_benchmark() - - -if __name__ == '__main__': - tf.app.run() diff --git a/benchmark/tensorflow/image/run.sh b/benchmark/tensorflow/image/run.sh deleted file mode 100755 index cf894fe3f2..0000000000 --- a/benchmark/tensorflow/image/run.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -set -e - -function test() { - cfg=$1 - batch_size=$2 - prefix=$3 - python $cfg --batch_size=$batch_size > logs/${prefix}-1gpu-${batch_size}.log 2>&1 -} - -if [ ! -d "logs" ]; then - mkdir logs -fi - -# alexnet -test alexnet.py 64 alexnet -test alexnet.py 128 alexnet -test alexnet.py 256 alexnet -test alexnet.py 512 alexnet - -# googlenet -test googlenet.py 64 googlenet -test googlenet.py 128 googlenet - -# smallnet -test smallnet_mnist_cifar.py 64 smallnet -test smallnet_mnist_cifar.py 128 smallnet -test smallnet_mnist_cifar.py 256 smallnet -test smallnet_mnist_cifar.py 512 smallnet diff --git a/benchmark/tensorflow/image/run_multi.sh b/benchmark/tensorflow/image/run_multi.sh deleted file mode 100755 index bf1435bc55..0000000000 --- a/benchmark/tensorflow/image/run_multi.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -set -e - -function test() { - cfg=$1 - num_gpu=$2 - batch_size=$3 - batch_per_gpu=`expr ${batch_size} / ${num_gpu}` - prefix=$4 - python $cfg --num_gpus=$num_gpu --batch_size=${batch_per_gpu} > logs/${prefix}-4gpu-${batch_size}.log 2>&1 -} - -if [ ! -d "logs" ]; then - mkdir logs -fi - -# alexnet -test alexnet_multi_gpu.py 4 512 alexnet -test alexnet_multi_gpu.py 4 1024 alexnet - -# googlenet -test googlenet_multi_gpu.py 4 512 alexnet -test googlenet_multi_gpu.py 4 1024 alexnet diff --git a/benchmark/tensorflow/image/smallnet_mnist_cifar.py b/benchmark/tensorflow/image/smallnet_mnist_cifar.py deleted file mode 100644 index 2ca1623b6b..0000000000 --- a/benchmark/tensorflow/image/smallnet_mnist_cifar.py +++ /dev/null @@ -1,318 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from six.moves import xrange # pylint: disable=redefined-builtin -from datetime import datetime -import math -import time - -import tensorflow.python.platform -import tensorflow as tf - -FLAGS = tf.app.flags.FLAGS - -tf.app.flags.DEFINE_integer('batch_size', 128, """Batch size.""") -tf.app.flags.DEFINE_integer('num_batches', 100, """Number of batches to run.""") -tf.app.flags.DEFINE_boolean('forward_only', False, - """Only run the forward pass.""") -tf.app.flags.DEFINE_boolean('forward_backward_only', False, - """Only run the forward-forward pass.""") -tf.app.flags.DEFINE_string('data_format', 'NCHW', - """The data format for Convnet operations. - Can be either NHWC or NCHW. - """) -tf.app.flags.DEFINE_boolean('log_device_placement', False, - """Whether to log device placement.""") - -parameters = [] - -conv_counter = 1 -pool_counter = 1 -affine_counter = 1 - - -def _conv(inpOp, nIn, nOut, kH, kW, dH, dW, padType, wd=0.005, act=True): - global conv_counter - global parameters - name = 'conv' + str(conv_counter) - conv_counter += 1 - with tf.name_scope(name) as scope: - kernel = tf.Variable( - tf.truncated_normal( - [kH, kW, nIn, nOut], dtype=tf.float32, stddev=1e-1), - name='weights') - - if wd is not None: - weight_decay = tf.mul(tf.nn.l2_loss(kernel), wd, name='weight_loss') - tf.add_to_collection('losses', weight_decay) - - if FLAGS.data_format == 'NCHW': - strides = [1, 1, dH, dW] - else: - strides = [1, dH, dW, 1] - conv = tf.nn.conv2d( - inpOp, - kernel, - strides, - padding=padType, - data_format=FLAGS.data_format) - biases = tf.Variable( - tf.constant( - 0.0, shape=[nOut], dtype=tf.float32), - trainable=True, - name='biases') - bias = tf.reshape( - tf.nn.bias_add( - conv, biases, data_format=FLAGS.data_format), - conv.get_shape()) - - conv1 = tf.nn.relu(bias, name=scope) if act else bias - - parameters += [kernel, biases] - - return conv1 - - -def _affine(inpOp, nIn, nOut, wd=None, act=True): - global affine_counter - global parameters - name = 'affine' + str(affine_counter) - affine_counter += 1 - with tf.name_scope(name) as scope: - kernel = tf.Variable( - tf.truncated_normal( - [nIn, nOut], dtype=tf.float32, stddev=1e-1), - name='weights') - - if wd is not None: - weight_decay = tf.mul(tf.nn.l2_loss(kernel), wd, name='weight_loss') - tf.add_to_collection('losses', weight_decay) - - biases = tf.Variable( - tf.constant( - 0.0, shape=[nOut], dtype=tf.float32), - trainable=True, - name='biases') - - affine1 = tf.nn.relu_layer( - inpOp, kernel, biases, - name=name) if act else tf.matmul(inpOp, kernel) + biases - - parameters += [kernel, biases] - - return affine1 - - -def _mpool(inpOp, kH, kW, dH, dW, padding): - global pool_counter - global parameters - name = 'pool' + str(pool_counter) - pool_counter += 1 - if FLAGS.data_format == 'NCHW': - ksize = [1, 1, kH, kW] - strides = [1, 1, dH, dW] - else: - ksize = [1, kH, kW, 1] - strides = [1, dH, dW, 1] - return tf.nn.max_pool( - inpOp, - ksize=ksize, - strides=strides, - padding=padding, - data_format=FLAGS.data_format, - name=name) - - -def _apool(inpOp, kH, kW, dH, dW, padding): - global pool_counter - global parameters - name = 'pool' + str(pool_counter) - pool_counter += 1 - if FLAGS.data_format == 'NCHW': - ksize = [1, 1, kH, kW] - strides = [1, 1, dH, dW] - else: - ksize = [1, kH, kW, 1] - strides = [1, dH, dW, 1] - return tf.nn.avg_pool( - inpOp, - ksize=ksize, - strides=strides, - padding=padding, - data_format=FLAGS.data_format, - name=name) - - -def _norm(name, l_input, lsize=4): - return tf.nn.lrn(l_input, - lsize, - bias=1.0, - alpha=0.001 / 9.0, - beta=0.75, - name=name) - - -def loss(logits, labels): - batch_size = tf.size(labels) - labels = tf.expand_dims(labels, 1) - indices = tf.expand_dims(tf.range(0, batch_size, 1), 1) - concated = tf.concat(1, [indices, labels]) - onehot_labels = tf.sparse_to_dense(concated, - tf.pack([batch_size, 10]), 1.0, 0.0) - cross_entropy = tf.nn.softmax_cross_entropy_with_logits( - logits, onehot_labels, name='xentropy') - loss = tf.reduce_mean(cross_entropy, name='xentropy_mean') - return loss - - -def get_incoming_shape(incoming): - """ Returns the incoming data shape """ - if isinstance(incoming, tf.Tensor): - return incoming.get_shape().as_list() - elif type(incoming) in [np.array, list, tuple]: - return np.shape(incoming) - else: - raise Exception("Invalid incoming layer.") - - -def inference(images): - conv1 = _conv(images, 3, 32, 5, 5, 1, 1, 'SAME') - pool1 = _mpool(conv1, 3, 3, 2, 2, 'SAME') - conv2 = _conv(pool1, 32, 32, 5, 5, 1, 1, 'SAME') - pool2 = _apool(conv2, 3, 3, 2, 2, 'SAME') - conv3 = _conv(pool2, 32, 64, 5, 5, 1, 1, 'SAME') - pool3 = _apool(conv3, 3, 3, 2, 2, 'SAME') - resh1 = tf.reshape(pool3, [-1, 64 * 4 * 4]) - affn1 = _affine(resh1, 64 * 4 * 4, 64) - affn2 = _affine(affn1, 64, 10, act=False) - - print('conv1:', get_incoming_shape(conv1)) - print('pool1:', get_incoming_shape(pool1)) - print('conv2:', get_incoming_shape(conv2)) - print('pool2:', get_incoming_shape(pool2)) - print('conv3:', get_incoming_shape(conv3)) - print('pool3:', get_incoming_shape(pool3)) - - return affn2 - - -def time_tensorflow_run(session, target, info_string): - num_steps_burn_in = 10 - total_duration = 0.0 - total_duration_squared = 0.0 - if not isinstance(target, list): - target = [target] - target_op = tf.group(*target) - for i in xrange(FLAGS.num_batches + num_steps_burn_in): - start_time = time.time() - _ = session.run(target_op) - duration = time.time() - start_time - if i > num_steps_burn_in: - if not i % 10: - print('%s: step %d, duration = %.3f' % - (datetime.now(), i - num_steps_burn_in, duration)) - total_duration += duration - total_duration_squared += duration * duration - mn = total_duration / FLAGS.num_batches - vr = total_duration_squared / FLAGS.num_batches - mn * mn - sd = math.sqrt(vr) - print('%s: %s across %d steps, %.3f +/- %.3f sec / batch' % - (datetime.now(), info_string, FLAGS.num_batches, mn, sd)) - - -def run_benchmark(): - global parameters - with tf.Graph().as_default(): - # Generate some dummy images. - image_size = 32 - # Note that our padding definition is slightly different the cuda-convnet. - # In order to force the model to start with the same activations sizes, - # we add 3 to the image_size and employ VALID padding above. - if FLAGS.data_format == 'NCHW': - image_shape = [FLAGS.batch_size, 3, image_size, image_size] - else: - image_shape = [FLAGS.batch_size, image_size, image_size, 3] - - images = tf.get_variable( - 'image', - image_shape, - initializer=tf.truncated_normal_initializer( - stddev=0.1, dtype=tf.float32), - dtype=tf.float32, - trainable=False) - - labels = tf.get_variable( - 'label', [FLAGS.batch_size], - initializer=tf.constant_initializer(1), - dtype=tf.int32, - trainable=False) - - # Build a Graph that computes the logits predictions from the - # inference model. - last_layer = inference(images) - - objective = loss(last_layer, labels) - - # Compute gradients. - opt = tf.train.MomentumOptimizer(0.001, 0.9) - grads = opt.compute_gradients(objective) - global_step = tf.get_variable( - 'global_step', [], - initializer=tf.constant_initializer( - 0.0, dtype=tf.float32), - trainable=False, - dtype=tf.float32) - apply_gradient_op = opt.apply_gradients(grads, global_step=global_step) - - # Track the moving averages of all trainable variables. - variable_averages = tf.train.ExponentialMovingAverage(0.9, global_step) - variables_averages_op = variable_averages.apply(tf.trainable_variables( - )) - - # Build an initialization operation. - init = tf.initialize_all_variables() - - # Start running operations on the Graph. - sess = tf.Session(config=tf.ConfigProto( - allow_soft_placement=True, - log_device_placement=FLAGS.log_device_placement)) - sess.run(init) - - run_forward = True - run_forward_backward = True - if FLAGS.forward_only and FLAGS.forward_backward_only: - raise ValueError("Cannot specify --forward_only and " - "--forward_backward_only at the same time.") - if FLAGS.forward_only: - run_forward_backward = False - elif FLAGS.forward_backward_only: - run_forward = False - - if run_forward: - # Run the forward benchmark. - time_tensorflow_run(sess, last_layer, "Forward") - - if run_forward_backward: - with tf.control_dependencies( - [apply_gradient_op, variables_averages_op]): - train_op = tf.no_op(name='train') - time_tensorflow_run(sess, [train_op, objective], "Forward-backward") - - -def main(_): - run_benchmark() - - -if __name__ == '__main__': - tf.app.run() diff --git a/benchmark/tensorflow/machine_translation.py b/benchmark/tensorflow/machine_translation.py deleted file mode 100644 index 7837669edc..0000000000 --- a/benchmark/tensorflow/machine_translation.py +++ /dev/null @@ -1,624 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import tensorflow as tf -from tensorflow.python.framework import dtypes -from tensorflow.python.layers.core import Dense -from tensorflow.python.ops import check_ops -from tensorflow.python.ops import math_ops -from tensorflow.python.framework import ops -from tensorflow.python.ops import rnn_cell_impl -from tensorflow.python.ops.rnn_cell_impl import RNNCell, BasicLSTMCell -from tensorflow.python.ops.rnn_cell_impl import LSTMStateTuple -from tensorflow.contrib.rnn.python.ops import core_rnn_cell -from tensorflow.python.ops import array_ops -from tensorflow.python.util import nest -import tensorflow.contrib.seq2seq as seq2seq -from tensorflow.contrib.seq2seq.python.ops import beam_search_decoder -import numpy as np -import os -import argparse -import time - -parser = argparse.ArgumentParser(description=__doc__) -parser.add_argument( - "--embedding_dim", - type=int, - default=512, - help="The dimension of embedding table. (default: %(default)d)") -parser.add_argument( - "--encoder_size", - type=int, - default=512, - help="The size of encoder bi-rnn unit. (default: %(default)d)") -parser.add_argument( - "--decoder_size", - type=int, - default=512, - help="The size of decoder rnn unit. (default: %(default)d)") -parser.add_argument( - "--batch_size", - type=int, - default=128, - help="The sequence number of a mini-batch data. (default: %(default)d)") -parser.add_argument( - "--dict_size", - type=int, - default=30000, - help="The dictionary capacity. Dictionaries of source sequence and " - "target dictionary have same capacity. (default: %(default)d)") -parser.add_argument( - "--max_time_steps", - type=int, - default=81, - help="Max number of time steps for sequence. (default: %(default)d)") -parser.add_argument( - "--pass_num", - type=int, - default=10, - help="The pass number to train. (default: %(default)d)") -parser.add_argument( - "--learning_rate", - type=float, - default=0.0002, - help="Learning rate used to train the model. (default: %(default)f)") -parser.add_argument( - "--infer_only", action='store_true', help="If set, run forward only.") -parser.add_argument( - "--beam_size", - type=int, - default=3, - help="The width for beam searching. (default: %(default)d)") -parser.add_argument( - "--max_generation_length", - type=int, - default=250, - help="The maximum length of sequence when doing generation. " - "(default: %(default)d)") -parser.add_argument( - "--save_freq", - type=int, - default=500, - help="Save model checkpoint every this interation. (default: %(default)d)") -parser.add_argument( - "--model_dir", - type=str, - default='./checkpoint', - help="Path to save model checkpoints. (default: %(default)d)") - -_Linear = core_rnn_cell._Linear # pylint: disable=invalid-name - -START_TOKEN_IDX = 0 -END_TOKEN_IDX = 1 - - -class LSTMCellWithSimpleAttention(RNNCell): - """Add attention mechanism to BasicLSTMCell. - This class is a wrapper based on tensorflow's `BasicLSTMCell`. - """ - - def __init__(self, - num_units, - encoder_vector, - encoder_proj, - source_sequence_length, - forget_bias=1.0, - state_is_tuple=True, - activation=None, - reuse=None): - super(LSTMCellWithSimpleAttention, self).__init__(_reuse=reuse) - if not state_is_tuple: - logging.warn("%s: Using a concatenated state is slower and will " - "soon be deprecated. Use state_is_tuple=True.", self) - self._num_units = num_units - # set padding part to 0 - self._encoder_vector = self._reset_padding(encoder_vector, - source_sequence_length) - self._encoder_proj = self._reset_padding(encoder_proj, - source_sequence_length) - self._forget_bias = forget_bias - self._state_is_tuple = state_is_tuple - self._activation = activation or math_ops.tanh - self._linear = None - - @property - def state_size(self): - return (LSTMStateTuple(self._num_units, self._num_units) \ - if self._state_is_tuple else 2 * self._num_units) - - @property - def output_size(self): - return self._num_units - - def zero_state(self, batch_size, dtype): - state_size = self.state_size - if hasattr(self, "_last_zero_state"): - (last_state_size, last_batch_size, last_dtype, - last_output) = getattr(self, "_last_zero_state") - if (last_batch_size == batch_size and last_dtype == dtype and - last_state_size == state_size): - return last_output - with ops.name_scope( - type(self).__name__ + "ZeroState", values=[batch_size]): - output = _zero_state_tensors(state_size, batch_size, dtype) - self._last_zero_state = (state_size, batch_size, dtype, output) - return output - - def call(self, inputs, state): - sigmoid = math_ops.sigmoid - # Parameters of gates are concatenated into one multiply for efficiency. - if self._state_is_tuple: - c, h = state - else: - c, h = array_ops.split(value=state, num_or_size_splits=2, axis=1) - - # get context from encoder outputs - context = self._simple_attention(self._encoder_vector, - self._encoder_proj, h) - - if self._linear is None: - self._linear = _Linear([inputs, context, h], 4 * self._num_units, - True) - # i = input_gate, j = new_input, f = forget_gate, o = output_gate - i, j, f, o = array_ops.split( - value=self._linear([inputs, context, h]), - num_or_size_splits=4, - axis=1) - - new_c = (c * sigmoid(f + self._forget_bias) + sigmoid(i) * - self._activation(j)) - new_h = self._activation(new_c) * sigmoid(o) - - if self._state_is_tuple: - new_state = LSTMStateTuple(new_c, new_h) - else: - new_state = array_ops.concat([new_c, new_h], 1) - return new_h, new_state - - def _simple_attention(self, encoder_vec, encoder_proj, decoder_state): - """Implement the attention function. - The implementation has the same logic to the fluid decoder. - """ - decoder_state_proj = tf.contrib.layers.fully_connected( - inputs=decoder_state, - num_outputs=self._num_units, - activation_fn=None, - biases_initializer=None) - decoder_state_expand = tf.tile( - tf.expand_dims( - input=decoder_state_proj, axis=1), - [1, tf.shape(encoder_proj)[1], 1]) - concated = tf.concat([decoder_state_expand, encoder_proj], axis=2) - # need reduce the first dimension - attention_weights = tf.contrib.layers.fully_connected( - inputs=tf.reshape( - concated, shape=[-1, self._num_units * 2]), - num_outputs=1, - activation_fn=tf.nn.tanh, - biases_initializer=None) - attention_weights_reshaped = tf.reshape( - attention_weights, shape=[tf.shape(encoder_vec)[0], -1, 1]) - # normalize the attention weights using softmax - attention_weights_normed = tf.nn.softmax( - attention_weights_reshaped, dim=1) - scaled = tf.multiply(attention_weights_normed, encoder_vec) - context = tf.reduce_sum(scaled, axis=1) - return context - - def _reset_padding(self, - memory, - memory_sequence_length, - check_inner_dims_defined=True): - """Reset the padding part for encoder inputs. - This funtion comes from tensorflow's `_prepare_memory` function. - """ - memory = nest.map_structure( - lambda m: ops.convert_to_tensor(m, name="memory"), memory) - if memory_sequence_length is not None: - memory_sequence_length = ops.convert_to_tensor( - memory_sequence_length, name="memory_sequence_length") - if check_inner_dims_defined: - - def _check_dims(m): - if not m.get_shape()[2:].is_fully_defined(): - raise ValueError( - "Expected memory %s to have fully defined inner dims, " - "but saw shape: %s" % (m.name, m.get_shape())) - - nest.map_structure(_check_dims, memory) - if memory_sequence_length is None: - seq_len_mask = None - else: - seq_len_mask = array_ops.sequence_mask( - memory_sequence_length, - maxlen=array_ops.shape(nest.flatten(memory)[0])[1], - dtype=nest.flatten(memory)[0].dtype) - seq_len_batch_size = (memory_sequence_length.shape[0].value or - array_ops.shape(memory_sequence_length)[0]) - - def _maybe_mask(m, seq_len_mask): - rank = m.get_shape().ndims - rank = rank if rank is not None else array_ops.rank(m) - extra_ones = array_ops.ones(rank - 2, dtype=dtypes.int32) - m_batch_size = m.shape[0].value or array_ops.shape(m)[0] - if memory_sequence_length is not None: - message = ("memory_sequence_length and memory tensor " - "batch sizes do not match.") - with ops.control_dependencies([ - check_ops.assert_equal( - seq_len_batch_size, m_batch_size, message=message) - ]): - seq_len_mask = array_ops.reshape( - seq_len_mask, - array_ops.concat( - (array_ops.shape(seq_len_mask), extra_ones), 0)) - return m * seq_len_mask - else: - return m - - return nest.map_structure(lambda m: _maybe_mask(m, seq_len_mask), - memory) - - -def seq_to_seq_net(embedding_dim, encoder_size, decoder_size, source_dict_dim, - target_dict_dim, is_generating, beam_size, - max_generation_length): - src_word_idx = tf.placeholder(tf.int32, shape=[None, None]) - src_sequence_length = tf.placeholder(tf.int32, shape=[None, ]) - - src_embedding_weights = tf.get_variable("source_word_embeddings", - [source_dict_dim, embedding_dim]) - src_embedding = tf.nn.embedding_lookup(src_embedding_weights, src_word_idx) - - src_forward_cell = tf.nn.rnn_cell.BasicLSTMCell(encoder_size) - src_reversed_cell = tf.nn.rnn_cell.BasicLSTMCell(encoder_size) - # no peephole - encoder_outputs, _ = tf.nn.bidirectional_dynamic_rnn( - cell_fw=src_forward_cell, - cell_bw=src_reversed_cell, - inputs=src_embedding, - sequence_length=src_sequence_length, - dtype=tf.float32) - - # concat the forward outputs and backward outputs - encoded_vec = tf.concat(encoder_outputs, axis=2) - - # project the encoder outputs to size of decoder lstm - encoded_proj = tf.contrib.layers.fully_connected( - inputs=tf.reshape( - encoded_vec, shape=[-1, embedding_dim * 2]), - num_outputs=decoder_size, - activation_fn=None, - biases_initializer=None) - encoded_proj_reshape = tf.reshape( - encoded_proj, shape=[-1, tf.shape(encoded_vec)[1], decoder_size]) - - # get init state for decoder lstm's H - backword_first = tf.slice(encoder_outputs[1], [0, 0, 0], [-1, 1, -1]) - decoder_boot = tf.contrib.layers.fully_connected( - inputs=tf.reshape( - backword_first, shape=[-1, embedding_dim]), - num_outputs=decoder_size, - activation_fn=tf.nn.tanh, - biases_initializer=None) - - # prepare the initial state for decoder lstm - cell_init = tf.zeros(tf.shape(decoder_boot), tf.float32) - initial_state = LSTMStateTuple(cell_init, decoder_boot) - - # create decoder lstm cell - decoder_cell = LSTMCellWithSimpleAttention( - decoder_size, - encoded_vec - if not is_generating else seq2seq.tile_batch(encoded_vec, beam_size), - encoded_proj_reshape if not is_generating else - seq2seq.tile_batch(encoded_proj_reshape, beam_size), - src_sequence_length if not is_generating else - seq2seq.tile_batch(src_sequence_length, beam_size), - forget_bias=0.0) - - output_layer = Dense(target_dict_dim, name='output_projection') - - if not is_generating: - trg_word_idx = tf.placeholder(tf.int32, shape=[None, None]) - trg_sequence_length = tf.placeholder(tf.int32, shape=[None, ]) - trg_embedding_weights = tf.get_variable( - "target_word_embeddings", [target_dict_dim, embedding_dim]) - trg_embedding = tf.nn.embedding_lookup(trg_embedding_weights, - trg_word_idx) - - training_helper = seq2seq.TrainingHelper( - inputs=trg_embedding, - sequence_length=trg_sequence_length, - time_major=False, - name='training_helper') - - training_decoder = seq2seq.BasicDecoder( - cell=decoder_cell, - helper=training_helper, - initial_state=initial_state, - output_layer=output_layer) - - # get the max length of target sequence - max_decoder_length = tf.reduce_max(trg_sequence_length) - - decoder_outputs_train, _, _ = seq2seq.dynamic_decode( - decoder=training_decoder, - output_time_major=False, - impute_finished=True, - maximum_iterations=max_decoder_length) - - decoder_logits_train = tf.identity(decoder_outputs_train.rnn_output) - decoder_pred_train = tf.argmax( - decoder_logits_train, axis=-1, name='decoder_pred_train') - masks = tf.sequence_mask( - lengths=trg_sequence_length, - maxlen=max_decoder_length, - dtype=tf.float32, - name='masks') - - # place holder of label sequence - lbl_word_idx = tf.placeholder(tf.int32, shape=[None, None]) - - # compute the loss - loss = seq2seq.sequence_loss( - logits=decoder_logits_train, - targets=lbl_word_idx, - weights=masks, - average_across_timesteps=True, - average_across_batch=True) - - # return feeding list and loss operator - return { - 'src_word_idx': src_word_idx, - 'src_sequence_length': src_sequence_length, - 'trg_word_idx': trg_word_idx, - 'trg_sequence_length': trg_sequence_length, - 'lbl_word_idx': lbl_word_idx - }, loss - else: - start_tokens = tf.ones([tf.shape(src_word_idx)[0], ], - tf.int32) * START_TOKEN_IDX - # share the same embedding weights with target word - trg_embedding_weights = tf.get_variable( - "target_word_embeddings", [target_dict_dim, embedding_dim]) - - inference_decoder = beam_search_decoder.BeamSearchDecoder( - cell=decoder_cell, - embedding=lambda tokens: tf.nn.embedding_lookup(trg_embedding_weights, tokens), - start_tokens=start_tokens, - end_token=END_TOKEN_IDX, - initial_state=tf.nn.rnn_cell.LSTMStateTuple( - tf.contrib.seq2seq.tile_batch(initial_state[0], beam_size), - tf.contrib.seq2seq.tile_batch(initial_state[1], beam_size)), - beam_width=beam_size, - output_layer=output_layer) - - decoder_outputs_decode, _, _ = seq2seq.dynamic_decode( - decoder=inference_decoder, - output_time_major=False, - #impute_finished=True,# error occurs - maximum_iterations=max_generation_length) - - predicted_ids = decoder_outputs_decode.predicted_ids - - return { - 'src_word_idx': src_word_idx, - 'src_sequence_length': src_sequence_length - }, predicted_ids - - -def print_arguments(args): - print('----------- Configuration Arguments -----------') - for arg, value in vars(args).iteritems(): - print('%s: %s' % (arg, value)) - print('------------------------------------------------') - - -def padding_data(data, padding_size, value): - data = data + [value] * padding_size - return data[:padding_size] - - -def save(sess, path, var_list=None, global_step=None): - saver = tf.train.Saver(var_list) - save_path = saver.save(sess, save_path=path, global_step=global_step) - print('Model save at %s' % save_path) - - -def restore(sess, path, var_list=None): - # var_list = None returns the list of all saveable variables - saver = tf.train.Saver(var_list) - saver.restore(sess, save_path=path) - print('model restored from %s' % path) - - -def adapt_batch_data(data): - src_seq = map(lambda x: x[0], data) - trg_seq = map(lambda x: x[1], data) - lbl_seq = map(lambda x: x[2], data) - - src_sequence_length = np.array( - [len(seq) for seq in src_seq]).astype('int32') - src_seq_maxlen = np.max(src_sequence_length) - - trg_sequence_length = np.array( - [len(seq) for seq in trg_seq]).astype('int32') - trg_seq_maxlen = np.max(trg_sequence_length) - - src_seq = np.array( - [padding_data(seq, src_seq_maxlen, END_TOKEN_IDX) - for seq in src_seq]).astype('int32') - - trg_seq = np.array( - [padding_data(seq, trg_seq_maxlen, END_TOKEN_IDX) - for seq in trg_seq]).astype('int32') - - lbl_seq = np.array( - [padding_data(seq, trg_seq_maxlen, END_TOKEN_IDX) - for seq in lbl_seq]).astype('int32') - - return { - 'src_word_idx': src_seq, - 'src_sequence_length': src_sequence_length, - 'trg_word_idx': trg_seq, - 'trg_sequence_length': trg_sequence_length, - 'lbl_word_idx': lbl_seq - } - - -def train(): - feeding_dict, loss = seq_to_seq_net( - embedding_dim=args.embedding_dim, - encoder_size=args.encoder_size, - decoder_size=args.decoder_size, - source_dict_dim=args.dict_size, - target_dict_dim=args.dict_size, - is_generating=False, - beam_size=args.beam_size, - max_generation_length=args.max_generation_length) - - global_step = tf.Variable(0, trainable=False, name='global_step') - trainable_params = tf.trainable_variables() - optimizer = tf.train.AdamOptimizer(learning_rate=args.learning_rate) - - gradients = tf.gradients(loss, trainable_params) - # may clip the parameters - clip_gradients, _ = tf.clip_by_global_norm(gradients, 1.0) - - updates = optimizer.apply_gradients( - zip(gradients, trainable_params), global_step=global_step) - - src_dict, trg_dict = paddle.dataset.wmt14.get_dict(args.dict_size) - - train_batch_generator = paddle.batch( - paddle.reader.shuffle( - paddle.dataset.wmt14.train(args.dict_size), buf_size=1000), - batch_size=args.batch_size) - - test_batch_generator = paddle.batch( - paddle.reader.shuffle( - paddle.dataset.wmt14.test(args.dict_size), buf_size=1000), - batch_size=args.batch_size) - - def do_validataion(): - total_loss = 0.0 - count = 0 - for batch_id, data in enumerate(test_batch_generator()): - adapted_batch_data = adapt_batch_data(data) - outputs = sess.run([loss], - feed_dict={ - item[1]: adapted_batch_data[item[0]] - for item in feeding_dict.items() - }) - total_loss += outputs[0] - count += 1 - return total_loss / count - - config = tf.ConfigProto( - intra_op_parallelism_threads=1, inter_op_parallelism_threads=1) - config.gpu_options.allow_growth = True - - with tf.Session(config=config) as sess: - init_g = tf.global_variables_initializer() - init_l = tf.local_variables_initializer() - sess.run(init_l) - sess.run(init_g) - for pass_id in xrange(args.pass_num): - pass_start_time = time.time() - words_seen = 0 - for batch_id, data in enumerate(train_batch_generator()): - adapted_batch_data = adapt_batch_data(data) - words_seen += np.sum(adapted_batch_data['src_sequence_length']) - words_seen += np.sum(adapted_batch_data['trg_sequence_length']) - outputs = sess.run([updates, loss], - feed_dict={ - item[1]: adapted_batch_data[item[0]] - for item in feeding_dict.items() - }) - print("pass_id=%d, batch_id=%d, train_loss: %f" % - (pass_id, batch_id, outputs[1])) - pass_end_time = time.time() - test_loss = do_validataion() - time_consumed = pass_end_time - pass_start_time - words_per_sec = words_seen / time_consumed - print("pass_id=%d, test_loss: %f, words/s: %f, sec/pass: %f" % - (pass_id, test_loss, words_per_sec, time_consumed)) - - -def infer(): - feeding_dict, predicted_ids = seq_to_seq_net( - embedding_dim=args.embedding_dim, - encoder_size=args.encoder_size, - decoder_size=args.decoder_size, - source_dict_dim=args.dict_size, - target_dict_dim=args.dict_size, - is_generating=True, - beam_size=args.beam_size, - max_generation_length=args.max_generation_length) - - src_dict, trg_dict = paddle.dataset.wmt14.get_dict(args.dict_size) - test_batch_generator = paddle.batch( - paddle.reader.shuffle( - paddle.dataset.wmt14.train(args.dict_size), buf_size=1000), - batch_size=args.batch_size) - - config = tf.ConfigProto( - intra_op_parallelism_threads=1, inter_op_parallelism_threads=1) - with tf.Session(config=config) as sess: - restore(sess, './checkpoint/tf_seq2seq-1500') - for batch_id, data in enumerate(test_batch_generator()): - src_seq = map(lambda x: x[0], data) - - source_language_seq = [ - src_dict[item] for seq in src_seq for item in seq - ] - - src_sequence_length = np.array( - [len(seq) for seq in src_seq]).astype('int32') - src_seq_maxlen = np.max(src_sequence_length) - src_seq = np.array([ - padding_data(seq, src_seq_maxlen, END_TOKEN_IDX) - for seq in src_seq - ]).astype('int32') - - outputs = sess.run([predicted_ids], - feed_dict={ - feeding_dict['src_word_idx']: src_seq, - feeding_dict['src_sequence_length']: - src_sequence_length - }) - - print("\nDecoder result comparison: ") - source_language_seq = ' '.join(source_language_seq).lstrip( - '').rstrip('').strip() - inference_seq = '' - print(" --> source: " + source_language_seq) - for item in outputs[0][0]: - if item[0] == END_TOKEN_IDX: break - inference_seq += ' ' + trg_dict.get(item[0], '') - print(" --> inference: " + inference_seq) - - -if __name__ == '__main__': - args = parser.parse_args() - print_arguments(args) - if args.infer_only: - infer() - else: - train() diff --git a/benchmark/tensorflow/mnist.py b/benchmark/tensorflow/mnist.py deleted file mode 100644 index 03d533fecf..0000000000 --- a/benchmark/tensorflow/mnist.py +++ /dev/null @@ -1,179 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import argparse -import time -import numpy as np - -import tensorflow as tf - -DTYPE = tf.float32 - - -def parse_args(): - parser = argparse.ArgumentParser("mnist model benchmark.") - parser.add_argument( - '--batch_size', type=int, default=128, help='The minibatch size.') - parser.add_argument( - '--iterations', type=int, default=35, help='The number of minibatches.') - parser.add_argument( - '--pass_num', type=int, default=5, help='The number of passes.') - parser.add_argument( - '--device', - type=str, - default='GPU', - choices=['CPU', 'GPU'], - help='The device type.') - args = parser.parse_args() - return args - - -def run_benchmark(args): - def weight_variable(dtype, shape): - initial = tf.truncated_normal(shape, stddev=0.1, dtype=dtype) - return tf.Variable(initial) - - def bias_variable(dtype, shape): - initial = tf.constant(0.1, shape=shape, dtype=dtype) - return tf.Variable(initial) - - device = '/cpu:0' if args.device == 'CPU' else '/device:GPU:0' - with tf.device(device): - images = tf.placeholder(DTYPE, shape=(None, 28, 28, 1)) - labels = tf.placeholder(tf.int64, shape=(None, )) - - # conv1, relu, pool1 - conv1_weights = weight_variable(DTYPE, [5, 5, 1, 20]) - conv1_bias = bias_variable(DTYPE, [20]) - conv1 = tf.nn.conv2d( - images, conv1_weights, strides=[1, 1, 1, 1], padding="VALID") - relu1 = tf.nn.relu(tf.nn.bias_add(conv1, conv1_bias)) - pool1 = tf.nn.max_pool( - relu1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="VALID") - - # conv2, relu, pool2 - conv2_weights = weight_variable(DTYPE, [5, 5, 20, 50]) - conv2_bias = bias_variable(DTYPE, [50]) - conv2 = tf.nn.conv2d( - pool1, conv2_weights, strides=[1, 1, 1, 1], padding="VALID") - relu2 = tf.nn.relu(tf.nn.bias_add(conv2, conv2_bias)) - pool2 = tf.nn.max_pool( - relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="VALID") - - # FC - pool_shape = pool2.get_shape().as_list() - hidden_dim = reduce(lambda a, b: a * b, pool_shape[1:], 1) - reshape = tf.reshape(pool2, shape=(tf.shape(pool2)[0], hidden_dim)) - fc_weights = weight_variable(DTYPE, [hidden_dim, 10]) - fc_bias = bias_variable(DTYPE, [10]) - logits = tf.matmul(reshape, fc_weights) + fc_bias - - # Get prediction - prediction = tf.nn.softmax(logits) - - # Loss - one_hot_labels = tf.one_hot(labels, depth=10) - cost = -tf.reduce_sum(tf.log(prediction) * one_hot_labels, [1]) - avg_cost = tf.reduce_mean(cost) - - # Get accuracy - correct = tf.equal(tf.argmax(prediction, 1), labels) - accuracy = tf.reduce_mean(tf.cast(correct, tf.float32)) - - # metrics, g_accuracy - with tf.variable_scope("reset_metrics_accuracy_scope") as scope: - g_accuracy = tf.metrics.accuracy( - labels, tf.argmax( - prediction, axis=1)) - vars = tf.contrib.framework.get_variables( - scope, collection=tf.GraphKeys.LOCAL_VARIABLES) - g_accuracy_reset_op = tf.variables_initializer(vars) - - # Optimizer - opt = tf.train.AdamOptimizer( - learning_rate=0.001, beta1=0.9, beta2=0.999) - train_op = opt.minimize(avg_cost) - # train_op = tf.train.AdamOptimizer(1e-4).minimize(avg_cost) - - train_reader = paddle.batch( - paddle.dataset.mnist.train(), batch_size=args.batch_size) - test_reader = paddle.batch( - paddle.dataset.mnist.test(), batch_size=args.batch_size) - - def eval_test(): - sess.run(g_accuracy_reset_op) - for batch_id, data in enumerate(test_reader()): - images_data = np.array( - map(lambda x: np.transpose(x[0].reshape([1, 28, 28]), axes=[1,2,0]), data)).astype("float32") - labels_data = np.array(map(lambda x: x[1], data)).astype("int64") - - loss, acc, g_acc = sess.run( - [avg_cost, accuracy, g_accuracy], - feed_dict={images: images_data, - labels: labels_data}) - return g_acc[1] - - config = tf.ConfigProto( - intra_op_parallelism_threads=1, inter_op_parallelism_threads=1) - config.gpu_options.allow_growth = True - - with tf.Session(config=config) as sess: - init_g = tf.global_variables_initializer() - init_l = tf.local_variables_initializer() - sess.run(init_g) - sess.run(init_l) - for pass_id in range(args.pass_num): - sess.run(g_accuracy_reset_op) - - pass_start = time.time() - for batch_id, data in enumerate(train_reader()): - images_data = np.array( - map(lambda x: np.transpose(x[0].reshape([1, 28, 28]), axes=[1,2,0]), data)).astype("float32") - labels_data = np.array(map(lambda x: x[1], data)).astype( - "int64") - - start = time.time() - _, loss, acc, g_acc = sess.run( - [train_op, avg_cost, accuracy, g_accuracy], - feed_dict={images: images_data, - labels: labels_data}) - end = time.time() - - print("pass=%d, batch=%d, loss=%f, error=%f, elapse=%f" % - (pass_id, batch_id, loss, 1 - acc, (end - start) / 1000)) - - pass_end = time.time() - test_avg_acc = eval_test() - - print( - "pass=%d, training_avg_accuracy=%f, test_avg_acc=%f, elapse=%f" - % (pass_id, g_acc[1], test_avg_acc, - (pass_end - pass_start) / 1000)) - - -def print_arguments(args): - print('----------- Configuration Arguments -----------') - for arg, value in sorted(vars(args).iteritems()): - print('%s: %s' % (arg, value)) - print('------------------------------------------------') - - -if __name__ == '__main__': - args = parse_args() - print_arguments(args) - run_benchmark(args) diff --git a/benchmark/tensorflow/resnet.py b/benchmark/tensorflow/resnet.py deleted file mode 100644 index fdb0441957..0000000000 --- a/benchmark/tensorflow/resnet.py +++ /dev/null @@ -1,503 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -based on https://github.com/tensorflow/models/blob/master/official/resnet/resnet_model.py - -Get help: python resnet.py --help -See performance on flowers: python resnet.py -Train on cifar10: python resnet.py --data=cifar10 --with_test -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import argparse -import time -import numpy as np - -import tensorflow as tf - -DTYPE = tf.float32 - - -def parse_args(): - parser = argparse.ArgumentParser('Convolution model benchmark.') - parser.add_argument( - '--model', - type=str, - choices=['resnet'], - default='resnet', - help='The model architecture.') - parser.add_argument( - '--batch_size', type=int, default=32, help='The minibatch size.') - parser.add_argument( - '--use_fake_data', - action='store_true', - help='use real data or fake data') - parser.add_argument( - '--skip_batch_num', - type=int, - default=5, - help='The first num of minibatch num to skip, for better performance test' - ) - parser.add_argument( - '--iterations', - type=int, - default=105, - help='The number of minibatches.') - parser.add_argument( - '--pass_num', type=int, default=300, help='The number of passes.') - parser.add_argument( - '--order', - type=str, - default='NHWC', - choices=['NCHW', 'NHWC'], - help='The data order, now only support NCHW.') - parser.add_argument( - '--device', - type=str, - default='GPU', - choices=['CPU', 'GPU'], - help='The device type.') - parser.add_argument( - '--data', - type=str, - default='flowers102', - choices=['flowers102', 'cifar10'], - help='The kinds of data.') - parser.add_argument( - '--infer_only', action='store_true', help='If set, run forward only.') - parser.add_argument( - '--use_cprof', action='store_true', help='If set, use cProfile.') - parser.add_argument( - '--with_test', - action='store_true', - help='If set, test the testset during training.') - parser.add_argument( - '--use_nvprof', - action='store_true', - help='If set, use nvprof for CUDA.') - args = parser.parse_args() - return args - - -def print_arguments(args): - vars(args)['use_nvprof'] = (vars(args)['use_nvprof'] and - vars(args)['device'] == 'GPU') - vars(args)['iterations'] = vars(args)['pass_num'] * 1000 if vars(args)[ - 'with_test'] else vars(args)['iterations'] - print('----------- Configuration Arguments -----------') - for arg, value in sorted(vars(args).iteritems()): - print('%s: %s' % (arg, value)) - print('------------------------------------------------') - - -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). - # This is consistent with PaddlePaddle. - # In addition, the calculation for output size in TensorFlow can refer: - # https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/framework/common_shape_fns.cc - 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) - - -def conv_bn(inputs, - filters, - kernel_size, - strides, - is_training, - data_format, - act=True): - # def conv2d_fixed_padding(inputs, filters, kernel_size, strides, data_format): - # set fused=True for a significant performance boost. See - # https://www.tensorflow.org/performance/performance_guide#common_fused_ops - inputs = conv2d_fixed_padding( - inputs=inputs, - filters=filters, - kernel_size=kernel_size, - strides=strides, - data_format=data_format) - inputs = tf.layers.batch_normalization( - inputs=inputs, - axis=1 if data_format == 'channels_first' else 3, - momentum=0.9, - epsilon=1e-05, - center=True, - scale=True, - training=is_training, - fused=True) - if act: - inputs = tf.nn.relu(inputs) - return inputs - - -def basicblock(inputs, filters, is_training, projection_shortcut, strides, - data_format): - shortcut = inputs - if projection_shortcut is not None: - shortcut = projection_shortcut(inputs) - inputs = conv_bn(inputs, filters, 3, strides, is_training, data_format) - inputs = conv_bn(inputs, filters, 3, 1, is_training, data_format, act=False) - inputs = inputs + shortcut - inputs = tf.nn.relu(inputs) - return inputs - - -def bottleneck(inputs, filters, is_training, projection_shortcut, strides, - data_format): - shortcut = inputs - if projection_shortcut is not None: - shortcut = projection_shortcut(inputs) - inputs = conv_bn(inputs, filters, 1, strides, is_training, data_format) - inputs = conv_bn(inputs, filters, 3, 1, is_training, data_format, act=False) - inputs = conv_bn( - inputs, filters * 4, 1, 1, is_training, data_format, act=False) - inputs = inputs + shortcut - inputs = tf.nn.relu(inputs) - return inputs - - -def block_layer(inputs, filters, block_fn, blocks, strides, is_training, name, - data_format): - # Bottleneck blocks end with 4x the number of filters as they start with - filters_out = 4 * filters if block_fn is 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, is_training, projection_shortcut, - strides, data_format) - - for _ in range(1, blocks): - inputs = block_fn(inputs, filters, is_training, None, 1, data_format) - - return tf.identity(inputs, name) - - -def resnet_imagenet(depth, class_dim, data_format): - """Returns the ResNet model for a given size and number of output classes.""" - - def resnet_generator(block_fn, - layers, - num_classes, - data_format='channels_last'): - if data_format is None: - data_format = ('channels_first' - if tf.test.is_built_with_cuda() else 'channels_last') - - def model(inputs, is_training): - """Constructs the ResNet model given the inputs.""" - if 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 = conv_bn(inputs, 64, 7, 2, is_training, data_format) - inputs = tf.identity(inputs, 'initial_conv') - inputs = tf.layers.max_pooling2d( - inputs=inputs, - pool_size=3, - strides=2, - padding='SAME', - data_format=data_format) - inputs = tf.identity(inputs, 'initial_max_pool') - inputs = block_layer(inputs, 64, block_fn, layers[0], 1, - is_training, 'block_layer1', data_format) - inputs = block_layer(inputs, 128, block_fn, layers[1], 2, - is_training, 'block_layer2', data_format) - inputs = block_layer(inputs, 256, block_fn, layers[2], 2, - is_training, 'block_layer3', data_format) - inputs = block_layer(inputs, 512, block_fn, layers[3], 2, - is_training, 'block_layer4', data_format) - inputs = tf.layers.average_pooling2d( - inputs=inputs, - pool_size=7, - strides=1, - padding='VALID', - data_format=data_format) - inputs = tf.identity(inputs, 'final_avg_pool') - inputs = tf.reshape(inputs, - [-1, 512 if block_fn is basicblock else 2048]) - inputs = tf.layers.dense(inputs=inputs, units=num_classes) - inputs = tf.identity(inputs, 'final_dense') - return inputs - - return model - - model_params = { - 18: { - 'block': basicblock, - 'layers': [2, 2, 2, 2] - }, - 34: { - 'block': basicblock, - 'layers': [3, 4, 6, 3] - }, - 50: { - 'block': bottleneck, - 'layers': [3, 4, 6, 3] - }, - 101: { - 'block': bottleneck, - 'layers': [3, 4, 23, 3] - }, - 152: { - 'block': bottleneck, - 'layers': [3, 8, 36, 3] - }, - 200: { - 'block': bottleneck, - 'layers': [3, 24, 36, 3] - } - } - if depth not in model_params: - raise ValueError('Not a valid depth:', depth) - params = model_params[depth] - return resnet_generator(params['block'], params['layers'], class_dim, - data_format) - - -def resnet_cifar10(depth, num_classes, data_format): - if depth % 6 != 2: - raise ValueError('depth must be 6n + 2:', depth) - - num_blocks = (depth - 2) // 6 - - if data_format is None: - data_format = ('channels_first' - if tf.test.is_built_with_cuda() else 'channels_last') - - def model(inputs, is_training): - inputs = conv_bn(inputs, 16, 3, 1, is_training, data_format) - inputs = tf.identity(inputs, 'initial_conv') - inputs = block_layer(inputs, 16, basicblock, num_blocks, 1, is_training, - 'block_layer1', data_format) - inputs = block_layer(inputs, 32, basicblock, num_blocks, 2, is_training, - 'block_layer2', data_format) - inputs = block_layer(inputs, 64, basicblock, num_blocks, 2, is_training, - 'block_layer3', data_format) - inputs = tf.layers.average_pooling2d( - inputs=inputs, - pool_size=8, - strides=1, - padding='VALID', - data_format=data_format) - inputs = tf.identity(inputs, 'final_avg_pool') - inputs = tf.reshape(inputs, [-1, 64]) - inputs = tf.layers.dense(inputs=inputs, units=num_classes) - inputs = tf.identity(inputs, 'final_dense') - return inputs - - return model - - -def run_benchmark(args, data_format='channels_last', device='/cpu:0'): - """Our model_fn for ResNet to be used with our Estimator.""" - - class_dim = 1000 - dshape = (None, 224, 224, 3) - - pdshape = (3, 224, 224) - if args.data == 'flowers102': - class_dim = 102 - dshape = (None, 224, 224, 3) - pdshape = (3, 224, 224) - elif args.data == 'cifar10': - class_dim = 10 - dshape = (None, 32, 32, 3) - pdshape = (3, 32, 32) - - with tf.device(device): - images = tf.placeholder(DTYPE, shape=dshape) - labels = tf.placeholder(tf.int64, shape=(None, )) - is_training = tf.placeholder('bool') - onehot_labels = tf.one_hot(labels, depth=class_dim) - - network = resnet_cifar10( - 32, class_dim, - data_format) if args.data == 'cifar10' else resnet_imagenet( - 50, class_dim, data_format) - - logits = network(inputs=images, is_training=is_training) - - cross_entropy = tf.losses.softmax_cross_entropy( - logits=logits, onehot_labels=onehot_labels) - avg_cost = tf.reduce_mean(cross_entropy) - - correct = tf.equal(tf.argmax(logits, 1), labels) - accuracy = tf.reduce_mean(tf.cast(correct, tf.float32)) - - lr = 0.1 if args.data == 'cifar10' else 0.01 - optimizer = tf.train.MomentumOptimizer(learning_rate=lr, momentum=0.9) - - # Batch norm requires update_ops to be added as a train_op dependency. - update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) - with tf.control_dependencies(update_ops): - train_op = optimizer.minimize(avg_cost) - - train_reader = paddle.batch( - paddle.reader.shuffle( - paddle.dataset.cifar.train10() - if args.data == 'cifar10' else paddle.dataset.flowers.train(), - buf_size=5120), - batch_size=args.batch_size) - test_reader = paddle.batch( - paddle.dataset.cifar.test10() - if args.data == 'cifar10' else paddle.dataset.flowers.test(), - batch_size=100) - - def test(): - test_accs = [] - for batch_id, data in enumerate(test_reader()): - test_images = np.array( - map(lambda x: np.transpose(x[0].reshape(pdshape), - axes=[1, 2, 0]), data)).astype("float32") - test_labels = np.array(map(lambda x: x[1], data)).astype('int64') - test_accs.append( - accuracy.eval(feed_dict={ - images: test_images, - labels: test_labels, - is_training: False - })) - print("Pass = %d, Train performance = %f imgs/s, Test accuracy = %f\n" % - (pass_id, num_samples / train_elapsed, np.mean(test_accs))) - - config = tf.ConfigProto( - intra_op_parallelism_threads=1, inter_op_parallelism_threads=1) - config.gpu_options.allow_growth = True - - with tf.Session(config=config) as sess: - init_g = tf.global_variables_initializer() - init_l = tf.local_variables_initializer() - sess.run(init_g) - sess.run(init_l) - - if args.use_fake_data: - data = train_reader().next() - images_data = np.array( - map(lambda x: np.transpose(x[0].reshape(pdshape), - axes=[1, 2, 0]), data)).astype("float32") - labels_data = np.array(map(lambda x: x[1], data)).astype('int64') - iters, num_samples, start_time = 0, 0, 0.0 - for pass_id in range(args.pass_num): - if iters == args.iterations: - break - train_accs = [] - train_losses = [] - for batch_id, data in enumerate(train_reader()): - if iters == args.skip_batch_num: - start_time = time.time() - num_samples = 0 - if iters == args.iterations: - break - if not args.use_fake_data: - images_data = np.array( - map(lambda x: np.transpose(x[0].reshape(pdshape), - axes=[1, 2, 0]), data)).astype("float32") - labels_data = np.array(map(lambda x: x[1], data)).astype( - 'int64') - _, loss, acc = sess.run([train_op, avg_cost, accuracy], - feed_dict={ - images: images_data, - labels: labels_data, - is_training: True - }) - iters += 1 - train_accs.append(acc) - train_losses.append(loss) - num_samples += len(data) - print("Pass=%d, Iter=%d, Loss=%f, Accuray=%f\n" % - (pass_id, iters, loss, acc)) - - train_elapsed = time.time() - start_time - print("Pass=%d, Loss=%f, Accuray=%f\n" % - (pass_id, np.mean(train_losses), np.mean(train_accs))) - - # evaluation - if args.with_test: - test() - - if not args.with_test: - duration = time.time() - start_time - examples_per_sec = num_samples / duration - sec_per_batch = duration / (iters - args.skip_batch_num) - - print('Total examples: %d, total time: %.5f' % - (num_samples, duration)) - print('%.5f examples/sec, %.5f sec/batch' % - (examples_per_sec, sec_per_batch)) - - -if __name__ == '__main__': - args = parse_args() - print_arguments(args) - if tf.test.is_built_with_cuda(): - device = '/device:GPU:0' - if args.order == 'NHWC': - data_format = 'channels_last' - else: - data_format = 'channels_first' - else: - device = '/cpu:0' - if args.order == 'NHWC': - data_format = 'channels_last' - else: - raise ValueError('Only support NHWC order in CPU mode') - - run_benchmark(args, data_format, device) diff --git a/benchmark/tensorflow/rnn/README.md b/benchmark/tensorflow/rnn/README.md deleted file mode 100644 index da8e7b8b07..0000000000 --- a/benchmark/tensorflow/rnn/README.md +++ /dev/null @@ -1,5 +0,0 @@ -You also should install tflearn: - -```bash -pip install -r requirements.txt -``` diff --git a/benchmark/tensorflow/rnn/reader.py b/benchmark/tensorflow/rnn/reader.py deleted file mode 100755 index ac08c10a42..0000000000 --- a/benchmark/tensorflow/rnn/reader.py +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os.path -import io -import numpy as np -import tensorflow as tf - -# tflearn -import tflearn -from tflearn.data_utils import to_categorical, pad_sequences -from tflearn.datasets import imdb - -FLAGS = tf.app.flags.FLAGS - - -class DataSet(object): - def __init__(self, data, labels): - assert data.shape[0] == labels.shape[0], ( - 'data.shape: %s labels.shape: %s' % (data.shape, labels.shape)) - self._num_examples = data.shape[0] - - self._data = data - self._labels = labels - self._epochs_completed = 0 - self._index_in_epoch = 0 - - @property - def data(self): - return self._data - - @property - def labels(self): - return self._labels - - @property - def num_examples(self): - return self._num_examples - - @property - def epochs_completed(self): - return self._epochs_completed - - def next_batch(self, batch_size): - assert batch_size <= self._num_examples - - start = self._index_in_epoch - self._index_in_epoch += batch_size - if self._index_in_epoch > self._num_examples: - # Finished epoch - self._epochs_completed += 1 - # Shuffle the data - perm = np.arange(self._num_examples) - np.random.shuffle(perm) - self._data = self._data[perm] - self._labels = self._labels[perm] - # Start next epoch - start = 0 - self._index_in_epoch = batch_size - - end = self._index_in_epoch - - return self._data[start:end], self._labels[start:end] - - -def create_datasets(file_path, vocab_size=30000, val_fraction=0.0): - - # IMDB Dataset loading - train, test, _ = imdb.load_data( - path=file_path, - n_words=vocab_size, - valid_portion=val_fraction, - sort_by_len=False) - trainX, trainY = train - testX, testY = test - - # Data preprocessing - # Sequence padding - trainX = pad_sequences(trainX, maxlen=FLAGS.max_len, value=0.) - testX = pad_sequences(testX, maxlen=FLAGS.max_len, value=0.) - # Converting labels to binary vectors - trainY = to_categorical(trainY, nb_classes=2) - testY = to_categorical(testY, nb_classes=2) - - train_dataset = DataSet(trainX, trainY) - - return train_dataset - - -def main(): - create_datasets('imdb.pkl') - - -if __name__ == "__main__": - main() diff --git a/benchmark/tensorflow/rnn/requirements.txt b/benchmark/tensorflow/rnn/requirements.txt deleted file mode 100644 index 4242e7d24f..0000000000 --- a/benchmark/tensorflow/rnn/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -tflearn diff --git a/benchmark/tensorflow/rnn/rnn.py b/benchmark/tensorflow/rnn/rnn.py deleted file mode 100755 index f288083e13..0000000000 --- a/benchmark/tensorflow/rnn/rnn.py +++ /dev/null @@ -1,223 +0,0 @@ -#!/usr/bin/env python -from six.moves import xrange # pylint: disable=redefined-builtin -import math -import time -import numpy as np -from datetime import datetime - -import reader -import tensorflow as tf -from tensorflow.python.ops import rnn - -FLAGS = tf.app.flags.FLAGS - -tf.app.flags.DEFINE_integer('batch_size', 128, """Batch size.""") -tf.app.flags.DEFINE_integer('num_batches', 100, """Number of batches to run.""") -tf.app.flags.DEFINE_integer('num_layers', 1, """Number of batches to run.""") -tf.app.flags.DEFINE_integer('max_len', 100, """Number of batches to run.""") -tf.app.flags.DEFINE_boolean('forward_only', False, - """Only run the forward pass.""") -tf.app.flags.DEFINE_boolean('forward_backward_only', False, - """Only run the forward-forward pass.""") -tf.app.flags.DEFINE_integer('hidden_size', 128, """Number of batches to run.""") -tf.app.flags.DEFINE_integer('emb_size', 128, """Number of batches to run.""") -tf.app.flags.DEFINE_boolean('log_device_placement', False, - """Whether to log device placement.""") - -VOCAB_SIZE = 30000 -NUM_CLASS = 2 - - -def get_feed_dict(x_data, y_data=None): - feed_dict = {} - - if y_data is not None: - feed_dict[y_input] = y_data - - for i in xrange(x_data.shape[0]): - feed_dict[x_input[i]] = x_data[i, :, :] - - return feed_dict - - -def get_incoming_shape(incoming): - """ Returns the incoming data shape """ - if isinstance(incoming, tf.Tensor): - return incoming.get_shape().as_list() - elif type(incoming) in [np.array, list, tuple]: - return np.shape(incoming) - else: - raise Exception("Invalid incoming layer.") - - -# Note input * W is done in LSTMCell, -# which is different from PaddlePaddle -def single_lstm(name, - incoming, - n_units, - use_peepholes=True, - return_seq=False, - return_state=False): - with tf.name_scope(name) as scope: - cell = tf.nn.rnn_cell.LSTMCell(n_units, use_peepholes=use_peepholes) - output, _cell_state = rnn.rnn(cell, incoming, dtype=tf.float32) - out = output if return_seq else output[-1] - return (out, _cell_state) if return_state else out - - -def lstm(name, - incoming, - n_units, - use_peepholes=True, - return_seq=False, - return_state=False, - num_layers=1): - with tf.name_scope(name) as scope: - lstm_cell = tf.nn.rnn_cell.LSTMCell( - n_units, use_peepholes=use_peepholes) - cell = tf.nn.rnn_cell.MultiRNNCell([lstm_cell] * num_layers) - initial_state = cell.zero_state(FLAGS.batch_size, dtype=tf.float32) - if not isinstance(incoming, list): - # if the input is embeding, the Tensor shape : [None, time_step, emb_size] - incoming = [ - tf.squeeze(input_, [1]) - for input_ in tf.split(1, FLAGS.max_len, incoming) - ] - outputs, state = tf.nn.rnn(cell, - incoming, - initial_state=initial_state, - dtype=tf.float32) - out = outputs if return_seq else outputs[-1] - return (out, _cell_state) if return_state else out - - -def embedding(name, incoming, vocab_size, emb_size): - with tf.name_scope(name) as scope: - #with tf.device("/cpu:0"): - embedding = tf.get_variable( - name + '_emb', [vocab_size, emb_size], dtype=tf.float32) - out = tf.nn.embedding_lookup(embedding, incoming) - return out - - -def fc(name, inpOp, nIn, nOut, act=True): - with tf.name_scope(name) as scope: - kernel = tf.get_variable( - name + '_w', [nIn, nOut], - initializer=tf.truncated_normal_initializer( - stddev=0.01, dtype=tf.float32), - dtype=tf.float32) - - biases = tf.get_variable( - name + '_b', [nOut], - initializer=tf.constant_initializer( - value=0.0, dtype=tf.float32), - dtype=tf.float32, - trainable=True) - - net = tf.nn.relu_layer(inpOp, kernel, biases, name=name) if act else \ - tf.matmul(inpOp, kernel) + biases - - return net - - -def inference(seq): - net = embedding('emb', seq, VOCAB_SIZE, FLAGS.emb_size) - print "emb:", get_incoming_shape(net) - net = lstm('lstm', net, FLAGS.hidden_size, num_layers=FLAGS.num_layers) - print "lstm:", get_incoming_shape(net) - net = fc('fc1', net, FLAGS.hidden_size, 2) - return net - - -def loss(logits, labels): - # one label index for one sample - labels = tf.cast(labels, tf.float32) - cross_entropy = tf.nn.softmax_cross_entropy_with_logits( - logits, labels, name='cross_entropy_per_example') - cross_entropy_mean = tf.reduce_mean(cross_entropy, name='cross_entropy') - tf.add_to_collection('losses', cross_entropy_mean) - return tf.add_n(tf.get_collection('losses'), name='total_loss') - - -def time_tensorflow_run(session, target, x_input, y_input, info_string): - num_steps_burn_in = 50 - total_duration = 0.0 - total_duration_squared = 0.0 - if not isinstance(target, list): - target = [target] - target_op = tf.group(*target) - train_dataset = reader.create_datasets("imdb.pkl", VOCAB_SIZE) - for i in xrange(FLAGS.num_batches + num_steps_burn_in): - start_time = time.time() - data, label = train_dataset.next_batch(FLAGS.batch_size) - _ = session.run(target_op, feed_dict={x_input: data, y_input: label}) - duration = time.time() - start_time - if i > num_steps_burn_in: - if not i % 10: - print('%s: step %d, duration = %.3f' % - (datetime.now(), i - num_steps_burn_in, duration)) - total_duration += duration - total_duration_squared += duration * duration - mn = total_duration / FLAGS.num_batches - vr = total_duration_squared / FLAGS.num_batches - mn * mn - sd = math.sqrt(vr) - print('%s: %s across %d steps, %.3f +/- %.3f sec / batch' % - (datetime.now(), info_string, FLAGS.num_batches, mn, sd)) - - -def run_benchmark(): - with tf.Graph().as_default(): - global_step = 0 - with tf.device('/cpu:0'): - global_step = tf.Variable(0, trainable=False) - with tf.device('/gpu:0'): - #x_input = tf.placeholder(tf.int32, [None, FLAGS.max_len], name="x_input") - #y_input = tf.placeholder(tf.int32, [None, NUM_CLASS], name="y_input") - x_input = tf.placeholder( - tf.int32, [FLAGS.batch_size, FLAGS.max_len], name="x_input") - y_input = tf.placeholder( - tf.int32, [FLAGS.batch_size, NUM_CLASS], name="y_input") - # Generate some dummy sequnce. - - last_layer = inference(x_input) - - objective = loss(last_layer, y_input) - opt = tf.train.AdamOptimizer(0.001) - grads = opt.compute_gradients(objective) - apply_gradient_op = opt.apply_gradients( - grads, global_step=global_step) - - init = tf.initialize_all_variables() - sess = tf.Session(config=tf.ConfigProto( - allow_soft_placement=True, - log_device_placement=FLAGS.log_device_placement)) - sess.run(init) - - run_forward = True - run_forward_backward = True - if FLAGS.forward_only and FLAGS.forward_backward_only: - raise ValueError("Cannot specify --forward_only and " - "--forward_backward_only at the same time.") - if FLAGS.forward_only: - run_forward_backward = False - elif FLAGS.forward_backward_only: - run_forward = False - - if run_forward: - time_tensorflow_run(sess, last_layer, x_input, y_input, - "Forward") - - if run_forward_backward: - with tf.control_dependencies([apply_gradient_op]): - train_op = tf.no_op(name='train') - time_tensorflow_run(sess, [train_op, objective], x_input, - y_input, "Forward-backward") - - -def main(_): - run_benchmark() - - -if __name__ == '__main__': - tf.app.run() diff --git a/benchmark/tensorflow/rnn/rnn_multi_gpu.py b/benchmark/tensorflow/rnn/rnn_multi_gpu.py deleted file mode 100755 index eabee4fa8f..0000000000 --- a/benchmark/tensorflow/rnn/rnn_multi_gpu.py +++ /dev/null @@ -1,322 +0,0 @@ -#!/usr/bin/env python -from six.moves import xrange # pylint: disable=redefined-builtin -import re -import math -import time -import numpy as np -from datetime import datetime - -import reader -import tensorflow as tf -from tensorflow.python.ops import rnn - -FLAGS = tf.app.flags.FLAGS - -tf.app.flags.DEFINE_integer('batch_size', 64, """Batch size.""") -tf.app.flags.DEFINE_integer('num_batches', 100, """Number of batches to run.""") -tf.app.flags.DEFINE_integer('num_layers', 1, """Number of batches to run.""") -tf.app.flags.DEFINE_integer('max_len', 100, """Number of batches to run.""") -tf.app.flags.DEFINE_integer('hidden_size', 128, """Number of batches to run.""") -tf.app.flags.DEFINE_integer('emb_size', 64, """Number of batches to run.""") -tf.app.flags.DEFINE_boolean('log_device_placement', False, - """Whether to log device placement.""") -tf.app.flags.DEFINE_integer('num_gpus', 4, """How many GPUs to use.""") - -VOCAB_SIZE = 30000 -NUM_CLASS = 2 - -NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN = 50000 -NUM_EPOCHS_PER_DECAY = 50 -INITIAL_LEARNING_RATE = 0.1 -LEARNING_RATE_DECAY_FACTOR = 0.1 -TOWER_NAME = 'tower' - -train_dataset = reader.create_datasets("imdb.pkl", VOCAB_SIZE) - - -def get_incoming_shape(incoming): - """ Returns the incoming data shape """ - if isinstance(incoming, tf.Tensor): - return incoming.get_shape().as_list() - elif type(incoming) in [np.array, list, tuple]: - return np.shape(incoming) - else: - raise Exception("Invalid incoming layer.") - - -# Note input * W is done in LSTMCell, -# which is different from PaddlePaddle -def single_lstm(name, - incoming, - n_units, - use_peepholes=True, - return_seq=False, - return_state=False): - with tf.name_scope(name) as scope: - cell = tf.nn.rnn_cell.LSTMCell(n_units, use_peepholes=use_peepholes) - output, _cell_state = rnn.rnn(cell, incoming, dtype=tf.float32) - out = output if return_seq else output[-1] - return (out, _cell_state) if return_state else out - - -def lstm(name, - incoming, - n_units, - use_peepholes=True, - return_seq=False, - return_state=False, - num_layers=1): - with tf.name_scope(name) as scope: - lstm_cell = tf.nn.rnn_cell.LSTMCell( - n_units, use_peepholes=use_peepholes) - cell = tf.nn.rnn_cell.MultiRNNCell([lstm_cell] * num_layers) - initial_state = cell.zero_state(FLAGS.batch_size, dtype=tf.float32) - if not isinstance(incoming, list): - # if the input is embeding, the Tensor shape : [None, time_step, emb_size] - incoming = [ - tf.squeeze(input_, [1]) - for input_ in tf.split(1, FLAGS.max_len, incoming) - ] - outputs, state = tf.nn.rnn(cell, - incoming, - initial_state=initial_state, - dtype=tf.float32) - out = outputs if return_seq else outputs[-1] - return (out, _cell_state) if return_state else out - - -def embedding(name, incoming, vocab_size, emb_size): - with tf.name_scope(name) as scope: - #with tf.device("/cpu:0"): - embedding = tf.get_variable( - name + '_emb', [vocab_size, emb_size], dtype=tf.float32) - out = tf.nn.embedding_lookup(embedding, incoming) - return out - - -def fc(name, inpOp, nIn, nOut, act=True): - with tf.name_scope(name) as scope: - kernel = tf.get_variable( - name + '_w', [nIn, nOut], - initializer=tf.truncated_normal_initializer( - stddev=0.01, dtype=tf.float32), - dtype=tf.float32) - - biases = tf.get_variable( - name + '_b', [nOut], - initializer=tf.constant_initializer( - value=0.0, dtype=tf.float32), - dtype=tf.float32, - trainable=True) - - net = tf.nn.relu_layer(inpOp, kernel, biases, name=name) if act else \ - tf.matmul(inpOp, kernel) + biases - - return net - - -def inference(seq): - net = embedding('emb', seq, VOCAB_SIZE, FLAGS.emb_size) - print "emb:", get_incoming_shape(net) - net = lstm('lstm', net, FLAGS.hidden_size, num_layers=FLAGS.num_layers) - print "lstm:", get_incoming_shape(net) - net = fc('fc1', net, FLAGS.hidden_size, 2) - return net - - -def loss(logits, labels): - # one label index for one sample - #labels = tf.cast(labels, tf.int64) - # cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits( - # logits, labels, name='cross_entropy_per_example') - labels = tf.cast(labels, tf.float32) - cross_entropy = tf.nn.softmax_cross_entropy_with_logits( - logits, labels, name='cross_entropy_per_example') - cross_entropy_mean = tf.reduce_mean(cross_entropy, name='cross_entropy') - tf.add_to_collection('losses', cross_entropy_mean) - return tf.add_n(tf.get_collection('losses'), name='total_loss') - - -def tower_loss(scope): - """Calculate the total loss on a single tower running the model. - Args: - scope: unique prefix string identifying the tower, e.g. 'tower_0' - Returns: - Tensor of shape [] containing the total loss for a batch of data - """ - data, label = train_dataset.next_batch(FLAGS.batch_size) - - # Build a Graph that computes the logits predictions from the - # inference model. - last_layer = inference(data) - - # Build the portion of the Graph calculating the losses. Note that we will - # assemble the total_loss using a custom function below. - #_ = loss(last_layer, label) - _ = loss(last_layer, label) - - # Assemble all of the losses for the current tower only. - losses = tf.get_collection('losses', scope) - - # Calculate the total loss for the current tower. - total_loss = tf.add_n(losses, name='total_loss') - - # Compute the moving average of all individual losses and the total loss. - loss_averages = tf.train.ExponentialMovingAverage(0.9, name='avg') - loss_averages_op = loss_averages.apply(losses + [total_loss]) - - # Attach a scalar summary to all individual losses and the total loss; do the - # same for the averaged version of the losses. - for l in losses + [total_loss]: - # Remove 'tower_[0-9]/' from the name in case this is a multi-GPU training - # session. This helps the clarity of presentation on tensorboard. - loss_name = re.sub('%s_[0-9]*/' % TOWER_NAME, '', l.op.name) - # Name each loss as '(raw)' and name the moving average version of the loss - # as the original loss name. - tf.scalar_summary(loss_name + ' (raw)', l) - #tf.scalar_summary(loss_name, loss_averages.average(l)) - - with tf.control_dependencies([loss_averages_op]): - total_loss = tf.identity(total_loss) - return total_loss - - -def average_gradients(tower_grads): - """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 individual gradients. The inner list is over the gradient - calculation for each tower. - Returns: - List of pairs of (gradient, variable) where the gradient has been averaged - across all towers. - """ - average_grads = [] - for grad_and_vars in zip(*tower_grads): - # Note that each grad_and_vars looks like the following: - # ((grad0_gpu0, var0_gpu0), ... , (grad0_gpuN, var0_gpuN)) - grads = [] - for g, _ in grad_and_vars: - # Add 0 dimension to the gradients to represent the tower. - expanded_g = tf.expand_dims(g, 0) - - # Append on a 'tower' dimension which we will average over below. - grads.append(expanded_g) - - # Average over the 'tower' dimension. - grad = tf.concat(0, grads) - grad = tf.reduce_mean(grad, 0) - - # Keep in mind that the Variables are redundant because they are shared - # across towers. So .. we will just return the first tower's pointer to - # the Variable. - v = grad_and_vars[0][1] - grad_and_var = (grad, v) - average_grads.append(grad_and_var) - return average_grads - - -def time_tensorflow_run(session, target): - num_steps_burn_in = 80 - total_duration = 0.0 - total_duration_squared = 0.0 - for i in xrange(FLAGS.num_batches + num_steps_burn_in): - start_time = time.time() - _ = session.run(target, feed_dict={x_input: data, y_input: label}) - _, loss_value = session.run(target) - duration = time.time() - start_time - if i > num_steps_burn_in: - if not i % 10: - num_examples_per_step = FLAGS.batch_size * FLAGS.num_gpus - examples_per_sec = num_examples_per_step / duration - # sec_per_batch = duration / FLAGS.num_gpus - sec_per_batch = duration - - format_str = ( - '%s: step %d, loss= %.2f (%.1f examples/sec; %.3f ' - 'sec/batch batch_size= %d)') - print(format_str % - (datetime.now(), i - num_steps_burn_in, loss_value, - duration, sec_per_batch, num_examples_per_step)) - - total_duration += duration - total_duration_squared += duration * duration - - mn = total_duration / FLAGS.num_batches - vr = total_duration_squared / FLAGS.num_batches - mn * mn - sd = math.sqrt(vr) - print('%s: FwdBwd across %d steps, %.3f +/- %.3f sec / batch' % - (datetime.now(), FLAGS.num_batches, mn, sd)) - - -def run_benchmark(): - with tf.Graph().as_default(), tf.device('/cpu:0'): - # Create a variable to count the number of train() calls. This equals the - # number of batches processed * FLAGS.num_gpus. - global_step = tf.get_variable( - 'global_step', [], - initializer=tf.constant_initializer(0), - trainable=False) - - # Calculate the learning rate schedule. - num_batches_per_epoch = (NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN / - FLAGS.batch_size) - decay_steps = int(num_batches_per_epoch * NUM_EPOCHS_PER_DECAY) - - # Create an optimizer that performs gradient descent. - opt = tf.train.AdamOptimizer(0.001) - - #train_dataset = reader.create_datasets("imdb.pkl", VOCAB_SIZE) - - # Calculate the gradients for each model tower. - tower_grads = [] - for i in xrange(FLAGS.num_gpus): - with tf.device('/gpu:%d' % i): - with tf.name_scope('%s_%d' % (TOWER_NAME, i)) as scope: - # Calculate the loss for one tower of the model. This function - # constructs the entire model but shares the variables across - # all towers. - loss = tower_loss(scope) - - # Reuse variables for the next tower. - tf.get_variable_scope().reuse_variables() - - # Retain the summaries from the final tower. - # summaries = tf.get_collection(tf.GraphKeys.SUMMARIES, scope) - - # Calculate the gradients for the batch of data on this tower. - grads = opt.compute_gradients(loss) - - # Keep track of the gradients across all towers. - tower_grads.append(grads) - - # We must calculate the mean of each gradient. Note that this is the - # synchronization point across all towers. - grads = average_gradients(tower_grads) - - # Apply the gradients to adjust the shared variables. - apply_gradient_op = opt.apply_gradients(grads, global_step=global_step) - - # Group all updates to into a single train op. - train_op = tf.group(apply_gradient_op) - - # Build an initialization operation. - init = tf.initialize_all_variables() - - # Start running operations on the Graph. allow_soft_placement must be set to - # True to build towers on GPU, as some of the ops do not have GPU - # implementations. - sess = tf.Session(config=tf.ConfigProto( - allow_soft_placement=True, - log_device_placement=FLAGS.log_device_placement)) - sess.run(init) - time_tensorflow_run(sess, [train_op, loss]) - - -def main(_): - run_benchmark() - - -if __name__ == '__main__': - tf.app.run() diff --git a/benchmark/tensorflow/rnn/run.sh b/benchmark/tensorflow/rnn/run.sh deleted file mode 100755 index db10eefdea..0000000000 --- a/benchmark/tensorflow/rnn/run.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash - -set -e - -function test() { - lstm_num=$1 - batch_size=$2 - hid_size=$3 - prefix=$4 - python rnn.py --num_layers=${lstm_num} --batch_size=$batch_size \ - --hidden_size=${hid_size} \ - --forward_backward_only=1 \ - > logs/1gpu-${lstm_num}lstm-batch${batch_size}-hid${hid_size}.log 2>&1 -} - -if [ ! -d "logs" ]; then - mkdir logs -fi - -#--lstm_num--batch_size--hidden_size--# -test 2 64 256 -test 2 64 512 -test 2 64 1280 - -test 2 128 256 -test 2 128 512 -test 2 128 1280 - -test 2 256 256 -test 2 256 512 -test 2 256 1280 diff --git a/benchmark/tensorflow/rnn/run_multi.sh b/benchmark/tensorflow/rnn/run_multi.sh deleted file mode 100755 index ec62fc26b5..0000000000 --- a/benchmark/tensorflow/rnn/run_multi.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -set -e - -function test() { - num_gpu=$1 - lstm_num=$2 - hid_size=$3 - batch_per_gpu=`expr ${batch_size} / ${num_gpu}` - batch_size=$4 - python rnn_multi_gpu.py --num_layers=${lstm_num} --batch_size=$batch_per_gpu \ - --num_gpus=${num_gpu} \ - --hidden_size=${hid_size} \ - --forward_backward_only=1 \ - > logs/${num_gpu}gpu-${lstm_num}lstm-hid${hid_size}-batch${batch_size}.log 2>&1 -} - -if [ ! -d "logs" ]; then - mkdir logs -fi - -#--num_gpus--lstm_num--hiddne_size--batch_size--# -test 4 2 256 128 -test 4 2 256 256 -test 4 2 256 512 - -test 4 2 512 128 -test 4 2 512 256 -test 4 2 512 512 diff --git a/benchmark/tensorflow/stacked_dynamic_lstm.py b/benchmark/tensorflow/stacked_dynamic_lstm.py deleted file mode 100644 index 1f532dc2fa..0000000000 --- a/benchmark/tensorflow/stacked_dynamic_lstm.py +++ /dev/null @@ -1,218 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -import numpy as np -import argparse -import time -import tensorflow as tf - - -def parse_args(): - parser = argparse.ArgumentParser("LSTM model benchmark.") - parser.add_argument( - '--batch_size', - type=int, - default=32, - help='The sequence number of a batch data. (default: %(default)d)') - parser.add_argument( - '--stacked_num', - type=int, - default=5, - help='Number of lstm layers to stack. (default: %(default)d)') - parser.add_argument( - '--embedding_dim', - type=int, - default=512, - help='Dimension of embedding table. (default: %(default)d)') - parser.add_argument( - '--hidden_dim', - type=int, - default=512, - help='Hidden size of lstm unit. (default: %(default)d)') - parser.add_argument( - '--pass_num', - type=int, - default=10, - help='Epoch number to train. (default: %(default)d)') - parser.add_argument( - '--learning_rate', - type=float, - default=0.0002, - help='Learning rate used to train. (default: %(default)f)') - parser.add_argument( - '--infer_only', action='store_true', help='If set, run forward only.') - args = parser.parse_args() - return args - - -def print_arguments(args): - print('----------- Configuration Arguments -----------') - for arg, value in sorted(vars(args).iteritems()): - print('%s: %s' % (arg, value)) - print('------------------------------------------------') - - -def dynamic_lstm_model(dict_size, - embedding_dim, - hidden_dim, - stacked_num, - class_num=2, - is_train=True): - word_idx = tf.placeholder(tf.int64, shape=[None, None]) - sequence_length = tf.placeholder(tf.int64, shape=[None, ]) - - embedding_weights = tf.get_variable('word_embeddings', - [dict_size, embedding_dim]) - embedding = tf.nn.embedding_lookup(embedding_weights, word_idx) - - lstm_cell = tf.nn.rnn_cell.LSTMCell( - num_units=hidden_dim, use_peepholes=False) - stacked_cell = tf.nn.rnn_cell.MultiRNNCell([lstm_cell] * stacked_num) - - # final_state [LSTMTuple(c, h), LSTMTuple(c, h) ...] total stacked_num LSTMTuples - _, final_state = tf.nn.dynamic_rnn( - cell=stacked_cell, - inputs=embedding, - dtype=tf.float32, - sequence_length=sequence_length) - - w = tf.Variable( - tf.truncated_normal([hidden_dim, class_num]), dtype=tf.float32) - bias = tf.Variable( - tf.constant( - value=0.0, shape=[class_num], dtype=tf.float32)) - prediction = tf.matmul(final_state[-1][1], w) + bias - - if not is_train: - return (word_idx, sequence_length), tf.nn.softmax(prediction) - - label = tf.placeholder(tf.int64, shape=[None, ]) - loss = tf.nn.softmax_cross_entropy_with_logits( - labels=tf.one_hot(label, 2), logits=prediction) - avg_loss = tf.reduce_mean(loss) - - correct_count = tf.equal(tf.argmax(prediction, 1), label) - acc = tf.reduce_mean(tf.cast(correct_count, tf.float32)) - - with tf.variable_scope("reset_metrics_accuracy_scope") as scope: - g_acc = tf.metrics.accuracy(label, tf.argmax(prediction, axis=1)) - vars = tf.contrib.framework.get_variables( - scope, collection=tf.GraphKeys.LOCAL_VARIABLES) - reset_op = tf.variables_initializer(vars) - - return (word_idx, sequence_length, label), avg_loss, acc, g_acc, reset_op - - -def padding_data(data, padding_size, value): - data = data + [value] * padding_size - return data[:padding_size] - - -def train(args): - word_dict = paddle.dataset.imdb.word_dict() - dict_size = len(word_dict) - - feeding_list, avg_loss, acc, g_acc, reset_op = dynamic_lstm_model( - dict_size, args.embedding_dim, args.hidden_dim, args.stacked_num) - - adam_optimizer = tf.train.AdamOptimizer(learning_rate=args.learning_rate) - train_op = adam_optimizer.minimize(avg_loss) - - train_reader = paddle.batch( - paddle.reader.shuffle( - paddle.dataset.imdb.train(word_dict), buf_size=25000), - batch_size=args.batch_size) - - test_reader = paddle.batch( - paddle.reader.shuffle( - paddle.dataset.imdb.test(word_dict), buf_size=25000), - batch_size=args.batch_size) - - def do_validation(sess): - sess.run(reset_op) - for batch_id, data in enumerate(test_reader()): - word_idx = map(lambda x: x[0], data) - sequence_length = np.array( - [len(seq) for seq in word_idx]).astype('int64') - maxlen = np.max(sequence_length) - word_idx = [padding_data(seq, maxlen, 0) for seq in word_idx] - word_idx = np.array(word_idx).astype('int64') - label = np.array(map(lambda x: x[1], data)).astype('int64') - - _, loss, fetch_acc, fetch_g_acc = sess.run( - [train_op, avg_loss, acc, g_acc], - feed_dict={ - feeding_list[0]: word_idx, - feeding_list[1]: sequence_length, - feeding_list[2]: label - }) - - return fetch_g_acc[1] - - config = tf.ConfigProto( - intra_op_parallelism_threads=1, inter_op_parallelism_threads=1) - config.gpu_options.allow_growth = True - with tf.Session(config=config) as sess: - init_g = tf.global_variables_initializer() - init_l = tf.local_variables_initializer() - sess.run(init_l) - sess.run(init_g) - - for pass_id in xrange(args.pass_num): - # clear accuracy local variable - sess.run(reset_op) - pass_start_time = time.time() - words_seen = 0 - - for batch_id, data in enumerate(train_reader()): - word_idx = map(lambda x: x[0], data) - sequence_length = np.array( - [len(seq) for seq in word_idx]).astype('int64') - words_seen += np.sum(sequence_length) - maxlen = np.max(sequence_length) - word_idx = [padding_data(seq, maxlen, 0) for seq in word_idx] - word_idx = np.array(word_idx).astype('int64') - label = np.array(map(lambda x: x[1], data)).astype('int64') - - _, loss, fetch_acc, fetch_g_acc = sess.run( - [train_op, avg_loss, acc, g_acc], - feed_dict={ - feeding_list[0]: word_idx, - feeding_list[1]: sequence_length, - feeding_list[2]: label - }) - - print("pass_id=%d, batch_id=%d, loss: %f, acc: %f, avg_acc: %f" - % (pass_id, batch_id, loss, fetch_acc, fetch_g_acc[1])) - - pass_end_time = time.time() - time_consumed = pass_end_time - pass_start_time - words_per_sec = words_seen / time_consumed - test_acc = do_validation(sess) - print("pass_id=%d, test_acc: %f, words/s: %f, sec/pass: %f" % - (pass_id, test_acc, words_per_sec, time_consumed)) - - -if __name__ == '__main__': - args = parse_args() - print_arguments(args) - - if args.infer_only: - pass - else: - train(args) diff --git a/benchmark/tensorflow/vgg.py b/benchmark/tensorflow/vgg.py deleted file mode 100644 index d32c835bd7..0000000000 --- a/benchmark/tensorflow/vgg.py +++ /dev/null @@ -1,323 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""VGG16 benchmark in TensorFlow""" -import tensorflow as tf -import numpy as np -import argparse -import time - -parser = argparse.ArgumentParser(description=__doc__) -parser.add_argument( - '--batch_size', type=int, default=128, help="Batch size for training.") -parser.add_argument( - '--skip_batch_num', - type=int, - default=5, - help='The first num of minibatch num to skip, for better performance test') -parser.add_argument( - '--iterations', type=int, default=80, help='The number of minibatches.') -parser.add_argument( - '--learning_rate', - type=float, - default=1e-3, - help="Learning rate for training.") -parser.add_argument('--num_passes', type=int, default=50, help="No. of passes.") -parser.add_argument( - '--device', - type=str, - default='GPU', - choices=['CPU', 'GPU'], - help="The device type.") -parser.add_argument( - '--data_format', - type=str, - default='NHWC', - choices=['NCHW', 'NHWC'], - help='The data order, NCHW=[batch, channels, height, width].' - 'Only support NHWC right now.') -parser.add_argument( - '--data_set', - type=str, - default='cifar10', - choices=['cifar10', 'flowers'], - help='Optional dataset for benchmark.') -args = parser.parse_args() - - -class VGG16Model(object): - def __init__(self): - self.parameters = [] - - def batch_norm_relu(self, inputs, is_training): - """Performs a batch normalization followed by a ReLU.""" - # We set fused=True for a significant speed boost. See - # https://www.tensorflow.org/speed/speed_guide#common_fused_ops - inputs = tf.layers.batch_normalization( - inputs=inputs, - axis=1 if args.data_format == 'NCHW' else -1, - momentum=0.9, - epsilon=1e-05, - center=True, - scale=True, - training=is_training, - fused=True) - inputs = tf.nn.relu(inputs) - return inputs - - def conv_bn_layer(self, - name, - images, - kernel_shape, - is_training, - drop_rate=0.0): - with tf.name_scope(name) as scope: - kernel = tf.Variable( - tf.truncated_normal( - kernel_shape, dtype=tf.float32, stddev=1e-1), - name='weights') - conv = tf.nn.conv2d( - images, - kernel, [1, 1, 1, 1], - data_format=args.data_format, - padding='SAME') - biases = tf.Variable( - tf.constant( - 0.0, shape=[kernel_shape[-1]], dtype=tf.float32), - trainable=True, - name='biases') - out = tf.nn.bias_add(conv, biases) - out = self.batch_norm_relu(out, is_training) - out = tf.layers.dropout(out, rate=drop_rate, training=is_training) - return out - - def fc_layer(self, name, inputs, shape): - with tf.name_scope(name) as scope: - fc_w = tf.Variable( - tf.truncated_normal( - shape, dtype=tf.float32, stddev=1e-1), - name='weights') - fc_b = tf.Variable( - tf.constant( - 0.0, shape=[shape[-1]], dtype=tf.float32), - trainable=True, - name='biases') - out = tf.nn.bias_add(tf.matmul(inputs, fc_w), fc_b) - return out - - def network(self, images, class_dim, is_training): - """ VGG16 model structure. - - TODO(kuke): enable this network to support the 'NCHW' data format - """ - - # conv1 - conv1_1 = self.conv_bn_layer( - 'conv1_1', images, [3, 3, 3, 64], is_training, drop_rate=0.3) - conv1_2 = self.conv_bn_layer( - 'conv1_2', conv1_1, [3, 3, 64, 64], is_training, drop_rate=0.0) - # pool1 - pool1 = tf.nn.max_pool( - conv1_2, - ksize=[1, 2, 2, 1], - strides=[1, 2, 2, 1], - padding='SAME', - name='pool1') - # conv2 - conv2_1 = self.conv_bn_layer( - 'conv2_1', pool1, [3, 3, 64, 128], is_training, drop_rate=0.4) - conv2_2 = self.conv_bn_layer( - 'conv2_2', conv2_1, [3, 3, 128, 128], is_training, drop_rate=0.0) - # pool2 - pool2 = tf.nn.max_pool( - conv2_2, - ksize=[1, 2, 2, 1], - strides=[1, 2, 2, 1], - padding='SAME', - name='pool2') - # conv3 - conv3_1 = self.conv_bn_layer( - 'conv3_1', pool2, [3, 3, 128, 256], is_training, drop_rate=0.4) - conv3_2 = self.conv_bn_layer( - 'conv3_2', conv3_1, [3, 3, 256, 256], is_training, drop_rate=0.4) - conv3_3 = self.conv_bn_layer( - 'conv3_3', conv3_2, [3, 3, 256, 256], is_training, drop_rate=0.0) - # pool3 - pool3 = tf.nn.max_pool( - conv3_3, - ksize=[1, 2, 2, 1], - strides=[1, 2, 2, 1], - padding='SAME', - name='pool3') - # conv4 - conv4_1 = self.conv_bn_layer( - 'conv4_1', pool3, [3, 3, 256, 512], is_training, drop_rate=0.4) - conv4_2 = self.conv_bn_layer( - 'conv4_2', conv4_1, [3, 3, 512, 512], is_training, drop_rate=0.4) - conv4_3 = self.conv_bn_layer( - 'conv4_3', conv4_2, [3, 3, 512, 512], is_training, drop_rate=0.0) - # pool4 - pool4 = tf.nn.max_pool( - conv4_3, - ksize=[1, 2, 2, 1], - strides=[1, 2, 2, 1], - padding='SAME', - name='pool4') - # conv5 - conv5_1 = self.conv_bn_layer( - 'conv5_1', pool4, [3, 3, 512, 512], is_training, drop_rate=0.4) - conv5_2 = self.conv_bn_layer( - 'conv5_2', conv5_1, [3, 3, 512, 512], is_training, drop_rate=0.4) - conv5_3 = self.conv_bn_layer( - 'conv5_3', conv5_2, [3, 3, 512, 512], is_training, drop_rate=0.0) - # pool5 - pool5 = tf.nn.max_pool( - conv5_3, - ksize=[1, 2, 2, 1], - strides=[1, 2, 2, 1], - padding='SAME', - name='pool4') - # flatten - shape = int(np.prod(pool5.get_shape()[1:])) - pool5_flat = tf.reshape(pool5, [-1, shape]) - # fc1 - drop = tf.layers.dropout(pool5_flat, rate=0.5, training=is_training) - fc1 = self.fc_layer('fc1', drop, [shape, 512]) - # fc2 - bn = self.batch_norm_relu(fc1, is_training) - drop = tf.layers.dropout(bn, rate=0.5, training=is_training) - fc2 = self.fc_layer('fc2', drop, [512, 512]) - - fc3 = self.fc_layer('fc3', fc2, [512, class_dim]) - - return fc3 - - -def run_benchmark(): - """Run benchmark on cifar10 or flowers.""" - - if args.data_set == "cifar10": - class_dim = 10 - raw_shape = (3, 32, 32) - dat_shape = (None, 32, 32, 3) if args.data_format == 'NHWC' else ( - None, 3, 32, 32) - else: - class_dim = 102 - raw_shape = (3, 224, 224) - dat_shape = (None, 224, 224, 3) if args.data_format == 'NHWC' else ( - None, 3, 224, 224) - - device = '/cpu:0' if args.device == 'CPU' else '/device:GPU:0' - - with tf.device(device): - images = tf.placeholder(tf.float32, shape=dat_shape) - labels = tf.placeholder(tf.int64, shape=(None, )) - is_training = tf.placeholder('bool') - onehot_labels = tf.one_hot(labels, depth=class_dim) - - vgg16 = VGG16Model() - logits = vgg16.network(images, class_dim, is_training) - loss = tf.losses.softmax_cross_entropy( - onehot_labels=onehot_labels, logits=logits) - avg_loss = tf.reduce_mean(loss) - - correct = tf.equal(tf.argmax(logits, 1), labels) - accuracy = tf.reduce_mean(tf.cast(correct, tf.float32)) - - optimizer = tf.train.AdamOptimizer(learning_rate=args.learning_rate) - update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) - with tf.control_dependencies(update_ops): - train_op = optimizer.minimize(avg_loss) - - # data reader - train_reader = paddle.batch( - paddle.reader.shuffle( - paddle.dataset.cifar.train10() - if args.data_set == 'cifar10' else paddle.dataset.flowers.train(), - buf_size=5120), - batch_size=args.batch_size) - test_reader = paddle.batch( - paddle.reader.shuffle( - paddle.dataset.cifar.test10() - if args.data_set == 'cifar10' else paddle.dataset.flowers.test(), - buf_size=5120), - batch_size=args.batch_size) - - # test - def test(): - test_accs = [] - for batch_id, data in enumerate(test_reader()): - test_images = np.array( - map(lambda x: np.transpose(x[0].reshape(raw_shape), - axes=[1, 2, 0]) if args.data_format == 'NHWC' else x[0], data)).astype("float32") - test_labels = np.array(map(lambda x: x[1], data)).astype('int64') - test_accs.append( - accuracy.eval(feed_dict={ - images: test_images, - labels: test_labels, - is_training: False - })) - return np.mean(test_accs) - - config = tf.ConfigProto( - intra_op_parallelism_threads=1, inter_op_parallelism_threads=1) - config.gpu_options.allow_growth = True - - with tf.Session(config=config) as sess: - init_g = tf.global_variables_initializer() - init_l = tf.local_variables_initializer() - sess.run(init_g) - sess.run(init_l) - iters, num_samples, start_time = 0, 0, time.time() - for pass_id in range(args.num_passes): - # train - num_samples = 0 - start_time = time.time() - for batch_id, data in enumerate(train_reader()): - if iters == args.skip_batch_num: - start_time = time.time() - num_samples = 0 - if iters == args.iterations: - break - train_images = np.array( - map(lambda x: np.transpose(x[0].reshape(raw_shape), - axes=[1, 2, 0]) if args.data_format == 'NHWC' else x[0], data)).astype("float32") - train_labels = np.array(map(lambda x: x[1], data)).astype( - 'int64') - _, loss, acc = sess.run([train_op, avg_loss, accuracy], - feed_dict={ - images: train_images, - labels: train_labels, - is_training: True - }) - iters += 1 - num_samples += len(data) - print("Pass = %d, Iters = %d, Loss = %f, Accuracy = %f" % - (pass_id, iters, loss, acc)) - train_elapsed = time.time() - start_time - # test - pass_test_acc = test() - print("Pass = %d, Train speed = %f imgs/s, Test accuracy = %f\n" % - (pass_id, num_samples / train_elapsed, pass_test_acc)) - - -def print_arguments(): - print('----------- Configuration Arguments -----------') - for arg, value in sorted(vars(args).iteritems()): - print('%s: %s' % (arg, value)) - print('------------------------------------------------') - - -if __name__ == '__main__': - print_arguments() - run_benchmark() -- GitLab