From 600e53b615d346d3901c426ba0e58df52b43691c Mon Sep 17 00:00:00 2001 From: Yifan Zhao <yifanz16@illinois.edu> Date: Fri, 26 Mar 2021 21:18:42 -0500 Subject: [PATCH] Getting started (documentation) --- hpvm/docs/getting-started.rst | 229 +++++++++++++++++- .../docs/tradeoff-curves/alexnet2_cifar10.png | Bin 0 -> 25556 bytes 2 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 hpvm/docs/tradeoff-curves/alexnet2_cifar10.png diff --git a/hpvm/docs/getting-started.rst b/hpvm/docs/getting-started.rst index f2f0c84525..0f1ee13cb8 100644 --- a/hpvm/docs/getting-started.rst +++ b/hpvm/docs/getting-started.rst @@ -1,4 +1,231 @@ Getting Started =============== -TODO: this is the system-wide tour Sasa was suggesting. Finish this. +This tutorial covers the basic usage of all components in HPVM +(components listed :doc:`here </components/index>`). +We will generate a DNN model, AlexNet2 (for CIFAR10 dataset), into HPVM code, compile it with HPVM, +perform autotuning on the compiled binary to find approximation choices (configurations), +and profile the selected configurations to get real performance on device. +The result will be a figure showing the accuracy-performance tradeoff of AlexNet2 over the +(pre-defined) approximations and the configurations in a few formats. + +Please check ``test/dnn_benchmarks/model_params/`` exists and contains +``alexnet2_cifar10/`` and ``pytorch/alexnet2_cifar10.pth.tar``, +which may not be the case if you opted out of model parameter download in the installer. +In that case, you may run the installer again to download the parameter. +It will not rebuild everything from scratch. + +Generating and Compiling a DNN Model +------------------------------------ + +Below we will use `torch2hpvm`, the PyTorch API as an example. +This package lives at ``projects/torch2hpvm`` and should have been installed by the installer. +The Keras frontend serves a similar purpose, and its usage can be found in the +:doc:`documentation </components/keras-frontend>`. + +*Note* that below we'll be working under directory ``test/dnn_benchmarks``, +for easier access to ``test/dnn_benchmarks/model_params/``. +You can also symlink it to other locations -- don't move it: it's used in test cases -- +and adjust the paths below accordingly. + +First, prepare 2 datasets for autotuning and testing for AlexNet2. +These datasets are provided as ``model_params/alexnet2_cifar10/{tune|test}_{input|labels}.bin``, +where ``tune`` and ``test`` prefixes signify tuning and testing set. + +.. code-block:: python + + from torch2hpvm import BinDataset + from pathlib import Path + + data_dir = Path("model_params/alexnet2_cifar10") + dataset_shape = 5000, 3, 32, 32 # NCHW format. + tuneset = BinDataset(data_dir / "tune_input.bin", data_dir / "tune_labels.bin", dataset_shape) + testset = BinDataset(data_dir / "test_input.bin", data_dir / "test_labels.bin", dataset_shape) + +`BinDataset` is a utility `torch2hpvm` provides for creating dataset over binary files. +Any instance `torch.utils.data.Dataset` can be used here. + +*Note* that each `module` is bound to 2 datasets: a "tune" and a "test" set. +The generated binary accepts an argument to be either the string "tune" or "test", +and performs inference over a dataset accordingly. + +Create a DNN `module` and load the checkpoint: + +.. code-block:: python + + import torch + from torch.nn import Module + import dnn # Defined at `hpvm/test/dnn_benchmarks/pytorch` + + model: Module = dnn.AlexNet2() + checkpoint = "model_params/alexnet2_cifar10.pth.tar" + model.load_state_dict(torch.load(checkpoint)) + +Any `torch.nn.Module` can be similarly used, +as long as they only contain the tensor operators supported in HPVM. +See "Supported Operators" in :doc:`PyTorch frontend <components/torch2hpvm>` +and :doc:`Keras frontend <components/keras-frontend>`. + +Now we are ready to export the model. The main functioning class of `torch2hpvm` is `ModelExporter`: + +.. code-block:: python + + from torch2hpvm import ModelExporter + + output_dir = Path("./alexnet2_cifar10") + build_dir = output_dir / "build" + target_binary = build_dir / "alexnet2_cifar10" + batch_size = 500 + conf_file = "hpvm-c/benchmarks/alexnet2_cifar10/data/tuner_confs.txt" + exporter = ModelExporter(model, tuneset, testset, output_dir, config_file=conf_file) + exporter.generate(batch_size=batch_size).compile(target_binary, build_dir) + +`output_dir`, `build_dir`, and `target_binary` define the folder for code generation, compilation, +and path to the compiled binary respectively. +`batch_size` is the batch size the binary uses during inference. + +* + *Note* that `conf_file` is the path to an HPVM approximation configuration file. + This file decides what approximation the binary will use during inference. + This path is hardcoded into the binary and is only read when the binary starts, + so it's fine to have `conf_file` point to a non-existing path. + An example can be found at ``hpvm-c/benchmarks/alexnet2_cifar10/data/tuner_confs.txt``. + +* `exporter.generate` generates the HPVM-C code while `exporter.compile` is + a helper that invokes the HPVM compiler for you. + +Now there should be a binary at ``./alexnet2_cifar10/build/alexnet2_cifar10``. +Try running ``./alexnet2_cifar10/build/alexnet2_cifar10 test`` for inference over the test set. + +Compiling a Tuner Binary +------------------------ + +The previous binary is used for inference purpose. +To use the autotuner, we need a slightly different binary that can talk with the tuner. +The following code is almost identical to the last code block, +but it adds `target="hpvm_tensor_inspect"` to `ModelExporter`, +to require an autotuner binary. +It also doesn't define a `conf_file`. + +.. code-block:: python + + from torch2hpvm import ModelExporter + + tuner_output_dir = Path("./alexnet2_cifar10_tuner") + tuner_build_dir = tuner_output_dir / "build" + tuner_binary = tuner_build_dir / "alexnet2_cifar10" + exporter = ModelExporter(model, tuneset, testset, tuner_output_dir, target="hpvm_tensor_inspect") + exporter.generate(batch_size=500).compile(tuner_binary, tuner_build_dir) + +This binary is generated at ``alexnet2_cifar10_tuner/build/alexnet2_cifar10``. +It waits for autotuner signal and doesn't run on its own, so don't run it by yourself. +Instead, import and use the tuner `predtuner`: + +.. code-block:: python + + from predtuner import PipedBinaryApp, config_pylogger + + # Set up logger to put log file in /tmp + msg_logger = config_pylogger(output_dir="/tmp", verbose=True) + + metadata_file = output_dir / exporter.metadata_file_name + # Create a `PipedBinaryApp` that communicates with HPVM bin. + # "TestHPVMApp" is an identifier of this app (used in logging, etc.) and can be anything. + # Other arguments: + # base_dir: which directory to run binary in (default: the dir the binary is in) + # qos_relpath: the name of accuracy file generated by the binary. + # Defaults to "final_accuracy". For HPVM apps this shouldn't change. + # model_storage_folder: where to put saved P1/P2 models. + app = PipedBinaryApp( + "TestHPVMApp", + tuner_binary, + metadata_file, + # Where to serialize prediction models if they are used + # For example, if you use p1 (see below), this will leave you a + # tuner_results/vgg16_cifar10/p1.pkl + # which can be quickly reloaded the next time you do tuning with + model_storage_folder="tuner_results/vgg16_cifar10", + ) + tuner = app.get_tuner() + tuner.tune( + max_iter=1000, # Number of iterations in tuning. In practice, use at least 5000, or 10000. + qos_tuner_threshold=3.0, # QoS threshold to guide tuner into + qos_keep_threshold=3.0, # QoS threshold for which we actually keep the configurations + is_threshold_relative=True, # Thresholds are relative to baseline -- baseline_acc - 3.0 + take_best_n=50, # Take the best 50 configurations, + cost_model="cost_linear", # Use linear performance predictor + qos_model="qos_p1", # Use P1 QoS predictor + ) + fig = tuner.plot_configs(show_qos_loss=True) + fig.savefig("configs.png", dpi=300) + app.dump_hpvm_configs(tuner.best_configs, "hpvm_confs.txt") + +* + *Note* that the performance shown here is estimated. + The next section talks about profiling (on a different machine), + which shows the real performance. + +* Arguments `cost_model` and `qos_model` controls the models used in tuning. + No models are used when the argument is omitted. + For example, you can do an empirical tuning run by removing `qos_model="qos_p1"`. + +* `cost_model="cost_linear"` estimates the performance of a configuration + using the FLOPs of each operator and the FLOPs reduction of each approximation. + If you are tuning on the end device that you wish to run the inference on, (which is a rare case), + then removing this argument will make the tuner use real performance instead. + In that case, you may skip the profiling step. + +This tuning process should take a few minutes to half an hour, +depending on your GPU performance. +After the tuning finishes, the tuner will + +* generate a figure showing the performance-accuracy tradeoff, at ``./configs.png``, and +* save the HPVM config format (write-only) at ``./hpvm_confs.txt``. + +It is also possible to save the configuration in other formats +(see the :doc:`predtuner documentation <components/predtuner>`). + +Profiling the Configurations +---------------------------- + +We will use `hpvm_profiler`, another Python package for profiling the ``./hpvm_confs.txt`` +we obtained in the tuning step. + +* The profiler uses the *plain* binary generated in the beginning (its path is `target_binary`) + instead of the tuner binary. + +* + *Note* that you may want to run this profiling step on the edge device + where the performance gain is desired. + As the compiled binary is usually not compatible across architectures, + you need to install HPVM on the edge device and recompile the model. + +* + *Also note* that currently, + the approximation implementations in the tensor runtime are tuned for Jetson TX2, + and speedup may be less for other architectures. + +.. code-block:: python + + from hpvm_profiler import profile_configs, plot_hpvm_configs + + # Set `target_binary` to the path of the plain binary. + target_binary = "./alexnet2_cifar10/build/alexnet2_cifar10" + # Set `config_file` to the config file produced in tuning, such as "hpvm_confs.txt". + config_file = "hpvm_confs.txt" + out_config_file = "hpvm_confs_profiled.txt" + profile_configs(target_binary, config_file, out_config_file) + plot_hpvm_configs(out_config_file, "configs_profiled.png") + +``hpvm_confs_profiled.txt`` contains the profiled configurations in HPVM format, +while ``configs_profiled.png`` shows the final performance-accuracy tradeoff curve. + +An example of ``configs_profiled.png`` looks like this (proportion of your image may be different): + +.. image:: tradeoff-curves/alexnet2_cifar10.png + +----------------------- + +This concludes the whole workflow of HPVM. +For more detailed usages, check out the documentation of each component listed +:doc:`here <components/index>`. diff --git a/hpvm/docs/tradeoff-curves/alexnet2_cifar10.png b/hpvm/docs/tradeoff-curves/alexnet2_cifar10.png new file mode 100644 index 0000000000000000000000000000000000000000..754f03248b402e7d55661364311cf9a08763acc0 GIT binary patch literal 25556 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	_hwr8PGzD3oKoo_@!9)T05Ojq7pqkVEbT!{vA$b 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 literal 0 HcmV?d00001 -- GitLab