From 353174f00df77ef19be0c99a39ded0b15ac2ec12 Mon Sep 17 00:00:00 2001 From: James Reed Date: Wed, 10 Jun 2020 17:24:12 -0700 Subject: [PATCH 01/33] Add TorchScript fork/join tutorial --- .../cropped/TorchScript-Parallelism.jpg | Bin 0 -> 233755 bytes advanced_source/torch-script-parallelism.rst | 278 ++++++++++++++++++ index.rst | 8 + 3 files changed, 286 insertions(+) create mode 100644 _static/img/thumbnails/cropped/TorchScript-Parallelism.jpg create mode 100644 advanced_source/torch-script-parallelism.rst diff --git a/_static/img/thumbnails/cropped/TorchScript-Parallelism.jpg b/_static/img/thumbnails/cropped/TorchScript-Parallelism.jpg new file mode 100644 index 0000000000000000000000000000000000000000..237990a046033b8870b119a3ebdf5dd6ae5d4b8f GIT binary patch literal 233755 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

lm+(;=FNK{P4K&=v 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 E+0M1|m;e9( literal 0 HcmV?d00001 diff --git a/advanced_source/torch-script-parallelism.rst b/advanced_source/torch-script-parallelism.rst new file mode 100644 index 00000000000..f2777fad563 --- /dev/null +++ b/advanced_source/torch-script-parallelism.rst @@ -0,0 +1,278 @@ +Dynamic Parallelism in TorchScript +================================== + +In this tutorial, we introduce the syntax for doing *dynamic inter-op parallelism* +in TorchScript. This parallelism has the following properties: + +* dynamic - The number of parallel tasks created and their workload can depend on the control flow of the program. +* inter-op - The parallelism is concerned with running TorchScript program fragments in parallel. This is distinct from *intra-op parallelism*, which is concerned with splitting up individual operators and running subsets of the operator's work in parallel. +Basic Syntax +------------ + +The two important APIs for dynamic parallelism are: + +* ``torch.jit.fork(fn : Callable[..., T], *args, **kwargs) -> torch.jit.Future[T]`` +* ``torch.jit.wait(fut : torch.jit.Future[T]) -> T`` + +A good way to demonstrate how these work is by way of an example: + +.. code-block:: python + + import torch + + def foo(x): + return torch.neg(x) + + @torch.jit.script + def example(x): + # Call `foo` using parallelism: + # First, we "fork" off a task. This task will run `foo` with argument `x` + future = torch.jit.fork(foo, x) + + # Call `foo` normally + x_normal = foo(x) + + # Second, we "wait" on the task. Since the task may be running in + # parallel, we have to "wait" for its result to become available. + # Notice that by having lines of code between the "fork()" and "wait()" + # call for a given Future, we can overlap computations so that they + # run in parallel. + x_parallel = torch.jit.wait(future) + + return x_normal, x_parallel + + print(example(torch.ones(1))) # (-1., -1.) + + +``fork()`` takes the callable ``fn`` and arguments to that callable ``args`` +and ``kwargs`` and creates an asynchronous task for the execution of ``fn``. +``fn`` can be a function, method, or Module instance. ``fork()`` returns a +reference to the value of the result of this execution, called a ``Future``. +Because ``fork`` returns immediately after creating the async task, ``fn`` may +not have been executed by the time the line of code after the ``fork()`` call +is executed. Thus, ``wait()`` is used to wait for the async task to complete +and return the value. + +These constructs can be used to overlap the execution of statements within a +function (shown in the worked example section) or be composed with other language +constructs like loops: + +.. code-block:: python + + import torch + from typing import List + + def foo(x): + return torch.neg(x) + + @torch.jit.script + def example(x): + futures : List[torch.jit.Future[torch.Tensor]] = [] + for _ in range(100): + futures.append(torch.jit.fork(foo, x)) + + results = [] + for future in futures: + results.append(torch.jit.wait(future)) + + return torch.sum(torch.stack(results)) + + print(example(torch.ones([]))) + +.. note:: + + When we initialized an empty list of Futures, we needed to add an explicit + type annotation to ``futures``. In TorchScript, empty containers default + to assuming they contain Tensor values, so we annotate the list constructor + # as being of type ``List[torch.jit.Future[torch.Tensor]]`` + +This example uses ``fork()`` to launch 100 instances of the function ``foo``, +waits on the 100 tasks to complete, then sums the results, returning ``-100.0``. + +Applied Example: Ensemble of Bidirectional LSTMs +------------------------------------------------ + +Let's try to apply parallelism to a more realistic example and see what sort +of performance we can get out of it. First, let's define the baseline model: an +ensemble of bidirectional LSTM layers. + +.. code-block:: python + + import torch, time + + # In RNN parlance, the dimensions we care about are: + # # of time-steps (T) + # Batch size (B) + # Hidden size/number of "channels" (C) + T, B, C = 50, 50, 1024 + + # A module that defines a single "bidirectional LSTM". This is simply two + # LSTMs applied to the same sequence, but one in reverse + class BidirectionalRecurrentLSTM(torch.nn.Module): + def __init__(self): + super().__init__() + self.cell_f = torch.nn.LSTM(input_size=C, hidden_size=C) + self.cell_b = torch.nn.LSTM(input_size=C, hidden_size=C) + + def forward(self, x : torch.Tensor) -> torch.Tensor: + # Forward layer + output_f, _ = self.cell_f(x) + + # Backward layer. Flip input in the time dimension (dim 0), apply the + # layer, then flip the outputs in the time dimension + x_rev = torch.flip(x, dims=[0]) + output_b, _ = self.cell_b(torch.flip(x, dims=[0])) + output_b_rev = torch.flip(output_b, dims=[0]) + + return torch.cat((output_f, output_b_rev), dim=2) + + + # An "ensemble" of `BidirectionalRecurrentLSTM` modules. The modules in the + # ensemble are run one-by-one on the same input then their results are + # stacked and summed together, returning the combined result. + class LSTMEnsemble(torch.nn.Module): + def __init__(self, n_models): + super().__init__() + self.n_models = n_models + self.models = torch.nn.ModuleList([ + BidirectionalRecurrentLSTM() for _ in range(self.n_models)]) + + def forward(self, x : torch.Tensor) -> torch.Tensor: + results = [] + for model in self.models: + results.append(model(x)) + return torch.stack(results).sum(dim=0) + + # For a head-to-head comparison to what we're going to do with fork/wait, let's + # instantiate the model and compile it with TorchScript + ens = torch.jit.script(LSTMEnsemble(n_models=4)) + + # Normally you would pull this input out of an embedding table, but for the + # purpose of this demo let's just use random data. + x = torch.rand(T, B, C) + + # Let's run the model once to warm up things like the memory allocator + ens(x) + + x = torch.rand(T, B, C) + + # Let's see how fast it runs! + s = time.time() + ens(x) + print('Inference took', time.time() - s, ' seconds') + +On my machine, this network runs in ``2.05`` seconds. We can do a lot better! + +Parallelizing Forward and Backward Layers +----------------------------------------- + +A very simple thing we can do is parallelize the forward and backward layers +within ``BidirectionalRecurrentLSTM``. For this, the structure of the computation +is static, so we don't actually even need any loops. Let's rewrite the ``forward`` +method of ``BidirectionalRecurrentLSTM`` like so: + +.. code-block:: python + + def forward(self, x : torch.Tensor) -> torch.Tensor: + # Forward layer - fork() so this can run in parallel to the backward + # layer + future_f = torch.jit.fork(self.cell_f, x) + + # Backward layer. Flip input in the time dimension (dim 0), apply the + # layer, then flip the outputs in the time dimension + x_rev = torch.flip(x, dims=[0]) + output_b, _ = self.cell_b(torch.flip(x, dims=[0])) + output_b_rev = torch.flip(output_b, dims=[0]) + + # Retrieve the output from the forward layer. Note this needs to happen + # *after* the stuff we want to parallelize with + output_f, _ = torch.jit.wait(future_f) + + return torch.cat((output_f, output_b_rev), dim=2) + +In this example, ``forward()`` delegates execution of ``cell_f`` to another thread, +while it continues to execute ``cell_b``. This causes the execution of both the +cells to be overlapped with each other. + +Running the script again with this simple modification yields a runtime of +``1.71`` seconds for an improvement of ``17%``! + +Aside: Visualizing Parallelism +------------------------------ + +We're not done optimizing our model but it's worth introducing the tooling we +have for visualizing performance. One important tool is the `PyTorch profiler `_. + +Let's use the profiler along with the Chrome trace export functionality to +visualize the performance of our parallelized model: + +.. code-block:: python + with torch.autograd.profiler.profile() as prof: + ens(x) + prof.export_chrome_trace('parallel.json') + +This snippet of code will write out a file named ``parallel.json``. If you +navigate Google Chrome to ``chrome://tracing``, click the ``Load`` button, and +load in that JSON file, you should see a timeline like the following: + +.. image:: https://i.imgur.com/rm5hdG9.png + +The horizontal axis of the timeline represents time and the vertical axis +represents threads of execution. As we can see, we are running two ``lstm`` +instances at a time. This is the result of our hard work parallelizing the +bidirectional layers! + +Parallelizing Models in the Ensemble +------------------------------------ + +You may have noticed that there is a further parallelization opportunity in our +code: we can also run the models contained in ``LSTMEnsemble`` in parallel with +each other. The way to do that is simple enough, this is how we should change +the ``forward`` method of ``LSTMEnsemble``: + +.. code-block:: python + + def forward(self, x : torch.Tensor) -> torch.Tensor: + # Launch tasks for each model + futures : List[torch.jit.Future[torch.Tensor]] = [] + for model in self.models: + futures.append(torch.jit.fork(model, x)) + + # Collect the results from the launched tasks + results : List[torch.Tensor] = [] + for future in futures: + results.append(torch.jit.wait(future)) + + return torch.stack(results).sum(dim=0) + +Or, if you value brevity, we can use list comprehensions: + +.. code-block:: python + + def forward(self, x : torch.Tensor) -> torch.Tensor: + futures = [torch.jit.fork(model, x) for model in self.models] + results = [torch.jit.wait(fut) for fut in futures] + return torch.stack(results).sum(dim=0) + +Like described in the intro, we've used loops to fork off tasks for each of the +models in our ensemble. We've then used another loop to wait for all of the +tasks to be completed. This provides even more overlap of computation. + +With this small update, the script runs in ``1.4`` seconds, for a total speedup +of ``32%``! Pretty good for two lines of code. + +We can also use the Chrome tracer again to see where's going on: + +.. image:: https://i.imgur.com/kA0gyQm.png + +We can now see that all ``LSTM`` instances are being run fully in parallel. + +Conclusion +---------- + +In this tutorial, we learned about ``fork()`` and ``wait()``, the basic APIs +for doing dynamic, inter-op parallelism in TorchScript. We saw a few typical +usage patterns for using these functions to parallelize the execution of +functions, methods, or ``Modules`` in TorchScript code. Finally, we worked through +an example of optimizing a model using this technique and explored the performance +measurement and visualization tooling available in PyTorch. diff --git a/index.rst b/index.rst index 81113c919ed..687cb7e526c 100644 --- a/index.rst +++ b/index.rst @@ -244,6 +244,13 @@ Welcome to PyTorch Tutorials :link: advanced/torch_script_custom_classes.html :tags: Frontend-APIs,TorchScript,C++ +.. customcarditem:: + :header: Dynamic Parallelism in TorchScript + :card_description: This tutorial introduces the syntax for doing *dynamic inter-op parallelism* in TorchScript. + :image: _static/img/thumbnails/cropped/TorchScript-Parallelism.jpg + :link: advanced/torch-script-parallelism.html + :tags: Frontend-APIs,TorchScript,C++ + .. customcarditem:: :header: Autograd in C++ Frontend :card_description: The autograd package helps build flexible and dynamic nerural netorks. In this tutorial, exploreseveral examples of doing autograd in PyTorch C++ frontend @@ -471,6 +478,7 @@ Additional Resources advanced/cpp_extension advanced/torch_script_custom_ops advanced/torch_script_custom_classes + advanced/torch-script-parallelism advanced/cpp_autograd .. toctree:: From 4e97bcebf56dab687acd5e25d6f4790b0c82a6a9 Mon Sep 17 00:00:00 2001 From: James Reed Date: Fri, 19 Jun 2020 13:26:13 -0700 Subject: [PATCH 02/33] Add note about zipfile format in serialization tutorial --- beginner_source/saving_loading_models.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/beginner_source/saving_loading_models.py b/beginner_source/saving_loading_models.py index 73d3554ceab..74a8edc892d 100644 --- a/beginner_source/saving_loading_models.py +++ b/beginner_source/saving_loading_models.py @@ -156,6 +156,12 @@ # model.load_state_dict(torch.load(PATH)) # model.eval() # +# .. note:: +# The 1.6 release of PyTorch switched ``torch.save`` to use a new +# zipfile-based file format. ``torch.load`` still retains the ability to +# load files in the old format. If for any reason you want ``torch.save`` +# to use the old format, pass the kwarg ``_use_new_zipfile_serialization=False``. +# # When saving a model for inference, it is only necessary to save the # trained model’s learned parameters. Saving the model’s *state_dict* with # the ``torch.save()`` function will give you the most flexibility for From 823ddcaee62bd4ce521d2847a9c2f14936e95723 Mon Sep 17 00:00:00 2001 From: ilia-cher <30845429+ilia-cher@users.noreply.github.com> Date: Tue, 23 Jun 2020 12:57:02 -0700 Subject: [PATCH 03/33] Profiler recipe (#1019) * Profiler recipe Summary: Adding a recipe for profiler Test Plan: make html-noplot --- _static/img/thumbnails/cropped/profiler.png | Bin 0 -> 35776 bytes _static/img/trace_img.png | Bin 0 -> 136899 bytes recipes_source/recipes/profiler.py | 215 ++++++++++++++++++++ recipes_source/recipes_index.rst | 8 + 4 files changed, 223 insertions(+) create mode 100644 _static/img/thumbnails/cropped/profiler.png create mode 100644 _static/img/trace_img.png create mode 100644 recipes_source/recipes/profiler.py diff --git a/_static/img/thumbnails/cropped/profiler.png b/_static/img/thumbnails/cropped/profiler.png new file mode 100644 index 0000000000000000000000000000000000000000..426a14d98f5f7fbbf695626658ad1946fe4ef63e GIT binary patch literal 35776 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 literal 0 HcmV?d00001 diff --git a/_static/img/trace_img.png b/_static/img/trace_img.png new file mode 100644 index 0000000000000000000000000000000000000000..172aeb1bef0f11f0433f814ec76e196d073d9816 GIT binary patch literal 136899 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
@9H6k`VFsYKj*;I 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 tutorial +# diff --git a/recipes_source/recipes_index.rst b/recipes_source/recipes_index.rst index 9dc3f4db2dd..1979319717c 100644 --- a/recipes_source/recipes_index.rst +++ b/recipes_source/recipes_index.rst @@ -95,6 +95,13 @@ Recipes are bite-sized bite-sized, actionable examples of how to use specific Py :link: ../recipes/recipes/zeroing_out_gradients.html :tags: Basics +.. customcarditem:: + :header: PyTorch Profiler + :card_description: Learn how to use PyTorch's profiler to measure operators time and memory consumption + :image: ../_static/img/thumbnails/cropped/profiler.png + :link: ../recipes/recipes/profiler.html + :tags: Basics + .. Customization .. customcarditem:: @@ -174,6 +181,7 @@ Recipes are bite-sized bite-sized, actionable examples of how to use specific Py /recipes/recipes/warmstarting_model_using_parameters_from_a_different_model /recipes/recipes/save_load_across_devices /recipes/recipes/zeroing_out_gradients + /recipes/recipes/profiler /recipes/recipes/custom_dataset_transforms_loader /recipes/recipes/Captum_Recipe /recipes/recipes/tensorboard_with_pytorch From 910dfa90f8df79039fda6ae01b30916340563a78 Mon Sep 17 00:00:00 2001 From: Ivan Kobzarev Date: Tue, 16 Jun 2020 14:24:48 -0700 Subject: [PATCH 04/33] [mobile] Mobile Perf Recipe --- recipes_source/mobile_perf.rst | 223 ++++++++++++++++++++++++++++++ recipes_source/recipes/README.txt | 4 + recipes_source/recipes_index.rst | 7 + 3 files changed, 234 insertions(+) create mode 100644 recipes_source/mobile_perf.rst diff --git a/recipes_source/mobile_perf.rst b/recipes_source/mobile_perf.rst new file mode 100644 index 00000000000..d0fbf3cb6fb --- /dev/null +++ b/recipes_source/mobile_perf.rst @@ -0,0 +1,223 @@ +Pytorch Mobile Performance Recipes +================================== + +Introduction +------------ +Performance (aka latency) is crucial to most, if not all, +applications and use-cases of ML model inference on mobile devices. + +Today, PyTorch executes the models on the CPU backend pending availability +of other hardware backends such as GPU, DSP, and NPU. + + +Model preparation +----------------- + +Next recipes you can take (offline) while preparing the model +to have an optimized model that will probably have shorter execution time +(higher performance, lower latency) on the mobile device. + + +Setup +_____ +First we need to installed pytorch using conda or pip with version at least 1.5.0. + +:: + + conda install pytorch torchvision -c pytorch + +or + +:: + + pip install torch torchvision + +Code your model: + +:: + + import torch + from torch.utils.mobile_optimizer import optimize_for_mobile + + class AnnotatedConvBnReLUModel(torch.nn.Module): + def __init__(self): + super(AnnotatedConvBnReLUModel, self).__init__() + self.conv = torch.nn.Conv2d(3, 5, 3, bias=False).to(dtype=torch.float) + self.bn = torch.nn.BatchNorm2d(5).to(dtype=torch.float) + self.relu = torch.nn.ReLU(inplace=True) + self.quant = torch.quantization.QuantStub() + self.dequant = torch.quantization.DeQuantStub() + + def forward(self, x): + x = self.quant(x) + x = self.conv(x) + x = self.bn(x) + x = self.relu(x) + x = self.dequant(x) + return x + + model = AnnotatedConvBnReLUModel() + + +``torch.quantization.QuantStub`` and ``torch.quantization.DeQuantStub()`` are no-op stubs, which will be used for quantization step. + + +1. Fuse operators using ``torch.quantization.fuse_modules`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Do not be confused that fuse_modules is in the quantization package. +It works for all ``torcn.nn.Module``. + +``torch.quantization.fuse_modules`` fuses a list of modules into a single module. +It fuses only the following sequence of modules: + +- Convolution, Batch normalization +- Convolution, Batch normalization, Relu +- Convolution, Relu +- Linear, Relu + +This script will fuse Convolution, Batch Normalization and Relu in previously declared model. + +:: + + torch.quantization.fuse_modules(model, [['conv', 'bn', 'relu']], inplace=True) + + +2. Quantize your model +~~~~~~~~~~~~~~~~~~~~~~ + +You can find more about PyTorch quantization in +`the dedicated tutorial `_. + +Quantization of the model not only moves computation to int8, +but also reduces the size of your model on a disk. +That size reduction helps to reduce disk read operations during the first load of the model and decreases the amount of RAM. +Both of those resources can be crucial for the performance of mobile applications. +This code does quantization, using stub for model calibration function, you can find more about it `here `__. + +:: + + model.qconfig = torch.quantization.get_default_qconfig('qnnpack') + torch.quantization.prepare(model, inplace=True) + # Calibrate your model + def calibrate(model, calibration_data): + # Your calibration code here + return + calibrate(model, []) + torch.quantization.convert(model, inplace=True) + + + +3. Use torch.utils.mobile_optimizer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Torch mobile_optimizer package does several optimizations with the scripted model, +which will help to conv2d and linear operations. +It pre-packs model weights in an optimized format and fuses ops above with relu +if it is the next operation. + +First we script the result model from previous step: + +:: + + torchscript_model = torch.jit.script(model) + +Next we call ``optimize_for_mobile`` and save model on the disk. + +:: + + torchscript_model_optimized = optimize_for_mobile(torchscript_model) + torch.jit.save(torchscript_model_optimized, "model.pt") + + +4. Android. Reusing tensors for forward. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This recipe is Android only. +Memory is a critical resource for android performance, especially on old devices. +Tensors can need a significant amount of memory. +For example, standard computer vision tensor contains 1*3*224*224 elements, +assuming that data type is float and will need 588Kb of memory. + +:: + + FloatBuffer buffer = Tensor.allocateFloatBuffer(1*3*224*224); + Tensor tensor = Tensor.fromBlob(buffer, new long[]{1, 3, 224, 224}); + + +Here we allocate native memory as ``java.nio.FloatBuffer`` and creating ``org.pytorch.Tensor`` which storage will be pointing to the memory of the allocated buffer. + +For most of the use cases, we do not do model forward only once, repeating it with some frequency or as fast as possible. + +If we are doing new memory allocation for every module forward - that will be suboptimal. +Instead of this, we can reuse the same memory that we allocated on the previous step, fill it with new data, and run module forward again on the same tensor object. + +You can check how it looks in code in `pytorch android application example `_. + +:: + + protected AnalysisResult analyzeImage(ImageProxy image, int rotationDegrees) { + if (mModule == null) { + mModule = Module.load(moduleFileAbsoluteFilePath); + mInputTensorBuffer = + Tensor.allocateFloatBuffer(3 * 224 * 224); + mInputTensor = Tensor.fromBlob(mInputTensorBuffer, new long[]{1, 3, 224, 224}); + } + + TensorImageUtils.imageYUV420CenterCropToFloatBuffer( + image.getImage(), rotationDegrees, + 224, 224, + TensorImageUtils.TORCHVISION_NORM_MEAN_RGB, + TensorImageUtils.TORCHVISION_NORM_STD_RGB, + mInputTensorBuffer, 0); + + Tensor outputTensor = mModule.forward(IValue.from(mInputTensor)).toTensor(); + } + +Member fields ``mModule``, ``mInputTensorBuffer`` and ``mInputTensor`` are initialized only once +and buffer is refilled using ``org.pytorch.torchvision.TensorImageUtils.imageYUV420CenterCropToFloatBuffer``. + +Benchmarking +------------ + +The best way to benchmark (to check if optimizations helped your use case) - to measure your particular use case that you want to optimize, as performance behavior can vary in different environments. + +PyTorch distribution provides a way to benchmark naked binary that runs the model forward, +this approach can give more stable measurements rather than testing inside the application. + + +Android +------- + +For this you first need to build benchmark binary: + +:: + + + rm -rf build_android + BUILD_PYTORCH_MOBILE=1 ANDROID_ABI=arm64-v8a ./scripts/build_android.sh -DBUILD_BINARY=ON + +You should have arm64 binary at: ``build_android/bin/speed_benchmark_torch``. +This binary takes ``--model=``, ``--input_dim="1,3,224,224"`` as dimension information for the input and ``--input_type="float"`` as the type of the input as arguments. + +Once you have your android device connected, +push speedbenchark_torch binary and your model to the phone: + +:: + + adb push /data/local/tmp + adb push /data/local/tmp + + +Now we are ready to benchmark your model: + +:: + + adb shell "/data/local/tmp/speed_benchmark_torch --model="/data/local/tmp/model.pt" --input_dims="1,3,224,224" --input_type="float" + ----- output ----- + Starting benchmark. + Running warmup runs. + Main runs. + Main run finished. Microseconds per iter: 121318. Iters per second: 8.24281 + + diff --git a/recipes_source/recipes/README.txt b/recipes_source/recipes/README.txt index 30573e2da86..f93ee92c2c6 100644 --- a/recipes_source/recipes/README.txt +++ b/recipes_source/recipes/README.txt @@ -52,3 +52,7 @@ PyTorch Recipes 13. zeroing_out_gradients.py Zeroing out gradients https://pytorch.org/tutorials/recipes/recipes/zeroing_out_gradients.html + +14. mobile_perf.py + PyTorch Mobile Performance Recipes + https://pytorch.org/tutorials/recipes/mobile_perf.html diff --git a/recipes_source/recipes_index.rst b/recipes_source/recipes_index.rst index 9dc3f4db2dd..da2f5202dcf 100644 --- a/recipes_source/recipes_index.rst +++ b/recipes_source/recipes_index.rst @@ -146,6 +146,13 @@ Recipes are bite-sized bite-sized, actionable examples of how to use specific Py :link: ../recipes/deployment_with_flask.html :tags: Production,TorchScript +.. customcarditem:: + :header: PyTorch Mobile Performance Recipes + :card_description: List of recipes for performance optimizations for using PyTorch on Mobile. + :image: ../_static/img/thumbnails/cropped/zeroing-out-gradients.PNG + :link: ../recipes/mobile_perf.html + :tags: Mobile,Model-Optimization + .. End of tutorial card section From 02aef1dcefaa9d81ed65133dcb883d3da98b6009 Mon Sep 17 00:00:00 2001 From: Jessica Lin Date: Mon, 29 Jun 2020 17:41:36 -0700 Subject: [PATCH 05/33] Minor syntax edits to mobile perf recipe --- recipes/deployment_with_flask.rst | 284 +++++++++ recipes/index.rst | 391 +++++++++++++ recipes/mobile_perf.rst | 226 ++++++++ recipes/recipes/Captum_Recipe.ipynb | 160 ++++++ recipes/recipes/Captum_Recipe.py | 189 ++++++ recipes/recipes/Captum_Recipe.rst | 220 +++++++ .../custom_dataset_transforms_loader.ipynb | 261 +++++++++ .../custom_dataset_transforms_loader.py | 480 ++++++++++++++++ .../custom_dataset_transforms_loader.rst | 539 ++++++++++++++++++ .../recipes/defining_a_neural_network.ipynb | 122 ++++ recipes/recipes/defining_a_neural_network.py | 183 ++++++ recipes/recipes/defining_a_neural_network.rst | 234 ++++++++ recipes/recipes/dynamic_quantization.ipynb | 133 +++++ recipes/recipes/dynamic_quantization.py | 306 ++++++++++ recipes/recipes/dynamic_quantization.rst | 364 ++++++++++++ .../thumb/sphx_glr_Captum_Recipe_thumb.png | Bin 0 -> 26786 bytes ...custom_dataset_transforms_loader_thumb.png | Bin 0 -> 26786 bytes ...hx_glr_defining_a_neural_network_thumb.png | Bin 0 -> 26786 bytes .../sphx_glr_dynamic_quantization_thumb.png | Bin 0 -> 26786 bytes .../sphx_glr_loading_data_recipe_thumb.png | Bin 0 -> 26786 bytes .../images/thumb/sphx_glr_profiler_thumb.png | Bin 0 -> 26786 bytes ...phx_glr_save_load_across_devices_thumb.png | Bin 0 -> 26786 bytes ...and_loading_a_general_checkpoint_thumb.png | Bin 0 -> 26786 bytes ...and_loading_models_for_inference_thumb.png | Bin 0 -> 26786 bytes ...ving_multiple_models_in_one_file_thumb.png | Bin 0 -> 26786 bytes ...phx_glr_tensorboard_with_pytorch_thumb.png | Bin 0 -> 26786 bytes ...arameters_from_a_different_model_thumb.png | Bin 0 -> 26786 bytes .../sphx_glr_what_is_state_dict_thumb.png | Bin 0 -> 26786 bytes .../sphx_glr_zeroing_out_gradients_thumb.png | Bin 0 -> 26786 bytes recipes/recipes/loading_data_recipe.ipynb | 140 +++++ recipes/recipes/loading_data_recipe.py | 171 ++++++ recipes/recipes/loading_data_recipe.rst | 221 +++++++ recipes/recipes/profiler.ipynb | 201 +++++++ recipes/recipes/profiler.py | 215 +++++++ recipes/recipes/profiler.rst | 281 +++++++++ .../recipes/save_load_across_devices.ipynb | 158 +++++ recipes/recipes/save_load_across_devices.py | 190 ++++++ recipes/recipes/save_load_across_devices.rst | 250 ++++++++ ...ing_and_loading_a_general_checkpoint.ipynb | 140 +++++ ...saving_and_loading_a_general_checkpoint.py | 162 ++++++ ...aving_and_loading_a_general_checkpoint.rst | 216 +++++++ ...ing_and_loading_models_for_inference.ipynb | 140 +++++ ...saving_and_loading_models_for_inference.py | 168 ++++++ ...aving_and_loading_models_for_inference.rst | 223 ++++++++ .../saving_multiple_models_in_one_file.ipynb | 140 +++++ .../saving_multiple_models_in_one_file.py | 162 ++++++ .../saving_multiple_models_in_one_file.rst | 218 +++++++ .../recipes/tensorboard_with_pytorch.ipynb | 125 ++++ recipes/recipes/tensorboard_with_pytorch.py | 168 ++++++ recipes/recipes/tensorboard_with_pytorch.rst | 212 +++++++ ...ng_parameters_from_a_different_model.ipynb | 122 ++++ ...using_parameters_from_a_different_model.py | 142 +++++ ...sing_parameters_from_a_different_model.rst | 194 +++++++ recipes/recipes/what_is_state_dict.ipynb | 122 ++++ recipes/recipes/what_is_state_dict.py | 132 +++++ recipes/recipes/what_is_state_dict.rst | 183 ++++++ recipes/recipes/zeroing_out_gradients.ipynb | 140 +++++ recipes/recipes/zeroing_out_gradients.py | 193 +++++++ recipes/recipes/zeroing_out_gradients.rst | 248 ++++++++ recipes/recipes_index.rst | 197 +++++++ recipes/torchscript_inference.rst | 197 +++++++ recipes_source/mobile_perf.rst | 29 +- 62 files changed, 9879 insertions(+), 13 deletions(-) create mode 100644 recipes/deployment_with_flask.rst create mode 100644 recipes/index.rst create mode 100644 recipes/mobile_perf.rst create mode 100644 recipes/recipes/Captum_Recipe.ipynb create mode 100644 recipes/recipes/Captum_Recipe.py create mode 100644 recipes/recipes/Captum_Recipe.rst create mode 100644 recipes/recipes/custom_dataset_transforms_loader.ipynb create mode 100644 recipes/recipes/custom_dataset_transforms_loader.py create mode 100644 recipes/recipes/custom_dataset_transforms_loader.rst create mode 100644 recipes/recipes/defining_a_neural_network.ipynb create mode 100644 recipes/recipes/defining_a_neural_network.py create mode 100644 recipes/recipes/defining_a_neural_network.rst create mode 100644 recipes/recipes/dynamic_quantization.ipynb create mode 100644 recipes/recipes/dynamic_quantization.py create mode 100644 recipes/recipes/dynamic_quantization.rst create mode 100644 recipes/recipes/images/thumb/sphx_glr_Captum_Recipe_thumb.png create mode 100644 recipes/recipes/images/thumb/sphx_glr_custom_dataset_transforms_loader_thumb.png create mode 100644 recipes/recipes/images/thumb/sphx_glr_defining_a_neural_network_thumb.png create mode 100644 recipes/recipes/images/thumb/sphx_glr_dynamic_quantization_thumb.png create mode 100644 recipes/recipes/images/thumb/sphx_glr_loading_data_recipe_thumb.png create mode 100644 recipes/recipes/images/thumb/sphx_glr_profiler_thumb.png create mode 100644 recipes/recipes/images/thumb/sphx_glr_save_load_across_devices_thumb.png create mode 100644 recipes/recipes/images/thumb/sphx_glr_saving_and_loading_a_general_checkpoint_thumb.png create mode 100644 recipes/recipes/images/thumb/sphx_glr_saving_and_loading_models_for_inference_thumb.png create mode 100644 recipes/recipes/images/thumb/sphx_glr_saving_multiple_models_in_one_file_thumb.png create mode 100644 recipes/recipes/images/thumb/sphx_glr_tensorboard_with_pytorch_thumb.png create mode 100644 recipes/recipes/images/thumb/sphx_glr_warmstarting_model_using_parameters_from_a_different_model_thumb.png create mode 100644 recipes/recipes/images/thumb/sphx_glr_what_is_state_dict_thumb.png create mode 100644 recipes/recipes/images/thumb/sphx_glr_zeroing_out_gradients_thumb.png create mode 100644 recipes/recipes/loading_data_recipe.ipynb create mode 100644 recipes/recipes/loading_data_recipe.py create mode 100644 recipes/recipes/loading_data_recipe.rst create mode 100644 recipes/recipes/profiler.ipynb create mode 100644 recipes/recipes/profiler.py create mode 100644 recipes/recipes/profiler.rst create mode 100644 recipes/recipes/save_load_across_devices.ipynb create mode 100644 recipes/recipes/save_load_across_devices.py create mode 100644 recipes/recipes/save_load_across_devices.rst create mode 100644 recipes/recipes/saving_and_loading_a_general_checkpoint.ipynb create mode 100644 recipes/recipes/saving_and_loading_a_general_checkpoint.py create mode 100644 recipes/recipes/saving_and_loading_a_general_checkpoint.rst create mode 100644 recipes/recipes/saving_and_loading_models_for_inference.ipynb create mode 100644 recipes/recipes/saving_and_loading_models_for_inference.py create mode 100644 recipes/recipes/saving_and_loading_models_for_inference.rst create mode 100644 recipes/recipes/saving_multiple_models_in_one_file.ipynb create mode 100644 recipes/recipes/saving_multiple_models_in_one_file.py create mode 100644 recipes/recipes/saving_multiple_models_in_one_file.rst create mode 100644 recipes/recipes/tensorboard_with_pytorch.ipynb create mode 100644 recipes/recipes/tensorboard_with_pytorch.py create mode 100644 recipes/recipes/tensorboard_with_pytorch.rst create mode 100644 recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.ipynb create mode 100644 recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.py create mode 100644 recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.rst create mode 100644 recipes/recipes/what_is_state_dict.ipynb create mode 100644 recipes/recipes/what_is_state_dict.py create mode 100644 recipes/recipes/what_is_state_dict.rst create mode 100644 recipes/recipes/zeroing_out_gradients.ipynb create mode 100644 recipes/recipes/zeroing_out_gradients.py create mode 100644 recipes/recipes/zeroing_out_gradients.rst create mode 100644 recipes/recipes_index.rst create mode 100644 recipes/torchscript_inference.rst diff --git a/recipes/deployment_with_flask.rst b/recipes/deployment_with_flask.rst new file mode 100644 index 00000000000..0d1291c8d89 --- /dev/null +++ b/recipes/deployment_with_flask.rst @@ -0,0 +1,284 @@ +Deploying with Flask +==================== + +In this recipe, you will learn: + +- How to wrap your trained PyTorch model in a Flask container to expose + it via a web API +- How to translate incoming web requests into PyTorch tensors for your + model +- How to package your model’s output for an HTTP response + +Requirements +------------ + +You will need a Python 3 environment with the following packages (and +their dependencies) installed: + +- PyTorch 1.5 +- TorchVision 0.6.0 +- Flask 1.1 + +Optionally, to get some of the supporting files, you'll need git. + +The instructions for installing PyTorch and TorchVision are available at +`pytorch.org`_. Instructions for installing Flask are available on `the +Flask site`_. + +What is Flask? +-------------- + +Flask is a lightweight web server written in Python. It provides a +convenient way for you to quickly set up a web API for predictions from +your trained PyTorch model, either for direct use, or as a web service +within a larger system. + +Setup and Supporting Files +-------------------------- + +We're going to create a web service that takes in images, and maps them +to one of the 1000 classes of the ImageNet dataset. To do this, you'll +need an image file for testing. Optionally, you can also get a file that +will map the class index output by the model to a human-readable class +name. + +Option 1: To Get Both Files Quickly +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can pull both of the supporting files quickly by checking out the +TorchServe repository and copying them to your working folder. *(NB: +There is no dependency on TorchServe for this tutorial - it's just a +quick way to get the files.)* Issue the following commands from your +shell prompt: + +:: + + git clone https://github.com/pytorch/serve + cp serve/examples/image_classifier/kitten.jpg . + cp serve/examples/image_classifier/index_to_name.json . + +And you've got them! + +Option 2: Bring Your Own Image +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ``index_to_name.json`` file is optional in the Flask service below. +You can test your service with your own image - just make sure it's a +3-color JPEG. + +Building Your Flask Service +--------------------------- + +The full Python script for the Flask service is shown at the end of this +recipe; you can copy and paste that into your own ``app.py`` file. Below +we'll look at individual sections to make their functions clear. + +Imports +~~~~~~~ + +:: + + import torchvision.models as models + import torchvision.transforms as transforms + from PIL import Image + from flask import Flask, jsonify, request + +In order: + +- We'll be using a pre-trained DenseNet model from + ``torchvision.models`` +- ``torchvision.transforms`` contains tools for manipulating your image + data +- Pillow (``PIL``) is what we'll use to load the image file initially +- And of course we'll need classes from ``flask`` + +Pre-Processing +~~~~~~~~~~~~~~ + +:: + + def transform_image(infile): + input_transforms = [transforms.Resize(255), + transforms.CenterCrop(224), + transforms.ToTensor(), + transforms.Normalize([0.485, 0.456, 0.406], + [0.229, 0.224, 0.225])] + my_transforms = transforms.Compose(input_transforms) + image = Image.open(infile) + timg = my_transforms(image) + timg.unsqueeze_(0) + return timg + +The web request gave us an image file, but our model expects a PyTorch +tensor of shape (N, 3, 224, 224) where *N* is the number of items in the +input batch. (We will just have a batch size of 1.) The first thing we +do is compose a set of TorchVision transforms that resize and crop the +image, convert it to a tensor, then normalize the values in the tensor. +(For more information on this normalization, see the documentation for +``torchvision.models_``.) + +After that, we open the file and apply the transforms. The transforms +return a tensor of shape (3, 224, 224) - the 3 color channels of a +224x224 image. Because we need to make this single image a batch, we use +the ``unsqueeze_(0)`` call to modify the tensor in place by adding a new +first dimension. The tensor contains the same data, but now has shape +(1, 3, 224, 224). + +In general, even if you're not working with image data, you will need to +transform the input from your HTTP request into a tensor that PyTorch +can consume. + +Inference +~~~~~~~~~ + +:: + + def get_prediction(input_tensor): + outputs = model.forward(input_tensor) + _, y_hat = outputs.max(1) + prediction = y_hat.item() + return prediction + +The inference itself is the simplest part: When we pass the input tensor +to them model, we get back a tensor of values that represent the model's +estimated likelihood that the image belongs to a particular class. The +``max()`` call finds the class with the maximum likelihood value, and +returns that value with the ImageNet class index. Finally, we extract +that class index from the tensor containing it with the ``item()`` call, and +return it. + +Post-Processing +~~~~~~~~~~~~~~~ + +:: + + def render_prediction(prediction_idx): + stridx = str(prediction_idx) + class_name = 'Unknown' + if img_class_map is not None: + if stridx in img_class_map is not None: + class_name = img_class_map[stridx][1] + + return prediction_idx, class_name + +The ``render_prediction()`` method maps the predicted class index to a +human-readable class label. It's typical, after getting the prediction +from your model, to perform post-processing to make the prediction ready +for either human consumption, or for another piece of software. + +Running The Full Flask App +-------------------------- + +Paste the following into a file called ``app.py``: + +:: + + import io + import json + import os + + import torchvision.models as models + import torchvision.transforms as transforms + from PIL import Image + from flask import Flask, jsonify, request + + + app = Flask(__name__) + model = models.densenet121(pretrained=True) # Trained on 1000 classes from ImageNet + model.eval() # Turns off autograd and + + + + img_class_map = None + mapping_file_path = 'index_to_name.json' # Human-readable names for Imagenet classes + if os.path.isfile(mapping_file_path): + with open (mapping_file_path) as f: + img_class_map = json.load(f) + + + + # Transform input into the form our model expects + def transform_image(infile): + input_transforms = [transforms.Resize(255), # We use multiple TorchVision transforms to ready the image + transforms.CenterCrop(224), + transforms.ToTensor(), + transforms.Normalize([0.485, 0.456, 0.406], # Standard normalization for ImageNet model input + [0.229, 0.224, 0.225])] + my_transforms = transforms.Compose(input_transforms) + image = Image.open(infile) # Open the image file + timg = my_transforms(image) # Transform PIL image to appropriately-shaped PyTorch tensor + timg.unsqueeze_(0) # PyTorch models expect batched input; create a batch of 1 + return timg + + + # Get a prediction + def get_prediction(input_tensor): + outputs = model.forward(input_tensor) # Get likelihoods for all ImageNet classes + _, y_hat = outputs.max(1) # Extract the most likely class + prediction = y_hat.item() # Extract the int value from the PyTorch tensor + return prediction + + # Make the prediction human-readable + def render_prediction(prediction_idx): + stridx = str(prediction_idx) + class_name = 'Unknown' + if img_class_map is not None: + if stridx in img_class_map is not None: + class_name = img_class_map[stridx][1] + + return prediction_idx, class_name + + + @app.route('/', methods=['GET']) + def root(): + return jsonify({'msg' : 'Try POSTing to the /predict endpoint with an RGB image attachment'}) + + + @app.route('/predict', methods=['POST']) + def predict(): + if request.method == 'POST': + file = request.files['file'] + if file is not None: + input_tensor = transform_image(file) + prediction_idx = get_prediction(input_tensor) + class_id, class_name = render_prediction(prediction_idx) + return jsonify({'class_id': class_id, 'class_name': class_name}) + + + if __name__ == '__main__': + app.run() + +To start the server from your shell prompt, issue the following command: + +:: + + FLASK_APP=app.py flask run + +By default, your Flask server is listening on port 5000. Once the server +is running, open another terminal window, and test your new inference +server: + +:: + + curl -X POST -H "Content-Type: multipart/form-data" http://localhost:5000/predict -F "file=@kitten.jpg" + +If everything is set up correctly, you should recevie a response similar +to the following: + +:: + + {"class_id":285,"class_name":"Egyptian_cat"} + +Important Resources +------------------- + +- `pytorch.org`_ for installation instructions, and more documentation + and tutorials +- The `Flask site`_ has a `Quick Start guide`_ that goes into more + detail on setting up a simple Flask service + +.. _pytorch.org: https://pytorch.org +.. _Flask site: https://flask.palletsprojects.com/en/1.1.x/ +.. _Quick Start guide: https://flask.palletsprojects.com/en/1.1.x/quickstart/ +.. _torchvision.models: https://pytorch.org/docs/stable/torchvision/models.html +.. _the Flask site: https://flask.palletsprojects.com/en/1.1.x/installation/ diff --git a/recipes/index.rst b/recipes/index.rst new file mode 100644 index 00000000000..d30ee3e4e29 --- /dev/null +++ b/recipes/index.rst @@ -0,0 +1,391 @@ +:orphan: + + + +.. _sphx_glr_recipes: + +Recipes +------------------ +1. recipes/* and recipes_index.rst + PyTorch Recipes + https://pytorch.org/tutorials/recipes/recipes_index.html + + + + +.. raw:: html + +
+ + + +.. _sphx_glr_recipes_recipes: + +PyTorch Recipes +--------------------------------------------- +1. loading_data_recipe.py + Loading Data in PyTorch + https://pytorch.org/tutorials/recipes/recipes/loading_data_recipe.html + +2. defining_a_neural_network.py + Defining a Neural Network in PyTorch + https://pytorch.org/tutorials/recipes/recipes/defining_a_neural_network.html + +3. what_is_state_dict.py + What is a state_dict in PyTorch + https://pytorch.org/tutorials/recipes/recipes/what_is_state_dict.html + +4. saving_and_loading_models_for_inference.py + Saving and loading models for inference in PyTorch + https://pytorch.org/tutorials/recipes/recipes/saving_and_loading_models_for_inference.html + +5. custom_dataset_transforms_loader.py + Developing Custom PyTorch Dataloaders + https://pytorch.org/tutorials/recipes/recipes/custom_dataset_transforms_loader.html + + +6. Captum_Recipe.py + Model Interpretability using Captum + https://pytorch.org/tutorials/recipes/recipes/Captum_Recipe.html + +7. dynamic_quantization.py + Dynamic Quantization + https://pytorch.org/tutorials/recipes/recipes/dynamic_quantization.html + +8. save_load_across_devices.py + Saving and loading models across devices in PyTorch + https://pytorch.org/tutorials/recipes/recipes/save_load_across_devices.html + +9. saving_and_loading_a_general_checkpoint.py + Saving and loading a general checkpoint in PyTorch + https://pytorch.org/tutorials/recipes/recipes/saving_and_loading_a_general_checkpoint.html + +10. saving_and_loading_models_for_inference.py + Saving and loading models for inference in PyTorch + https://pytorch.org/tutorials/recipes/recipes/saving_and_loading_models_for_inference.html + +11. saving_multiple_models_in_one_file.py + Saving and loading multiple models in one file using PyTorch + https://pytorch.org/tutorials/recipes/recipes/saving_multiple_models_in_one_file.html + +12. warmstarting_model_using_parameters_from_a_different_model.py + Warmstarting models using parameters from different model + https://pytorch.org/tutorials/recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.html + +13. zeroing_out_gradients.py + Zeroing out gradients + https://pytorch.org/tutorials/recipes/recipes/zeroing_out_gradients.html + +14. mobile_perf.py + PyTorch Mobile Performance Recipes + https://pytorch.org/tutorials/recipes/mobile_perf.html + + + +.. raw:: html + +
+ +.. only:: html + + .. figure:: /recipes/recipes/images/thumb/sphx_glr_tensorboard_with_pytorch_thumb.png + + :ref:`sphx_glr_recipes_recipes_tensorboard_with_pytorch.py` + +.. raw:: html + +
+ + +.. toctree:: + :hidden: + + /recipes/recipes/tensorboard_with_pytorch + +.. raw:: html + +
+ +.. only:: html + + .. figure:: /recipes/recipes/images/thumb/sphx_glr_saving_and_loading_models_for_inference_thumb.png + + :ref:`sphx_glr_recipes_recipes_saving_and_loading_models_for_inference.py` + +.. raw:: html + +
+ + +.. toctree:: + :hidden: + + /recipes/recipes/saving_and_loading_models_for_inference + +.. raw:: html + +
+ +.. only:: html + + .. figure:: /recipes/recipes/images/thumb/sphx_glr_what_is_state_dict_thumb.png + + :ref:`sphx_glr_recipes_recipes_what_is_state_dict.py` + +.. raw:: html + +
+ + +.. toctree:: + :hidden: + + /recipes/recipes/what_is_state_dict + +.. raw:: html + +
+ +.. only:: html + + .. figure:: /recipes/recipes/images/thumb/sphx_glr_saving_and_loading_a_general_checkpoint_thumb.png + + :ref:`sphx_glr_recipes_recipes_saving_and_loading_a_general_checkpoint.py` + +.. raw:: html + +
+ + +.. toctree:: + :hidden: + + /recipes/recipes/saving_and_loading_a_general_checkpoint + +.. raw:: html + +
+ +.. only:: html + + .. figure:: /recipes/recipes/images/thumb/sphx_glr_warmstarting_model_using_parameters_from_a_different_model_thumb.png + + :ref:`sphx_glr_recipes_recipes_warmstarting_model_using_parameters_from_a_different_model.py` + +.. raw:: html + +
+ + +.. toctree:: + :hidden: + + /recipes/recipes/warmstarting_model_using_parameters_from_a_different_model + +.. raw:: html + +
+ +.. only:: html + + .. figure:: /recipes/recipes/images/thumb/sphx_glr_loading_data_recipe_thumb.png + + :ref:`sphx_glr_recipes_recipes_loading_data_recipe.py` + +.. raw:: html + +
+ + +.. toctree:: + :hidden: + + /recipes/recipes/loading_data_recipe + +.. raw:: html + +
+ +.. only:: html + + .. figure:: /recipes/recipes/images/thumb/sphx_glr_save_load_across_devices_thumb.png + + :ref:`sphx_glr_recipes_recipes_save_load_across_devices.py` + +.. raw:: html + +
+ + +.. toctree:: + :hidden: + + /recipes/recipes/save_load_across_devices + +.. raw:: html + +
+ +.. only:: html + + .. figure:: /recipes/recipes/images/thumb/sphx_glr_saving_multiple_models_in_one_file_thumb.png + + :ref:`sphx_glr_recipes_recipes_saving_multiple_models_in_one_file.py` + +.. raw:: html + +
+ + +.. toctree:: + :hidden: + + /recipes/recipes/saving_multiple_models_in_one_file + +.. raw:: html + +
+ +.. only:: html + + .. figure:: /recipes/recipes/images/thumb/sphx_glr_defining_a_neural_network_thumb.png + + :ref:`sphx_glr_recipes_recipes_defining_a_neural_network.py` + +.. raw:: html + +
+ + +.. toctree:: + :hidden: + + /recipes/recipes/defining_a_neural_network + +.. raw:: html + +
+ +.. only:: html + + .. figure:: /recipes/recipes/images/thumb/sphx_glr_zeroing_out_gradients_thumb.png + + :ref:`sphx_glr_recipes_recipes_zeroing_out_gradients.py` + +.. raw:: html + +
+ + +.. toctree:: + :hidden: + + /recipes/recipes/zeroing_out_gradients + +.. raw:: html + +
+ +.. only:: html + + .. figure:: /recipes/recipes/images/thumb/sphx_glr_dynamic_quantization_thumb.png + + :ref:`sphx_glr_recipes_recipes_dynamic_quantization.py` + +.. raw:: html + +
+ + +.. toctree:: + :hidden: + + /recipes/recipes/dynamic_quantization + +.. raw:: html + +
+ +.. only:: html + + .. figure:: /recipes/recipes/images/thumb/sphx_glr_Captum_Recipe_thumb.png + + :ref:`sphx_glr_recipes_recipes_Captum_Recipe.py` + +.. raw:: html + +
+ + +.. toctree:: + :hidden: + + /recipes/recipes/Captum_Recipe + +.. raw:: html + +
+ +.. only:: html + + .. figure:: /recipes/recipes/images/thumb/sphx_glr_profiler_thumb.png + + :ref:`sphx_glr_recipes_recipes_profiler.py` + +.. raw:: html + +
+ + +.. toctree:: + :hidden: + + /recipes/recipes/profiler + +.. raw:: html + +
+ +.. only:: html + + .. figure:: /recipes/recipes/images/thumb/sphx_glr_custom_dataset_transforms_loader_thumb.png + + :ref:`sphx_glr_recipes_recipes_custom_dataset_transforms_loader.py` + +.. raw:: html + +
+ + +.. toctree:: + :hidden: + + /recipes/recipes/custom_dataset_transforms_loader +.. raw:: html + +
+ + + +.. only :: html + + .. container:: sphx-glr-footer + :class: sphx-glr-footer-gallery + + + .. container:: sphx-glr-download + + :download:`Download all examples in Python source code: recipes_python.zip ` + + + + .. container:: sphx-glr-download + + :download:`Download all examples in Jupyter notebooks: recipes_jupyter.zip ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/mobile_perf.rst b/recipes/mobile_perf.rst new file mode 100644 index 00000000000..366318e7533 --- /dev/null +++ b/recipes/mobile_perf.rst @@ -0,0 +1,226 @@ +Pytorch Mobile Performance Recipes +================================== + +Introduction +---------------- +Performance (aka latency) is crucial to most, if not all, +applications and use-cases of ML model inference on mobile devices. + +Today, PyTorch executes the models on the CPU backend pending availability +of other hardware backends such as GPU, DSP, and NPU. + +In this recipe, you will learn: + +- How to optimize your model to help decrease execution time (higher performance, lower latency) on the mobile device. +- How to benchmark (to check if optimizations helped your use case). + + +Model preparation +----------------- + +We will start with preparing to optimize your model to help decrease execution time +(higher performance, lower latency) on the mobile device. + + +Setup +####### + +First we need to installed pytorch using conda or pip with version at least 1.5.0. + +:: + + conda install pytorch torchvision -c pytorch + +or + +:: + + pip install torch torchvision + +Code your model: + +:: + + import torch + from torch.utils.mobile_optimizer import optimize_for_mobile + + class AnnotatedConvBnReLUModel(torch.nn.Module): + def __init__(self): + super(AnnotatedConvBnReLUModel, self).__init__() + self.conv = torch.nn.Conv2d(3, 5, 3, bias=False).to(dtype=torch.float) + self.bn = torch.nn.BatchNorm2d(5).to(dtype=torch.float) + self.relu = torch.nn.ReLU(inplace=True) + self.quant = torch.quantization.QuantStub() + self.dequant = torch.quantization.DeQuantStub() + + def forward(self, x): + x = self.quant(x) + x = self.conv(x) + x = self.bn(x) + x = self.relu(x) + x = self.dequant(x) + return x + + model = AnnotatedConvBnReLUModel() + + +``torch.quantization.QuantStub`` and ``torch.quantization.DeQuantStub()`` are no-op stubs, which will be used for quantization step. + + +1. Fuse operators using ``torch.quantization.fuse_modules`` +############################################################# + +Do not be confused that fuse_modules is in the quantization package. +It works for all ``torcn.nn.Module``. + +``torch.quantization.fuse_modules`` fuses a list of modules into a single module. +It fuses only the following sequence of modules: + +- Convolution, Batch normalization +- Convolution, Batch normalization, Relu +- Convolution, Relu +- Linear, Relu + +This script will fuse Convolution, Batch Normalization and Relu in previously declared model. + +:: + + torch.quantization.fuse_modules(model, [['conv', 'bn', 'relu']], inplace=True) + + +2. Quantize your model +############################################################# + +You can find more about PyTorch quantization in +`the dedicated tutorial `_. + +Quantization of the model not only moves computation to int8, +but also reduces the size of your model on a disk. +That size reduction helps to reduce disk read operations during the first load of the model and decreases the amount of RAM. +Both of those resources can be crucial for the performance of mobile applications. +This code does quantization, using stub for model calibration function, you can find more about it `here `__. + +:: + + model.qconfig = torch.quantization.get_default_qconfig('qnnpack') + torch.quantization.prepare(model, inplace=True) + # Calibrate your model + def calibrate(model, calibration_data): + # Your calibration code here + return + calibrate(model, []) + torch.quantization.convert(model, inplace=True) + + + +3. Use torch.utils.mobile_optimizer +############################################################# + +Torch mobile_optimizer package does several optimizations with the scripted model, +which will help to conv2d and linear operations. +It pre-packs model weights in an optimized format and fuses ops above with relu +if it is the next operation. + +First we script the result model from previous step: + +:: + + torchscript_model = torch.jit.script(model) + +Next we call ``optimize_for_mobile`` and save model on the disk. + +:: + + torchscript_model_optimized = optimize_for_mobile(torchscript_model) + torch.jit.save(torchscript_model_optimized, "model.pt") + + +4. Android. Reusing tensors for forward. +############################################################# + +This recipe is Android only. +Memory is a critical resource for android performance, especially on old devices. +Tensors can need a significant amount of memory. +For example, standard computer vision tensor contains 1*3*224*224 elements, +assuming that data type is float and will need 588Kb of memory. + +:: + + FloatBuffer buffer = Tensor.allocateFloatBuffer(1*3*224*224); + Tensor tensor = Tensor.fromBlob(buffer, new long[]{1, 3, 224, 224}); + + +Here we allocate native memory as ``java.nio.FloatBuffer`` and creating ``org.pytorch.Tensor`` which storage will be pointing to the memory of the allocated buffer. + +For most of the use cases, we do not do model forward only once, repeating it with some frequency or as fast as possible. + +If we are doing new memory allocation for every module forward - that will be suboptimal. +Instead of this, we can reuse the same memory that we allocated on the previous step, fill it with new data, and run module forward again on the same tensor object. + +You can check how it looks in code in `pytorch android application example `_. + +:: + + protected AnalysisResult analyzeImage(ImageProxy image, int rotationDegrees) { + if (mModule == null) { + mModule = Module.load(moduleFileAbsoluteFilePath); + mInputTensorBuffer = + Tensor.allocateFloatBuffer(3 * 224 * 224); + mInputTensor = Tensor.fromBlob(mInputTensorBuffer, new long[]{1, 3, 224, 224}); + } + + TensorImageUtils.imageYUV420CenterCropToFloatBuffer( + image.getImage(), rotationDegrees, + 224, 224, + TensorImageUtils.TORCHVISION_NORM_MEAN_RGB, + TensorImageUtils.TORCHVISION_NORM_STD_RGB, + mInputTensorBuffer, 0); + + Tensor outputTensor = mModule.forward(IValue.from(mInputTensor)).toTensor(); + } + +Member fields ``mModule``, ``mInputTensorBuffer`` and ``mInputTensor`` are initialized only once +and buffer is refilled using ``org.pytorch.torchvision.TensorImageUtils.imageYUV420CenterCropToFloatBuffer``. + +Benchmarking +------------ + +The best way to benchmark (to check if optimizations helped your use case) - is to measure your particular use case that you want to optimize, as performance behavior can vary in different environments. + +PyTorch distribution provides a way to benchmark naked binary that runs the model forward, +this approach can give more stable measurements rather than testing inside the application. + + +Android - Benchmarking Setup +############################# + +For this you first need to build benchmark binary: + +:: + + + rm -rf build_android + BUILD_PYTORCH_MOBILE=1 ANDROID_ABI=arm64-v8a ./scripts/build_android.sh -DBUILD_BINARY=ON + +You should have arm64 binary at: ``build_android/bin/speed_benchmark_torch``. +This binary takes ``--model=``, ``--input_dim="1,3,224,224"`` as dimension information for the input and ``--input_type="float"`` as the type of the input as arguments. + +Once you have your android device connected, +push speedbenchark_torch binary and your model to the phone: + +:: + + adb push /data/local/tmp + adb push /data/local/tmp + + +Now we are ready to benchmark your model: + +:: + + adb shell "/data/local/tmp/speed_benchmark_torch --model="/data/local/tmp/model.pt" --input_dims="1,3,224,224" --input_type="float" + ----- output ----- + Starting benchmark. + Running warmup runs. + Main runs. + Main run finished. Microseconds per iter: 121318. Iters per second: 8.24281 diff --git a/recipes/recipes/Captum_Recipe.ipynb b/recipes/recipes/Captum_Recipe.ipynb new file mode 100644 index 00000000000..9feb2f0bc4a --- /dev/null +++ b/recipes/recipes/Captum_Recipe.ipynb @@ -0,0 +1,160 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\nModel Interpretability using Captum\n===================================\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Captum helps you understand how the data features impact your model\npredictions or neuron activations, shedding light on how your model\noperates.\n\nUsing Captum, you can apply a wide range of state-of-the-art feature\nattribution algorithms such as \\ ``Guided GradCam``\\ and\n\\ ``Integrated Gradients``\\ in a unified way.\n\nIn this recipe you will learn how to use Captum to: \\* attribute the\npredictions of an image classifier to their corresponding image\nfeatures. \\* visualize the attribution results.\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Before you begin\n----------------\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Make sure Captum is installed in your active Python environment. Captum\nis available both on GitHub, as a ``pip`` package, or as a ``conda``\npackage. For detailed instructions, consult the installation guide at\nhttps://captum.ai/\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For a model, we use a built-in image classifier in PyTorch. Captum can\nreveal which parts of a sample image support certain predictions made by\nthe model.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import torchvision\nfrom torchvision import transforms\nfrom PIL import Image\nimport requests\nfrom io import BytesIO\n\nmodel = torchvision.models.resnet18(pretrained=True).eval()\n\nresponse = requests.get(\"https://image.freepik.com/free-photo/two-beautiful-puppies-cat-dog_58409-6024.jpg\")\nimg = Image.open(BytesIO(response.content))\n\ncenter_crop = transforms.Compose([\n transforms.Resize(256),\n transforms.CenterCrop(224),\n])\n\nnormalize = transforms.Compose([\n transforms.ToTensor(), # converts the image to a tensor with values between 0 and 1\n transforms.Normalize( # normalize to follow 0-centered imagenet pixel rgb distribution\n mean=[0.485, 0.456, 0.406],\n std=[0.229, 0.224, 0.225]\n )\n])\ninput_img = normalize(center_crop(img)).unsqueeze(0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Computing Attribution\n---------------------\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Among the top-3 predictions of the models are classes 208 and 283 which\ncorrespond to dog and cat.\n\nLet us attribute each of these predictions to the corresponding part of\nthe input, using Captum\u2019s \\ ``Occlusion``\\ algorithm.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from captum.attr import Occlusion \n\nocclusion = Occlusion(model)\n\nstrides = (3, 9, 9) # smaller = more fine-grained attribution but slower\ntarget=208, # Labrador index in ImageNet \nsliding_window_shapes=(3,45, 45) # choose size enough to change object appearance\nbaselines = 0 # values to occlude the image with. 0 corresponds to gray\n\nattribution_dog = occlusion.attribute(input_img,\n strides = strides,\n target=target,\n sliding_window_shapes=sliding_window_shapes,\n baselines=baselines)\n\n\ntarget=283, # Persian cat index in ImageNet \nattribution_cat = occlusion.attribute(input_img,\n strides = strides,\n target=target,\n sliding_window_shapes=sliding_window_shapes,\n baselines=0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Besides ``Occlusion``, Captum features many algorithms such as\n\\ ``Integrated Gradients``\\ , \\ ``Deconvolution``\\ ,\n\\ ``GuidedBackprop``\\ , \\ ``Guided GradCam``\\ , \\ ``DeepLift``\\ , and\n\\ ``GradientShap``\\ . All of these algorithms are subclasses of\n``Attribution`` which expects your model as a callable ``forward_func``\nupon initialization and has an ``attribute(...)`` method which returns\nthe attribution result in a unified format.\n\nLet us visualize the computed attribution results in case of images.\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Visualizing the Results\n-----------------------\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Captum\u2019s \\ ``visualization``\\ utility provides out-of-the-box methods\nto visualize attribution results both for pictorial and for textual\ninputs.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import numpy as np\nfrom captum.attr import visualization as viz\n\n# Convert the compute attribution tensor into an image-like numpy array\nattribution_dog = np.transpose(attribution_dog.squeeze().cpu().detach().numpy(), (1,2,0))\n\nvis_types = [\"heat_map\", \"original_image\"]\nvis_signs = [\"all\", \"all\"], # \"positive\", \"negative\", or \"all\" to show both\n# positive attribution indicates that the presence of the area increases the prediction score\n# negative attribution indicates distractor areas whose absence increases the score\n\n_ = viz.visualize_image_attr_multiple(attribution_dog,\n center_crop(img),\n vis_types,\n vis_signs,\n [\"attribution for dog\", \"image\"],\n show_colorbar = True\n )\n\n\nattribution_cat = np.transpose(attribution_cat.squeeze().cpu().detach().numpy(), (1,2,0))\n\n_ = viz.visualize_image_attr_multiple(attribution_cat,\n center_crop(img),\n [\"heat_map\", \"original_image\"], \n [\"all\", \"all\"], # positive/negative attribution or all\n [\"attribution for cat\", \"image\"],\n show_colorbar = True\n )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If your data is textual, ``visualization.visualize_text()`` offers a\ndedicated view to explore attribution on top of the input text. Find out\nmore at http://captum.ai/tutorials/IMDB_TorchText_Interpret\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Final Notes\n-----------\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Captum can handle most model types in PyTorch across modalities\nincluding vision, text, and more. With Captum you can: \\* Attribute a\nspecific output to the model input as illustrated above. \\* Attribute a\nspecific output to a hidden-layer neuron (see Captum API reference). \\*\nAttribute a hidden-layer neuron response to the model input (see Captum\nAPI reference).\n\nFor complete API of the supported methods and a list of tutorials,\nconsult our website http://captum.ai\n\nAnother useful post by Gilbert Tanner:\nhttps://gilberttanner.com/blog/interpreting-pytorch-models-with-captum\n\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/recipes/recipes/Captum_Recipe.py b/recipes/recipes/Captum_Recipe.py new file mode 100644 index 00000000000..105c8cd5f67 --- /dev/null +++ b/recipes/recipes/Captum_Recipe.py @@ -0,0 +1,189 @@ +""" +Model Interpretability using Captum +=================================== + +""" + + +###################################################################### +# Captum helps you understand how the data features impact your model +# predictions or neuron activations, shedding light on how your model +# operates. +# +# Using Captum, you can apply a wide range of state-of-the-art feature +# attribution algorithms such as \ ``Guided GradCam``\ and +# \ ``Integrated Gradients``\ in a unified way. +# +# In this recipe you will learn how to use Captum to: \* attribute the +# predictions of an image classifier to their corresponding image +# features. \* visualize the attribution results. +# + + +###################################################################### +# Before you begin +# ---------------- +# + + +###################################################################### +# Make sure Captum is installed in your active Python environment. Captum +# is available both on GitHub, as a ``pip`` package, or as a ``conda`` +# package. For detailed instructions, consult the installation guide at +# https://captum.ai/ +# + + +###################################################################### +# For a model, we use a built-in image classifier in PyTorch. Captum can +# reveal which parts of a sample image support certain predictions made by +# the model. +# + +import torchvision +from torchvision import transforms +from PIL import Image +import requests +from io import BytesIO + +model = torchvision.models.resnet18(pretrained=True).eval() + +response = requests.get("https://image.freepik.com/free-photo/two-beautiful-puppies-cat-dog_58409-6024.jpg") +img = Image.open(BytesIO(response.content)) + +center_crop = transforms.Compose([ + transforms.Resize(256), + transforms.CenterCrop(224), +]) + +normalize = transforms.Compose([ + transforms.ToTensor(), # converts the image to a tensor with values between 0 and 1 + transforms.Normalize( # normalize to follow 0-centered imagenet pixel rgb distribution + mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225] + ) +]) +input_img = normalize(center_crop(img)).unsqueeze(0) + + +###################################################################### +# Computing Attribution +# --------------------- +# + + +###################################################################### +# Among the top-3 predictions of the models are classes 208 and 283 which +# correspond to dog and cat. +# +# Let us attribute each of these predictions to the corresponding part of +# the input, using Captum’s \ ``Occlusion``\ algorithm. +# + +from captum.attr import Occlusion + +occlusion = Occlusion(model) + +strides = (3, 9, 9) # smaller = more fine-grained attribution but slower +target=208, # Labrador index in ImageNet +sliding_window_shapes=(3,45, 45) # choose size enough to change object appearance +baselines = 0 # values to occlude the image with. 0 corresponds to gray + +attribution_dog = occlusion.attribute(input_img, + strides = strides, + target=target, + sliding_window_shapes=sliding_window_shapes, + baselines=baselines) + + +target=283, # Persian cat index in ImageNet +attribution_cat = occlusion.attribute(input_img, + strides = strides, + target=target, + sliding_window_shapes=sliding_window_shapes, + baselines=0) + + +###################################################################### +# Besides ``Occlusion``, Captum features many algorithms such as +# \ ``Integrated Gradients``\ , \ ``Deconvolution``\ , +# \ ``GuidedBackprop``\ , \ ``Guided GradCam``\ , \ ``DeepLift``\ , and +# \ ``GradientShap``\ . All of these algorithms are subclasses of +# ``Attribution`` which expects your model as a callable ``forward_func`` +# upon initialization and has an ``attribute(...)`` method which returns +# the attribution result in a unified format. +# +# Let us visualize the computed attribution results in case of images. +# + + +###################################################################### +# Visualizing the Results +# ----------------------- +# + + +###################################################################### +# Captum’s \ ``visualization``\ utility provides out-of-the-box methods +# to visualize attribution results both for pictorial and for textual +# inputs. +# + +import numpy as np +from captum.attr import visualization as viz + +# Convert the compute attribution tensor into an image-like numpy array +attribution_dog = np.transpose(attribution_dog.squeeze().cpu().detach().numpy(), (1,2,0)) + +vis_types = ["heat_map", "original_image"] +vis_signs = ["all", "all"], # "positive", "negative", or "all" to show both +# positive attribution indicates that the presence of the area increases the prediction score +# negative attribution indicates distractor areas whose absence increases the score + +_ = viz.visualize_image_attr_multiple(attribution_dog, + center_crop(img), + vis_types, + vis_signs, + ["attribution for dog", "image"], + show_colorbar = True + ) + + +attribution_cat = np.transpose(attribution_cat.squeeze().cpu().detach().numpy(), (1,2,0)) + +_ = viz.visualize_image_attr_multiple(attribution_cat, + center_crop(img), + ["heat_map", "original_image"], + ["all", "all"], # positive/negative attribution or all + ["attribution for cat", "image"], + show_colorbar = True + ) + + +###################################################################### +# If your data is textual, ``visualization.visualize_text()`` offers a +# dedicated view to explore attribution on top of the input text. Find out +# more at http://captum.ai/tutorials/IMDB_TorchText_Interpret +# + + +###################################################################### +# Final Notes +# ----------- +# + + +###################################################################### +# Captum can handle most model types in PyTorch across modalities +# including vision, text, and more. With Captum you can: \* Attribute a +# specific output to the model input as illustrated above. \* Attribute a +# specific output to a hidden-layer neuron (see Captum API reference). \* +# Attribute a hidden-layer neuron response to the model input (see Captum +# API reference). +# +# For complete API of the supported methods and a list of tutorials, +# consult our website http://captum.ai +# +# Another useful post by Gilbert Tanner: +# https://gilberttanner.com/blog/interpreting-pytorch-models-with-captum +# \ No newline at end of file diff --git a/recipes/recipes/Captum_Recipe.rst b/recipes/recipes/Captum_Recipe.rst new file mode 100644 index 00000000000..aff05ebd305 --- /dev/null +++ b/recipes/recipes/Captum_Recipe.rst @@ -0,0 +1,220 @@ +.. note:: + :class: sphx-glr-download-link-note + + Click :ref:`here ` to download the full example code +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_recipes_recipes_Captum_Recipe.py: + + +Model Interpretability using Captum +=================================== +Captum helps you understand how the data features impact your model +predictions or neuron activations, shedding light on how your model +operates. + +Using Captum, you can apply a wide range of state-of-the-art feature +attribution algorithms such as \ ``Guided GradCam``\ and +\ ``Integrated Gradients``\ in a unified way. + +In this recipe you will learn how to use Captum to: \* attribute the +predictions of an image classifier to their corresponding image +features. \* visualize the attribution results. + + +Before you begin +---------------- + + +Make sure Captum is installed in your active Python environment. Captum +is available both on GitHub, as a ``pip`` package, or as a ``conda`` +package. For detailed instructions, consult the installation guide at +https://captum.ai/ + + +For a model, we use a built-in image classifier in PyTorch. Captum can +reveal which parts of a sample image support certain predictions made by +the model. + + + +.. code-block:: default + + + import torchvision + from torchvision import transforms + from PIL import Image + import requests + from io import BytesIO + + model = torchvision.models.resnet18(pretrained=True).eval() + + response = requests.get("https://image.freepik.com/free-photo/two-beautiful-puppies-cat-dog_58409-6024.jpg") + img = Image.open(BytesIO(response.content)) + + center_crop = transforms.Compose([ + transforms.Resize(256), + transforms.CenterCrop(224), + ]) + + normalize = transforms.Compose([ + transforms.ToTensor(), # converts the image to a tensor with values between 0 and 1 + transforms.Normalize( # normalize to follow 0-centered imagenet pixel rgb distribution + mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225] + ) + ]) + input_img = normalize(center_crop(img)).unsqueeze(0) + + + +Computing Attribution +--------------------- + + +Among the top-3 predictions of the models are classes 208 and 283 which +correspond to dog and cat. + +Let us attribute each of these predictions to the corresponding part of +the input, using Captum’s \ ``Occlusion``\ algorithm. + + + +.. code-block:: default + + + from captum.attr import Occlusion + + occlusion = Occlusion(model) + + strides = (3, 9, 9) # smaller = more fine-grained attribution but slower + target=208, # Labrador index in ImageNet + sliding_window_shapes=(3,45, 45) # choose size enough to change object appearance + baselines = 0 # values to occlude the image with. 0 corresponds to gray + + attribution_dog = occlusion.attribute(input_img, + strides = strides, + target=target, + sliding_window_shapes=sliding_window_shapes, + baselines=baselines) + + + target=283, # Persian cat index in ImageNet + attribution_cat = occlusion.attribute(input_img, + strides = strides, + target=target, + sliding_window_shapes=sliding_window_shapes, + baselines=0) + + + +Besides ``Occlusion``, Captum features many algorithms such as +\ ``Integrated Gradients``\ , \ ``Deconvolution``\ , +\ ``GuidedBackprop``\ , \ ``Guided GradCam``\ , \ ``DeepLift``\ , and +\ ``GradientShap``\ . All of these algorithms are subclasses of +``Attribution`` which expects your model as a callable ``forward_func`` +upon initialization and has an ``attribute(...)`` method which returns +the attribution result in a unified format. + +Let us visualize the computed attribution results in case of images. + + +Visualizing the Results +----------------------- + + +Captum’s \ ``visualization``\ utility provides out-of-the-box methods +to visualize attribution results both for pictorial and for textual +inputs. + + + +.. code-block:: default + + + import numpy as np + from captum.attr import visualization as viz + + # Convert the compute attribution tensor into an image-like numpy array + attribution_dog = np.transpose(attribution_dog.squeeze().cpu().detach().numpy(), (1,2,0)) + + vis_types = ["heat_map", "original_image"] + vis_signs = ["all", "all"], # "positive", "negative", or "all" to show both + # positive attribution indicates that the presence of the area increases the prediction score + # negative attribution indicates distractor areas whose absence increases the score + + _ = viz.visualize_image_attr_multiple(attribution_dog, + center_crop(img), + vis_types, + vis_signs, + ["attribution for dog", "image"], + show_colorbar = True + ) + + + attribution_cat = np.transpose(attribution_cat.squeeze().cpu().detach().numpy(), (1,2,0)) + + _ = viz.visualize_image_attr_multiple(attribution_cat, + center_crop(img), + ["heat_map", "original_image"], + ["all", "all"], # positive/negative attribution or all + ["attribution for cat", "image"], + show_colorbar = True + ) + + + +If your data is textual, ``visualization.visualize_text()`` offers a +dedicated view to explore attribution on top of the input text. Find out +more at http://captum.ai/tutorials/IMDB_TorchText_Interpret + + +Final Notes +----------- + + +Captum can handle most model types in PyTorch across modalities +including vision, text, and more. With Captum you can: \* Attribute a +specific output to the model input as illustrated above. \* Attribute a +specific output to a hidden-layer neuron (see Captum API reference). \* +Attribute a hidden-layer neuron response to the model input (see Captum +API reference). + +For complete API of the supported methods and a list of tutorials, +consult our website http://captum.ai + +Another useful post by Gilbert Tanner: +https://gilberttanner.com/blog/interpreting-pytorch-models-with-captum + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** ( 0 minutes 0.000 seconds) + + +.. _sphx_glr_download_recipes_recipes_Captum_Recipe.py: + + +.. only :: html + + .. container:: sphx-glr-footer + :class: sphx-glr-footer-example + + + + .. container:: sphx-glr-download + + :download:`Download Python source code: Captum_Recipe.py ` + + + + .. container:: sphx-glr-download + + :download:`Download Jupyter notebook: Captum_Recipe.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/custom_dataset_transforms_loader.ipynb b/recipes/recipes/custom_dataset_transforms_loader.ipynb new file mode 100644 index 00000000000..207d56dc868 --- /dev/null +++ b/recipes/recipes/custom_dataset_transforms_loader.ipynb @@ -0,0 +1,261 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\nDeveloping Custom PyTorch Dataloaders\n=====================================\n\nA significant amount of the effort applied to developing machine\nlearning algorithms is related to data preparation. PyTorch provides\nmany tools to make data loading easy and hopefully, makes your code more\nreadable. In this recipe, you will learn how to:\n\n 1. Create a custom dataset leveraging the PyTorch dataset APIs;\n 2. Create callable custom transforms that can be composable; and\n 3. Put these components together to create a custom dataloader.\n\nPlease note, to run this tutorial, ensure the following packages are\ninstalled:\n - ``scikit-image``: For image io and transforms\n - ``pandas``: For easier csv parsing\n\nAs a point of attribution, this recipe is based on the original tutorial\nfrom `Sasank Chilamkurthy `__ and was later\nedited by `Joe Spisak `__.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Setup\n----------------------\nFirst let\u2019s import all of the needed libraries for this recipe.\n\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from __future__ import print_function, division\nimport os\nimport torch\nimport pandas as pd\nfrom skimage import io, transform\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms, utils\n\n# Ignore warnings\nimport warnings\nwarnings.filterwarnings(\"ignore\")\n\nplt.ion() # interactive mode" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Part 1: The Dataset\n-------------------\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The dataset we are going to deal with is that of facial pose. Overall,\n68 different landmark points are annotated for each face.\n\nAs a next step, please download the dataset from\n`here `_ so that the\nimages are in a directory named \u2018data/faces/\u2019.\n\n**Note:** This dataset was actually generated by applying\n`dlib's pose estimation `_\non images from the imagenet dataset containing the \u2018face\u2019 tag.\n\n::\n\n !wget https://download.pytorch.org/tutorial/faces.zip\n !mkdir data/faces/\n import zipfile\n with zipfile.ZipFile(\"faces.zip\",\"r\") as zip_ref:\n zip_ref.extractall(\"/data/faces/\")\n %cd /data/faces/\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The dataset comes with a csv file with annotations which looks like\nthis:\n\n::\n\n image_name,part_0_x,part_0_y,part_1_x,part_1_y,part_2_x, ... ,part_67_x,part_67_y\n 0805personali01.jpg,27,83,27,98, ... 84,134\n 1084239450_e76e00b7e7.jpg,70,236,71,257, ... ,128,312\n\nLet\u2019s quickly read the CSV and get the annotations in an (N, 2) array\nwhere N is the number of landmarks.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "landmarks_frame = pd.read_csv('faces/face_landmarks.csv')\n\nn = 65\nimg_name = landmarks_frame.iloc[n, 0]\nlandmarks = landmarks_frame.iloc[n, 1:]\nlandmarks = np.asarray(landmarks)\nlandmarks = landmarks.astype('float').reshape(-1, 2)\n\nprint('Image name: {}'.format(img_name))\nprint('Landmarks shape: {}'.format(landmarks.shape))\nprint('First 4 Landmarks: {}'.format(landmarks[:4]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1.1 Write a simple helper function to show an image\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nNext let\u2019s write a simple helper function to show an image, its landmarks and use it to show a sample.\n\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def show_landmarks(image, landmarks):\n \"\"\"Show image with landmarks\"\"\"\n plt.imshow(image)\n plt.scatter(landmarks[:, 0], landmarks[:, 1], s=10, marker='.', c='r')\n plt.pause(0.001) # pause a bit so that plots are updated\n\nplt.figure()\nshow_landmarks(io.imread(os.path.join('faces/', img_name)),\n landmarks)\nplt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1.2 Create a dataset class\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nNow lets talk about the PyTorch dataset class\n\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "``torch.utils.data.Dataset`` is an abstract class representing a\ndataset. Your custom dataset should inherit ``Dataset`` and override the\nfollowing methods:\n\n- ``__len__`` so that ``len(dataset)`` returns the size of the dataset.\n- ``__getitem__`` to support indexing such that ``dataset[i]`` can be\n used to get $`i$`\u00a0th sample\n\nLet\u2019s create a dataset class for our face landmarks dataset. We will\nread the csv in ``__init__`` but leave the reading of images to\n``__getitem__``. This is memory efficient because all the images are not\nstored in the memory at once but read as required.\n\nHere we show a sample of our dataset in the forma of a dict\n``{'image': image, 'landmarks': landmarks}``. Our dataset will take an\noptional argument ``transform`` so that any required processing can be\napplied on the sample. We will see the usefulness of ``transform`` in\nanother recipe.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "class FaceLandmarksDataset(Dataset):\n \"\"\"Face Landmarks dataset.\"\"\"\n\n def __init__(self, csv_file, root_dir, transform=None):\n \"\"\"\n Args:\n csv_file (string): Path to the csv file with annotations.\n root_dir (string): Directory with all the images.\n transform (callable, optional): Optional transform to be applied\n on a sample.\n \"\"\"\n self.landmarks_frame = pd.read_csv(csv_file)\n self.root_dir = root_dir\n self.transform = transform\n\n def __len__(self):\n return len(self.landmarks_frame)\n\n def __getitem__(self, idx):\n if torch.is_tensor(idx):\n idx = idx.tolist()\n\n img_name = os.path.join(self.root_dir,\n self.landmarks_frame.iloc[idx, 0])\n image = io.imread(img_name)\n landmarks = self.landmarks_frame.iloc[idx, 1:]\n landmarks = np.array([landmarks])\n landmarks = landmarks.astype('float').reshape(-1, 2)\n sample = {'image': image, 'landmarks': landmarks}\n\n if self.transform:\n sample = self.transform(sample)\n\n return sample" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1.3 Iterate through data samples\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next let\u2019s instantiate this class and iterate through the data samples.\nWe will print the sizes of first 4 samples and show their landmarks.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "face_dataset = FaceLandmarksDataset(csv_file='faces/face_landmarks.csv',\n root_dir='faces/')\n\nfig = plt.figure()\n\nfor i in range(len(face_dataset)):\n sample = face_dataset[i]\n\n print(i, sample['image'].shape, sample['landmarks'].shape)\n\n ax = plt.subplot(1, 4, i + 1)\n plt.tight_layout()\n ax.set_title('Sample #{}'.format(i))\n ax.axis('off')\n show_landmarks(**sample)\n\n if i == 3:\n plt.show()\n break" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Part 2: Data Tranformations\n---------------------------\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now that we have a dataset to work with and have done some level of\ncustomization, we can move to creating custom transformations. In\ncomputer vision, these come in handy to help generalize algorithms and\nimprove accuracy. A suite of transformations used at training time is\ntypically referred to as data augmentation and is a common practice for\nmodern model development.\n\nOne issue common in handling datasets is that the samples may not all be\nthe same size. Most neural networks expect the images of a fixed size.\nTherefore, we will need to write some prepocessing code. Let\u2019s create\nthree transforms:\n\n- ``Rescale``: to scale the image\n- ``RandomCrop``: to crop from image randomly. This is data\n augmentation.\n- ``ToTensor``: to convert the numpy images to torch images (we need to\n swap axes).\n\nWe will write them as callable classes instead of simple functions so\nthat parameters of the transform need not be passed everytime it\u2019s\ncalled. For this, we just need to implement ``__call__`` method and if\nrequired, ``__init__`` method. We can then use a transform like this:\n\n::\n\n tsfm = Transform(params)\n transformed_sample = tsfm(sample)\n\nObserve below how these transforms had to be applied both on the image\nand landmarks.\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2.1 Create callable classes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nLet\u2019s start with creating callable classes for each transform\n\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "class Rescale(object):\n \"\"\"Rescale the image in a sample to a given size.\n\n Args:\n output_size (tuple or int): Desired output size. If tuple, output is\n matched to output_size. If int, smaller of image edges is matched\n to output_size keeping aspect ratio the same.\n \"\"\"\n\n def __init__(self, output_size):\n assert isinstance(output_size, (int, tuple))\n self.output_size = output_size\n\n def __call__(self, sample):\n image, landmarks = sample['image'], sample['landmarks']\n\n h, w = image.shape[:2]\n if isinstance(self.output_size, int):\n if h > w:\n new_h, new_w = self.output_size * h / w, self.output_size\n else:\n new_h, new_w = self.output_size, self.output_size * w / h\n else:\n new_h, new_w = self.output_size\n\n new_h, new_w = int(new_h), int(new_w)\n\n img = transform.resize(image, (new_h, new_w))\n\n # h and w are swapped for landmarks because for images,\n # x and y axes are axis 1 and 0 respectively\n landmarks = landmarks * [new_w / w, new_h / h]\n\n return {'image': img, 'landmarks': landmarks}\n\n\nclass RandomCrop(object):\n \"\"\"Crop randomly the image in a sample.\n\n Args:\n output_size (tuple or int): Desired output size. If int, square crop\n is made.\n \"\"\"\n\n def __init__(self, output_size):\n assert isinstance(output_size, (int, tuple))\n if isinstance(output_size, int):\n self.output_size = (output_size, output_size)\n else:\n assert len(output_size) == 2\n self.output_size = output_size\n\n def __call__(self, sample):\n image, landmarks = sample['image'], sample['landmarks']\n\n h, w = image.shape[:2]\n new_h, new_w = self.output_size\n\n top = np.random.randint(0, h - new_h)\n left = np.random.randint(0, w - new_w)\n\n image = image[top: top + new_h,\n left: left + new_w]\n\n landmarks = landmarks - [left, top]\n\n return {'image': image, 'landmarks': landmarks}\n\n\nclass ToTensor(object):\n \"\"\"Convert ndarrays in sample to Tensors.\"\"\"\n\n def __call__(self, sample):\n image, landmarks = sample['image'], sample['landmarks']\n\n # swap color axis because\n # numpy image: H x W x C\n # torch image: C X H X W\n image = image.transpose((2, 0, 1))\n return {'image': torch.from_numpy(image),\n 'landmarks': torch.from_numpy(landmarks)}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2.2 Compose transforms and apply to a sample\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nNext let\u2019s compose these transforms and apply to a sample\n\n\nLet\u2019s say we want to rescale the shorter side of the image to 256 and\nthen randomly crop a square of size 224 from it. i.e, we want to compose\n``Rescale`` and ``RandomCrop`` transforms.\n``torchvision.transforms.Compose`` is a simple callable class which\nallows us to do this.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scale = Rescale(256)\ncrop = RandomCrop(128)\ncomposed = transforms.Compose([Rescale(256),\n RandomCrop(224)])\n\n# Apply each of the above transforms on sample.\nfig = plt.figure()\nsample = face_dataset[65]\nfor i, tsfrm in enumerate([scale, crop, composed]):\n transformed_sample = tsfrm(sample)\n\n ax = plt.subplot(1, 3, i + 1)\n plt.tight_layout()\n ax.set_title(type(tsfrm).__name__)\n show_landmarks(**transformed_sample)\n\nplt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2.3 Iterate through the dataset\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nNext we will iterate through the dataset\n\n\nLet\u2019s put this all together to create a dataset with composed\ntransforms. To summarize, every time this dataset is sampled:\n\n- An image is read from the file on the fly\n- Transforms are applied on the read image\n- Since one of the transforms is random, data is augmentated on\n sampling\n\nWe can iterate over the created dataset with a ``for i in range`` loop\nas before.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "transformed_dataset = FaceLandmarksDataset(csv_file='faces/face_landmarks.csv',\n root_dir='faces/',\n transform=transforms.Compose([\n Rescale(256),\n RandomCrop(224),\n ToTensor()\n ]))\n\nfor i in range(len(transformed_dataset)):\n sample = transformed_dataset[i]\n\n print(i, sample['image'].size(), sample['landmarks'].size())\n\n if i == 3:\n break" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Part 3: The Dataloader\n----------------------\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "By operating on the dataset directly, we are losing out on a lot of\nfeatures by using a simple ``for`` loop to iterate over the data. In\nparticular, we are missing out on:\n\n- Batching the data\n- Shuffling the data\n- Load the data in parallel using ``multiprocessing`` workers.\n\n``torch.utils.data.DataLoader`` is an iterator which provides all these\nfeatures. Parameters used below should be clear. One parameter of\ninterest is ``collate_fn``. You can specify how exactly the samples need\nto be batched using ``collate_fn``. However, default collate should work\nfine for most use cases.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "dataloader = DataLoader(transformed_dataset, batch_size=4,\n shuffle=True, num_workers=4)\n\n\n# Helper function to show a batch\ndef show_landmarks_batch(sample_batched):\n \"\"\"Show image with landmarks for a batch of samples.\"\"\"\n images_batch, landmarks_batch = \\\n sample_batched['image'], sample_batched['landmarks']\n batch_size = len(images_batch)\n im_size = images_batch.size(2)\n\n grid = utils.make_grid(images_batch)\n plt.imshow(grid.numpy().transpose((1, 2, 0)))\n\n for i in range(batch_size):\n plt.scatter(landmarks_batch[i, :, 0].numpy() + i * im_size,\n landmarks_batch[i, :, 1].numpy(),\n s=10, marker='.', c='r')\n\n plt.title('Batch from dataloader')\n\nfor i_batch, sample_batched in enumerate(dataloader):\n print(i_batch, sample_batched['image'].size(),\n sample_batched['landmarks'].size())\n\n # observe 4th batch and stop.\n if i_batch == 3:\n plt.figure()\n show_landmarks_batch(sample_batched)\n plt.axis('off')\n plt.ioff()\n plt.show()\n break" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now that you\u2019ve learned how to create a custom dataloader with PyTorch,\nwe recommend diving deeper into the docs and customizing your workflow\neven further. You can learn more in the ``torch.utils.data`` docs\n`here `__.\n\n\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/recipes/recipes/custom_dataset_transforms_loader.py b/recipes/recipes/custom_dataset_transforms_loader.py new file mode 100644 index 00000000000..077dcee8001 --- /dev/null +++ b/recipes/recipes/custom_dataset_transforms_loader.py @@ -0,0 +1,480 @@ +""" +Developing Custom PyTorch Dataloaders +===================================== + +A significant amount of the effort applied to developing machine +learning algorithms is related to data preparation. PyTorch provides +many tools to make data loading easy and hopefully, makes your code more +readable. In this recipe, you will learn how to: + + 1. Create a custom dataset leveraging the PyTorch dataset APIs; + 2. Create callable custom transforms that can be composable; and + 3. Put these components together to create a custom dataloader. + +Please note, to run this tutorial, ensure the following packages are +installed: + - ``scikit-image``: For image io and transforms + - ``pandas``: For easier csv parsing + +As a point of attribution, this recipe is based on the original tutorial +from `Sasank Chilamkurthy `__ and was later +edited by `Joe Spisak `__. +""" + + +###################################################################### +# Setup +# ---------------------- +# First let’s import all of the needed libraries for this recipe. +# +# + +from __future__ import print_function, division +import os +import torch +import pandas as pd +from skimage import io, transform +import numpy as np +import matplotlib.pyplot as plt +from torch.utils.data import Dataset, DataLoader +from torchvision import transforms, utils + +# Ignore warnings +import warnings +warnings.filterwarnings("ignore") + +plt.ion() # interactive mode + + +###################################################################### +# Part 1: The Dataset +# ------------------- +# + +###################################################################### +# The dataset we are going to deal with is that of facial pose. Overall, +# 68 different landmark points are annotated for each face. +# +# As a next step, please download the dataset from +# `here `_ so that the +# images are in a directory named ‘data/faces/’. +# +# **Note:** This dataset was actually generated by applying +# `dlib's pose estimation `_ +# on images from the imagenet dataset containing the ‘face’ tag. +# +# :: +# +# !wget https://download.pytorch.org/tutorial/faces.zip +# !mkdir data/faces/ +# import zipfile +# with zipfile.ZipFile("faces.zip","r") as zip_ref: +# zip_ref.extractall("/data/faces/") +# %cd /data/faces/ + + + +###################################################################### +# The dataset comes with a csv file with annotations which looks like +# this: +# +# :: +# +# image_name,part_0_x,part_0_y,part_1_x,part_1_y,part_2_x, ... ,part_67_x,part_67_y +# 0805personali01.jpg,27,83,27,98, ... 84,134 +# 1084239450_e76e00b7e7.jpg,70,236,71,257, ... ,128,312 +# +# Let’s quickly read the CSV and get the annotations in an (N, 2) array +# where N is the number of landmarks. +# + + +landmarks_frame = pd.read_csv('faces/face_landmarks.csv') + +n = 65 +img_name = landmarks_frame.iloc[n, 0] +landmarks = landmarks_frame.iloc[n, 1:] +landmarks = np.asarray(landmarks) +landmarks = landmarks.astype('float').reshape(-1, 2) + +print('Image name: {}'.format(img_name)) +print('Landmarks shape: {}'.format(landmarks.shape)) +print('First 4 Landmarks: {}'.format(landmarks[:4])) + +###################################################################### +# 1.1 Write a simple helper function to show an image +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Next let’s write a simple helper function to show an image, its landmarks and use it to show a sample. +# +# + +def show_landmarks(image, landmarks): + """Show image with landmarks""" + plt.imshow(image) + plt.scatter(landmarks[:, 0], landmarks[:, 1], s=10, marker='.', c='r') + plt.pause(0.001) # pause a bit so that plots are updated + +plt.figure() +show_landmarks(io.imread(os.path.join('faces/', img_name)), + landmarks) +plt.show() + + +###################################################################### +# 1.2 Create a dataset class +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Now lets talk about the PyTorch dataset class +# +# + + +###################################################################### +# ``torch.utils.data.Dataset`` is an abstract class representing a +# dataset. Your custom dataset should inherit ``Dataset`` and override the +# following methods: +# +# - ``__len__`` so that ``len(dataset)`` returns the size of the dataset. +# - ``__getitem__`` to support indexing such that ``dataset[i]`` can be +# used to get :math:``i`` th sample +# +# Let’s create a dataset class for our face landmarks dataset. We will +# read the csv in ``__init__`` but leave the reading of images to +# ``__getitem__``. This is memory efficient because all the images are not +# stored in the memory at once but read as required. +# +# Here we show a sample of our dataset in the forma of a dict +# ``{'image': image, 'landmarks': landmarks}``. Our dataset will take an +# optional argument ``transform`` so that any required processing can be +# applied on the sample. We will see the usefulness of ``transform`` in +# another recipe. +# + +class FaceLandmarksDataset(Dataset): + """Face Landmarks dataset.""" + + def __init__(self, csv_file, root_dir, transform=None): + """ + Args: + csv_file (string): Path to the csv file with annotations. + root_dir (string): Directory with all the images. + transform (callable, optional): Optional transform to be applied + on a sample. + """ + self.landmarks_frame = pd.read_csv(csv_file) + self.root_dir = root_dir + self.transform = transform + + def __len__(self): + return len(self.landmarks_frame) + + def __getitem__(self, idx): + if torch.is_tensor(idx): + idx = idx.tolist() + + img_name = os.path.join(self.root_dir, + self.landmarks_frame.iloc[idx, 0]) + image = io.imread(img_name) + landmarks = self.landmarks_frame.iloc[idx, 1:] + landmarks = np.array([landmarks]) + landmarks = landmarks.astype('float').reshape(-1, 2) + sample = {'image': image, 'landmarks': landmarks} + + if self.transform: + sample = self.transform(sample) + + return sample + + +###################################################################### +# 1.3 Iterate through data samples +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# + + +###################################################################### +# Next let’s instantiate this class and iterate through the data samples. +# We will print the sizes of first 4 samples and show their landmarks. +# + +face_dataset = FaceLandmarksDataset(csv_file='faces/face_landmarks.csv', + root_dir='faces/') + +fig = plt.figure() + +for i in range(len(face_dataset)): + sample = face_dataset[i] + + print(i, sample['image'].shape, sample['landmarks'].shape) + + ax = plt.subplot(1, 4, i + 1) + plt.tight_layout() + ax.set_title('Sample #{}'.format(i)) + ax.axis('off') + show_landmarks(**sample) + + if i == 3: + plt.show() + break + + +###################################################################### +# Part 2: Data Tranformations +# --------------------------- +# + + +###################################################################### +# Now that we have a dataset to work with and have done some level of +# customization, we can move to creating custom transformations. In +# computer vision, these come in handy to help generalize algorithms and +# improve accuracy. A suite of transformations used at training time is +# typically referred to as data augmentation and is a common practice for +# modern model development. +# +# One issue common in handling datasets is that the samples may not all be +# the same size. Most neural networks expect the images of a fixed size. +# Therefore, we will need to write some prepocessing code. Let’s create +# three transforms: +# +# - ``Rescale``: to scale the image +# - ``RandomCrop``: to crop from image randomly. This is data +# augmentation. +# - ``ToTensor``: to convert the numpy images to torch images (we need to +# swap axes). +# +# We will write them as callable classes instead of simple functions so +# that parameters of the transform need not be passed everytime it’s +# called. For this, we just need to implement ``__call__`` method and if +# required, ``__init__`` method. We can then use a transform like this: +# +# :: +# +# tsfm = Transform(params) +# transformed_sample = tsfm(sample) +# +# Observe below how these transforms had to be applied both on the image +# and landmarks. +# + + +###################################################################### +# 2.1 Create callable classes +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Let’s start with creating callable classes for each transform +# +# + +class Rescale(object): + """Rescale the image in a sample to a given size. + + Args: + output_size (tuple or int): Desired output size. If tuple, output is + matched to output_size. If int, smaller of image edges is matched + to output_size keeping aspect ratio the same. + """ + + def __init__(self, output_size): + assert isinstance(output_size, (int, tuple)) + self.output_size = output_size + + def __call__(self, sample): + image, landmarks = sample['image'], sample['landmarks'] + + h, w = image.shape[:2] + if isinstance(self.output_size, int): + if h > w: + new_h, new_w = self.output_size * h / w, self.output_size + else: + new_h, new_w = self.output_size, self.output_size * w / h + else: + new_h, new_w = self.output_size + + new_h, new_w = int(new_h), int(new_w) + + img = transform.resize(image, (new_h, new_w)) + + # h and w are swapped for landmarks because for images, + # x and y axes are axis 1 and 0 respectively + landmarks = landmarks * [new_w / w, new_h / h] + + return {'image': img, 'landmarks': landmarks} + + +class RandomCrop(object): + """Crop randomly the image in a sample. + + Args: + output_size (tuple or int): Desired output size. If int, square crop + is made. + """ + + def __init__(self, output_size): + assert isinstance(output_size, (int, tuple)) + if isinstance(output_size, int): + self.output_size = (output_size, output_size) + else: + assert len(output_size) == 2 + self.output_size = output_size + + def __call__(self, sample): + image, landmarks = sample['image'], sample['landmarks'] + + h, w = image.shape[:2] + new_h, new_w = self.output_size + + top = np.random.randint(0, h - new_h) + left = np.random.randint(0, w - new_w) + + image = image[top: top + new_h, + left: left + new_w] + + landmarks = landmarks - [left, top] + + return {'image': image, 'landmarks': landmarks} + + +class ToTensor(object): + """Convert ndarrays in sample to Tensors.""" + + def __call__(self, sample): + image, landmarks = sample['image'], sample['landmarks'] + + # swap color axis because + # numpy image: H x W x C + # torch image: C X H X W + image = image.transpose((2, 0, 1)) + return {'image': torch.from_numpy(image), + 'landmarks': torch.from_numpy(landmarks)} + + +###################################################################### +# 2.2 Compose transforms and apply to a sample +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Next let’s compose these transforms and apply to a sample +# +# +# Let’s say we want to rescale the shorter side of the image to 256 and +# then randomly crop a square of size 224 from it. i.e, we want to compose +# ``Rescale`` and ``RandomCrop`` transforms. +# ``torchvision.transforms.Compose`` is a simple callable class which +# allows us to do this. +# + +scale = Rescale(256) +crop = RandomCrop(128) +composed = transforms.Compose([Rescale(256), + RandomCrop(224)]) + +# Apply each of the above transforms on sample. +fig = plt.figure() +sample = face_dataset[65] +for i, tsfrm in enumerate([scale, crop, composed]): + transformed_sample = tsfrm(sample) + + ax = plt.subplot(1, 3, i + 1) + plt.tight_layout() + ax.set_title(type(tsfrm).__name__) + show_landmarks(**transformed_sample) + +plt.show() + + +###################################################################### +# 2.3 Iterate through the dataset +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Next we will iterate through the dataset +# +# +# Let’s put this all together to create a dataset with composed +# transforms. To summarize, every time this dataset is sampled: +# +# - An image is read from the file on the fly +# - Transforms are applied on the read image +# - Since one of the transforms is random, data is augmentated on +# sampling +# +# We can iterate over the created dataset with a ``for i in range`` loop +# as before. +# + +transformed_dataset = FaceLandmarksDataset(csv_file='faces/face_landmarks.csv', + root_dir='faces/', + transform=transforms.Compose([ + Rescale(256), + RandomCrop(224), + ToTensor() + ])) + +for i in range(len(transformed_dataset)): + sample = transformed_dataset[i] + + print(i, sample['image'].size(), sample['landmarks'].size()) + + if i == 3: + break + + +###################################################################### +# Part 3: The Dataloader +# ---------------------- +# + + +###################################################################### +# By operating on the dataset directly, we are losing out on a lot of +# features by using a simple ``for`` loop to iterate over the data. In +# particular, we are missing out on: +# +# - Batching the data +# - Shuffling the data +# - Load the data in parallel using ``multiprocessing`` workers. +# +# ``torch.utils.data.DataLoader`` is an iterator which provides all these +# features. Parameters used below should be clear. One parameter of +# interest is ``collate_fn``. You can specify how exactly the samples need +# to be batched using ``collate_fn``. However, default collate should work +# fine for most use cases. +# + +dataloader = DataLoader(transformed_dataset, batch_size=4, + shuffle=True, num_workers=4) + + +# Helper function to show a batch +def show_landmarks_batch(sample_batched): + """Show image with landmarks for a batch of samples.""" + images_batch, landmarks_batch = \ + sample_batched['image'], sample_batched['landmarks'] + batch_size = len(images_batch) + im_size = images_batch.size(2) + + grid = utils.make_grid(images_batch) + plt.imshow(grid.numpy().transpose((1, 2, 0))) + + for i in range(batch_size): + plt.scatter(landmarks_batch[i, :, 0].numpy() + i * im_size, + landmarks_batch[i, :, 1].numpy(), + s=10, marker='.', c='r') + + plt.title('Batch from dataloader') + +for i_batch, sample_batched in enumerate(dataloader): + print(i_batch, sample_batched['image'].size(), + sample_batched['landmarks'].size()) + + # observe 4th batch and stop. + if i_batch == 3: + plt.figure() + show_landmarks_batch(sample_batched) + plt.axis('off') + plt.ioff() + plt.show() + break + + +###################################################################### +# Now that you’ve learned how to create a custom dataloader with PyTorch, +# we recommend diving deeper into the docs and customizing your workflow +# even further. You can learn more in the ``torch.utils.data`` docs +# `here `__. +# diff --git a/recipes/recipes/custom_dataset_transforms_loader.rst b/recipes/recipes/custom_dataset_transforms_loader.rst new file mode 100644 index 00000000000..5122e4ad931 --- /dev/null +++ b/recipes/recipes/custom_dataset_transforms_loader.rst @@ -0,0 +1,539 @@ +.. note:: + :class: sphx-glr-download-link-note + + Click :ref:`here ` to download the full example code +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_recipes_recipes_custom_dataset_transforms_loader.py: + + +Developing Custom PyTorch Dataloaders +===================================== + +A significant amount of the effort applied to developing machine +learning algorithms is related to data preparation. PyTorch provides +many tools to make data loading easy and hopefully, makes your code more +readable. In this recipe, you will learn how to: + + 1. Create a custom dataset leveraging the PyTorch dataset APIs; + 2. Create callable custom transforms that can be composable; and + 3. Put these components together to create a custom dataloader. + +Please note, to run this tutorial, ensure the following packages are +installed: + - ``scikit-image``: For image io and transforms + - ``pandas``: For easier csv parsing + +As a point of attribution, this recipe is based on the original tutorial +from `Sasank Chilamkurthy `__ and was later +edited by `Joe Spisak `__. +Setup +---------------------- +First let’s import all of the needed libraries for this recipe. + + + + +.. code-block:: default + + + from __future__ import print_function, division + import os + import torch + import pandas as pd + from skimage import io, transform + import numpy as np + import matplotlib.pyplot as plt + from torch.utils.data import Dataset, DataLoader + from torchvision import transforms, utils + + # Ignore warnings + import warnings + warnings.filterwarnings("ignore") + + plt.ion() # interactive mode + + + +Part 1: The Dataset +------------------- + + +The dataset we are going to deal with is that of facial pose. Overall, +68 different landmark points are annotated for each face. + +As a next step, please download the dataset from +`here `_ so that the +images are in a directory named ‘data/faces/’. + +**Note:** This dataset was actually generated by applying +`dlib's pose estimation `_ +on images from the imagenet dataset containing the ‘face’ tag. + +:: + + !wget https://download.pytorch.org/tutorial/faces.zip + !mkdir data/faces/ + import zipfile + with zipfile.ZipFile("faces.zip","r") as zip_ref: + zip_ref.extractall("/data/faces/") + %cd /data/faces/ + +The dataset comes with a csv file with annotations which looks like +this: + +:: + + image_name,part_0_x,part_0_y,part_1_x,part_1_y,part_2_x, ... ,part_67_x,part_67_y + 0805personali01.jpg,27,83,27,98, ... 84,134 + 1084239450_e76e00b7e7.jpg,70,236,71,257, ... ,128,312 + +Let’s quickly read the CSV and get the annotations in an (N, 2) array +where N is the number of landmarks. + + + +.. code-block:: default + + + + landmarks_frame = pd.read_csv('faces/face_landmarks.csv') + + n = 65 + img_name = landmarks_frame.iloc[n, 0] + landmarks = landmarks_frame.iloc[n, 1:] + landmarks = np.asarray(landmarks) + landmarks = landmarks.astype('float').reshape(-1, 2) + + print('Image name: {}'.format(img_name)) + print('Landmarks shape: {}'.format(landmarks.shape)) + print('First 4 Landmarks: {}'.format(landmarks[:4])) + + +1.1 Write a simple helper function to show an image +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Next let’s write a simple helper function to show an image, its landmarks and use it to show a sample. + + + + +.. code-block:: default + + + def show_landmarks(image, landmarks): + """Show image with landmarks""" + plt.imshow(image) + plt.scatter(landmarks[:, 0], landmarks[:, 1], s=10, marker='.', c='r') + plt.pause(0.001) # pause a bit so that plots are updated + + plt.figure() + show_landmarks(io.imread(os.path.join('faces/', img_name)), + landmarks) + plt.show() + + + +1.2 Create a dataset class +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Now lets talk about the PyTorch dataset class + + + +``torch.utils.data.Dataset`` is an abstract class representing a +dataset. Your custom dataset should inherit ``Dataset`` and override the +following methods: + +- ``__len__`` so that ``len(dataset)`` returns the size of the dataset. +- ``__getitem__`` to support indexing such that ``dataset[i]`` can be + used to get :math:``i`` th sample + +Let’s create a dataset class for our face landmarks dataset. We will +read the csv in ``__init__`` but leave the reading of images to +``__getitem__``. This is memory efficient because all the images are not +stored in the memory at once but read as required. + +Here we show a sample of our dataset in the forma of a dict +``{'image': image, 'landmarks': landmarks}``. Our dataset will take an +optional argument ``transform`` so that any required processing can be +applied on the sample. We will see the usefulness of ``transform`` in +another recipe. + + + +.. code-block:: default + + + class FaceLandmarksDataset(Dataset): + """Face Landmarks dataset.""" + + def __init__(self, csv_file, root_dir, transform=None): + """ + Args: + csv_file (string): Path to the csv file with annotations. + root_dir (string): Directory with all the images. + transform (callable, optional): Optional transform to be applied + on a sample. + """ + self.landmarks_frame = pd.read_csv(csv_file) + self.root_dir = root_dir + self.transform = transform + + def __len__(self): + return len(self.landmarks_frame) + + def __getitem__(self, idx): + if torch.is_tensor(idx): + idx = idx.tolist() + + img_name = os.path.join(self.root_dir, + self.landmarks_frame.iloc[idx, 0]) + image = io.imread(img_name) + landmarks = self.landmarks_frame.iloc[idx, 1:] + landmarks = np.array([landmarks]) + landmarks = landmarks.astype('float').reshape(-1, 2) + sample = {'image': image, 'landmarks': landmarks} + + if self.transform: + sample = self.transform(sample) + + return sample + + + +1.3 Iterate through data samples +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +Next let’s instantiate this class and iterate through the data samples. +We will print the sizes of first 4 samples and show their landmarks. + + + +.. code-block:: default + + + face_dataset = FaceLandmarksDataset(csv_file='faces/face_landmarks.csv', + root_dir='faces/') + + fig = plt.figure() + + for i in range(len(face_dataset)): + sample = face_dataset[i] + + print(i, sample['image'].shape, sample['landmarks'].shape) + + ax = plt.subplot(1, 4, i + 1) + plt.tight_layout() + ax.set_title('Sample #{}'.format(i)) + ax.axis('off') + show_landmarks(**sample) + + if i == 3: + plt.show() + break + + + +Part 2: Data Tranformations +--------------------------- + + +Now that we have a dataset to work with and have done some level of +customization, we can move to creating custom transformations. In +computer vision, these come in handy to help generalize algorithms and +improve accuracy. A suite of transformations used at training time is +typically referred to as data augmentation and is a common practice for +modern model development. + +One issue common in handling datasets is that the samples may not all be +the same size. Most neural networks expect the images of a fixed size. +Therefore, we will need to write some prepocessing code. Let’s create +three transforms: + +- ``Rescale``: to scale the image +- ``RandomCrop``: to crop from image randomly. This is data + augmentation. +- ``ToTensor``: to convert the numpy images to torch images (we need to + swap axes). + +We will write them as callable classes instead of simple functions so +that parameters of the transform need not be passed everytime it’s +called. For this, we just need to implement ``__call__`` method and if +required, ``__init__`` method. We can then use a transform like this: + +:: + + tsfm = Transform(params) + transformed_sample = tsfm(sample) + +Observe below how these transforms had to be applied both on the image +and landmarks. + + +2.1 Create callable classes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Let’s start with creating callable classes for each transform + + + + +.. code-block:: default + + + class Rescale(object): + """Rescale the image in a sample to a given size. + + Args: + output_size (tuple or int): Desired output size. If tuple, output is + matched to output_size. If int, smaller of image edges is matched + to output_size keeping aspect ratio the same. + """ + + def __init__(self, output_size): + assert isinstance(output_size, (int, tuple)) + self.output_size = output_size + + def __call__(self, sample): + image, landmarks = sample['image'], sample['landmarks'] + + h, w = image.shape[:2] + if isinstance(self.output_size, int): + if h > w: + new_h, new_w = self.output_size * h / w, self.output_size + else: + new_h, new_w = self.output_size, self.output_size * w / h + else: + new_h, new_w = self.output_size + + new_h, new_w = int(new_h), int(new_w) + + img = transform.resize(image, (new_h, new_w)) + + # h and w are swapped for landmarks because for images, + # x and y axes are axis 1 and 0 respectively + landmarks = landmarks * [new_w / w, new_h / h] + + return {'image': img, 'landmarks': landmarks} + + + class RandomCrop(object): + """Crop randomly the image in a sample. + + Args: + output_size (tuple or int): Desired output size. If int, square crop + is made. + """ + + def __init__(self, output_size): + assert isinstance(output_size, (int, tuple)) + if isinstance(output_size, int): + self.output_size = (output_size, output_size) + else: + assert len(output_size) == 2 + self.output_size = output_size + + def __call__(self, sample): + image, landmarks = sample['image'], sample['landmarks'] + + h, w = image.shape[:2] + new_h, new_w = self.output_size + + top = np.random.randint(0, h - new_h) + left = np.random.randint(0, w - new_w) + + image = image[top: top + new_h, + left: left + new_w] + + landmarks = landmarks - [left, top] + + return {'image': image, 'landmarks': landmarks} + + + class ToTensor(object): + """Convert ndarrays in sample to Tensors.""" + + def __call__(self, sample): + image, landmarks = sample['image'], sample['landmarks'] + + # swap color axis because + # numpy image: H x W x C + # torch image: C X H X W + image = image.transpose((2, 0, 1)) + return {'image': torch.from_numpy(image), + 'landmarks': torch.from_numpy(landmarks)} + + + +2.2 Compose transforms and apply to a sample +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Next let’s compose these transforms and apply to a sample + + +Let’s say we want to rescale the shorter side of the image to 256 and +then randomly crop a square of size 224 from it. i.e, we want to compose +``Rescale`` and ``RandomCrop`` transforms. +``torchvision.transforms.Compose`` is a simple callable class which +allows us to do this. + + + +.. code-block:: default + + + scale = Rescale(256) + crop = RandomCrop(128) + composed = transforms.Compose([Rescale(256), + RandomCrop(224)]) + + # Apply each of the above transforms on sample. + fig = plt.figure() + sample = face_dataset[65] + for i, tsfrm in enumerate([scale, crop, composed]): + transformed_sample = tsfrm(sample) + + ax = plt.subplot(1, 3, i + 1) + plt.tight_layout() + ax.set_title(type(tsfrm).__name__) + show_landmarks(**transformed_sample) + + plt.show() + + + +2.3 Iterate through the dataset +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Next we will iterate through the dataset + + +Let’s put this all together to create a dataset with composed +transforms. To summarize, every time this dataset is sampled: + +- An image is read from the file on the fly +- Transforms are applied on the read image +- Since one of the transforms is random, data is augmentated on + sampling + +We can iterate over the created dataset with a ``for i in range`` loop +as before. + + + +.. code-block:: default + + + transformed_dataset = FaceLandmarksDataset(csv_file='faces/face_landmarks.csv', + root_dir='faces/', + transform=transforms.Compose([ + Rescale(256), + RandomCrop(224), + ToTensor() + ])) + + for i in range(len(transformed_dataset)): + sample = transformed_dataset[i] + + print(i, sample['image'].size(), sample['landmarks'].size()) + + if i == 3: + break + + + +Part 3: The Dataloader +---------------------- + + +By operating on the dataset directly, we are losing out on a lot of +features by using a simple ``for`` loop to iterate over the data. In +particular, we are missing out on: + +- Batching the data +- Shuffling the data +- Load the data in parallel using ``multiprocessing`` workers. + +``torch.utils.data.DataLoader`` is an iterator which provides all these +features. Parameters used below should be clear. One parameter of +interest is ``collate_fn``. You can specify how exactly the samples need +to be batched using ``collate_fn``. However, default collate should work +fine for most use cases. + + + +.. code-block:: default + + + dataloader = DataLoader(transformed_dataset, batch_size=4, + shuffle=True, num_workers=4) + + + # Helper function to show a batch + def show_landmarks_batch(sample_batched): + """Show image with landmarks for a batch of samples.""" + images_batch, landmarks_batch = \ + sample_batched['image'], sample_batched['landmarks'] + batch_size = len(images_batch) + im_size = images_batch.size(2) + + grid = utils.make_grid(images_batch) + plt.imshow(grid.numpy().transpose((1, 2, 0))) + + for i in range(batch_size): + plt.scatter(landmarks_batch[i, :, 0].numpy() + i * im_size, + landmarks_batch[i, :, 1].numpy(), + s=10, marker='.', c='r') + + plt.title('Batch from dataloader') + + for i_batch, sample_batched in enumerate(dataloader): + print(i_batch, sample_batched['image'].size(), + sample_batched['landmarks'].size()) + + # observe 4th batch and stop. + if i_batch == 3: + plt.figure() + show_landmarks_batch(sample_batched) + plt.axis('off') + plt.ioff() + plt.show() + break + + + +Now that you’ve learned how to create a custom dataloader with PyTorch, +we recommend diving deeper into the docs and customizing your workflow +even further. You can learn more in the ``torch.utils.data`` docs +`here `__. + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** ( 0 minutes 0.000 seconds) + + +.. _sphx_glr_download_recipes_recipes_custom_dataset_transforms_loader.py: + + +.. only :: html + + .. container:: sphx-glr-footer + :class: sphx-glr-footer-example + + + + .. container:: sphx-glr-download + + :download:`Download Python source code: custom_dataset_transforms_loader.py ` + + + + .. container:: sphx-glr-download + + :download:`Download Jupyter notebook: custom_dataset_transforms_loader.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/defining_a_neural_network.ipynb b/recipes/recipes/defining_a_neural_network.ipynb new file mode 100644 index 00000000000..7709cfaf1c5 --- /dev/null +++ b/recipes/recipes/defining_a_neural_network.ipynb @@ -0,0 +1,122 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\nDefining a Neural Network in PyTorch\n====================================\nDeep learning uses artificial neural networks (models), which are\ncomputing systems that are composed of many layers of interconnected\nunits. By passing data through these interconnected units, a neural\nnetwork is able to learn how to approximate the computations required to\ntransform inputs into outputs. In PyTorch, neural networks can be\nconstructed using the ``torch.nn`` package.\n\nIntroduction\n------------\nPyTorch provides the elegantly designed modules and classes, including\n``torch.nn``, to help you create and train neural networks. An\n``nn.Module`` contains layers, and a method ``forward(input)`` that\nreturns the ``output``.\n\nIn this recipe, we will use ``torch.nn`` to define a neural network\nintended for the `MNIST\ndataset `__.\n\nSetup\n-----\nBefore we begin, we need to install ``torch`` if it isn\u2019t already\navailable.\n\n::\n\n pip install torchaudio\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Steps\n-----\n\n1. Import all necessary libraries for loading our data\n2. Define and intialize the neural network\n3. Specify how data will pass through your model\n4. [Optional] Pass data through your model to test\n\n1. Import necessary libraries for loading our data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor this recipe, we will use ``torch`` and its subsidiaries ``torch.nn``\nand ``torch.nn.functional``.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2. Define and intialize the neural network\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nOur network will recognize images. We will use a process built into\nPyTorch called convolution. Convolution adds each element of an image to\nits local neighbors, weighted by a kernel, or a small martrix, that\nhelps us extract certain features (like edge detection, sharpness,\nblurriness, etc.) from the input image.\n\nThere are two requirements for defining the ``Net`` class of your model.\nThe first is writing an ``__init__`` function that references\n``nn.Module``. This function is where you define the fully connected\nlayers in your neural network.\n\nUsing convolution, we will define our model to take 1 input image\nchannel, and output match our target of 10 labels representing numbers 0\nthrough 9. This algorithm is yours to create, we will follow a standard\nMNIST algorithm.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "class Net(nn.Module):\n def __init__(self):\n super(Net, self).__init__()\n\n # First 2D convolutional layer, taking in 1 input channel (image),\n # outputting 32 convolutional features, with a square kernel size of 3\n self.conv1 = nn.Conv2d(1, 32, 3, 1)\n # Second 2D convolutional layer, taking in the 32 input layers,\n # outputting 64 convolutional features, with a square kernel size of 3\n self.conv2 = nn.Conv2d(32, 64, 3, 1)\n\n # Designed to ensure that adjacent pixels are either all 0s or all active\n # with an input probability\n self.dropout1 = nn.Dropout2d(0.25)\n self.dropout2 = nn.Dropout2d(0.5)\n\n # First fully connected layer\n self.fc1 = nn.Linear(9216, 128)\n # Second fully connected layer that outputs our 10 labels\n self.fc2 = nn.Linear(128, 10)\n\nmy_nn = Net()\nprint(my_nn)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We have finished defining our neural network, now we have to define how\nour data will pass through it.\n\n3. Specify how data will pass through your model\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhen you use PyTorch to build a model, you just have to define the\n``forward`` function, that will pass the data into the computation graph\n(i.e. our neural network). This will represent our feed-forward\nalgorithm.\n\nYou can use any of the Tensor operations in the ``forward`` function.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "class Net(nn.Module):\n def __init__(self):\n super(Net, self).__init__()\n self.conv1 = nn.Conv2d(1, 32, 3, 1)\n self.conv2 = nn.Conv2d(32, 64, 3, 1)\n self.dropout1 = nn.Dropout2d(0.25)\n self.dropout2 = nn.Dropout2d(0.5)\n self.fc1 = nn.Linear(9216, 128)\n self.fc2 = nn.Linear(128, 10)\n\n # x represents our data\n def forward(self, x):\n # Pass data through conv1\n x = self.conv1(x)\n # Use the rectified-linear activation function over x\n x = F.relu(x)\n\n x = self.conv2(x)\n x = F.relu(x)\n\n # Run max pooling over x\n x = F.max_pool2d(x, 2)\n # Pass data through dropout1\n x = self.dropout1(x)\n # Flatten x with start_dim=1\n x = torch.flatten(x, 1)\n # Pass data through fc1\n x = self.fc1(x)\n x = F.relu(x)\n x = self.dropout2(x)\n x = self.fc2(x)\n\n # Apply softmax to x \n output = F.log_softmax(x, dim=1)\n return output" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4. [Optional] Pass data through your model to test\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nTo ensure we receive our desired output, let\u2019s test our model by passing\nsome random data through it.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Equates to one random 28x28 image\nrandom_data = torch.rand((1, 1, 28, 28))\n\nmy_nn = Net()\nresult = my_nn(random_data)\nprint (result)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Each number in this resulting tensor equates to the prediction of the\nlabel the random tensor is associated to.\n\nCongratulations! You have successfully defined a neural network in\nPyTorch.\n\nLearn More\n----------\n\nTake a look at these other recipes to continue your learning:\n\n- `What is a state_dict in PyTorch `__\n- `Saving and loading models for inference in PyTorch `__\n\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/recipes/recipes/defining_a_neural_network.py b/recipes/recipes/defining_a_neural_network.py new file mode 100644 index 00000000000..42e2d3370ca --- /dev/null +++ b/recipes/recipes/defining_a_neural_network.py @@ -0,0 +1,183 @@ +""" +Defining a Neural Network in PyTorch +==================================== +Deep learning uses artificial neural networks (models), which are +computing systems that are composed of many layers of interconnected +units. By passing data through these interconnected units, a neural +network is able to learn how to approximate the computations required to +transform inputs into outputs. In PyTorch, neural networks can be +constructed using the ``torch.nn`` package. + +Introduction +------------ +PyTorch provides the elegantly designed modules and classes, including +``torch.nn``, to help you create and train neural networks. An +``nn.Module`` contains layers, and a method ``forward(input)`` that +returns the ``output``. + +In this recipe, we will use ``torch.nn`` to define a neural network +intended for the `MNIST +dataset `__. + +Setup +----- +Before we begin, we need to install ``torch`` if it isn’t already +available. + +:: + + pip install torchaudio + + +""" + + +###################################################################### +# Steps +# ----- +# +# 1. Import all necessary libraries for loading our data +# 2. Define and intialize the neural network +# 3. Specify how data will pass through your model +# 4. [Optional] Pass data through your model to test +# +# 1. Import necessary libraries for loading our data +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` +# and ``torch.nn.functional``. +# + +import torch +import torch.nn as nn +import torch.nn.functional as F + + +###################################################################### +# 2. Define and intialize the neural network +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Our network will recognize images. We will use a process built into +# PyTorch called convolution. Convolution adds each element of an image to +# its local neighbors, weighted by a kernel, or a small martrix, that +# helps us extract certain features (like edge detection, sharpness, +# blurriness, etc.) from the input image. +# +# There are two requirements for defining the ``Net`` class of your model. +# The first is writing an ``__init__`` function that references +# ``nn.Module``. This function is where you define the fully connected +# layers in your neural network. +# +# Using convolution, we will define our model to take 1 input image +# channel, and output match our target of 10 labels representing numbers 0 +# through 9. This algorithm is yours to create, we will follow a standard +# MNIST algorithm. +# + +class Net(nn.Module): + def __init__(self): + super(Net, self).__init__() + + # First 2D convolutional layer, taking in 1 input channel (image), + # outputting 32 convolutional features, with a square kernel size of 3 + self.conv1 = nn.Conv2d(1, 32, 3, 1) + # Second 2D convolutional layer, taking in the 32 input layers, + # outputting 64 convolutional features, with a square kernel size of 3 + self.conv2 = nn.Conv2d(32, 64, 3, 1) + + # Designed to ensure that adjacent pixels are either all 0s or all active + # with an input probability + self.dropout1 = nn.Dropout2d(0.25) + self.dropout2 = nn.Dropout2d(0.5) + + # First fully connected layer + self.fc1 = nn.Linear(9216, 128) + # Second fully connected layer that outputs our 10 labels + self.fc2 = nn.Linear(128, 10) + +my_nn = Net() +print(my_nn) + + +###################################################################### +# We have finished defining our neural network, now we have to define how +# our data will pass through it. +# +# 3. Specify how data will pass through your model +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# When you use PyTorch to build a model, you just have to define the +# ``forward`` function, that will pass the data into the computation graph +# (i.e. our neural network). This will represent our feed-forward +# algorithm. +# +# You can use any of the Tensor operations in the ``forward`` function. +# + +class Net(nn.Module): + def __init__(self): + super(Net, self).__init__() + self.conv1 = nn.Conv2d(1, 32, 3, 1) + self.conv2 = nn.Conv2d(32, 64, 3, 1) + self.dropout1 = nn.Dropout2d(0.25) + self.dropout2 = nn.Dropout2d(0.5) + self.fc1 = nn.Linear(9216, 128) + self.fc2 = nn.Linear(128, 10) + + # x represents our data + def forward(self, x): + # Pass data through conv1 + x = self.conv1(x) + # Use the rectified-linear activation function over x + x = F.relu(x) + + x = self.conv2(x) + x = F.relu(x) + + # Run max pooling over x + x = F.max_pool2d(x, 2) + # Pass data through dropout1 + x = self.dropout1(x) + # Flatten x with start_dim=1 + x = torch.flatten(x, 1) + # Pass data through fc1 + x = self.fc1(x) + x = F.relu(x) + x = self.dropout2(x) + x = self.fc2(x) + + # Apply softmax to x + output = F.log_softmax(x, dim=1) + return output + + +###################################################################### +# 4. [Optional] Pass data through your model to test +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# To ensure we receive our desired output, let’s test our model by passing +# some random data through it. +# + +# Equates to one random 28x28 image +random_data = torch.rand((1, 1, 28, 28)) + +my_nn = Net() +result = my_nn(random_data) +print (result) + + +###################################################################### +# Each number in this resulting tensor equates to the prediction of the +# label the random tensor is associated to. +# +# Congratulations! You have successfully defined a neural network in +# PyTorch. +# +# Learn More +# ---------- +# +# Take a look at these other recipes to continue your learning: +# +# - `What is a state_dict in PyTorch `__ +# - `Saving and loading models for inference in PyTorch `__ diff --git a/recipes/recipes/defining_a_neural_network.rst b/recipes/recipes/defining_a_neural_network.rst new file mode 100644 index 00000000000..6097b49d295 --- /dev/null +++ b/recipes/recipes/defining_a_neural_network.rst @@ -0,0 +1,234 @@ +.. note:: + :class: sphx-glr-download-link-note + + Click :ref:`here ` to download the full example code +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_recipes_recipes_defining_a_neural_network.py: + + +Defining a Neural Network in PyTorch +==================================== +Deep learning uses artificial neural networks (models), which are +computing systems that are composed of many layers of interconnected +units. By passing data through these interconnected units, a neural +network is able to learn how to approximate the computations required to +transform inputs into outputs. In PyTorch, neural networks can be +constructed using the ``torch.nn`` package. + +Introduction +------------ +PyTorch provides the elegantly designed modules and classes, including +``torch.nn``, to help you create and train neural networks. An +``nn.Module`` contains layers, and a method ``forward(input)`` that +returns the ``output``. + +In this recipe, we will use ``torch.nn`` to define a neural network +intended for the `MNIST +dataset `__. + +Setup +----- +Before we begin, we need to install ``torch`` if it isn’t already +available. + +:: + + pip install torchaudio +Steps +----- + +1. Import all necessary libraries for loading our data +2. Define and intialize the neural network +3. Specify how data will pass through your model +4. [Optional] Pass data through your model to test + +1. Import necessary libraries for loading our data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` +and ``torch.nn.functional``. + + + +.. code-block:: default + + + import torch + import torch.nn as nn + import torch.nn.functional as F + + + +2. Define and intialize the neural network +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Our network will recognize images. We will use a process built into +PyTorch called convolution. Convolution adds each element of an image to +its local neighbors, weighted by a kernel, or a small martrix, that +helps us extract certain features (like edge detection, sharpness, +blurriness, etc.) from the input image. + +There are two requirements for defining the ``Net`` class of your model. +The first is writing an ``__init__`` function that references +``nn.Module``. This function is where you define the fully connected +layers in your neural network. + +Using convolution, we will define our model to take 1 input image +channel, and output match our target of 10 labels representing numbers 0 +through 9. This algorithm is yours to create, we will follow a standard +MNIST algorithm. + + + +.. code-block:: default + + + class Net(nn.Module): + def __init__(self): + super(Net, self).__init__() + + # First 2D convolutional layer, taking in 1 input channel (image), + # outputting 32 convolutional features, with a square kernel size of 3 + self.conv1 = nn.Conv2d(1, 32, 3, 1) + # Second 2D convolutional layer, taking in the 32 input layers, + # outputting 64 convolutional features, with a square kernel size of 3 + self.conv2 = nn.Conv2d(32, 64, 3, 1) + + # Designed to ensure that adjacent pixels are either all 0s or all active + # with an input probability + self.dropout1 = nn.Dropout2d(0.25) + self.dropout2 = nn.Dropout2d(0.5) + + # First fully connected layer + self.fc1 = nn.Linear(9216, 128) + # Second fully connected layer that outputs our 10 labels + self.fc2 = nn.Linear(128, 10) + + my_nn = Net() + print(my_nn) + + + +We have finished defining our neural network, now we have to define how +our data will pass through it. + +3. Specify how data will pass through your model +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When you use PyTorch to build a model, you just have to define the +``forward`` function, that will pass the data into the computation graph +(i.e. our neural network). This will represent our feed-forward +algorithm. + +You can use any of the Tensor operations in the ``forward`` function. + + + +.. code-block:: default + + + class Net(nn.Module): + def __init__(self): + super(Net, self).__init__() + self.conv1 = nn.Conv2d(1, 32, 3, 1) + self.conv2 = nn.Conv2d(32, 64, 3, 1) + self.dropout1 = nn.Dropout2d(0.25) + self.dropout2 = nn.Dropout2d(0.5) + self.fc1 = nn.Linear(9216, 128) + self.fc2 = nn.Linear(128, 10) + + # x represents our data + def forward(self, x): + # Pass data through conv1 + x = self.conv1(x) + # Use the rectified-linear activation function over x + x = F.relu(x) + + x = self.conv2(x) + x = F.relu(x) + + # Run max pooling over x + x = F.max_pool2d(x, 2) + # Pass data through dropout1 + x = self.dropout1(x) + # Flatten x with start_dim=1 + x = torch.flatten(x, 1) + # Pass data through fc1 + x = self.fc1(x) + x = F.relu(x) + x = self.dropout2(x) + x = self.fc2(x) + + # Apply softmax to x + output = F.log_softmax(x, dim=1) + return output + + + +4. [Optional] Pass data through your model to test +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To ensure we receive our desired output, let’s test our model by passing +some random data through it. + + + +.. code-block:: default + + + # Equates to one random 28x28 image + random_data = torch.rand((1, 1, 28, 28)) + + my_nn = Net() + result = my_nn(random_data) + print (result) + + + +Each number in this resulting tensor equates to the prediction of the +label the random tensor is associated to. + +Congratulations! You have successfully defined a neural network in +PyTorch. + +Learn More +---------- + +Take a look at these other recipes to continue your learning: + +- `What is a state_dict in PyTorch `__ +- `Saving and loading models for inference in PyTorch `__ + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** ( 0 minutes 0.000 seconds) + + +.. _sphx_glr_download_recipes_recipes_defining_a_neural_network.py: + + +.. only :: html + + .. container:: sphx-glr-footer + :class: sphx-glr-footer-example + + + + .. container:: sphx-glr-download + + :download:`Download Python source code: defining_a_neural_network.py ` + + + + .. container:: sphx-glr-download + + :download:`Download Jupyter notebook: defining_a_neural_network.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/dynamic_quantization.ipynb b/recipes/recipes/dynamic_quantization.ipynb new file mode 100644 index 00000000000..f341da5275a --- /dev/null +++ b/recipes/recipes/dynamic_quantization.ipynb @@ -0,0 +1,133 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\nDynamic Quantization\n====================\n\nIn this recipe you will see how to take advantage of Dynamic\nQuantization to accelerate inference on an LSTM-style recurrent neural\nnetwork. This reduces the size of the model weights and speeds up model\nexecution.\n\nIntroduction\n-------------\n\nThere are a number of trade-offs that can be made when designing neural\nnetworks. During model developmenet and training you can alter the\nnumber of layers and number of parameters in a recurrent neural network\nand trade-off accuracy against model size and/or model latency or\nthroughput. Such changes can take lot of time and compute resources\nbecause you are iterating over the model training. Quantization gives\nyou a way to make a similar trade off between performance and model\naccuracy with a known model after training is completed.\n\nYou can give it a try in a single session and you will certainly reduce\nyour model size significantly and may get a significant latency\nreduction without losing a lot of accuracy.\n\nWhat is dynamic quantization?\n-------------\n\nQuantizing a network means converting it to use a reduced precision\ninteger representation for the weights and/or activations. This saves on\nmodel size and allows the use of higher throughput math operations on\nyour CPU or GPU.\n\nWhen converting from floating point to integer values you are\nessentially multiplying the floating point value by some scale factor\nand rounding the result to a whole number. The various quantization\napproaches differ in the way they approach determining that scale\nfactor.\n\nThe key idea with dynamic quantization as described here is that we are\ngoing to determine the scale factor for activations dynamically based on\nthe data range observed at runtime. This ensures that the scale factor\nis \"tuned\" so that as much signal as possible about each observed\ndataset is preserved.\n\nThe model parameters on the other hand are known during model conversion\nand they are converted ahead of time and stored in INT8 form.\n\nArithmetic in the quantized model is done using vectorized INT8\ninstructions. Accumulation is typically done with INT16 or INT32 to\navoid overflow. This higher precision value is scaled back to INT8 if\nthe next layer is quantized or converted to FP32 for output.\n\nDynamic quantization is relatively free of tuning parameters which makes\nit well suited to be added into production pipelines as a standard part\nof converting LSTM models to deployment.\n\n\n\n

Note

Limitations on the approach taken here\n\n\n This recipe provides a quick introduction to the dynamic quantization\n features in PyTorch and the workflow for using it. Our focus is on\n explaining the specific functions used to convert the model. We will\n make a number of significant simplifications in the interest of brevity\n and clarity

\n\n\n1. You will start with a minimal LSTM network\n2. You are simply going to initialize the network with a random hidden\n state\n3. You are going to test the network with random inputs\n4. You are not going to train the network in this tutorial\n5. You will see that the quantized form of this network is smaller and\n runs faster than the floating point network we started with\n6. You will see that the output values are generally in the same\n ballpark as the output of the FP32 network, but we are not\n demonstrating here the expected accuracy loss on a real trained\n network\n\nYou will see how dynamic quantization is done and be able to see\nsuggestive reductions in memory use and latency times. Providing a\ndemonstration that the technique can preserve high levels of model\naccuracy on a trained LSTM is left to a more advanced tutorial. If you\nwant to move right away to that more rigorous treatment please proceed\nto the `advanced dynamic quantization\ntutorial `__.\n\nSteps\n-------------\n\nThis recipe has 5 steps.\n\n1. Set Up - Here you define a very simple LSTM, import modules, and establish\n some random input tensors.\n\n2. Do the Quantization - Here you instantiate a floating point model and then create quantized\n version of it.\n\n3. Look at Model Size - Here you show that the model size gets smaller.\n\n4. Look at Latency - Here you run the two models and compare model runtime (latency).\n\n5. Look at Accuracy - Here you run the two models and compare outputs.\n\n\n1: Set Up\n~~~~~~~~~~~~~~~\nThis is a straightfoward bit of code to set up for the rest of the\nrecipe.\n\nThe unique module we are importing here is torch.quantization which\nincludes PyTorch's quantized operators and conversion functions. We also\ndefine a very simple LSTM model and set up some inputs.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# import the modules used here in this recipe\nimport torch\nimport torch.quantization\nimport torch.nn as nn\nimport copy\nimport os\nimport time\n\n# define a very, very simple LSTM for demonstration purposes\n# in this case, we are wrapping nn.LSTM, one layer, no pre or post processing\n# inspired by \n# https://pytorch.org/tutorials/beginner/nlp/sequence_models_tutorial.html, by Robert Guthrie\n# and https://pytorch.org/tutorials/advanced/dynamic_quantization_tutorial.html\nclass lstm_for_demonstration(nn.Module):\n \"\"\"Elementary Long Short Term Memory style model which simply wraps nn.LSTM\n Not to be used for anything other than demonstration. \n \"\"\" \n def __init__(self,in_dim,out_dim,depth):\n super(lstm_for_demonstration,self).__init__()\n self.lstm = nn.LSTM(in_dim,out_dim,depth)\n\n def forward(self,inputs,hidden):\n out,hidden = self.lstm(inputs,hidden)\n return out, hidden\n\n \ntorch.manual_seed(29592) # set the seed for reproducibility\n\n#shape parameters\nmodel_dimension=8\nsequence_length=20\nbatch_size=1\nlstm_depth=1\n\n# random data for input\ninputs = torch.randn(sequence_length,batch_size,model_dimension)\n# hidden is actually is a tuple of the initial hidden state and the initial cell state\nhidden = (torch.randn(lstm_depth,batch_size,model_dimension), torch.randn(lstm_depth,batch_size,model_dimension))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2: Do the Quantization\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nNow we get to the fun part. First we create an instance of the model\ncalled float\\_lstm then we are going to quantize it. We're going to use\nthe\n\n::\n\n torch.quantization.quantize_dynamic()\n\nfunction here (`see\ndocumentation `__)\nwhich takes the model, then a list of the submodules which we want to\nhave quantized if they appear, then the datatype we are targeting. This\nfunction returns a quantized version of the original model as a new\nmodule.\n\nThat's all it takes.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# here is our floating point instance \nfloat_lstm = lstm_for_demonstration(model_dimension, model_dimension,lstm_depth)\n\n# this is the call that does the work\nquantized_lstm = torch.quantization.quantize_dynamic(\n float_lstm, {nn.LSTM, nn.Linear}, dtype=torch.qint8\n)\n\n# show the changes that were made\nprint('Here is the floating point version of this module:')\nprint(float_lstm)\nprint('')\nprint('and now the quantized version:')\nprint(quantized_lstm)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3. Look at Model Size\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nOk, so we've quantized the model. What does that get us? Well the first\nbenefit is that we've replaced the FP32 model parameters with INT8\nvalues (and some recorded scale factors). This means about 75% less data\nto store and move around. With the default values the reduction shown\nbelow will be less than 75% but if you increase the model size above\n(for example you can set model dimension to something like 80) this will\nconverge towards 4x smaller as the stored model size dominated more and\nmore by the parameter values.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def print_size_of_model(model, label=\"\"):\n torch.save(model.state_dict(), \"temp.p\")\n size=os.path.getsize(\"temp.p\")\n print(\"model: \",label,' \\t','Size (KB):', size/1e3)\n os.remove('temp.p')\n return size\n\n# compare the sizes\nf=print_size_of_model(float_lstm,\"fp32\")\nq=print_size_of_model(quantized_lstm,\"int8\")\nprint(\"{0:.2f} times smaller\".format(f/q))\n\n# note that this value is wrong in PyTorch 1.4 due to https://github.com/pytorch/pytorch/issues/31468\n# this will be fixed in 1.5 with https://github.com/pytorch/pytorch/pull/31540" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4. Look at Latency\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nThe second benefit is that the quantized model will typically run\nfaster. This is due to a combinations of effects including at least:\n\n1. Less time spent moving parameter data in\n2. Faster INT8 operations\n\nAs you will see the quantized version of this super-simple network runs\nfaster. This will generally be true of more complex networks but as they\nsay \"your milage may vary\" depending on a number of factors including\nthe structure of the model and the hardware you are running on.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# compare the performance\nprint(\"Floating point FP32\")\n# %timeit float_lstm.forward(inputs, hidden)\n\nprint(\"Quantized INT8\")\n# %timeit quantized_lstm.forward(inputs,hidden)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "5: Look at Accuracy\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nWe are not going to do a careful look at accuracy here because we are\nworking with a randomly initialized network rather than a properly\ntrained one. However, I think it is worth quickly showing that the\nquantized network does produce output tensors that are \"in the same\nballpark\" as the original one.\n\nFor a more detailed analysis please see the more advanced tutorials\nreferenced at the end of this recipe.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# run the float model\nout1, hidden1 = float_lstm(inputs, hidden)\nmag1 = torch.mean(abs(out1)).item()\nprint('mean absolute value of output tensor values in the FP32 model is {0:.5f} '.format(mag1))\n\n# run the quantized model\nout2, hidden2 = quantized_lstm(inputs, hidden)\nmag2 = torch.mean(abs(out2)).item()\nprint('mean absolute value of output tensor values in the INT8 model is {0:.5f}'.format(mag2))\n\n# compare them \nmag3 = torch.mean(abs(out1-out2)).item()\nprint('mean absolute value of the difference between the output tensors is {0:.5f} or {1:.2f} percent'.format(mag3,mag3/mag1*100))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Learn More\n------------\nWe've explained what dynamic quantization is, what benefits it brings,\nand you have used the ``torch.quantization.quantize_dynamic()`` function\nto quickly quantize a simple LSTM model.\n\nThis was a fast and high level treatment of this material; for more\ndetail please continue learning with `(experimental) Dynamic Quantization on an LSTM Word Language Model Tutorial `_.\n\n\nAdditional Resources\n=========\nDocumentation\n~~~~~~~~~~~~~~\n\n`Quantization API Documentaion `_\n\nTutorials\n~~~~~~~~~~~~~~\n\n`(experimental) Dynamic Quantization on BERT `_\n\n`(experimental) Dynamic Quantization on an LSTM Word Language Model `_\n\nBlogs\n~~~~~~~~~~~~~~\n` Introduction to Quantization on PyTorch `_\n\n\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/recipes/recipes/dynamic_quantization.py b/recipes/recipes/dynamic_quantization.py new file mode 100644 index 00000000000..78dc1f5408a --- /dev/null +++ b/recipes/recipes/dynamic_quantization.py @@ -0,0 +1,306 @@ +""" +Dynamic Quantization +==================== + +In this recipe you will see how to take advantage of Dynamic +Quantization to accelerate inference on an LSTM-style recurrent neural +network. This reduces the size of the model weights and speeds up model +execution. + +Introduction +------------- + +There are a number of trade-offs that can be made when designing neural +networks. During model developmenet and training you can alter the +number of layers and number of parameters in a recurrent neural network +and trade-off accuracy against model size and/or model latency or +throughput. Such changes can take lot of time and compute resources +because you are iterating over the model training. Quantization gives +you a way to make a similar trade off between performance and model +accuracy with a known model after training is completed. + +You can give it a try in a single session and you will certainly reduce +your model size significantly and may get a significant latency +reduction without losing a lot of accuracy. + +What is dynamic quantization? +------------- + +Quantizing a network means converting it to use a reduced precision +integer representation for the weights and/or activations. This saves on +model size and allows the use of higher throughput math operations on +your CPU or GPU. + +When converting from floating point to integer values you are +essentially multiplying the floating point value by some scale factor +and rounding the result to a whole number. The various quantization +approaches differ in the way they approach determining that scale +factor. + +The key idea with dynamic quantization as described here is that we are +going to determine the scale factor for activations dynamically based on +the data range observed at runtime. This ensures that the scale factor +is "tuned" so that as much signal as possible about each observed +dataset is preserved. + +The model parameters on the other hand are known during model conversion +and they are converted ahead of time and stored in INT8 form. + +Arithmetic in the quantized model is done using vectorized INT8 +instructions. Accumulation is typically done with INT16 or INT32 to +avoid overflow. This higher precision value is scaled back to INT8 if +the next layer is quantized or converted to FP32 for output. + +Dynamic quantization is relatively free of tuning parameters which makes +it well suited to be added into production pipelines as a standard part +of converting LSTM models to deployment. + + + +.. note:: + Limitations on the approach taken here + + + This recipe provides a quick introduction to the dynamic quantization + features in PyTorch and the workflow for using it. Our focus is on + explaining the specific functions used to convert the model. We will + make a number of significant simplifications in the interest of brevity + and clarity + + +1. You will start with a minimal LSTM network +2. You are simply going to initialize the network with a random hidden + state +3. You are going to test the network with random inputs +4. You are not going to train the network in this tutorial +5. You will see that the quantized form of this network is smaller and + runs faster than the floating point network we started with +6. You will see that the output values are generally in the same + ballpark as the output of the FP32 network, but we are not + demonstrating here the expected accuracy loss on a real trained + network + +You will see how dynamic quantization is done and be able to see +suggestive reductions in memory use and latency times. Providing a +demonstration that the technique can preserve high levels of model +accuracy on a trained LSTM is left to a more advanced tutorial. If you +want to move right away to that more rigorous treatment please proceed +to the `advanced dynamic quantization +tutorial `__. + +Steps +------------- + +This recipe has 5 steps. + +1. Set Up - Here you define a very simple LSTM, import modules, and establish + some random input tensors. + +2. Do the Quantization - Here you instantiate a floating point model and then create quantized + version of it. + +3. Look at Model Size - Here you show that the model size gets smaller. + +4. Look at Latency - Here you run the two models and compare model runtime (latency). + +5. Look at Accuracy - Here you run the two models and compare outputs. + + +1: Set Up +~~~~~~~~~~~~~~~ +This is a straightfoward bit of code to set up for the rest of the +recipe. + +The unique module we are importing here is torch.quantization which +includes PyTorch's quantized operators and conversion functions. We also +define a very simple LSTM model and set up some inputs. + +""" + +# import the modules used here in this recipe +import torch +import torch.quantization +import torch.nn as nn +import copy +import os +import time + +# define a very, very simple LSTM for demonstration purposes +# in this case, we are wrapping nn.LSTM, one layer, no pre or post processing +# inspired by +# https://pytorch.org/tutorials/beginner/nlp/sequence_models_tutorial.html, by Robert Guthrie +# and https://pytorch.org/tutorials/advanced/dynamic_quantization_tutorial.html +class lstm_for_demonstration(nn.Module): + """Elementary Long Short Term Memory style model which simply wraps nn.LSTM + Not to be used for anything other than demonstration. + """ + def __init__(self,in_dim,out_dim,depth): + super(lstm_for_demonstration,self).__init__() + self.lstm = nn.LSTM(in_dim,out_dim,depth) + + def forward(self,inputs,hidden): + out,hidden = self.lstm(inputs,hidden) + return out, hidden + + +torch.manual_seed(29592) # set the seed for reproducibility + +#shape parameters +model_dimension=8 +sequence_length=20 +batch_size=1 +lstm_depth=1 + +# random data for input +inputs = torch.randn(sequence_length,batch_size,model_dimension) +# hidden is actually is a tuple of the initial hidden state and the initial cell state +hidden = (torch.randn(lstm_depth,batch_size,model_dimension), torch.randn(lstm_depth,batch_size,model_dimension)) + + +###################################################################### +# 2: Do the Quantization +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Now we get to the fun part. First we create an instance of the model +# called float\_lstm then we are going to quantize it. We're going to use +# the +# +# :: +# +# torch.quantization.quantize_dynamic() +# +# function here (`see +# documentation `__) +# which takes the model, then a list of the submodules which we want to +# have quantized if they appear, then the datatype we are targeting. This +# function returns a quantized version of the original model as a new +# module. +# +# That's all it takes. +# + + # here is our floating point instance +float_lstm = lstm_for_demonstration(model_dimension, model_dimension,lstm_depth) + +# this is the call that does the work +quantized_lstm = torch.quantization.quantize_dynamic( + float_lstm, {nn.LSTM, nn.Linear}, dtype=torch.qint8 +) + +# show the changes that were made +print('Here is the floating point version of this module:') +print(float_lstm) +print('') +print('and now the quantized version:') +print(quantized_lstm) + + +###################################################################### +# 3. Look at Model Size +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Ok, so we've quantized the model. What does that get us? Well the first +# benefit is that we've replaced the FP32 model parameters with INT8 +# values (and some recorded scale factors). This means about 75% less data +# to store and move around. With the default values the reduction shown +# below will be less than 75% but if you increase the model size above +# (for example you can set model dimension to something like 80) this will +# converge towards 4x smaller as the stored model size dominated more and +# more by the parameter values. +# + +def print_size_of_model(model, label=""): + torch.save(model.state_dict(), "temp.p") + size=os.path.getsize("temp.p") + print("model: ",label,' \t','Size (KB):', size/1e3) + os.remove('temp.p') + return size + +# compare the sizes +f=print_size_of_model(float_lstm,"fp32") +q=print_size_of_model(quantized_lstm,"int8") +print("{0:.2f} times smaller".format(f/q)) + +# note that this value is wrong in PyTorch 1.4 due to https://github.com/pytorch/pytorch/issues/31468 +# this will be fixed in 1.5 with https://github.com/pytorch/pytorch/pull/31540 + + +###################################################################### +# 4. Look at Latency +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# The second benefit is that the quantized model will typically run +# faster. This is due to a combinations of effects including at least: +# +# 1. Less time spent moving parameter data in +# 2. Faster INT8 operations +# +# As you will see the quantized version of this super-simple network runs +# faster. This will generally be true of more complex networks but as they +# say "your milage may vary" depending on a number of factors including +# the structure of the model and the hardware you are running on. +# + +# compare the performance +print("Floating point FP32") +# %timeit float_lstm.forward(inputs, hidden) + +print("Quantized INT8") +# %timeit quantized_lstm.forward(inputs,hidden) + + +###################################################################### +# 5: Look at Accuracy +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# We are not going to do a careful look at accuracy here because we are +# working with a randomly initialized network rather than a properly +# trained one. However, I think it is worth quickly showing that the +# quantized network does produce output tensors that are "in the same +# ballpark" as the original one. +# +# For a more detailed analysis please see the more advanced tutorials +# referenced at the end of this recipe. +# + +# run the float model +out1, hidden1 = float_lstm(inputs, hidden) +mag1 = torch.mean(abs(out1)).item() +print('mean absolute value of output tensor values in the FP32 model is {0:.5f} '.format(mag1)) + +# run the quantized model +out2, hidden2 = quantized_lstm(inputs, hidden) +mag2 = torch.mean(abs(out2)).item() +print('mean absolute value of output tensor values in the INT8 model is {0:.5f}'.format(mag2)) + +# compare them +mag3 = torch.mean(abs(out1-out2)).item() +print('mean absolute value of the difference between the output tensors is {0:.5f} or {1:.2f} percent'.format(mag3,mag3/mag1*100)) + + +###################################################################### +# Learn More +# ------------ +# We've explained what dynamic quantization is, what benefits it brings, +# and you have used the ``torch.quantization.quantize_dynamic()`` function +# to quickly quantize a simple LSTM model. +# +# This was a fast and high level treatment of this material; for more +# detail please continue learning with `(experimental) Dynamic Quantization on an LSTM Word Language Model Tutorial `_. +# +# +# Additional Resources +# ========= +# Documentation +# ~~~~~~~~~~~~~~ +# +# `Quantization API Documentaion `_ +# +# Tutorials +# ~~~~~~~~~~~~~~ +# +# `(experimental) Dynamic Quantization on BERT `_ +# +# `(experimental) Dynamic Quantization on an LSTM Word Language Model `_ +# +# Blogs +# ~~~~~~~~~~~~~~ +# ` Introduction to Quantization on PyTorch `_ +# diff --git a/recipes/recipes/dynamic_quantization.rst b/recipes/recipes/dynamic_quantization.rst new file mode 100644 index 00000000000..7657ef80fcf --- /dev/null +++ b/recipes/recipes/dynamic_quantization.rst @@ -0,0 +1,364 @@ +.. note:: + :class: sphx-glr-download-link-note + + Click :ref:`here ` to download the full example code +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_recipes_recipes_dynamic_quantization.py: + + +Dynamic Quantization +==================== + +In this recipe you will see how to take advantage of Dynamic +Quantization to accelerate inference on an LSTM-style recurrent neural +network. This reduces the size of the model weights and speeds up model +execution. + +Introduction +------------- + +There are a number of trade-offs that can be made when designing neural +networks. During model developmenet and training you can alter the +number of layers and number of parameters in a recurrent neural network +and trade-off accuracy against model size and/or model latency or +throughput. Such changes can take lot of time and compute resources +because you are iterating over the model training. Quantization gives +you a way to make a similar trade off between performance and model +accuracy with a known model after training is completed. + +You can give it a try in a single session and you will certainly reduce +your model size significantly and may get a significant latency +reduction without losing a lot of accuracy. + +What is dynamic quantization? +------------- + +Quantizing a network means converting it to use a reduced precision +integer representation for the weights and/or activations. This saves on +model size and allows the use of higher throughput math operations on +your CPU or GPU. + +When converting from floating point to integer values you are +essentially multiplying the floating point value by some scale factor +and rounding the result to a whole number. The various quantization +approaches differ in the way they approach determining that scale +factor. + +The key idea with dynamic quantization as described here is that we are +going to determine the scale factor for activations dynamically based on +the data range observed at runtime. This ensures that the scale factor +is "tuned" so that as much signal as possible about each observed +dataset is preserved. + +The model parameters on the other hand are known during model conversion +and they are converted ahead of time and stored in INT8 form. + +Arithmetic in the quantized model is done using vectorized INT8 +instructions. Accumulation is typically done with INT16 or INT32 to +avoid overflow. This higher precision value is scaled back to INT8 if +the next layer is quantized or converted to FP32 for output. + +Dynamic quantization is relatively free of tuning parameters which makes +it well suited to be added into production pipelines as a standard part +of converting LSTM models to deployment. + + + +.. note:: + Limitations on the approach taken here + + + This recipe provides a quick introduction to the dynamic quantization + features in PyTorch and the workflow for using it. Our focus is on + explaining the specific functions used to convert the model. We will + make a number of significant simplifications in the interest of brevity + and clarity + + +1. You will start with a minimal LSTM network +2. You are simply going to initialize the network with a random hidden + state +3. You are going to test the network with random inputs +4. You are not going to train the network in this tutorial +5. You will see that the quantized form of this network is smaller and + runs faster than the floating point network we started with +6. You will see that the output values are generally in the same + ballpark as the output of the FP32 network, but we are not + demonstrating here the expected accuracy loss on a real trained + network + +You will see how dynamic quantization is done and be able to see +suggestive reductions in memory use and latency times. Providing a +demonstration that the technique can preserve high levels of model +accuracy on a trained LSTM is left to a more advanced tutorial. If you +want to move right away to that more rigorous treatment please proceed +to the `advanced dynamic quantization +tutorial `__. + +Steps +------------- + +This recipe has 5 steps. + +1. Set Up - Here you define a very simple LSTM, import modules, and establish + some random input tensors. + +2. Do the Quantization - Here you instantiate a floating point model and then create quantized + version of it. + +3. Look at Model Size - Here you show that the model size gets smaller. + +4. Look at Latency - Here you run the two models and compare model runtime (latency). + +5. Look at Accuracy - Here you run the two models and compare outputs. + + +1: Set Up +~~~~~~~~~~~~~~~ +This is a straightfoward bit of code to set up for the rest of the +recipe. + +The unique module we are importing here is torch.quantization which +includes PyTorch's quantized operators and conversion functions. We also +define a very simple LSTM model and set up some inputs. + +.. code-block:: default + + + # import the modules used here in this recipe + import torch + import torch.quantization + import torch.nn as nn + import copy + import os + import time + + # define a very, very simple LSTM for demonstration purposes + # in this case, we are wrapping nn.LSTM, one layer, no pre or post processing + # inspired by + # https://pytorch.org/tutorials/beginner/nlp/sequence_models_tutorial.html, by Robert Guthrie + # and https://pytorch.org/tutorials/advanced/dynamic_quantization_tutorial.html + class lstm_for_demonstration(nn.Module): + """Elementary Long Short Term Memory style model which simply wraps nn.LSTM + Not to be used for anything other than demonstration. + """ + def __init__(self,in_dim,out_dim,depth): + super(lstm_for_demonstration,self).__init__() + self.lstm = nn.LSTM(in_dim,out_dim,depth) + + def forward(self,inputs,hidden): + out,hidden = self.lstm(inputs,hidden) + return out, hidden + + + torch.manual_seed(29592) # set the seed for reproducibility + + #shape parameters + model_dimension=8 + sequence_length=20 + batch_size=1 + lstm_depth=1 + + # random data for input + inputs = torch.randn(sequence_length,batch_size,model_dimension) + # hidden is actually is a tuple of the initial hidden state and the initial cell state + hidden = (torch.randn(lstm_depth,batch_size,model_dimension), torch.randn(lstm_depth,batch_size,model_dimension)) + + + +2: Do the Quantization +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Now we get to the fun part. First we create an instance of the model +called float\_lstm then we are going to quantize it. We're going to use +the + +:: + + torch.quantization.quantize_dynamic() + +function here (`see +documentation `__) +which takes the model, then a list of the submodules which we want to +have quantized if they appear, then the datatype we are targeting. This +function returns a quantized version of the original model as a new +module. + +That's all it takes. + + + +.. code-block:: default + + + # here is our floating point instance + float_lstm = lstm_for_demonstration(model_dimension, model_dimension,lstm_depth) + + # this is the call that does the work + quantized_lstm = torch.quantization.quantize_dynamic( + float_lstm, {nn.LSTM, nn.Linear}, dtype=torch.qint8 + ) + + # show the changes that were made + print('Here is the floating point version of this module:') + print(float_lstm) + print('') + print('and now the quantized version:') + print(quantized_lstm) + + + +3. Look at Model Size +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Ok, so we've quantized the model. What does that get us? Well the first +benefit is that we've replaced the FP32 model parameters with INT8 +values (and some recorded scale factors). This means about 75% less data +to store and move around. With the default values the reduction shown +below will be less than 75% but if you increase the model size above +(for example you can set model dimension to something like 80) this will +converge towards 4x smaller as the stored model size dominated more and +more by the parameter values. + + + +.. code-block:: default + + + def print_size_of_model(model, label=""): + torch.save(model.state_dict(), "temp.p") + size=os.path.getsize("temp.p") + print("model: ",label,' \t','Size (KB):', size/1e3) + os.remove('temp.p') + return size + + # compare the sizes + f=print_size_of_model(float_lstm,"fp32") + q=print_size_of_model(quantized_lstm,"int8") + print("{0:.2f} times smaller".format(f/q)) + + # note that this value is wrong in PyTorch 1.4 due to https://github.com/pytorch/pytorch/issues/31468 + # this will be fixed in 1.5 with https://github.com/pytorch/pytorch/pull/31540 + + + +4. Look at Latency +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The second benefit is that the quantized model will typically run +faster. This is due to a combinations of effects including at least: + +1. Less time spent moving parameter data in +2. Faster INT8 operations + +As you will see the quantized version of this super-simple network runs +faster. This will generally be true of more complex networks but as they +say "your milage may vary" depending on a number of factors including +the structure of the model and the hardware you are running on. + + + +.. code-block:: default + + + # compare the performance + print("Floating point FP32") + # %timeit float_lstm.forward(inputs, hidden) + + print("Quantized INT8") + # %timeit quantized_lstm.forward(inputs,hidden) + + + +5: Look at Accuracy +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +We are not going to do a careful look at accuracy here because we are +working with a randomly initialized network rather than a properly +trained one. However, I think it is worth quickly showing that the +quantized network does produce output tensors that are "in the same +ballpark" as the original one. + +For a more detailed analysis please see the more advanced tutorials +referenced at the end of this recipe. + + + +.. code-block:: default + + + # run the float model + out1, hidden1 = float_lstm(inputs, hidden) + mag1 = torch.mean(abs(out1)).item() + print('mean absolute value of output tensor values in the FP32 model is {0:.5f} '.format(mag1)) + + # run the quantized model + out2, hidden2 = quantized_lstm(inputs, hidden) + mag2 = torch.mean(abs(out2)).item() + print('mean absolute value of output tensor values in the INT8 model is {0:.5f}'.format(mag2)) + + # compare them + mag3 = torch.mean(abs(out1-out2)).item() + print('mean absolute value of the difference between the output tensors is {0:.5f} or {1:.2f} percent'.format(mag3,mag3/mag1*100)) + + + +Learn More +------------ +We've explained what dynamic quantization is, what benefits it brings, +and you have used the ``torch.quantization.quantize_dynamic()`` function +to quickly quantize a simple LSTM model. + +This was a fast and high level treatment of this material; for more +detail please continue learning with `(experimental) Dynamic Quantization on an LSTM Word Language Model Tutorial `_. + + +Additional Resources +========= +Documentation +~~~~~~~~~~~~~~ + +`Quantization API Documentaion `_ + +Tutorials +~~~~~~~~~~~~~~ + +`(experimental) Dynamic Quantization on BERT `_ + +`(experimental) Dynamic Quantization on an LSTM Word Language Model `_ + +Blogs +~~~~~~~~~~~~~~ +` Introduction to Quantization on PyTorch `_ + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** ( 0 minutes 0.000 seconds) + + +.. _sphx_glr_download_recipes_recipes_dynamic_quantization.py: + + +.. only :: html + + .. container:: sphx-glr-footer + :class: sphx-glr-footer-example + + + + .. container:: sphx-glr-download + + :download:`Download Python source code: dynamic_quantization.py ` + + + + .. container:: sphx-glr-download + + :download:`Download Jupyter notebook: dynamic_quantization.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/images/thumb/sphx_glr_Captum_Recipe_thumb.png b/recipes/recipes/images/thumb/sphx_glr_Captum_Recipe_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..233f8e605efca4bef384a7c603d53fdc385428bc GIT binary patch literal 26786 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 literal 0 HcmV?d00001 diff --git a/recipes/recipes/images/thumb/sphx_glr_custom_dataset_transforms_loader_thumb.png b/recipes/recipes/images/thumb/sphx_glr_custom_dataset_transforms_loader_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..233f8e605efca4bef384a7c603d53fdc385428bc GIT binary patch literal 26786 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 literal 0 HcmV?d00001 diff --git a/recipes/recipes/images/thumb/sphx_glr_defining_a_neural_network_thumb.png b/recipes/recipes/images/thumb/sphx_glr_defining_a_neural_network_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..233f8e605efca4bef384a7c603d53fdc385428bc GIT binary patch literal 26786 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 literal 0 HcmV?d00001 diff --git a/recipes/recipes/images/thumb/sphx_glr_dynamic_quantization_thumb.png b/recipes/recipes/images/thumb/sphx_glr_dynamic_quantization_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..233f8e605efca4bef384a7c603d53fdc385428bc GIT binary patch literal 26786 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 literal 0 HcmV?d00001 diff --git a/recipes/recipes/images/thumb/sphx_glr_loading_data_recipe_thumb.png b/recipes/recipes/images/thumb/sphx_glr_loading_data_recipe_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..233f8e605efca4bef384a7c603d53fdc385428bc GIT binary patch literal 26786 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 literal 0 HcmV?d00001 diff --git a/recipes/recipes/images/thumb/sphx_glr_profiler_thumb.png b/recipes/recipes/images/thumb/sphx_glr_profiler_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..233f8e605efca4bef384a7c603d53fdc385428bc GIT binary patch literal 26786 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 literal 0 HcmV?d00001 diff --git a/recipes/recipes/images/thumb/sphx_glr_save_load_across_devices_thumb.png b/recipes/recipes/images/thumb/sphx_glr_save_load_across_devices_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..233f8e605efca4bef384a7c603d53fdc385428bc GIT binary patch literal 26786 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 literal 0 HcmV?d00001 diff --git a/recipes/recipes/images/thumb/sphx_glr_saving_and_loading_a_general_checkpoint_thumb.png b/recipes/recipes/images/thumb/sphx_glr_saving_and_loading_a_general_checkpoint_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..233f8e605efca4bef384a7c603d53fdc385428bc GIT binary patch literal 26786 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 literal 0 HcmV?d00001 diff --git a/recipes/recipes/images/thumb/sphx_glr_saving_and_loading_models_for_inference_thumb.png b/recipes/recipes/images/thumb/sphx_glr_saving_and_loading_models_for_inference_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..233f8e605efca4bef384a7c603d53fdc385428bc GIT binary patch literal 26786 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 literal 0 HcmV?d00001 diff --git a/recipes/recipes/images/thumb/sphx_glr_saving_multiple_models_in_one_file_thumb.png b/recipes/recipes/images/thumb/sphx_glr_saving_multiple_models_in_one_file_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..233f8e605efca4bef384a7c603d53fdc385428bc GIT binary patch literal 26786 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 literal 0 HcmV?d00001 diff --git a/recipes/recipes/images/thumb/sphx_glr_tensorboard_with_pytorch_thumb.png b/recipes/recipes/images/thumb/sphx_glr_tensorboard_with_pytorch_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..233f8e605efca4bef384a7c603d53fdc385428bc GIT binary patch literal 26786 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 literal 0 HcmV?d00001 diff --git a/recipes/recipes/images/thumb/sphx_glr_warmstarting_model_using_parameters_from_a_different_model_thumb.png b/recipes/recipes/images/thumb/sphx_glr_warmstarting_model_using_parameters_from_a_different_model_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..233f8e605efca4bef384a7c603d53fdc385428bc GIT binary patch literal 26786 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 literal 0 HcmV?d00001 diff --git a/recipes/recipes/images/thumb/sphx_glr_what_is_state_dict_thumb.png b/recipes/recipes/images/thumb/sphx_glr_what_is_state_dict_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..233f8e605efca4bef384a7c603d53fdc385428bc GIT binary patch literal 26786 zcmdRV^;2BU^L2u2U~zXzaCgrl!EKS?!Gl|HcbDK2+!l9tx8Uwhki}V?FVCmmf8zb& zR^6I=Yo=zVr~8~U-QmiL(eC?7t2K>sEKRQ>SbW8C{gfQ0bg;;5VB{^5g#S%F4jS*yo)sIw9jjnBhR8wuiv*@haoMbsRi`-;s) z9j%)m&qag`YmLl;-VH-wNFilp)`}=RE6iLb8P1ZXot7lPS$|;j zQ6%{N?}NV#OoGh&8zR;Jmp{}RVNldC0&_43DEv|>!G?j^q;OxAyX7Gs_2A9hI-55o zJ)F=U@-pAz+w*?0hK)p%z(s{qJ1Q+Wv_8Z|z3!m1Cs=&-?)J_5Y815AaDH?$jYLqo(p$kO2iW|9P%S0_{dC|2h=c{Q!&bPfpfv0!@c%J z#_Pjh7|{hTI0kIgMg zxdOWkb%vZV^4lVL61EU=I9L*Tl+?h$RKCIz*L82w5j+y@957yB-O`t_fpVnvVkw>7 zP^nvW>L7(+HJXphZ(3gXtZxLHZqdPVywxY{9!|2K&AEn|@;JMhAZj7?4y>5SAxJrx zurYaog3=|Jtznf^wPDLtdAwz{-~VzM&<1f=cKIarDeaAWbp~AUPnA+mO6+`%gWKCS zLp?PEPTuCPmyhdAo0>J$4)AJv7RQniq@AMH$$_|;sq({O!`Uktc6UT#*dpB361N~I z%}5Y5rnnv`0pPZ?4^O==rd+Ct+d=y*e@vt1VbH@M#e;)=cJ$F$(v2B^Vh2r+Vx~*+Z!G1Zgmq&GRTTsGq2_{@Nw>D#DWRt=`t`6c z7T8oA;ZHP!1QLB9M_mrUm>*io2P8vwgJhjCQtU#iZyN}MKEQUSOs}$gCgi)#1GuGMe*f#+Z&G1I+vYi6K9g)e*Db>=_;17hfr#kK~YJsMnHz>ujQG zarIZk$|9PqN6mLW?3l6YSILh*)(}TG6B!@=Y&v9B#NjQ@cyJA{AT*Rq{1RO3KfRt& zITYih)&2p&c2`~JyxOI7rZ8o2AVPcMJ0duaT+CTY?{Jzelx=^QZxo4TA}5e|0FnJx z2h9h%+q97bj6^`YgpWV zZE^bI*Om;wM(XMc%-aGKa}>axr_VZ=;zDWt8O2-J^Bxv}Ut2%&^MW1SJF|SbFz&kG zy&cWX=>0)&urGzaisqDY!NnC_{E{vDTOHp*`@+VJID&^uD425yvR;&V7JOge@>7l;gt``i}FMDWyMVa`YI-%kD!E>S> zL|JS2ysS@b3@)Qb=BIHHjmnEDA|!p~!={`J0RJ#+a8dEKKlk|iw9a|2w`u3eeq7J2 zf-BAe-~bY*&0(nx$VAm(#iNTWBKx>hl>cQ(d|miS=*dXB;Oo=bVeiP)u35s(Z6STU zHpM@WqxIr<<7Z_^I!@Fq-s0HMekIN5ZdZuA8p<$z5M*`7l{e(44IGp#EYwEGo9l@@ z6GbbJ5wt%PcTH`mE9=xg_LPL9p}aIOXebdn84NVw&M*s5qmw zSAf$>HX1)|Ed8?UGO(gY6zH<4agAW*RQ-Ux7>@~#J zmS$%j>>%DF4Y=98ql77up`%Kpu;DPvUrv*EhV?47yMlP0yl6hzZ|fFAuTWA614#IsaO$_k zcURoeoZI7P`+2*MMC}ycnKbq zdhu&vPwa4VxmvM1`j?s0O}oTP&vV-WMtq+90RkmnkO&!Yvc#TgvQ`BFLbdrz?S;T~ zehN^!jvpxcNekpZ#eH3Pq#LsoY`b@>QN$aAV{~(&#mAEtd`K+bsZ4IGv=%(zq*3Xs z;3Rfjc zkktoX6uL9`be86_LQik`_Ya-b3(;-vd_U$lnA*wemDJ|?HO9AdUpJdGrMmL_VXdOB z53f;FLk`&=fS`S&PG^o`)InjY@-!!OH&@QVDqhZ(i_7rANfL{LH&pQ<#6nd=k@R&QX0zW~~Wq*_`b9rNZYq=4x|Qbh_5fW+|0jZXKA2Gglg!KYS4dpoJq9281$ ztI5PC9ZkFni;lbsSRI+`dV_<2vI#7bicpH+{d`{I&a(w2bj5n;~YRsn$5{c$bbM zLp`gi<}PqEx5=fu{@$`b8|&0pik;)ojkp*SGu9J-aY?~8d}E6YnF?L^Q*N$;h)+)M zM%+AaC)QY@X5SI#N78RF95e}jrx?i{u%sUH( z&F-em&Q+v^3Hfvted*?*6eORtd44he9$m_2uf_-u6H^xFve=)EF>h`gg3$ic6_)ea zQjmCUv-pK|(_$10b)X&)yZeaX`^%g0HIP?RAgl7^kiyoT z2Sc+M8OsBmIm<3h^&9K>>%hL~oMI^=-xk0PY9Hx^Ewq?q#4JliEENe7 zE(%!vQ~SSRB+U}ICT{;Xs%G|W-``$ zTF?jz$B)r(#XFeWV2$xsxW0HgSK7HZ@UUu-{kkE@Lky5%Pt6>X5`q95F(?F5Eu70V z!Y>OcUe9E%E7;u4;(y!~BRy<>z1^!Gdg6;?irc1Kk2vwYfn1HrnS{p1m`s{7WIG?q zAmwgD`j59UQbjRcnPP5pEhm5`vOhBW)AUYPuYPW_hIU&}F7HbX3fy+AX{6>soT1g# zD2<)GielaDxB>of?4Ec<(TnLceijD3Ze7h0suV%Imdj$OeLqMG6_R z2~|YF_uo{q{8~vyb`SpCf4k4BOkZ)C9Z#YMq^*{p-nuS;<;qOG_UAM1&feCCiddBx zMJw3EZGul1{lKuaW+!gvI&1gqo8~S?EHUJEyGCTllavF1YKf&rfe_)nm%h6q7}tQG zoP&Jl>0O2#)6-ziNKN4>5eq5R1U*{hVX|Ea$nt8Up;o}NK_}Sa^+yp2c6@lwN7ugJ zx0l!|AeDK{vY)+KzY##WxgI_mwN07=>*i9|zWkt|6wvLW-)tY*xl*^^CiPvX-1&~S zyr_gH%_YmC|;YvYVG~9*;Mw3dd$|J7DCGwH}Xc#hH=bz`L$TM>C*V9Kc%UCtgd=DUUdizG>g|erOOUj<7DvV(6^D9or9O(7bT=6$}@LsxS zk@>EP%gzAq6D(&R$zi@M3fmbeY2f0**!_C6MG`}CfPGjfhD5fNZ!~+ytpwiY6q(sD%>cY^p2zfX5}_gov(_;9#$3S2FiPC0h8M$c1_s|YrK#c9hz3Z zS^Mo0f{rwaO8HX#f0Cl>E3B&p7Iimm*->hfWS|jI^s54--;kJs@ivU1Uvm0JIKAxQ z9j51*FUG(H%gM>y2h={YoqQRrIN@HuB#z3i#`*@4*>PfO$_#ANdi@V-3-h5ytMH$} zA5)R2hKPDZ!<|e!qLyWl&f!h1?T77=v{Ed_)U)7s4lI!ezI@H@ITI_=4w;|$gGC-- zqnT{sKOCjQJ+dG{&a7cEarZm7OYq%#s7Z=E$&_Mo)5?Yc_!+|zWIxKujt7;W{M*z7 zdIpA^lJFVpS|T&%8BGUV4|TSSSKRLlbJ3V zpf#PCmbS|L{_K<&Jxj6cCd9S3EnNFPkhg4(T8rL!hEfy9guQ5#m7F?d5$6xV!L9Ki zvHaoMuQd_TC&Et~x3#ncbG9jf(}$D_`H$A`s#`h?WvFA{O5II{L~ z;n;$McVrUq$V7R=1u8=R4}3M6KNs{7Y?KY2aI97+-_26QPIK_Q^3L0#MTZob${Z(6 z;c{kO{Db;5W(oKwO=vOta`Vb9@fp)=U%2jM&T~ZXGYiH5+Y`dVL)}tKBc;PN=8Rjf z-lq^DIQt9dUauz1cphzsnIBy<6&VKNtG+TDk7yTT1&JG^#$7@9Ug0QLX8Cesr zDT4$yL9BpBb*hLn(j~jnr0Mpala3iKY%U z9&0$&f~ukr%wUCJ1$7_S%k3^gJY3Qo&x#D+>5*k7asU{pbo=+tuT>=ZkisU2J)SQl zNnsdHAtoh@YGRKCd;MzEo9XlJJANNAeL_%VDc2jS_@r*mfT-^FE0_4GiV6BUA}jj3 zYmvDcoN{XT@}^wT`ScccVr~%P1~c;45AR?55X4Dy_6w+|WBYIFo`5ZT5n4GZ9r7^C zZX9%iE?D4>((*4f@oZ2xAnJ=_!6C=pi;GTYt2Q&=X^4`L+uHX!t9K@4Hvsq)9-|7JXfeN3?RjZTGBQ)tpe$Ph3xgNn2vX-#!!xgl!h<}a>k3zZ}Q1r z0=yeM$ERRwwY0GCDS7BQS)I28KOC|IpeAsl+sXUdhjm2);H+zeM(`3>r4Ccp?+Aaw zRonhho{{ZHfJ*r}dGr)&XAt)$f)^ zwG&gX6WQYDMf$JF+%(T%22m)b!H$5&i`8gDlg-JaP^D5JIqak@ckkoWIn|{&fjV`` z+xegEsEnub@7%Hx{->Wu$h*c;fA5MEQ5lQHLpHa!np8c#)i1GirdNF(^RKg-G@5)C zw@yQS;dKgEo*B%2PoUn{gD90lUy+<(B#ycGfxvZBICB^%4sg@xoh*-q<$OZ7heJ;I zudhy0@1Im0s6L<7M4Jh+I<*C#s!OxX;2ZfBx9h0JseLe=xN zah5^>ps>R(59~(zOi1-jll(L&4e$yQNO;xc@w$CJU!{IebA?~WmdMuW$_>|;<)AAR0^ZkVi0M% zrpexd?5bveEUm!wFQDq%{l$YPiOH|r+Vr<7#lYb7Sd?Q|X#(=({MdHW^jcbJaG!Fs zn1U(1Rk^!U;6(oPG4N5MB<*4AuwOAA&IE&C>F>XVbzVNPA}Z{p<)uQ~miwp0rj5^8 zWl-NN>uAVrQ_J1_^??qBKq0u)d?q(dtyJX}Zpx^;yA~#TH6YVMkmLV{rQX{Nt<(naEB6e?+*x4oOmJGPF?|oNY*0^LAB{K!~Icu^pN9Md|nEz@X?5?@C zXxIg5O#lXlWePd&$Uki$wj$A{KMx6hdpAL;95@4~em@@p?CLIidi+$4roQPHu_=3l zL$PrisR0at1aiHkYR$NJFed!+2Ll!4ti;i_y7$3M)eG=}$>WT!5{u^o=u|YnJqXZP z#7UDzX@(O8*TZ7h-_*3t@G!Qp^a?6qN*^Ki4*>XBhH`(FoMnW6wWpu|TQtC9+Gs*c zI^1%AbEds0r9=@M>V^U1Zm=yHK8le(C>wU={CdijvQu*HblVgb9Z}EAq-rqh-me+d zT!nQ*hxP6=*#40WvtS45hp?uFz)K5+twjXVm=9f)Og`{`qQp)UH+gm}e!e3jA zd`@(yV>EMEUg!1q@uuOaA;(nqZzMhowr#lb_1GOfLBRKWeG-|nj^97;GAgTTKh!{e z7f>HIq68dR*RlvE>{}u83_s*7j?g@HV~PHowFWL{WUO@9+ ztHGK7i_RP{8-K&Z+sT1EY?}p;?5rikIZL!k7)muurAQ?y<9^8PGMk+`7F~bFZWxYV zk(tpGGH*}}#R+EPuoT1ajCk+=0#1HT{sa^8q8X%@p_PPu@rG= z1jY0v?OGE>OZ6S9Dt%bs}Q=LyFq&oKYyAF5m5xBgI8Zv6{@AOb(I)-*E^Eqpc` zBINhLnkP&cKL7kB+7-(G^CBg-zIS56Vt*omIL}dLOBpMUlCvayA9AcnCjpWDXyOwa zJUGr71oK%LosW`~Y7G%h07D=#U|re9*<#W+K!_ai4~%MF-=}x(1m(|T+Z?_b)XFtz zolau3{WSx)Hu(ra1p-L|!uG%#%`ymd=f3VtVL_RgjzCT!y)o}V!7aV^){r2V+BJXA1{kCc%t=x-G4m_l$hRDAHZ zgf8y){c#SiOj531*iB^?%|BhrYdWRENof_1$SA94lw0JH&bOJ6oPU582DlOqjZ0+o zym^uKnz{4hoiIMC=sGZpp{#M@PWxlVb4Z4E=hy`d9NFqeJb^Rx!pT^umM4rb&Q<;+ zr zaUJ=~IQ+N1J_p@uoY5psZ6?)QhK`#y>ezxKhKFF7o^{8r#V&325P?SN)WqNh*KxVHE+;RVXR1G0zQt-C*wNv-{FJJT z%2=gOcyxE4wv+wB&=k|dBR5Xsicx^N;_B*%%%7k?`ojfV<}t-_`nzog%01*FM^uLt z-_SpQf>rwDni$JsxmxcU#rT*;@%*R-pqtpYCOo66HpTn>Qz)vSV9Gl%!+1KcJ3!}w zXJD8}`(!q!3n`Q2?w$-ZbJjH!2HqEE(OWLjhx7=3VzkxRhBUo2hj;X@pV;08@Z0jK z*kgB?GbABO^oR2$!;pXdq3l0Cf#!uBuG#MT0!rjXd=bTJduL-E<{bc&YcWCR$ZnLs z3cLocLPcVa_5?TZ>=zc8l3b6%W03G|EdL`kI2z004bJ=VW}QW&kJG_S*&8|Bvnt46 z+&ae!2!_e>r>sLRTCxSF{9+9kyTkWf`RHTGwR?DwzI}qae*BbS z3o;_%zK-{xlp|me%s={fd?wOVMcVB7#Wp4G zp5bASD>|*~$AaY`*;DS0z&(I9&uy6}WNu2rnk4%_DU*U-W;ri0M-$Er^$pIfhB=qTaRJ!3B zo!Ekl`c5O0d98e>?C-)?Fc}%1;oYp$g!zU(Mn$x~%a`6DJ_SRMr9e+Kc2{0?#w}Rq zN@BQa(~I-wo{ZBq?5*w%QI7 zVaW5>!PUoZ8=ycP!>pS7l7)c-%Py4IoH+Nkac@Ygj75$B_z|VJ`Pdy0p_@n0A;tbe zli&;B(hC%(8MzV3QAQ6XUJ;l})H2a^e$MPmFUfn~7SFjfA&6NF_IhE|!3%}H<0L_t z6m)5*JXUR9{!6fM^1)ce^he2J*`CN@a=|kA?U5=1wx#?_WQGULb^+O6_Z3#0 zlIlN^(2mg))Jw~pl717rLqznOjKbd@3Axyit$M7Rc_vXD`cXdX@td|be=UvS?6y{2 zN=vV_IX<$|k(D=Dmj3q!dKC|Tchm7T#eI&zZe{G7!_LCXc}xqadtzzxxl(ieJO^80 zB0am9B~=T}y(E^@60|}-4nuAibh=zBwJ^}f@6h6BzrNnMc2Y+fuGa%71s=7`q9#Bi zgc70U!G%55@eHW$O6sjtGgV5p^uIE7q($2MZQz?2^L{N{$CvF<*L5qwn)@YsHw+a} zJidzg<&4HZnBBEx^Dni#8P$E)`Leqhcrh)%uZ>QbI^f*ze&`U$+|#BO?R8~ryqy-` z?a+2;%Of~Gr?`&AjfsR&QQCT)W^XeN!$UY}E$VU!^PaoX5|`qvl| zi(`C$dYtc2w+Q-?)hA=;IJFqSKFZlFKrEmo9OUhRXaQ-+1di zHOtP>cUtozS;VFdN1A10VlKi__;Qk^-*s|jt3BwBn}!pg*C>(jrS>b(bwSdrgPB98 zn|+G{TA9y<2v^?{6hynG*@JQVYEMoM3_6--U;WS(j>l`dcOMi(@`utYTf9&Hss|&5 zhT`crTl&94rKk&ui|4cJ_2WW)TqSOxFUn^G^cw_$grGg~B^J-4kd|eEp2di$uP91t zI^W~j0UM721B&fnM2cHjxlKDJp8>-0%OUXDQU7XIoI)`CZHPMpr~ zo$Xc0D1YJ#6yaY+5(emlqt5vqN5&T>m~KBUQvwSdjc`rbVUfXgbHVrT2y57n4osM5 z1?fcFP-v}zMPoQi*TKI*u(k6z^_IJ?K{d%ooId4i@S_ire_tepd6wRm=$SX`{3rvb zXT&gl`=bp#d-m{=7YdH*4LFk6gH99=>P7qWn|0AQYd!SsB80Z^*;~O;i^B1ir(KPo z4Gb<0vUqU%7^A!atZ(6XyPMLr`$n;x4jJ!se>2f$KvWoXR94n#Xj|265%MIXeH)9C z{!KL&ac~_o7m+FQxPhYcr}Dp-M4I?1@m_^kGZSkMZ!(&sSvRpY(r|XF+K^TLEl-PG zJu?xNnRM)_pG(H_YwxykMFM*=jrF~IR=u93wmGoZNG^@uvLPY0)4`_%ThbaYMzq7 zx^EF=_wQ*R#=gY8v)$W9vD^+B?>n7Aa$nwSP9KT+H!8&Lc{zH1(K3j0E5CUUq)moG zl;Gwi*7)SoEGc~?So@L5v|6jg*0F2a4%yqHa_a_DL-I(gURRR4fvz26jZ$jdTkQgh zsULhVyT2{5AarwWamnky+ec=$W1HY7L}M~Kq!@j@C>=TNx~X;2)i`y|9Bs0!fk5um zWa-~%(cN*^Lofbti3F1;7R6^3z=}{eUz&Hs#mRi@lj1e6W!aWuX}ms78%&E&L^UIo z5b#U`iNHWs&gL+{J{4wcGr+q%XTNi%CKrCp#!Ano&?b9IzybQYx$&sIW~7BYKfXsG zH}AS0mFe4 zP5F+GUS{-T!_3ghpJwc`Bf8F?AfecS^I3zZ-wddmk!OA85+YZdxsUkY916X^)rygk zIa06&h`3s-i$YUk6}r5AoK8CSJ702JS#iY;ocWmU)sy#uk;<;xv}XZ!by@#*6-9S6 ziHB#VHGDk4@!22$Y`@}zVJYiH$cL0R;%={HTO0|EKJmq=R=f!{paP~=7V5yKc|NeQ zcGzR*C1&O>kJQYBf|u~f)Z~Z~(biHNo^sWE`ULP_m4~t+96rZ(@HQ{p>hz()hC&<~h5TO51VcYZuQ+`_q(@io}Y*O^Oww8#VFbyn>NQ{E@=f zbx6F#gxRn$B3r*3{qWNFEv`kkI$SRXEvZ+P>+mkLT$9?p!R>znEe^t<1z;X~9f>|q ztncS;JPir4e@M)4`T+&ED(Xt;u6yAa5?FDeDI=Ko8KEl^tbe)6co*fmZZ?X%ufG>R z=OQZd4v2<0jZ4x=Brno?G6yUh|653~NeS11uSL<-EpxM}y@8jk^1j!c0?`PLEktWt zkb9^WxC;-u$*g_H;^=vH$hguWv?~!q!lX`x_TaDWVg%mmOG)Efk}_2A3ra6MlI+nd z3k#pt(2Ed!6C=}i3_etMXIU(tA1yBiFTyl-V!fy`l}QD;0Ag-D*1v$h*iU|t1n#ky z71C0r3LaQDFMCGl@(W1Zjk{TKERx=nln_i@s+ft0Kwjp#*ER-(ocIEUjc@teKxGw; zZs^xCQH2>cFpKtGdpL)dicB+^|E2{vD&0@GE}Z5j=CLr46ISd+P?pfA#=Pq$I$J7| z?+!3h+BQ2^BBS8kDKQ>RDm?`FLVKjIsqld{D~Nh+4P@f)5s6I`6G>YtDqg?K%ebzd zb%MJt*t{s>;h%gg!nApSzDP;ob0>yhdS-LG-%clLSYEmVs-x!rC%S-ZuR`;3=mdsc8h4fLz~W!K8$0l5ZqcZve}2oT5L>vXpvm%C-|YJVuRd$vG^oLD zVU)YmpzQP?pcIP&L~5j0YovAQv~+PM$wBul^4XI@VX4!6I6h+wiqtQ8FON<*Iaij? zbcE=kVvpSTXnJTDlaXXq>pR2ZxoSz5oX^ceTAdv2ZBHu|la z_H0~(J(GX*nw;J{;b1$xs|)Q*RE+w-K74mLUY1zw_XIeaY3%5zuJsSRU!&~{F<*Ac zS$=gT;(L@Zd!d<*=q&=DtINhUYKZdlrZ9n!CAHH{zL9JtrC+YPB)om=dO^&AxKm|- zw@OL(+bx-29-V}*veZW^zfSDGTZmNUx(#NEUN}C;{m?no>sglzTGmR+4=}kU(rvPO zaX2>@y~tlQuzkboXLlm0vTp>M5bmXSOJWO0-fki}T$)P#NzeDbaSGZwJV68-9ehzS+`0ns6zyK+E{o#td|pbbTGbm%1_8fNi&Kj9o{Wd2@JD zS1+1DaB1FgAn8-ZjeN85n~0pvZC_Q` z7&mX15R1N^2k!<4xevA3%->tFbMFX0Ar2d8f&Hx#nSSS1*ibY$%B0D!Ed7jCrw*F&Ak z{+@Z!m3g+cQl-XyjV&RD`mo8r*Rs@YHa-~%9e~nP z*V@mF#4=|-h)~+=oeICmp2K)k^`Z<)n+9vj!3 z(6&A7m9P9AEkDG$=u?ShfMy-V`_){2Z$y{>FWLJ^S@}S8G;B$i?bB*Op4$H?T^>>A z|Fc)9v4rQZobN*@p0_&9cutg|YO_q=I&lCF%Z9xL-;DA!8ad7~7*>PhXv;Np>v`Dg z=@(krAL|xc!5!--#*?lvTm4+;fr=A|UL3O_s<~Y7#HbDElD0N^Dm{%FXIF;j>&6kb z!P?G6KS}g7y%vn&aN^T0C{Qv(@!0tX1V$wy(h>r_}UAL~mmu$7&2W@UpmP zNM_RXz9~z4)^Sc!%jnXkJ4i=g~|MdjCqvp|^d#%FdQdoEl;euf0ysPC(dZ zH32?wAHlQ%XD+4smbfDcHFEpDO0*)M{!Q+kC9)M%frZrtQ-SKmB}#Y2=*QHGt=+M# zoZa#SI8hU}_+}dxrUI|of6_;Jd7juc8n(9MsefdyRoCPq%nssJPM9Ipb@#Mw9#56+#N-ReXm&8#k~io@{r0BJ@0yanPW5Cm21znR zhDomFwb~Gx{dj?g`t&P^b7uPGHN#^L=&Gpb!l=IK1J?%7hwNjizPm?B#mVaC83Rh} zBP~9eXit@MAnOES&Ns{`t79@mNCD@Fe}fDkwNN9px3=`^1aC?ePiaSBzCC{i+~@YR zeKMO~VM))>$#=BdnJHiUvR-K`xzW)G>b&|Zd;2%peg3Zta4jM(I7$nb$^?>7CL0q^ ziG`^Kj6RyC0K4G=$7aH(J4S(wP%WEO8>!i=<&o#P8jaT$jW~MFq;Luo6mQ&r$QznT zfAUC!I}M${k0cl=CwmIjby{3&(wMCFR)z~~O(9r;+H3uzWh(|RJXuZpV1Y|QwQA`GxF91ddFomjA-ORw)_?V(;hYyeAjvsfdmwMzh zef$m|B{}R_o)Kz6s0H~viwwClk=w^q(mOc15b0VmEK%7HB6)#PY1KA)YaI=z;zxwP zd}?#$__w|2J*qHCHIrZ}uE3hOd+rCxre-x_KZ>Y&kX+K^)zU%_z0Mw_Iru&7!cO&gGbD@#w(J3=}(v zsuDR}JYBAE7Vrp=V~KChF!84$OC;{b-C7@QMo~vV_cwHj>1xfxLq2BJ)=vRdp46);>a5#(NlsMDtWSRkJX~b`X=^XKSXuuClu4x@?TBS z>4*Qwf4&d(HuS z$Pqf9xX5#GRN*(vrkg|~Z^iWxZXs_G27moZ^3(^!;6rRT9TWt&FNR81ffeW@C6dI= zKp^*I(-s*L^q}A3_u5-(@AVg<*=|eUU=rL!(Mn42!4=}d=VBeD6_ccM6ICa9?JoO; zVRgv!An;#)4_<><)I+?{ieQ$`?SxpefvL153rHYequ`FVH>o)g)R{bbhnE4BgK3vvGy&5bh7hF2X2M+sRkQ-$v0T~ ze3o@P^Ec~UgLm^J@r2l&I>zNOSgI))42b3CjWl*Lx^YW}nx}6&Z+oZ$9h&l3?#fu2 z4&3cU5an-@xo4+VWXIi`5BX~X3!Eszq4}t~@cJ>P=Ym>x%*u*8JTXH6p+*NQKYYFx z0O!E`S{Aq~qQc-};}K?o_2ZDPf>4QUe+LQ?jfYBH!}kPk`r!!Vlpl0K);K8^7RWd1 zeRxx}COQWzAaos>C5bx8Vd~s_k_%#?wKJ724Jn^84AkizXYJ+GaISovGKmZ^&h#b{ zyH>T-xHN50E0arflI=dDULv@Q!qn%y+T8AL;h#D4^v)X2{ zzCi$gHyN{By4i$LtJ#l$Cu!M!9XK$-`qIT(rT4nua@of`O2`%UO*>jHIU39s@{kIS zYIYw3p@=&IM%MXhdL4Qq{N4{baAf=|Pe~d1dMVsLn=~ko>`B>hy63VhbVl+?KV|GZ zzb0<&=#yn7wx`&;`#Lwe3)OPL zJyFa<9ea=|jsJo9U^(8s@^_FI(1)+1u!f@f}g z+gJywAIoZmQIa{tQqXFJdxm?EUprnrp&SLe0vg9FV6Dxn!=KFRAwQ<72aam!nX{jm zRxl^7Bxww!dSSlfSlqe)^nLT$l7jbDPt*GxUBj?qz3C;eqgLkadA;UwdzMtY@-vi; z8f%C$D(oo?TrXL$4`Ie7g8ZQ-g5$t-jFSk`U2`hs4HgH^HPta8qUU1DI>~-*je+)m zSr7j-J;0cW5>Hl5+v|br-4Oua(-um!{OtU7vOYi&k4OUZ1YEsMEDtg#DDE{t~VJTB^gO)s|KR-|-H9sNWpV*VEgD;pi0O+jwiCWGCMJdb*7<%U}a~nlM zu84HXc&AS}cFwWNpe4zhKX!ugu$7|38d>3J()IM5Ud}{2TQ9T5_orkkm5?rJ=;+Z7 zt|)wsS@x)#c_nLmql)qeax67t!l+#Q?{t(r%FGit=bP*e-PeLKaA@beHEk!H)(7Og zr&8A88Pr?dNuQ1dS{sZyTWYjzBF0cunZZjgL5$NkR}x*7oU@a0wMS&c z?{HYZJg_5r0D8edt--0`QhEQ_c5Q1F9CQ_Q<2j_>hsGAo8GTgHCXZ}HGEE23MiNIh z5^VHzRa$LZo!EGd6kXi88@l1`avJzej!PV9!OYuN0ilE;TmQG)NwZ+rdduO&nCY1L zx}c>cYDFhGw=nKXsYA2JXz`2S2K!W8M~74JkjH;)c4%as3^EVKsHb{r{Dgv6iQUqE0 z&bnOdga(%)$Vgqkw%gI2us<@$j*Vy7F7PBdT=Rhj+aGLW|7sPJAErOZrOD7^v?d$> z7QeMTE^l={I0ewORFjdDxTS!vb#7RE!W#zSQ}-t$Q?4~`iw1Vf!^Yi7S3moq2~~M| z#38q4xr3VIaUoh$B1dCypP|tEGeMW9Kcvrtm<&-U@tt9=>lM`Z5v?pb!H<9;|A&O45hG$PGwTG)yi-E=KKDawzf zsDSript;XE2l?AiE%mMf+B8-lQ;{uRrW)_3yVxbSz8-w;s6hTAHAE>$lOP%%;(k$s zDWPu#KR33tm1avjyTQSIf8iPXu6vW`gxf=DX81tCNoErP4uZ*j@K2*p^}Jh)L9X zphTR`nF_=$yXk2Fc6Ii38|n_dMr%6sU|XiT6+%V#+N#2#O{Rqj)zv!IXm5jnPLny7 zhv>pUzZ|3X7Tyb6Cv-5LT^q6GXb1%8c*OSKeykb2|JaSFdXl!kfq-)3Q5LD;d z&i-mkAnX)2c)#g?(XSX=rKa(=H)?iE&Uy>3sl2r25-eq*S9kuN*x1aAiF`}t{p7v& zU<<*C%f0ehh()rEX(Ln?a9-2qG)KP7h|VFVPE(JjU<~WzPAVm}4mPlYe+d@$LJZfV z(WZ#SX+&T$rfY5(Z{_vA-a(PGvrgcBO2$PO%9NFfVfb2m7q;>>9>=@UDJGIvNMVokh8R;@+2sPTB& zV8;w);Ypl~!h0;0xUHw&b>HY_-m!IU4m+Lf>cZfUBi(=DL4$-LfyE^Y{AK#GzC_Ne zgicua7`N=L`&50`hq${|rnzu_!86ZbLO8OV`Xi=e6jF~TocOL9sPyiTt5v@7%(wp7 zzpm-xmutZZ*Fo1c17wLiO8}+dzW{GP;e$xSy>V<2TR1A!n3&H#N2lV^pgk=)MMcPOd;rqW#= zJ5#vg6*!R6IdM>!%zZF8WsQQ)nl}LOm)M5@Q&+ApjvTCvewAQo2bvHDaOFEl)K8;o zxIE$eyp;`1XRjLW{fm8R`LhR|Trt!3zJ9ybJ(EWZ(Yysjr@m9sj#KGemh!Hj-3}Xw`p8+Wy>xQdAZq;bf32NmSDRfEsEfM3p1pA*!hR;nP1Ol`or& zji4{X$=c^f8pN*|rBlC&qcS1+9MmLeb*Ox&(phY0o;Q+YUlx9oLZdS-Pw_8@D5K#r zAV@z7NPm;|{E%a@oiXkF+B*e~xs&ZOyE$G}JHy5n>a1$?Tj1cMxMcDxEfNwuFh5Z_ zm|}d?co$}LoVLK+YACk2ktQRYgdrh(Qp+63kLc%Qm zY~r5-lmQStd$@at0H_CW>1rgj_T&}S`N8g)r(g-|Xh4Y;Gtx_t<9U;c0}>xURK3SA ziL5c~aQB=^kuVpH#{k}nT$T>}yvm(#z+qzK@BqsNV&&&yA~2D`U&z)3IHW=~NA1`1 z6EiJ!UvqOlw2Lx>KiUla)_O&&y= zUbgaiE*Bb!h1f`c(U!AA@&(QQ@-UDN==lo0e5)K)I}zvdfxj*s)NJUCP!fS>0Q$KbcU| zCR?u*2I-~wmxg|L6hVMeeii0>MqCS%{kvY=iiW})OLt0G_XyFFuu>(F3F&@A*nVN- z?^dZryzJ+TRI|A&8tY4`1C2(rh9br+ZFc_MsU*U_a`xy4jGdcd|4(x=V`|MKL&{Rd ztxKndY@i&4mHrmwJra4PNW5D-1n(ebwLbTJBvk2E%X4K6GAMDzw=?o%4hWBDb8p!6U#LamFcEUM#0vipCaC*8# zr^XVR_@832HF|+|h0F+3kR?H{&mIE4)+sN}=-kV_od-OT7u@|SRda|{i1G2a329Ts zb|p+SUoAm1gN|cokyxl19xW=g0tx<}`n7cC7blmCFKOOC+EQ3|wlrK{>^eSbQ=B4y z;*YX;6Y)Pc7yn#J-LV6jJF3$^Qj&YdaQ8Xc-jJRN1FlB5kwPD5Dv2W*8Ueqmhrgb% zCuDPRiuxt76lwK^KE`GgLQo3V$bvU=5p)>its7-CtCQl!<%imO4Jhu?=6>JM!NRpT5c1UwOzc zyAWV|YsbnIc^X(`Xl#8>Wk3qQER`g16}Pb|2#p~5>ahIvUqSfPucp>+f|u^LzO{E_ z<$mIhyBJhIf-Jvo`|dJhJ19}r8A)rsvC#WHRKYO$Mcl`Q!%9l`wDM|9ImH_{-n|#b zVUG?bBNq|b&y&D#1%(OX|4KO#i&=f{`i4s81aeRfXEYHH>tln~vdWQDoL16^PW?m4 zvY{GsZg3Py(F|na6JioY9Q^n>oUPa=r6kqET1G@9PtGMK5V?mS)1y11X6;?MVB%LB zV6Zb~<|n+YoHrOSZhB07Z1t!E^5q``TddvdI`79SvdB#9w8F4J*~nQev4J{^P@Y0C z@{H?w=!aV}h`()pjAZsPTJX8Mqy-ESe%eo6U!KN;Dh)D3ue9^t_M|pbqqlej%;+WN z_UG!09?9<_4Fxk(IB*I!EASab;*RSFgHNYk)rqOGFngv*udKG>cD)iL4cjmBfnUh` zqum|310+{)A2K4vrUnxm%qh7gzTk0~G>%`cN8v~bAJ@K(j(A(DHqTa|B9%$m));{THTKuo8L)wbpP`#c7Z0d@f4QPC(pu|*f#ut#W=+msoZ9~+ zbo_CJmk9%&Td=sDZonl2_3g=?1AejyKC^_N-BjCnzS##5Yi$@)f+|ACzy(NaHR-R3 zyeFq$*_Xz|r064X<^53DkDEM3@&S+}@teDuwJ=+uUI`!t(KOa&gOn6K)%F0i0?>td zPYNVQm3Xs&Yt>Zp({Quu!Dfh(bQX75ON4 zVtm?pp14-%SLw8f1xiG&Y>1^Hx^Cy_bO>IByKVloN^eQRDz$lF#lxxmNIuc(_S=sC zW|GIzsCE80_w6G2%_8tWT(i11^RokujhfUoQ%bIjFgru4Fp`R%j-L`_$@5H#3=Inj zFLPZio(u#Eq3e#PE2LxdY+P8iwWDjm&DYu4!P$;y870rQYDleuaU=G$yfN?^{kVKS zQ7j9Mm4HpCO{hRW0%)$4Lj{Nj5}ud7bd+tS#h@xlm(%kpo@&jFjSpxEi>Du_1}qRl z2I_V!mb4I={~hJPk0V}omTw|S%-}Cl_<7@L25<5g%yhljVC(L!l|LH@+x-*h%(4!a|e za{TQ$OXmvL%b9l0KK4ojdL|7EGR4@1Jkwa+N6Os+5-uHu4?TQIK4*y2cSM|6l)}%R z4%Ea|SpX{T^o*Qx=wv1uo$ z>(bBgzWyb8oI~fxwoNp$=vTir>%VYsq_@rty3vZXzqX8#8Hl=7yp$tHIDgv-gI3$2 zRLG1-37+heo%R1-F7DHwrQUfvEN=U0BC?jX@b_}fj`Enr((U@vKS)ZXLrG;E?UbM7 z>m`Xs2+rs~d?SI}A^_sU?D-sTCc@QNJ&jmKQ6zfb^&onM^A>y6L+mzm#n^GI9 z_x(h8BoiGE>R}MmN63f4g#0E9c!L0Hqa14!r^tS}dXaf4$?-<^)8Q5uB7gA6&#`&^ zQ7C5#WSJLo*oLU-C3+Q-T#YfI69V$7hP$Dt_QY)}9g8Y`#eJ0@NtKgs$q3(oeQDbh z7111xO?~^6SILg!M=OsC1tW7isCmc7@_*}8M1r1%!6wtI-UJX&Q^=LCd>=Xf_UsMQn z#vVf4UM=uRr?{RFQ&ZEM8K+Fk*8Azxfr*S{*V{RbN~t;72A`#WesUMgR$6a%Bz!$k z3jbZQ2*XE6N4Gw)y3fo{;Uve^{)XcIAkc9j8iMncHwx1E6U2+S!zv?%Z`>vX3-guU z0${Pka+YFg+=oMiE?&;E-^CU79`@{X1V>+ZDeI^hMWo8lbiVD?4BnR}efM6dwOfpr z+S%%Y6U{wP7S5K&{Q_6Qu`$XfzxCBc+75T@N`Pk>eJAa@mAWeeLfRM=nR^FU-K&L70ox%e{sB#P&D!-xsC!!&Vjku`M=GZ2&+6*1OO0WN`O6`3xB$$$1Cc~P2 zZvZ;-`G{OcKuAXMls2ap51--}H zptl=EM4@mcj3RV|IXagPYshgsecOeafP}%}Cm~IPPm#d@psNUyXWi6B8>f>TmgMa^RW>1~ANq%#_s5CR)dF%~< zcO1UgMsi51$+gZT=SRN2k8idb5?$(}cbM=8&ani~!zHyv=6UaXg45G;OO4b2RIqR3 zbEUZpy6gV?+TKX@S&O$|if+4#->4=28#C?pf8t1!3l9ioLdm+cWGu?2zRDwc#OQqx+1Hb zCr6{jo8`XhhD;q^A^$0hw_w*w2D=c3K)pQkH`G)#$8vFzl-m9kxau?lN;K`2=BmGO zFGOPQq6Be=)KDNVj7cyoy9_qD`5e2RALME3V4R3=p}~g@?2m|uvR%RfCSIr=j|IhWUVUG z4wE|M3M86pgDvJ86c3lRQwM{G$Ny{W^`aR{zedy<`N3!*R9gliJdw?Tjb2Z4ql&uQ zl3awrju8-VkB)Z3mQ_>}g@af{#9Seqn5dsNL`k^_c+qh?N+hOjZTVGk^U)chEfSy% z)kMo3uuxfx!b86KEK3IF=INbk(wdBp`fjnRAJK@`xEa923uB$G|w_cZ!>&4{?@TjBYG5cFY z^l5PobrAswMhKX3G2dc9?Kl0u69Bz~G&T6zxh3RA1rjefirxV7%{2_npzkHfk3w&K zn*V5@Rn7(a7#k);4QlN8w9d$y!OfYmfuCJoE2K4+GnI`57*T&gDq9j5s!~a zI0mo(T=wU1FJG8G8Ho%EOvg_1a@1@mJ--F%c2~H2eXOeJP=G$K(RiVLbm;O-M?6F# z{Y>v6J#76KYi;amLV!EO9tp2JE|1*uVhl;3OJ}psuMjMm{Pue3(9BU_RvA<+cj@u> zP1*!GrIoN6)=aS^Q}&{?ItPPol0yLgc_z`h^oShF z_~v)HFtZF#@fkiA(in}sR*lz?L9qW0+0(*!tWYenV5 z?oUr8^11yk9puWv;pR`Q%gjMKq(Bu_1-%+=S(2ycOeC$-)#}`V+G%OUZ{~f)T#5hU z`Cv&bg>^du(V-#bPxF}JTl?Qu!T83flX|?!Vjk^-+F}O?&BZyfyxAz(%+OvA` zCfM1z#6%52Li|V!v;t4J*XVsfG{BFG_0prMHSt>XRFUfxaDTV(ThpYzA%V8(T5UZc zDv)EFXbU@FIrc5LOkV&mtW4+u%GgZh=&8}S#z5<&G4@DOcf&NbdZ?pevs)-7rg!ks ztEi(QqaR%kEaUtPi4(9tJMC$&zIE{@sUKJkEuASY5R-qpk;WyH= z;()&U0O1sM2M^6yT~T3E{i0b(K&1MN&sYJVg$vuSSeNh4@VDlET0&|fZ-?qS6b(oi z@4yEBlcN*m$!CHLDt>$ssx6C^TjdhlT#?~6wN6%f?AK6k^%DISF{4!>-#rd8GCvOK zMMNVtA;nO)E&hn9@-UJ2<ie;$?sEKP9+TSLG|18a?+l_+Qds01{PN?(6kf_+4veh)cfwX{4% zhT3a6vqe?MS#7#WUoV*^9)4y*G^lV7iR(C^&sBes8Fp`ak`ppcaSO5aB67s`MulPC zOZTEr$0iI;T?tAeaV{hb3w=*^3x0D!6{{wIjM3>1+PXNx87ps=Qg|5N#3KmBpZLn` z{X}`aAfkf+Mg#-ln8&7wM{KYxfN(=3yh|!Aa$T4&J;N?7ped%^cEZoBlB1^U+-LYL8kn@YvEgJfH7g{7XYu zLJ19&86g%gCtGvlJVH3tArUCkCsBV(m9^W=&_kXRENdTn+2x4u69>>ta%z5+AVl53cg*0hPswpCm$ z3|boDuK$EWR~Us!YHm5LJ^u7)wH`EudtoeVi7(u9#x`5xNBQ5B(|4NTS>QbeE?>fI|Y1lOJI)Igu zqRZV?;IV^6p0db*q^q8E8%EV`If3m+T8FE|)_H3I-}ss3(bw{2qFl-l8dO5xo{iD* z-y-^-Dazi!mn?O8OK`u~%{^hKdo5UF%XcjiAO%8o3nf>Hb-`=$dJV6#J6E%zfko7A zr)D>rfa*)iV&6b@K%2V2D`L0Ple@R(^s287W$%Jpx@@5fW?DC#@{Duiz9yXV)nKr| zzPja>>??$n}?mOGPg2)K1~L#|$b=M3e}*3z&@|NhH_lz_I?6{e2hvekd%51f5F@ z>y@LD%7*8+??1hzM4_WmCWOZUh*2*&rbk7W4T(=$GRFdx__%)Ux3UHPd(r0u-NW*C zdj#pCCdTDEbI~$E{bQ0_J^q+_DzB)1-Ti)&8~=OscU2ic3b$G}O1ywi#@n7-wB5C} zEK^LCL`Y01p&yM=EDT^HCyqc9FP!d&z`AuZT9qH_bZ=wMp@``X?Hf*@)K@x&uCpJpMD z)9qL%Lc^%;Xm7plwKG`D8|yV03JH>*CT17S-K8L9lx$`JcEJnP_yGWc(Sut*1IR%T z`o&DNr*?_s{-?uzPi}6;=!TuZYH);1xwyPm4b4)y=OLPt^G=vK&W89AzyxwQO0d z2~K70E%=7GNDqGSRpvSNZ#BNpf!MguMv(sT;DWL7T5DjZ#vgrp|2m4R)U6-*BkkBl z43r=eFY`9PnWB+XbVCoE1NLQKsbxKq7U2b4oI!cleDmz$ zE6R%Otdd7B=$e!J=pamMu5?Wq7T``6y&}vfKe!}d3J^Z-gQ?5~l?5Fdm^cLNI9V?( z*1nL|y&#ioe@%hxV4ihRjPK0tv*f%C2KjRbUb`t+lsUsJz;?|YU%;0!iKfPP<_!y; zv6+5{&h>H6>TOA*g9>#YCmQP}aReeHItP;x9>O z3ZT;5xucB%ec4(ZTyO)4YS^>4IS8H1&I^0x#udc0RU<3NyOigj>Ep8i8$oFPE>XSf zIO*NbCb`O8Erw|dzSH>~OVLYa6l_H_V{au2Cd7MGe;2eEjjV1aN9RPZddjzCW!1-LXb2KXZi%C6EH+wjG z&QGRY_D$+;Fjgjt(PlQ_df$jnyMEAcIW%XfJ3lbuCb=v8^(FK2XFTk&UPhC=LgXs) ziX6S(pSAkSPC<4pmtqS-;N;=L^Fx_OM*1@uLJ{h^;J4j0L7)WUuS4(Y<{bzGm1C=& zG@pw@PiyW^ywAhSINQlVgCTYKU_5bcM6>ysLAG9R3ZRc+&Dyq5s$Hd?CiN;|;6MSn z0U|**XMlDKjOds7!xfKY%;S!3EV70~8Z5^ppRT$R+*; zGML;gT2idEh}1eh#Jtkqo_)4RZtC7N@y>-sj}A`U3y#%=p?6T3#K%hc_g4C<$4=+V zUVZO%w|O11RH?ac0F4b8FzP-R1MmCBWv-+*bB5-!cp^a6_3y*hQflUV#EO5wl!A1s zMVnYDiBzGZj)tYHzXbBvSOX3rl}8`B9d71x7-)I;pQ7m8niIo03HWQyR_&9%)dm?E ztdN$)6w(t`_L!YC!y5y?J5^_#PpT#EjFo`whI#GcplwcGluooj_L~O{WHdFxGL@N=rkmxa0S)*L-sQok%{Ah*|J-y`FIqayme&($ivGXsfUpv)+(%afz8j! z!08;``p~;z9%QLnH$RBkuc7FIJQAYk#w{4*nM5OKsIh?ziPh zlbF$66>8*moO}Dcm$Ni9{e^haU->dRpFtoGgt%WI7|>t$F4oZA=XTUKfv@#E^iK^D zG|J@-hX9GJjY0U943TpF{G+a**VbZc$f497GF{NtXC_D^aV5bpvoAie#%A|lk_`=|;2Q==D_-0YCu zV6)%XI{`}(>=eXYTtEHWE$(DWT`C|F-(bfts9dKE=0c6L9tzpn9nkVdka4scQMxn> z5=cfK@xbOXX#oQ`yO(l|VtS?i$CYy@jo4N3yXpn4d)@>r*|9AKP2ep|W5EtbXRU>= ztH&3UC_2X*Odb8c)YG|D*c`$O{O4Y>c^$jP9Wyou;j=~ewVuF0z0Ik-gm*3M{8~+s*9pXJB9L;Y{&+j8s?AcHKy@Z+&z+w*+gk!M=~x$x`>wE6j|Zc!_R##A z-;avRTrv)(&7nRJrH}1>hhh~mgLaOd<7_ z31;zf?gX+tSlzUBkaw9V+h|SWr|r!ak6u}kEbw@q#P5RH-GnL67!gMnW`2}4QkK-# zdqs$~C46elll_dQtlR4%l{DVrcx!&)^lsq~2QiC$FQ+zL4mYRvp3eA_`k|{QQ)=b* z2Z^_kG5kGtGJR>C?5xh8XQS^s{WLPY3IF%70z1~Mu919i2HX^%F*XMf?~wj@Dp6e` zgLrT~+l2I4nQp&epDISBI?Oew3)Z>G@(B5PNhPev^?rKHxtg%dnWy-V;5=Ga#Hh&k zdd-}5mYl`HfRpMO@*wkP%@7IeM-IIxx{q>VY&Geni76OwHjbsRE-d@Gu$;oPKD_BM| zQP}w%WulqpHFgyrUgiH{lh>pccj#8B8U#F&pVT){{aU#kiNM4*Edk&lCF|?RfqA5M zQ6(oI5l4tF?iy|AV`e7*~%lf0XEPQ)*! z3m}7SUV#oaW~AHA`8+lK0p+B_3Tb(!=btpW4#+ zjX5}^^s^@+Crj7;m9}qsywTmj{6|p6s0J6OSR3MYdT4m*4!%fKYa9NNGo(%q!#)TW zMeb1uI)uJq5BNb;fa4D7IOaZ{ZaFeru*#N`%o#06=Cl{!7ZIu|m>aqG*r@lD)QE=v zCpW90Reba4emlkw9uMj{5_(ohH!fq%3vB(zn{TbEnXH5$5OB;hu1Q#Cqt2bdS)w$I+C%U@=3Y6OJzFS>y zcJk2QaG7C%x)uu`vz8^Y zInlWbfWplmH3OMG%7zQ*7}NI3Y(?)|LV^C)pYYMvZ7S~0gwfX6DuzyNIlV^m3~i$| zJwuq$%dz!9(8H(lyXv{q?=d&OByaff51lu9-}B?p8uNd-(biDH`=koAKDsQS2r`Z3 z2TtjJD45F}5RTz5gmT4d5w{2X1$(1gG_#^$WGJ3meAy@=>Lye@e%2OYz$03g`c2>1NzH&4jN$56{J!?jJREqVY>=p=y>wZsp47}@FE~vWIn6HZEw=wqU!RGOeIXC z#;w|Ie)9Bu^%)(QCh^u!%OS0$#MXd)MfFDcPw_ybtG}kJ(SY~xeeNSXwlGog{VSW= w`d9B`zrC7bpnKJTiTR2P8TtR)$NYAw_Qv~}e-4QM9(tuDrv|E$`4aMf0M43JzW@LL literal 0 HcmV?d00001 diff --git a/recipes/recipes/images/thumb/sphx_glr_zeroing_out_gradients_thumb.png b/recipes/recipes/images/thumb/sphx_glr_zeroing_out_gradients_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..233f8e605efca4bef384a7c603d53fdc385428bc GIT binary patch literal 26786 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 literal 0 HcmV?d00001 diff --git a/recipes/recipes/loading_data_recipe.ipynb b/recipes/recipes/loading_data_recipe.ipynb new file mode 100644 index 00000000000..8332ae5a58f --- /dev/null +++ b/recipes/recipes/loading_data_recipe.ipynb @@ -0,0 +1,140 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\nLoading data in PyTorch\n=======================\nPyTorch features extensive neural network building blocks with a simple,\nintuitive, and stable API. PyTorch includes packages to prepare and load\ncommon datasets for your model.\n\nIntroduction\n------------\nAt the heart of PyTorch data loading utility is the\n`torch.utils.data.DataLoader `__\nclass. It represents a Python iterable over a dataset. Libraries in\nPyTorch offer built-in high-quality datasets for you to use in\n`torch.utils.data.Dataset `__.\nThese datasets are currently available in:\n\n* `torchvision `__\n* `torchaudio `__\n* `torchtext `__\n\nwith more to come.\nUsing the Yesno dataset from ``torchaudio.datasets.YESNO``, we will\ndemonstrate how to effectively and efficiently load data from a PyTorch\n``Dataset`` into a PyTorch ``DataLoader``.\n\nSetup\n-----\nBefore we begin, we need to install ``torchaudio`` to have access to the\ndataset.\n\n::\n\n pip install torchaudio\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Steps\n-----\n\n1. Import all necessary libraries for loading our data\n2. Access the data in the dataset\n3. Loading the data\n4. Iterate over the data\n5. [Optional] Visualize the data\n\n\n1. Import necessary libraries for loading our data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor this recipe, we will use ``torch`` and ``torchaudio``. Depending on\nwhat built-in datasets you use, you can also install and import\n``torchvision`` or ``torchtext``.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import torch\nimport torchaudio" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2. Access the data in the dataset\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe Yesno dataset in ``torchaudio`` features sixty recordings of one\nindividual saying yes or no in Hebrew; with each recording being eight\nwords long (`read more here `__).\n\n``torchaudio.datasets.YESNO`` creates a dataset for YesNo.\n\n::\n\n torchaudio.datasets.YESNO(\n root,\n url='http://www.openslr.org/resources/1/waves_yesno.tar.gz',\n folder_in_archive='waves_yesno',\n download=False,\n transform=None,\n target_transform=None)\n\nEach item in the dataset is a tuple of the form: (waveform, sample_rate,\nlabels).\n\nYou must set a ``root`` for the Yesno dataset, which is where the\ntraining and testing dataset will exist. The other parameters are\noptional, with their default values shown. Here is some additional\nuseful info on the other parameters:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# * ``download``: If true, downloads the dataset from the internet and puts it in root directory. If dataset is already downloaded, it is not downloaded again.\n# * ``transform``: Using transforms on your data allows you to take it from its source state and transform it into data that\u2019s joined together, de-normalized, and ready for training. Each library in PyTorch supports a growing list of transformations.\n# * ``target_transform``: A function/transform that takes in the target and transforms it.\n# \n# Let\u2019s access our Yesno data:\n# \n\n# A data point in Yesno is a tuple (waveform, sample_rate, labels) where labels \n# is a list of integers with 1 for yes and 0 for no.\nyesno_data_trainset = torchaudio.datasets.YESNO('./', download=True)\n\n# Pick data point number 3 to see an example of the the yesno_data:\nn = 3\nwaveform, sample_rate, labels = yesno_data[n]\nprint(\"Waveform: {}\\nSample rate: {}\\nLabels: {}\".format(waveform, sample_rate, labels))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When using this data in practice, it is best practice to provision the\ndata into a \u201ctraining\u201d dataset and a \u201ctesting\u201d dataset. This ensures\nthat you have out-of-sample data to test the performance of your model.\n\n3. Loading the data\n~~~~~~~~~~~~~~~~~~~~~~~\n\nNow that we have access to the dataset, we must pass it through\n``torch.utils.data.DataLoader``. The ``DataLoader`` combines the dataset\nand a sampler, returning an iterable over the dataset.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "data_loader = torch.utils.data.DataLoader(yesno_data,\n batch_size=1,\n shuffle=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4. Iterate over the data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nOur data is now iterable using the ``data_loader``. This will be\nnecessary when we begin training our model! You will notice that now\neach data entry in the ``data_loader`` object is converted to a tensor\ncontaining tensors representing our waveform, sample rate, and labels.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "for data in data_loader:\n print(\"Data: \", data)\n print(\"Waveform: {}\\nSample rate: {}\\nLabels: {}\".format(data[0], data[1], data[2]))\n break" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "5. [Optional] Visualize the data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou can optionally visualize your data to further understand the output\nfrom your ``DataLoader``.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n\nprint(data[0][0].numpy())\n\nplt.figure()\nplt.plot(waveform.t().numpy())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Congratulations! You have successfully loaded data in PyTorch.\n\nLearn More\n----------\n\nTake a look at these other recipes to continue your learning:\n\n- `Defining a Neural Network `__\n- `What is a state_dict in PyTorch `__\n\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/recipes/recipes/loading_data_recipe.py b/recipes/recipes/loading_data_recipe.py new file mode 100644 index 00000000000..99e685897c3 --- /dev/null +++ b/recipes/recipes/loading_data_recipe.py @@ -0,0 +1,171 @@ +""" +Loading data in PyTorch +======================= +PyTorch features extensive neural network building blocks with a simple, +intuitive, and stable API. PyTorch includes packages to prepare and load +common datasets for your model. + +Introduction +------------ +At the heart of PyTorch data loading utility is the +`torch.utils.data.DataLoader `__ +class. It represents a Python iterable over a dataset. Libraries in +PyTorch offer built-in high-quality datasets for you to use in +`torch.utils.data.Dataset `__. +These datasets are currently available in: + +* `torchvision `__ +* `torchaudio `__ +* `torchtext `__ + +with more to come. +Using the Yesno dataset from ``torchaudio.datasets.YESNO``, we will +demonstrate how to effectively and efficiently load data from a PyTorch +``Dataset`` into a PyTorch ``DataLoader``. + +Setup +----- +Before we begin, we need to install ``torchaudio`` to have access to the +dataset. + +:: + + pip install torchaudio + + +""" + + + + + + + +###################################################################### +# Steps +# ----- +# +# 1. Import all necessary libraries for loading our data +# 2. Access the data in the dataset +# 3. Loading the data +# 4. Iterate over the data +# 5. [Optional] Visualize the data +# +# +# 1. Import necessary libraries for loading our data +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# For this recipe, we will use ``torch`` and ``torchaudio``. Depending on +# what built-in datasets you use, you can also install and import +# ``torchvision`` or ``torchtext``. +# + +import torch +import torchaudio + + +###################################################################### +# 2. Access the data in the dataset +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# The Yesno dataset in ``torchaudio`` features sixty recordings of one +# individual saying yes or no in Hebrew; with each recording being eight +# words long (`read more here `__). +# +# ``torchaudio.datasets.YESNO`` creates a dataset for YesNo. +# +# :: +# +# torchaudio.datasets.YESNO( +# root, +# url='http://www.openslr.org/resources/1/waves_yesno.tar.gz', +# folder_in_archive='waves_yesno', +# download=False, +# transform=None, +# target_transform=None) +# +# Each item in the dataset is a tuple of the form: (waveform, sample_rate, +# labels). +# +# You must set a ``root`` for the Yesno dataset, which is where the +# training and testing dataset will exist. The other parameters are +# optional, with their default values shown. Here is some additional +# useful info on the other parameters: + +# * ``download``: If true, downloads the dataset from the internet and puts it in root directory. If dataset is already downloaded, it is not downloaded again. +# * ``transform``: Using transforms on your data allows you to take it from its source state and transform it into data that’s joined together, de-normalized, and ready for training. Each library in PyTorch supports a growing list of transformations. +# * ``target_transform``: A function/transform that takes in the target and transforms it. +# +# Let’s access our Yesno data: +# + +# A data point in Yesno is a tuple (waveform, sample_rate, labels) where labels +# is a list of integers with 1 for yes and 0 for no. +yesno_data_trainset = torchaudio.datasets.YESNO('./', download=True) + +# Pick data point number 3 to see an example of the the yesno_data: +n = 3 +waveform, sample_rate, labels = yesno_data[n] +print("Waveform: {}\nSample rate: {}\nLabels: {}".format(waveform, sample_rate, labels)) + + +###################################################################### +# When using this data in practice, it is best practice to provision the +# data into a “training” dataset and a “testing” dataset. This ensures +# that you have out-of-sample data to test the performance of your model. +# +# 3. Loading the data +# ~~~~~~~~~~~~~~~~~~~~~~~ +# +# Now that we have access to the dataset, we must pass it through +# ``torch.utils.data.DataLoader``. The ``DataLoader`` combines the dataset +# and a sampler, returning an iterable over the dataset. +# + +data_loader = torch.utils.data.DataLoader(yesno_data, + batch_size=1, + shuffle=True) + + +###################################################################### +# 4. Iterate over the data +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Our data is now iterable using the ``data_loader``. This will be +# necessary when we begin training our model! You will notice that now +# each data entry in the ``data_loader`` object is converted to a tensor +# containing tensors representing our waveform, sample rate, and labels. +# + +for data in data_loader: + print("Data: ", data) + print("Waveform: {}\nSample rate: {}\nLabels: {}".format(data[0], data[1], data[2])) + break + + +###################################################################### +# 5. [Optional] Visualize the data +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# You can optionally visualize your data to further understand the output +# from your ``DataLoader``. +# + +import matplotlib.pyplot as plt + +print(data[0][0].numpy()) + +plt.figure() +plt.plot(waveform.t().numpy()) + + +###################################################################### +# Congratulations! You have successfully loaded data in PyTorch. +# +# Learn More +# ---------- +# +# Take a look at these other recipes to continue your learning: +# +# - `Defining a Neural Network `__ +# - `What is a state_dict in PyTorch `__ diff --git a/recipes/recipes/loading_data_recipe.rst b/recipes/recipes/loading_data_recipe.rst new file mode 100644 index 00000000000..e2d70679b68 --- /dev/null +++ b/recipes/recipes/loading_data_recipe.rst @@ -0,0 +1,221 @@ +.. note:: + :class: sphx-glr-download-link-note + + Click :ref:`here ` to download the full example code +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_recipes_recipes_loading_data_recipe.py: + + +Loading data in PyTorch +======================= +PyTorch features extensive neural network building blocks with a simple, +intuitive, and stable API. PyTorch includes packages to prepare and load +common datasets for your model. + +Introduction +------------ +At the heart of PyTorch data loading utility is the +`torch.utils.data.DataLoader `__ +class. It represents a Python iterable over a dataset. Libraries in +PyTorch offer built-in high-quality datasets for you to use in +`torch.utils.data.Dataset `__. +These datasets are currently available in: + +* `torchvision `__ +* `torchaudio `__ +* `torchtext `__ + +with more to come. +Using the Yesno dataset from ``torchaudio.datasets.YESNO``, we will +demonstrate how to effectively and efficiently load data from a PyTorch +``Dataset`` into a PyTorch ``DataLoader``. + +Setup +----- +Before we begin, we need to install ``torchaudio`` to have access to the +dataset. + +:: + + pip install torchaudio +Steps +----- + +1. Import all necessary libraries for loading our data +2. Access the data in the dataset +3. Loading the data +4. Iterate over the data +5. [Optional] Visualize the data + + +1. Import necessary libraries for loading our data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For this recipe, we will use ``torch`` and ``torchaudio``. Depending on +what built-in datasets you use, you can also install and import +``torchvision`` or ``torchtext``. + + + +.. code-block:: default + + + import torch + import torchaudio + + + +2. Access the data in the dataset +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Yesno dataset in ``torchaudio`` features sixty recordings of one +individual saying yes or no in Hebrew; with each recording being eight +words long (`read more here `__). + +``torchaudio.datasets.YESNO`` creates a dataset for YesNo. + +:: + + torchaudio.datasets.YESNO( + root, + url='http://www.openslr.org/resources/1/waves_yesno.tar.gz', + folder_in_archive='waves_yesno', + download=False, + transform=None, + target_transform=None) + +Each item in the dataset is a tuple of the form: (waveform, sample_rate, +labels). + +You must set a ``root`` for the Yesno dataset, which is where the +training and testing dataset will exist. The other parameters are +optional, with their default values shown. Here is some additional +useful info on the other parameters: + + +.. code-block:: default + + + # * ``download``: If true, downloads the dataset from the internet and puts it in root directory. If dataset is already downloaded, it is not downloaded again. + # * ``transform``: Using transforms on your data allows you to take it from its source state and transform it into data that’s joined together, de-normalized, and ready for training. Each library in PyTorch supports a growing list of transformations. + # * ``target_transform``: A function/transform that takes in the target and transforms it. + # + # Let’s access our Yesno data: + # + + # A data point in Yesno is a tuple (waveform, sample_rate, labels) where labels + # is a list of integers with 1 for yes and 0 for no. + yesno_data_trainset = torchaudio.datasets.YESNO('./', download=True) + + # Pick data point number 3 to see an example of the the yesno_data: + n = 3 + waveform, sample_rate, labels = yesno_data[n] + print("Waveform: {}\nSample rate: {}\nLabels: {}".format(waveform, sample_rate, labels)) + + + +When using this data in practice, it is best practice to provision the +data into a “training” dataset and a “testing” dataset. This ensures +that you have out-of-sample data to test the performance of your model. + +3. Loading the data +~~~~~~~~~~~~~~~~~~~~~~~ + +Now that we have access to the dataset, we must pass it through +``torch.utils.data.DataLoader``. The ``DataLoader`` combines the dataset +and a sampler, returning an iterable over the dataset. + + + +.. code-block:: default + + + data_loader = torch.utils.data.DataLoader(yesno_data, + batch_size=1, + shuffle=True) + + + +4. Iterate over the data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Our data is now iterable using the ``data_loader``. This will be +necessary when we begin training our model! You will notice that now +each data entry in the ``data_loader`` object is converted to a tensor +containing tensors representing our waveform, sample rate, and labels. + + + +.. code-block:: default + + + for data in data_loader: + print("Data: ", data) + print("Waveform: {}\nSample rate: {}\nLabels: {}".format(data[0], data[1], data[2])) + break + + + +5. [Optional] Visualize the data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can optionally visualize your data to further understand the output +from your ``DataLoader``. + + + +.. code-block:: default + + + import matplotlib.pyplot as plt + + print(data[0][0].numpy()) + + plt.figure() + plt.plot(waveform.t().numpy()) + + + +Congratulations! You have successfully loaded data in PyTorch. + +Learn More +---------- + +Take a look at these other recipes to continue your learning: + +- `Defining a Neural Network `__ +- `What is a state_dict in PyTorch `__ + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** ( 0 minutes 0.000 seconds) + + +.. _sphx_glr_download_recipes_recipes_loading_data_recipe.py: + + +.. only :: html + + .. container:: sphx-glr-footer + :class: sphx-glr-footer-example + + + + .. container:: sphx-glr-download + + :download:`Download Python source code: loading_data_recipe.py ` + + + + .. container:: sphx-glr-download + + :download:`Download Jupyter notebook: loading_data_recipe.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/profiler.ipynb b/recipes/recipes/profiler.ipynb new file mode 100644 index 00000000000..6d0faeb373d --- /dev/null +++ b/recipes/recipes/profiler.ipynb @@ -0,0 +1,201 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\nPyTorch Profiler\n====================================\nThis recipe explains how to use PyTorch profiler and measure the time and\nmemory consumption of the model's operators.\n\nIntroduction\n------------\nPyTorch includes a simple profiler API that is useful when user needs\nto determine the most expensive operators in the model.\n\nIn this recipe, we will use a simple Resnet model to demonstrate how to\nuse profiler to analyze model performance.\n\nSetup\n-----\nTo install ``torch`` and ``torchvision`` use the following command:\n\n::\n\n pip install torch torchvision\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Steps\n-----\n\n1. Import all necessary libraries\n2. Instantiate a simple Resnet model\n3. Use profiler to analyze execution time\n4. Use profiler to analyze memory consumption\n5. Using tracing functionality\n\n1. Import all necessary libraries\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIn this recipe we will use ``torch``, ``torchvision.models``\nand ``profiler`` modules:\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import torch\nimport torchvision.models as models\nimport torch.autograd.profiler as profiler" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2. Instantiate a simple Resnet model\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nLet's create an instance of a Resnet model and prepare an input\nfor it:\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "model = models.resnet18()\ninputs = torch.randn(5, 3, 224, 224)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3. Use profiler to analyze execution time\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPyTorch profiler is enabled through the context manager and accepts\na number of parameters, some of the most useful are:\n\n- ``record_shapes`` - whether to record shapes of the operator inputs;\n- ``profile_memory`` - whether to report amount of memory consumed by\n model's Tensors;\n- ``use_cuda`` - whether to measure execution time of CUDA kernels.\n\nLet's see how we can use profiler to analyze the execution time:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "with profiler.profile(record_shapes=True) as prof:\n with profiler.record_function(\"model_inference\"):\n model(inputs)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that we can use ``record_function`` context manager to label\narbitrary code ranges with user provided names\n(``model_inference`` is used as a label in the example above).\nProfiler allows one to check which operators were called during the\nexecution of a code range wrapped with a profiler context manager.\nIf multiple profiler ranges are active at the same time (e.g. in\nparallel PyTorch threads), each profiling context manager tracks only\nthe operators of its corresponding range.\nProfiler also automatically profiles the async tasks launched\nwith ``torch.jit._fork`` and (in case of a backward pass)\nthe backward pass operators launched with ``backward()`` call.\n\nLet's print out the stats for the execution above:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(prof.key_averages().table(sort_by=\"cpu_time_total\", row_limit=10))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The output will look like (omitting some columns):\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# ------------------------- -------------- ---------- ------------ ---------\n# Name Self CPU total CPU total CPU time avg # Calls\n# ------------------------- -------------- ---------- ------------ ---------\n# model_inference 3.541ms 69.571ms 69.571ms 1\n# conv2d 69.122us 40.556ms 2.028ms 20\n# convolution 79.100us 40.487ms 2.024ms 20\n# _convolution 349.533us 40.408ms 2.020ms 20\n# mkldnn_convolution 39.822ms 39.988ms 1.999ms 20\n# batch_norm 105.559us 15.523ms 776.134us 20\n# _batch_norm_impl_index 103.697us 15.417ms 770.856us 20\n# native_batch_norm 9.387ms 15.249ms 762.471us 20\n# max_pool2d 29.400us 7.200ms 7.200ms 1\n# max_pool2d_with_indices 7.154ms 7.170ms 7.170ms 1\n# ------------------------- -------------- ---------- ------------ ---------" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here we see that, as expected, most of the time is spent in convolution (and specifically in ``mkldnn_convolution``\nfor PyTorch compiled with MKL-DNN support).\nNote the difference between self cpu time and cpu time - operators can call other operators, self cpu time exludes time\nspent in children operator calls, while total cpu time includes it.\n\nTo get a finer granularity of results and include operator input shapes, pass ``group_by_input_shape=True``:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(prof.key_averages(group_by_input_shape=True).table(sort_by=\"cpu_time_total\", row_limit=10))\n\n# (omitting some columns)\n# ------------------------- ----------- -------- -------------------------------------\n# Name CPU total # Calls Input Shapes\n# ------------------------- ----------- -------- -------------------------------------\n# model_inference 69.571ms 1 []\n# conv2d 9.019ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []]\n# convolution 9.006ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []]\n# _convolution 8.982ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []]\n# mkldnn_convolution 8.894ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []]\n# max_pool2d 7.200ms 1 [[5, 64, 112, 112]]\n# conv2d 7.189ms 3 [[5, 512, 7, 7], [512, 512, 3, 3], []]\n# convolution 7.180ms 3 [[5, 512, 7, 7], [512, 512, 3, 3], []]\n# _convolution 7.171ms 3 [[5, 512, 7, 7], [512, 512, 3, 3], []]\n# max_pool2d_with_indices 7.170ms 1 [[5, 64, 112, 112]]\n# ------------------------- ----------- -------- --------------------------------------" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4. Use profiler to analyze memory consumption\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPyTorch profiler can also show the amount of memory (used by the model's tensors)\nthat was allocated (or released) during the execution of the model's operators.\nIn the output below, 'self' memory corresponds to the memory allocated (released)\nby the operator, excluding the children calls to the other operators.\nTo enable memory profiling functionality pass ``profile_memory=True``.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "with profiler.profile(profile_memory=True, record_shapes=True) as prof:\n model(inputs)\n\nprint(prof.key_averages().table(sort_by=\"self_cpu_memory_usage\", row_limit=10))\n\n# (omitting some columns)\n# --------------------------- --------------- --------------- ---------------\n# Name CPU Mem Self CPU Mem Number of Calls\n# --------------------------- --------------- --------------- ---------------\n# empty 94.79 Mb 94.79 Mb 123\n# resize_ 11.48 Mb 11.48 Mb 2\n# addmm 19.53 Kb 19.53 Kb 1\n# empty_strided 4 b 4 b 1\n# conv2d 47.37 Mb 0 b 20\n# --------------------------- --------------- --------------- ---------------\n\nprint(prof.key_averages().table(sort_by=\"cpu_memory_usage\", row_limit=10))\n\n# (omitting some columns)\n# --------------------------- --------------- --------------- ---------------\n# Name CPU Mem Self CPU Mem Number of Calls\n# --------------------------- --------------- --------------- ---------------\n# empty 94.79 Mb 94.79 Mb 123\n# batch_norm 47.41 Mb 0 b 20\n# _batch_norm_impl_index 47.41 Mb 0 b 20\n# native_batch_norm 47.41 Mb 0 b 20\n# conv2d 47.37 Mb 0 b 20\n# convolution 47.37 Mb 0 b 20\n# _convolution 47.37 Mb 0 b 20\n# mkldnn_convolution 47.37 Mb 0 b 20\n# empty_like 47.37 Mb 0 b 20\n# max_pool2d 11.48 Mb 0 b 1\n# max_pool2d_with_indices 11.48 Mb 0 b 1\n# resize_ 11.48 Mb 11.48 Mb 2\n# addmm 19.53 Kb 19.53 Kb 1\n# adaptive_avg_pool2d 10.00 Kb 0 b 1\n# mean 10.00 Kb 0 b 1\n# --------------------------- --------------- --------------- ---------------" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "5. Using tracing functionality\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nProfiling results can be outputted as a .json trace file:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "with profiler.profile() as prof:\n with profiler.record_function(\"model_inference\"):\n model(inputs)\n\nprof.export_chrome_trace(\"trace.json\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "User can examine the sequence of profiled operators after loading the trace file\nin Chrome (``chrome://tracing``):\n\n![](../../_static/img/trace_img.png)\n\n :scale: 25 %\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Learn More\n----------\n\nTake a look at the following tutorial to learn how to visualize your model with TensorBoard:\n\n- `Visualizing models, data, and training with TensorBoard `_ tutorial\n\n\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/recipes/recipes/profiler.py b/recipes/recipes/profiler.py new file mode 100644 index 00000000000..b3233302176 --- /dev/null +++ b/recipes/recipes/profiler.py @@ -0,0 +1,215 @@ +""" +PyTorch Profiler +==================================== +This recipe explains how to use PyTorch profiler and measure the time and +memory consumption of the model's operators. + +Introduction +------------ +PyTorch includes a simple profiler API that is useful when user needs +to determine the most expensive operators in the model. + +In this recipe, we will use a simple Resnet model to demonstrate how to +use profiler to analyze model performance. + +Setup +----- +To install ``torch`` and ``torchvision`` use the following command: + +:: + + pip install torch torchvision + + +""" + + +###################################################################### +# Steps +# ----- +# +# 1. Import all necessary libraries +# 2. Instantiate a simple Resnet model +# 3. Use profiler to analyze execution time +# 4. Use profiler to analyze memory consumption +# 5. Using tracing functionality +# +# 1. Import all necessary libraries +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# In this recipe we will use ``torch``, ``torchvision.models`` +# and ``profiler`` modules: +# + +import torch +import torchvision.models as models +import torch.autograd.profiler as profiler + + +###################################################################### +# 2. Instantiate a simple Resnet model +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Let's create an instance of a Resnet model and prepare an input +# for it: +# + +model = models.resnet18() +inputs = torch.randn(5, 3, 224, 224) + +###################################################################### +# 3. Use profiler to analyze execution time +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# PyTorch profiler is enabled through the context manager and accepts +# a number of parameters, some of the most useful are: +# +# - ``record_shapes`` - whether to record shapes of the operator inputs; +# - ``profile_memory`` - whether to report amount of memory consumed by +# model's Tensors; +# - ``use_cuda`` - whether to measure execution time of CUDA kernels. +# +# Let's see how we can use profiler to analyze the execution time: + +with profiler.profile(record_shapes=True) as prof: + with profiler.record_function("model_inference"): + model(inputs) + +###################################################################### +# Note that we can use ``record_function`` context manager to label +# arbitrary code ranges with user provided names +# (``model_inference`` is used as a label in the example above). +# Profiler allows one to check which operators were called during the +# execution of a code range wrapped with a profiler context manager. +# If multiple profiler ranges are active at the same time (e.g. in +# parallel PyTorch threads), each profiling context manager tracks only +# the operators of its corresponding range. +# Profiler also automatically profiles the async tasks launched +# with ``torch.jit._fork`` and (in case of a backward pass) +# the backward pass operators launched with ``backward()`` call. +# +# Let's print out the stats for the execution above: + +print(prof.key_averages().table(sort_by="cpu_time_total", row_limit=10)) + +###################################################################### +# The output will look like (omitting some columns): + +# ------------------------- -------------- ---------- ------------ --------- +# Name Self CPU total CPU total CPU time avg # Calls +# ------------------------- -------------- ---------- ------------ --------- +# model_inference 3.541ms 69.571ms 69.571ms 1 +# conv2d 69.122us 40.556ms 2.028ms 20 +# convolution 79.100us 40.487ms 2.024ms 20 +# _convolution 349.533us 40.408ms 2.020ms 20 +# mkldnn_convolution 39.822ms 39.988ms 1.999ms 20 +# batch_norm 105.559us 15.523ms 776.134us 20 +# _batch_norm_impl_index 103.697us 15.417ms 770.856us 20 +# native_batch_norm 9.387ms 15.249ms 762.471us 20 +# max_pool2d 29.400us 7.200ms 7.200ms 1 +# max_pool2d_with_indices 7.154ms 7.170ms 7.170ms 1 +# ------------------------- -------------- ---------- ------------ --------- + +###################################################################### +# Here we see that, as expected, most of the time is spent in convolution (and specifically in ``mkldnn_convolution`` +# for PyTorch compiled with MKL-DNN support). +# Note the difference between self cpu time and cpu time - operators can call other operators, self cpu time exludes time +# spent in children operator calls, while total cpu time includes it. +# +# To get a finer granularity of results and include operator input shapes, pass ``group_by_input_shape=True``: + +print(prof.key_averages(group_by_input_shape=True).table(sort_by="cpu_time_total", row_limit=10)) + +# (omitting some columns) +# ------------------------- ----------- -------- ------------------------------------- +# Name CPU total # Calls Input Shapes +# ------------------------- ----------- -------- ------------------------------------- +# model_inference 69.571ms 1 [] +# conv2d 9.019ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []] +# convolution 9.006ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []] +# _convolution 8.982ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []] +# mkldnn_convolution 8.894ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []] +# max_pool2d 7.200ms 1 [[5, 64, 112, 112]] +# conv2d 7.189ms 3 [[5, 512, 7, 7], [512, 512, 3, 3], []] +# convolution 7.180ms 3 [[5, 512, 7, 7], [512, 512, 3, 3], []] +# _convolution 7.171ms 3 [[5, 512, 7, 7], [512, 512, 3, 3], []] +# max_pool2d_with_indices 7.170ms 1 [[5, 64, 112, 112]] +# ------------------------- ----------- -------- -------------------------------------- + + +###################################################################### +# 4. Use profiler to analyze memory consumption +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# PyTorch profiler can also show the amount of memory (used by the model's tensors) +# that was allocated (or released) during the execution of the model's operators. +# In the output below, 'self' memory corresponds to the memory allocated (released) +# by the operator, excluding the children calls to the other operators. +# To enable memory profiling functionality pass ``profile_memory=True``. + +with profiler.profile(profile_memory=True, record_shapes=True) as prof: + model(inputs) + +print(prof.key_averages().table(sort_by="self_cpu_memory_usage", row_limit=10)) + +# (omitting some columns) +# --------------------------- --------------- --------------- --------------- +# Name CPU Mem Self CPU Mem Number of Calls +# --------------------------- --------------- --------------- --------------- +# empty 94.79 Mb 94.79 Mb 123 +# resize_ 11.48 Mb 11.48 Mb 2 +# addmm 19.53 Kb 19.53 Kb 1 +# empty_strided 4 b 4 b 1 +# conv2d 47.37 Mb 0 b 20 +# --------------------------- --------------- --------------- --------------- + +print(prof.key_averages().table(sort_by="cpu_memory_usage", row_limit=10)) + +# (omitting some columns) +# --------------------------- --------------- --------------- --------------- +# Name CPU Mem Self CPU Mem Number of Calls +# --------------------------- --------------- --------------- --------------- +# empty 94.79 Mb 94.79 Mb 123 +# batch_norm 47.41 Mb 0 b 20 +# _batch_norm_impl_index 47.41 Mb 0 b 20 +# native_batch_norm 47.41 Mb 0 b 20 +# conv2d 47.37 Mb 0 b 20 +# convolution 47.37 Mb 0 b 20 +# _convolution 47.37 Mb 0 b 20 +# mkldnn_convolution 47.37 Mb 0 b 20 +# empty_like 47.37 Mb 0 b 20 +# max_pool2d 11.48 Mb 0 b 1 +# max_pool2d_with_indices 11.48 Mb 0 b 1 +# resize_ 11.48 Mb 11.48 Mb 2 +# addmm 19.53 Kb 19.53 Kb 1 +# adaptive_avg_pool2d 10.00 Kb 0 b 1 +# mean 10.00 Kb 0 b 1 +# --------------------------- --------------- --------------- --------------- + +###################################################################### +# 5. Using tracing functionality +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Profiling results can be outputted as a .json trace file: + +with profiler.profile() as prof: + with profiler.record_function("model_inference"): + model(inputs) + +prof.export_chrome_trace("trace.json") + +###################################################################### +# User can examine the sequence of profiled operators after loading the trace file +# in Chrome (``chrome://tracing``): +# +# .. image:: ../../_static/img/trace_img.png +# :scale: 25 % + +###################################################################### +# Learn More +# ---------- +# +# Take a look at the following tutorial to learn how to visualize your model with TensorBoard: +# +# - `Visualizing models, data, and training with TensorBoard `_ tutorial +# diff --git a/recipes/recipes/profiler.rst b/recipes/recipes/profiler.rst new file mode 100644 index 00000000000..76b7e5456c9 --- /dev/null +++ b/recipes/recipes/profiler.rst @@ -0,0 +1,281 @@ +.. note:: + :class: sphx-glr-download-link-note + + Click :ref:`here ` to download the full example code +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_recipes_recipes_profiler.py: + + +PyTorch Profiler +==================================== +This recipe explains how to use PyTorch profiler and measure the time and +memory consumption of the model's operators. + +Introduction +------------ +PyTorch includes a simple profiler API that is useful when user needs +to determine the most expensive operators in the model. + +In this recipe, we will use a simple Resnet model to demonstrate how to +use profiler to analyze model performance. + +Setup +----- +To install ``torch`` and ``torchvision`` use the following command: + +:: + + pip install torch torchvision +Steps +----- + +1. Import all necessary libraries +2. Instantiate a simple Resnet model +3. Use profiler to analyze execution time +4. Use profiler to analyze memory consumption +5. Using tracing functionality + +1. Import all necessary libraries +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In this recipe we will use ``torch``, ``torchvision.models`` +and ``profiler`` modules: + + + +.. code-block:: default + + + import torch + import torchvision.models as models + import torch.autograd.profiler as profiler + + + +2. Instantiate a simple Resnet model +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Let's create an instance of a Resnet model and prepare an input +for it: + + + +.. code-block:: default + + + model = models.resnet18() + inputs = torch.randn(5, 3, 224, 224) + + +3. Use profiler to analyze execution time +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +PyTorch profiler is enabled through the context manager and accepts +a number of parameters, some of the most useful are: + +- ``record_shapes`` - whether to record shapes of the operator inputs; +- ``profile_memory`` - whether to report amount of memory consumed by + model's Tensors; +- ``use_cuda`` - whether to measure execution time of CUDA kernels. + +Let's see how we can use profiler to analyze the execution time: + + +.. code-block:: default + + + with profiler.profile(record_shapes=True) as prof: + with profiler.record_function("model_inference"): + model(inputs) + + +Note that we can use ``record_function`` context manager to label +arbitrary code ranges with user provided names +(``model_inference`` is used as a label in the example above). +Profiler allows one to check which operators were called during the +execution of a code range wrapped with a profiler context manager. +If multiple profiler ranges are active at the same time (e.g. in +parallel PyTorch threads), each profiling context manager tracks only +the operators of its corresponding range. +Profiler also automatically profiles the async tasks launched +with ``torch.jit._fork`` and (in case of a backward pass) +the backward pass operators launched with ``backward()`` call. + +Let's print out the stats for the execution above: + + +.. code-block:: default + + + print(prof.key_averages().table(sort_by="cpu_time_total", row_limit=10)) + + +The output will look like (omitting some columns): + + +.. code-block:: default + + + # ------------------------- -------------- ---------- ------------ --------- + # Name Self CPU total CPU total CPU time avg # Calls + # ------------------------- -------------- ---------- ------------ --------- + # model_inference 3.541ms 69.571ms 69.571ms 1 + # conv2d 69.122us 40.556ms 2.028ms 20 + # convolution 79.100us 40.487ms 2.024ms 20 + # _convolution 349.533us 40.408ms 2.020ms 20 + # mkldnn_convolution 39.822ms 39.988ms 1.999ms 20 + # batch_norm 105.559us 15.523ms 776.134us 20 + # _batch_norm_impl_index 103.697us 15.417ms 770.856us 20 + # native_batch_norm 9.387ms 15.249ms 762.471us 20 + # max_pool2d 29.400us 7.200ms 7.200ms 1 + # max_pool2d_with_indices 7.154ms 7.170ms 7.170ms 1 + # ------------------------- -------------- ---------- ------------ --------- + + +Here we see that, as expected, most of the time is spent in convolution (and specifically in ``mkldnn_convolution`` +for PyTorch compiled with MKL-DNN support). +Note the difference between self cpu time and cpu time - operators can call other operators, self cpu time exludes time +spent in children operator calls, while total cpu time includes it. + +To get a finer granularity of results and include operator input shapes, pass ``group_by_input_shape=True``: + + +.. code-block:: default + + + print(prof.key_averages(group_by_input_shape=True).table(sort_by="cpu_time_total", row_limit=10)) + + # (omitting some columns) + # ------------------------- ----------- -------- ------------------------------------- + # Name CPU total # Calls Input Shapes + # ------------------------- ----------- -------- ------------------------------------- + # model_inference 69.571ms 1 [] + # conv2d 9.019ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []] + # convolution 9.006ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []] + # _convolution 8.982ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []] + # mkldnn_convolution 8.894ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []] + # max_pool2d 7.200ms 1 [[5, 64, 112, 112]] + # conv2d 7.189ms 3 [[5, 512, 7, 7], [512, 512, 3, 3], []] + # convolution 7.180ms 3 [[5, 512, 7, 7], [512, 512, 3, 3], []] + # _convolution 7.171ms 3 [[5, 512, 7, 7], [512, 512, 3, 3], []] + # max_pool2d_with_indices 7.170ms 1 [[5, 64, 112, 112]] + # ------------------------- ----------- -------- -------------------------------------- + + + +4. Use profiler to analyze memory consumption +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +PyTorch profiler can also show the amount of memory (used by the model's tensors) +that was allocated (or released) during the execution of the model's operators. +In the output below, 'self' memory corresponds to the memory allocated (released) +by the operator, excluding the children calls to the other operators. +To enable memory profiling functionality pass ``profile_memory=True``. + + +.. code-block:: default + + + with profiler.profile(profile_memory=True, record_shapes=True) as prof: + model(inputs) + + print(prof.key_averages().table(sort_by="self_cpu_memory_usage", row_limit=10)) + + # (omitting some columns) + # --------------------------- --------------- --------------- --------------- + # Name CPU Mem Self CPU Mem Number of Calls + # --------------------------- --------------- --------------- --------------- + # empty 94.79 Mb 94.79 Mb 123 + # resize_ 11.48 Mb 11.48 Mb 2 + # addmm 19.53 Kb 19.53 Kb 1 + # empty_strided 4 b 4 b 1 + # conv2d 47.37 Mb 0 b 20 + # --------------------------- --------------- --------------- --------------- + + print(prof.key_averages().table(sort_by="cpu_memory_usage", row_limit=10)) + + # (omitting some columns) + # --------------------------- --------------- --------------- --------------- + # Name CPU Mem Self CPU Mem Number of Calls + # --------------------------- --------------- --------------- --------------- + # empty 94.79 Mb 94.79 Mb 123 + # batch_norm 47.41 Mb 0 b 20 + # _batch_norm_impl_index 47.41 Mb 0 b 20 + # native_batch_norm 47.41 Mb 0 b 20 + # conv2d 47.37 Mb 0 b 20 + # convolution 47.37 Mb 0 b 20 + # _convolution 47.37 Mb 0 b 20 + # mkldnn_convolution 47.37 Mb 0 b 20 + # empty_like 47.37 Mb 0 b 20 + # max_pool2d 11.48 Mb 0 b 1 + # max_pool2d_with_indices 11.48 Mb 0 b 1 + # resize_ 11.48 Mb 11.48 Mb 2 + # addmm 19.53 Kb 19.53 Kb 1 + # adaptive_avg_pool2d 10.00 Kb 0 b 1 + # mean 10.00 Kb 0 b 1 + # --------------------------- --------------- --------------- --------------- + + +5. Using tracing functionality +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Profiling results can be outputted as a .json trace file: + + +.. code-block:: default + + + with profiler.profile() as prof: + with profiler.record_function("model_inference"): + model(inputs) + + prof.export_chrome_trace("trace.json") + + +User can examine the sequence of profiled operators after loading the trace file +in Chrome (``chrome://tracing``): + +.. image:: ../../_static/img/trace_img.png + :scale: 25 % + +Learn More +---------- + +Take a look at the following tutorial to learn how to visualize your model with TensorBoard: + +- `Visualizing models, data, and training with TensorBoard `_ tutorial + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** ( 0 minutes 0.000 seconds) + + +.. _sphx_glr_download_recipes_recipes_profiler.py: + + +.. only :: html + + .. container:: sphx-glr-footer + :class: sphx-glr-footer-example + + + + .. container:: sphx-glr-download + + :download:`Download Python source code: profiler.py ` + + + + .. container:: sphx-glr-download + + :download:`Download Jupyter notebook: profiler.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/save_load_across_devices.ipynb b/recipes/recipes/save_load_across_devices.ipynb new file mode 100644 index 00000000000..9ad519d95f5 --- /dev/null +++ b/recipes/recipes/save_load_across_devices.ipynb @@ -0,0 +1,158 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\nSaving and loading models across devices in PyTorch\n===================================================\n\nThere may be instances where you want to save and load your neural\nnetworks across different devices.\n\nIntroduction\n------------\n\nSaving and loading models across devices is relatively straightforward\nusing PyTorch. In this recipe, we will experiment with saving and\nloading models across CPUs and GPUs.\n\nSetup\n-----\n\nIn order for every code block to run properly in this recipe, you must\nfirst change the runtime to \u201cGPU\u201d or higher. Once you do, we need to\ninstall ``torch`` if it isn\u2019t already available.\n\n::\n\n pip install torch\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Steps\n-----\n\n1. Import all necessary libraries for loading our data\n2. Define and intialize the neural network\n3. Save on a GPU, load on a CPU\n4. Save on a GPU, load on a GPU\n5. Save on a CPU, load on a GPU\n6. Saving and loading ``DataParallel`` models\n\n1. Import necessary libraries for loading our data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor this recipe, we will use ``torch`` and its subsidiaries ``torch.nn``\nand ``torch.optim``.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import torch\nimport torch.nn as nn\nimport torch.optim as optim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2. Define and intialize the neural network\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor sake of example, we will create a neural network for training\nimages. To learn more see the Defining a Neural Network recipe.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "class Net(nn.Module):\n def __init__(self):\n super(Net, self).__init__()\n self.conv1 = nn.Conv2d(3, 6, 5)\n self.pool = nn.MaxPool2d(2, 2)\n self.conv2 = nn.Conv2d(6, 16, 5)\n self.fc1 = nn.Linear(16 * 5 * 5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, 10)\n\n def forward(self, x):\n x = self.pool(F.relu(self.conv1(x)))\n x = self.pool(F.relu(self.conv2(x)))\n x = x.view(-1, 16 * 5 * 5)\n x = F.relu(self.fc1(x))\n x = F.relu(self.fc2(x))\n x = self.fc3(x)\n return x\n\nnet = Net()\nprint(net)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3. Save on GPU, Load on CPU\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhen loading a model on a CPU that was trained with a GPU, pass\n``torch.device('cpu')`` to the ``map_location`` argument in the\n``torch.load()`` function.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Specify a path to save to\nPATH = \"model.pt\"\n\n# Save\ntorch.save(net.state_dict(), PATH)\n\n# Load\ndevice = torch.device('cpu')\nmodel = Net()\nmodel.load_state_dict(torch.load(PATH, map_location=device))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this case, the storages underlying the tensors are dynamically\nremapped to the CPU device using the ``map_location`` argument.\n\n4. Save on GPU, Load on GPU\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhen loading a model on a GPU that was trained and saved on GPU, simply\nconvert the initialized model to a CUDA optimized model using\n``model.to(torch.device('cuda'))``.\n\nBe sure to use the ``.to(torch.device('cuda'))`` function on all model\ninputs to prepare the data for the model.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Save\ntorch.save(net.state_dict(), PATH)\n\n# Load\ndevice = torch.device(\"cuda\")\nmodel = Net()\nmodel.load_state_dict(torch.load(PATH))\nmodel.to(device)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that calling ``my_tensor.to(device)`` returns a new copy of\n``my_tensor`` on GPU. It does NOT overwrite ``my_tensor``. Therefore,\nremember to manually overwrite tensors:\n``my_tensor = my_tensor.to(torch.device('cuda'))``.\n\n5. Save on CPU, Load on GPU\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhen loading a model on a GPU that was trained and saved on CPU, set the\n``map_location`` argument in the ``torch.load()`` function to\n``cuda:device_id``. This loads the model to a given GPU device.\n\nBe sure to call ``model.to(torch.device('cuda'))`` to convert the\nmodel\u2019s parameter tensors to CUDA tensors.\n\nFinally, also be sure to use the ``.to(torch.device('cuda'))`` function\non all model inputs to prepare the data for the CUDA optimized model.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Save\ntorch.save(net.state_dict(), PATH)\n\n# Load\ndevice = torch.device(\"cuda\")\nmodel = Net()\n# Choose whatever GPU device number you want\nmodel.load_state_dict(torch.load(PATH, map_location=\"cuda:0\"))\n# Make sure to call input = input.to(device) on any input tensors that you feed to the model\nmodel.to(device)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "6. Saving ``torch.nn.DataParallel`` Models\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n``torch.nn.DataParallel`` is a model wrapper that enables parallel GPU\nutilization.\n\nTo save a ``DataParallel`` model generically, save the\n``model.module.state_dict()``. This way, you have the flexibility to\nload the model any way you want to any device you want.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Save\ntorch.save(net.module.state_dict(), PATH)\n\n# Load to whatever device you want" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Congratulations! You have successfully saved and loaded models across\ndevices in PyTorch.\n\nLearn More\n----------\n\nTake a look at these other recipes to continue your learning:\n\n- TBD\n- TBD\n\n\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/recipes/recipes/save_load_across_devices.py b/recipes/recipes/save_load_across_devices.py new file mode 100644 index 00000000000..c2d86fbab50 --- /dev/null +++ b/recipes/recipes/save_load_across_devices.py @@ -0,0 +1,190 @@ +""" +Saving and loading models across devices in PyTorch +=================================================== + +There may be instances where you want to save and load your neural +networks across different devices. + +Introduction +------------ + +Saving and loading models across devices is relatively straightforward +using PyTorch. In this recipe, we will experiment with saving and +loading models across CPUs and GPUs. + +Setup +----- + +In order for every code block to run properly in this recipe, you must +first change the runtime to “GPU” or higher. Once you do, we need to +install ``torch`` if it isn’t already available. + +:: + + pip install torch + +""" + + +###################################################################### +# Steps +# ----- +# +# 1. Import all necessary libraries for loading our data +# 2. Define and intialize the neural network +# 3. Save on a GPU, load on a CPU +# 4. Save on a GPU, load on a GPU +# 5. Save on a CPU, load on a GPU +# 6. Saving and loading ``DataParallel`` models +# +# 1. Import necessary libraries for loading our data +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` +# and ``torch.optim``. +# + +import torch +import torch.nn as nn +import torch.optim as optim + + +###################################################################### +# 2. Define and intialize the neural network +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# For sake of example, we will create a neural network for training +# images. To learn more see the Defining a Neural Network recipe. +# + +class Net(nn.Module): + def __init__(self): + super(Net, self).__init__() + self.conv1 = nn.Conv2d(3, 6, 5) + self.pool = nn.MaxPool2d(2, 2) + self.conv2 = nn.Conv2d(6, 16, 5) + self.fc1 = nn.Linear(16 * 5 * 5, 120) + self.fc2 = nn.Linear(120, 84) + self.fc3 = nn.Linear(84, 10) + + def forward(self, x): + x = self.pool(F.relu(self.conv1(x))) + x = self.pool(F.relu(self.conv2(x))) + x = x.view(-1, 16 * 5 * 5) + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + x = self.fc3(x) + return x + +net = Net() +print(net) + + +###################################################################### +# 3. Save on GPU, Load on CPU +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# When loading a model on a CPU that was trained with a GPU, pass +# ``torch.device('cpu')`` to the ``map_location`` argument in the +# ``torch.load()`` function. +# + +# Specify a path to save to +PATH = "model.pt" + +# Save +torch.save(net.state_dict(), PATH) + +# Load +device = torch.device('cpu') +model = Net() +model.load_state_dict(torch.load(PATH, map_location=device)) + + +###################################################################### +# In this case, the storages underlying the tensors are dynamically +# remapped to the CPU device using the ``map_location`` argument. +# +# 4. Save on GPU, Load on GPU +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# When loading a model on a GPU that was trained and saved on GPU, simply +# convert the initialized model to a CUDA optimized model using +# ``model.to(torch.device('cuda'))``. +# +# Be sure to use the ``.to(torch.device('cuda'))`` function on all model +# inputs to prepare the data for the model. +# + +# Save +torch.save(net.state_dict(), PATH) + +# Load +device = torch.device("cuda") +model = Net() +model.load_state_dict(torch.load(PATH)) +model.to(device) + + +###################################################################### +# Note that calling ``my_tensor.to(device)`` returns a new copy of +# ``my_tensor`` on GPU. It does NOT overwrite ``my_tensor``. Therefore, +# remember to manually overwrite tensors: +# ``my_tensor = my_tensor.to(torch.device('cuda'))``. +# +# 5. Save on CPU, Load on GPU +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# When loading a model on a GPU that was trained and saved on CPU, set the +# ``map_location`` argument in the ``torch.load()`` function to +# ``cuda:device_id``. This loads the model to a given GPU device. +# +# Be sure to call ``model.to(torch.device('cuda'))`` to convert the +# model’s parameter tensors to CUDA tensors. +# +# Finally, also be sure to use the ``.to(torch.device('cuda'))`` function +# on all model inputs to prepare the data for the CUDA optimized model. +# + +# Save +torch.save(net.state_dict(), PATH) + +# Load +device = torch.device("cuda") +model = Net() +# Choose whatever GPU device number you want +model.load_state_dict(torch.load(PATH, map_location="cuda:0")) +# Make sure to call input = input.to(device) on any input tensors that you feed to the model +model.to(device) + + +###################################################################### +# 6. Saving ``torch.nn.DataParallel`` Models +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# ``torch.nn.DataParallel`` is a model wrapper that enables parallel GPU +# utilization. +# +# To save a ``DataParallel`` model generically, save the +# ``model.module.state_dict()``. This way, you have the flexibility to +# load the model any way you want to any device you want. +# + +# Save +torch.save(net.module.state_dict(), PATH) + +# Load to whatever device you want + + +###################################################################### +# Congratulations! You have successfully saved and loaded models across +# devices in PyTorch. +# +# Learn More +# ---------- +# +# Take a look at these other recipes to continue your learning: +# +# - TBD +# - TBD +# diff --git a/recipes/recipes/save_load_across_devices.rst b/recipes/recipes/save_load_across_devices.rst new file mode 100644 index 00000000000..30f23e353e7 --- /dev/null +++ b/recipes/recipes/save_load_across_devices.rst @@ -0,0 +1,250 @@ +.. note:: + :class: sphx-glr-download-link-note + + Click :ref:`here ` to download the full example code +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_recipes_recipes_save_load_across_devices.py: + + +Saving and loading models across devices in PyTorch +=================================================== + +There may be instances where you want to save and load your neural +networks across different devices. + +Introduction +------------ + +Saving and loading models across devices is relatively straightforward +using PyTorch. In this recipe, we will experiment with saving and +loading models across CPUs and GPUs. + +Setup +----- + +In order for every code block to run properly in this recipe, you must +first change the runtime to “GPU” or higher. Once you do, we need to +install ``torch`` if it isn’t already available. + +:: + + pip install torch +Steps +----- + +1. Import all necessary libraries for loading our data +2. Define and intialize the neural network +3. Save on a GPU, load on a CPU +4. Save on a GPU, load on a GPU +5. Save on a CPU, load on a GPU +6. Saving and loading ``DataParallel`` models + +1. Import necessary libraries for loading our data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` +and ``torch.optim``. + + + +.. code-block:: default + + + import torch + import torch.nn as nn + import torch.optim as optim + + + +2. Define and intialize the neural network +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For sake of example, we will create a neural network for training +images. To learn more see the Defining a Neural Network recipe. + + + +.. code-block:: default + + + class Net(nn.Module): + def __init__(self): + super(Net, self).__init__() + self.conv1 = nn.Conv2d(3, 6, 5) + self.pool = nn.MaxPool2d(2, 2) + self.conv2 = nn.Conv2d(6, 16, 5) + self.fc1 = nn.Linear(16 * 5 * 5, 120) + self.fc2 = nn.Linear(120, 84) + self.fc3 = nn.Linear(84, 10) + + def forward(self, x): + x = self.pool(F.relu(self.conv1(x))) + x = self.pool(F.relu(self.conv2(x))) + x = x.view(-1, 16 * 5 * 5) + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + x = self.fc3(x) + return x + + net = Net() + print(net) + + + +3. Save on GPU, Load on CPU +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When loading a model on a CPU that was trained with a GPU, pass +``torch.device('cpu')`` to the ``map_location`` argument in the +``torch.load()`` function. + + + +.. code-block:: default + + + # Specify a path to save to + PATH = "model.pt" + + # Save + torch.save(net.state_dict(), PATH) + + # Load + device = torch.device('cpu') + model = Net() + model.load_state_dict(torch.load(PATH, map_location=device)) + + + +In this case, the storages underlying the tensors are dynamically +remapped to the CPU device using the ``map_location`` argument. + +4. Save on GPU, Load on GPU +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When loading a model on a GPU that was trained and saved on GPU, simply +convert the initialized model to a CUDA optimized model using +``model.to(torch.device('cuda'))``. + +Be sure to use the ``.to(torch.device('cuda'))`` function on all model +inputs to prepare the data for the model. + + + +.. code-block:: default + + + # Save + torch.save(net.state_dict(), PATH) + + # Load + device = torch.device("cuda") + model = Net() + model.load_state_dict(torch.load(PATH)) + model.to(device) + + + +Note that calling ``my_tensor.to(device)`` returns a new copy of +``my_tensor`` on GPU. It does NOT overwrite ``my_tensor``. Therefore, +remember to manually overwrite tensors: +``my_tensor = my_tensor.to(torch.device('cuda'))``. + +5. Save on CPU, Load on GPU +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When loading a model on a GPU that was trained and saved on CPU, set the +``map_location`` argument in the ``torch.load()`` function to +``cuda:device_id``. This loads the model to a given GPU device. + +Be sure to call ``model.to(torch.device('cuda'))`` to convert the +model’s parameter tensors to CUDA tensors. + +Finally, also be sure to use the ``.to(torch.device('cuda'))`` function +on all model inputs to prepare the data for the CUDA optimized model. + + + +.. code-block:: default + + + # Save + torch.save(net.state_dict(), PATH) + + # Load + device = torch.device("cuda") + model = Net() + # Choose whatever GPU device number you want + model.load_state_dict(torch.load(PATH, map_location="cuda:0")) + # Make sure to call input = input.to(device) on any input tensors that you feed to the model + model.to(device) + + + +6. Saving ``torch.nn.DataParallel`` Models +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``torch.nn.DataParallel`` is a model wrapper that enables parallel GPU +utilization. + +To save a ``DataParallel`` model generically, save the +``model.module.state_dict()``. This way, you have the flexibility to +load the model any way you want to any device you want. + + + +.. code-block:: default + + + # Save + torch.save(net.module.state_dict(), PATH) + + # Load to whatever device you want + + + +Congratulations! You have successfully saved and loaded models across +devices in PyTorch. + +Learn More +---------- + +Take a look at these other recipes to continue your learning: + +- TBD +- TBD + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** ( 0 minutes 0.000 seconds) + + +.. _sphx_glr_download_recipes_recipes_save_load_across_devices.py: + + +.. only :: html + + .. container:: sphx-glr-footer + :class: sphx-glr-footer-example + + + + .. container:: sphx-glr-download + + :download:`Download Python source code: save_load_across_devices.py ` + + + + .. container:: sphx-glr-download + + :download:`Download Jupyter notebook: save_load_across_devices.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/saving_and_loading_a_general_checkpoint.ipynb b/recipes/recipes/saving_and_loading_a_general_checkpoint.ipynb new file mode 100644 index 00000000000..0952465a851 --- /dev/null +++ b/recipes/recipes/saving_and_loading_a_general_checkpoint.ipynb @@ -0,0 +1,140 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\nSaving and loading a general checkpoint in PyTorch\n==================================================\nSaving and loading a general checkpoint model for inference or \nresuming training can be helpful for picking up where you last left off.\nWhen saving a general checkpoint, you must save more than just the\nmodel\u2019s state_dict. It is important to also save the optimizer\u2019s\nstate_dict, as this contains buffers and parameters that are updated as\nthe model trains. Other items that you may want to save are the epoch\nyou left off on, the latest recorded training loss, external\n``torch.nn.Embedding`` layers, and more, based on your own algorithm.\n\nIntroduction\n------------\nTo save multiple checkpoints, you must organize them in a dictionary and\nuse ``torch.save()`` to serialize the dictionary. A common PyTorch\nconvention is to save these checkpoints using the ``.tar`` file\nextension. To load the items, first initialize the model and optimizer,\nthen load the dictionary locally using torch.load(). From here, you can\neasily access the saved items by simply querying the dictionary as you\nwould expect.\n\nIn this recipe, we will explore how to save and load multiple\ncheckpoints.\n\nSetup\n-----\nBefore we begin, we need to install ``torch`` if it isn\u2019t already\navailable.\n\n::\n\n pip install torch\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Steps\n-----\n\n1. Import all necessary libraries for loading our data\n2. Define and intialize the neural network\n3. Initialize the optimizer\n4. Save the general checkpoint\n5. Load the general checkpoint\n\n1. Import necessary libraries for loading our data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor this recipe, we will use ``torch`` and its subsidiaries ``torch.nn``\nand ``torch.optim``.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import torch\nimport torch.nn as nn\nimport torch.optim as optim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2. Define and intialize the neural network\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor sake of example, we will create a neural network for training\nimages. To learn more see the Defining a Neural Network recipe.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "class Net(nn.Module):\n def __init__(self):\n super(Net, self).__init__()\n self.conv1 = nn.Conv2d(3, 6, 5)\n self.pool = nn.MaxPool2d(2, 2)\n self.conv2 = nn.Conv2d(6, 16, 5)\n self.fc1 = nn.Linear(16 * 5 * 5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, 10)\n\n def forward(self, x):\n x = self.pool(F.relu(self.conv1(x)))\n x = self.pool(F.relu(self.conv2(x)))\n x = x.view(-1, 16 * 5 * 5)\n x = F.relu(self.fc1(x))\n x = F.relu(self.fc2(x))\n x = self.fc3(x)\n return x\n\nnet = Net()\nprint(net)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3. Initialize the optimizer\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWe will use SGD with momentum.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4. Save the general checkpoint\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nCollect all relevant information and build your dictionary.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Additional information\nEPOCH = 5\nPATH = \"model.pt\"\nLOSS = 0.4\n\ntorch.save({\n 'epoch': EPOCH,\n 'model_state_dict': net.state_dict(),\n 'optimizer_state_dict': optimizer.state_dict(),\n 'loss': LOSS,\n }, PATH)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "5. Load the general checkpoint\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nRemember to first initialize the model and optimizer, then load the\ndictionary locally.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "model = Net()\noptimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)\n\ncheckpoint = torch.load(PATH)\nmodel.load_state_dict(checkpoint['model_state_dict'])\noptimizer.load_state_dict(checkpoint['optimizer_state_dict'])\nepoch = checkpoint['epoch']\nloss = checkpoint['loss']\n\nmodel.eval()\n# - or -\nmodel.train()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You must call ``model.eval()`` to set dropout and batch normalization\nlayers to evaluation mode before running inference. Failing to do this\nwill yield inconsistent inference results.\n\nIf you wish to resuming training, call ``model.train()`` to ensure these\nlayers are in training mode.\n\nCongratulations! You have successfully saved and loaded a general\ncheckpoint for inference and/or resuming training in PyTorch.\n\nLearn More\n----------\n\nTake a look at these other recipes to continue your learning:\n\n- TBD\n- TBD\n\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/recipes/recipes/saving_and_loading_a_general_checkpoint.py b/recipes/recipes/saving_and_loading_a_general_checkpoint.py new file mode 100644 index 00000000000..6e0c490ec2a --- /dev/null +++ b/recipes/recipes/saving_and_loading_a_general_checkpoint.py @@ -0,0 +1,162 @@ +""" +Saving and loading a general checkpoint in PyTorch +================================================== +Saving and loading a general checkpoint model for inference or +resuming training can be helpful for picking up where you last left off. +When saving a general checkpoint, you must save more than just the +model’s state_dict. It is important to also save the optimizer’s +state_dict, as this contains buffers and parameters that are updated as +the model trains. Other items that you may want to save are the epoch +you left off on, the latest recorded training loss, external +``torch.nn.Embedding`` layers, and more, based on your own algorithm. + +Introduction +------------ +To save multiple checkpoints, you must organize them in a dictionary and +use ``torch.save()`` to serialize the dictionary. A common PyTorch +convention is to save these checkpoints using the ``.tar`` file +extension. To load the items, first initialize the model and optimizer, +then load the dictionary locally using torch.load(). From here, you can +easily access the saved items by simply querying the dictionary as you +would expect. + +In this recipe, we will explore how to save and load multiple +checkpoints. + +Setup +----- +Before we begin, we need to install ``torch`` if it isn’t already +available. + +:: + + pip install torch + + +""" + + + +###################################################################### +# Steps +# ----- +# +# 1. Import all necessary libraries for loading our data +# 2. Define and intialize the neural network +# 3. Initialize the optimizer +# 4. Save the general checkpoint +# 5. Load the general checkpoint +# +# 1. Import necessary libraries for loading our data +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` +# and ``torch.optim``. +# + +import torch +import torch.nn as nn +import torch.optim as optim + + +###################################################################### +# 2. Define and intialize the neural network +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# For sake of example, we will create a neural network for training +# images. To learn more see the Defining a Neural Network recipe. +# + +class Net(nn.Module): + def __init__(self): + super(Net, self).__init__() + self.conv1 = nn.Conv2d(3, 6, 5) + self.pool = nn.MaxPool2d(2, 2) + self.conv2 = nn.Conv2d(6, 16, 5) + self.fc1 = nn.Linear(16 * 5 * 5, 120) + self.fc2 = nn.Linear(120, 84) + self.fc3 = nn.Linear(84, 10) + + def forward(self, x): + x = self.pool(F.relu(self.conv1(x))) + x = self.pool(F.relu(self.conv2(x))) + x = x.view(-1, 16 * 5 * 5) + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + x = self.fc3(x) + return x + +net = Net() +print(net) + + +###################################################################### +# 3. Initialize the optimizer +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# We will use SGD with momentum. +# + +optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) + + +###################################################################### +# 4. Save the general checkpoint +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Collect all relevant information and build your dictionary. +# + +# Additional information +EPOCH = 5 +PATH = "model.pt" +LOSS = 0.4 + +torch.save({ + 'epoch': EPOCH, + 'model_state_dict': net.state_dict(), + 'optimizer_state_dict': optimizer.state_dict(), + 'loss': LOSS, + }, PATH) + + +###################################################################### +# 5. Load the general checkpoint +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Remember to first initialize the model and optimizer, then load the +# dictionary locally. +# + +model = Net() +optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) + +checkpoint = torch.load(PATH) +model.load_state_dict(checkpoint['model_state_dict']) +optimizer.load_state_dict(checkpoint['optimizer_state_dict']) +epoch = checkpoint['epoch'] +loss = checkpoint['loss'] + +model.eval() +# - or - +model.train() + + +###################################################################### +# You must call ``model.eval()`` to set dropout and batch normalization +# layers to evaluation mode before running inference. Failing to do this +# will yield inconsistent inference results. +# +# If you wish to resuming training, call ``model.train()`` to ensure these +# layers are in training mode. +# +# Congratulations! You have successfully saved and loaded a general +# checkpoint for inference and/or resuming training in PyTorch. +# +# Learn More +# ---------- +# +# Take a look at these other recipes to continue your learning: +# +# - TBD +# - TBD diff --git a/recipes/recipes/saving_and_loading_a_general_checkpoint.rst b/recipes/recipes/saving_and_loading_a_general_checkpoint.rst new file mode 100644 index 00000000000..2669f620781 --- /dev/null +++ b/recipes/recipes/saving_and_loading_a_general_checkpoint.rst @@ -0,0 +1,216 @@ +.. note:: + :class: sphx-glr-download-link-note + + Click :ref:`here ` to download the full example code +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_recipes_recipes_saving_and_loading_a_general_checkpoint.py: + + +Saving and loading a general checkpoint in PyTorch +================================================== +Saving and loading a general checkpoint model for inference or +resuming training can be helpful for picking up where you last left off. +When saving a general checkpoint, you must save more than just the +model’s state_dict. It is important to also save the optimizer’s +state_dict, as this contains buffers and parameters that are updated as +the model trains. Other items that you may want to save are the epoch +you left off on, the latest recorded training loss, external +``torch.nn.Embedding`` layers, and more, based on your own algorithm. + +Introduction +------------ +To save multiple checkpoints, you must organize them in a dictionary and +use ``torch.save()`` to serialize the dictionary. A common PyTorch +convention is to save these checkpoints using the ``.tar`` file +extension. To load the items, first initialize the model and optimizer, +then load the dictionary locally using torch.load(). From here, you can +easily access the saved items by simply querying the dictionary as you +would expect. + +In this recipe, we will explore how to save and load multiple +checkpoints. + +Setup +----- +Before we begin, we need to install ``torch`` if it isn’t already +available. + +:: + + pip install torch +Steps +----- + +1. Import all necessary libraries for loading our data +2. Define and intialize the neural network +3. Initialize the optimizer +4. Save the general checkpoint +5. Load the general checkpoint + +1. Import necessary libraries for loading our data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` +and ``torch.optim``. + + + +.. code-block:: default + + + import torch + import torch.nn as nn + import torch.optim as optim + + + +2. Define and intialize the neural network +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For sake of example, we will create a neural network for training +images. To learn more see the Defining a Neural Network recipe. + + + +.. code-block:: default + + + class Net(nn.Module): + def __init__(self): + super(Net, self).__init__() + self.conv1 = nn.Conv2d(3, 6, 5) + self.pool = nn.MaxPool2d(2, 2) + self.conv2 = nn.Conv2d(6, 16, 5) + self.fc1 = nn.Linear(16 * 5 * 5, 120) + self.fc2 = nn.Linear(120, 84) + self.fc3 = nn.Linear(84, 10) + + def forward(self, x): + x = self.pool(F.relu(self.conv1(x))) + x = self.pool(F.relu(self.conv2(x))) + x = x.view(-1, 16 * 5 * 5) + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + x = self.fc3(x) + return x + + net = Net() + print(net) + + + +3. Initialize the optimizer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We will use SGD with momentum. + + + +.. code-block:: default + + + optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) + + + +4. Save the general checkpoint +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Collect all relevant information and build your dictionary. + + + +.. code-block:: default + + + # Additional information + EPOCH = 5 + PATH = "model.pt" + LOSS = 0.4 + + torch.save({ + 'epoch': EPOCH, + 'model_state_dict': net.state_dict(), + 'optimizer_state_dict': optimizer.state_dict(), + 'loss': LOSS, + }, PATH) + + + +5. Load the general checkpoint +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Remember to first initialize the model and optimizer, then load the +dictionary locally. + + + +.. code-block:: default + + + model = Net() + optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) + + checkpoint = torch.load(PATH) + model.load_state_dict(checkpoint['model_state_dict']) + optimizer.load_state_dict(checkpoint['optimizer_state_dict']) + epoch = checkpoint['epoch'] + loss = checkpoint['loss'] + + model.eval() + # - or - + model.train() + + + +You must call ``model.eval()`` to set dropout and batch normalization +layers to evaluation mode before running inference. Failing to do this +will yield inconsistent inference results. + +If you wish to resuming training, call ``model.train()`` to ensure these +layers are in training mode. + +Congratulations! You have successfully saved and loaded a general +checkpoint for inference and/or resuming training in PyTorch. + +Learn More +---------- + +Take a look at these other recipes to continue your learning: + +- TBD +- TBD + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** ( 0 minutes 0.000 seconds) + + +.. _sphx_glr_download_recipes_recipes_saving_and_loading_a_general_checkpoint.py: + + +.. only :: html + + .. container:: sphx-glr-footer + :class: sphx-glr-footer-example + + + + .. container:: sphx-glr-download + + :download:`Download Python source code: saving_and_loading_a_general_checkpoint.py ` + + + + .. container:: sphx-glr-download + + :download:`Download Jupyter notebook: saving_and_loading_a_general_checkpoint.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/saving_and_loading_models_for_inference.ipynb b/recipes/recipes/saving_and_loading_models_for_inference.ipynb new file mode 100644 index 00000000000..7fc3fa2e3c3 --- /dev/null +++ b/recipes/recipes/saving_and_loading_models_for_inference.ipynb @@ -0,0 +1,140 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\nSaving and loading models for inference in PyTorch\n==================================================\nThere are two approaches for saving and loading models for inference in\nPyTorch. The first is saving and loading the ``state_dict``, and the\nsecond is saving and loading the entire model.\n\nIntroduction\n------------\nSaving the model\u2019s ``state_dict`` with the ``torch.save()`` function\nwill give you the most flexibility for restoring the model later. This\nis the recommended method for saving models, because it is only really\nnecessary to save the trained model\u2019s learned parameters.\nWhen saving and loading an entire model, you save the entire module\nusing Python\u2019s\n`pickle `__ module. Using\nthis approach yields the most intuitive syntax and involves the least\namount of code. The disadvantage of this approach is that the serialized\ndata is bound to the specific classes and the exact directory structure\nused when the model is saved. The reason for this is because pickle does\nnot save the model class itself. Rather, it saves a path to the file\ncontaining the class, which is used during load time. Because of this,\nyour code can break in various ways when used in other projects or after\nrefactors.\nIn this recipe, we will explore both ways on how to save and load models\nfor inference.\n\nSetup\n-----\nBefore we begin, we need to install ``torch`` if it isn\u2019t already\navailable.\n\n\n::\n\n pip install torch\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Steps\n-----\n\n1. Import all necessary libraries for loading our data\n2. Define and intialize the neural network\n3. Initialize the optimizer\n4. Save and load the model via ``state_dict``\n5. Save and load the entire model\n\n1. Import necessary libraries for loading our data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor this recipe, we will use ``torch`` and its subsidiaries ``torch.nn``\nand ``torch.optim``.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import torch\nimport torch.nn as nn\nimport torch.optim as optim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2. Define and intialize the neural network\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor sake of example, we will create a neural network for training\nimages. To learn more see the Defining a Neural Network recipe.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "class Net(nn.Module):\n def __init__(self):\n super(Net, self).__init__()\n self.conv1 = nn.Conv2d(3, 6, 5)\n self.pool = nn.MaxPool2d(2, 2)\n self.conv2 = nn.Conv2d(6, 16, 5)\n self.fc1 = nn.Linear(16 * 5 * 5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, 10)\n\n def forward(self, x):\n x = self.pool(F.relu(self.conv1(x)))\n x = self.pool(F.relu(self.conv2(x)))\n x = x.view(-1, 16 * 5 * 5)\n x = F.relu(self.fc1(x))\n x = F.relu(self.fc2(x))\n x = self.fc3(x)\n return x\n\nnet = Net()\nprint(net)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3. Initialize the optimizer\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWe will use SGD with momentum.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4. Save and load the model via ``state_dict``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nLet\u2019s save and load our model using just ``state_dict``.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Specify a path\nPATH = \"state_dict_model.pt\"\n\n# Save\ntorch.save(net.state_dict(), PATH)\n\n# Load\nmodel = Net()\nmodel.load_state_dict(torch.load(PATH))\nmodel.eval()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A common PyTorch convention is to save models using either a ``.pt`` or\n``.pth`` file extension.\n\nNotice that the ``load_state_dict()`` function takes a dictionary\nobject, NOT a path to a saved object. This means that you must\ndeserialize the saved state_dict before you pass it to the\n``load_state_dict()`` function. For example, you CANNOT load using\n``model.load_state_dict(PATH)``.\n\nRemember too, that you must call ``model.eval()`` to set dropout and\nbatch normalization layers to evaluation mode before running inference.\nFailing to do this will yield inconsistent inference results.\n\n5. Save and load entire model\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nNow let\u2019s try the same thing with the entire model.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Specify a path\nPATH = \"entire_model.pt\"\n\n# Save\ntorch.save(net, PATH)\n\n# Load\nmodel = torch.load(PATH)\nmodel.eval()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Again here, remember that you must call model.eval() to set dropout and\nbatch normalization layers to evaluation mode before running inference.\n\nCongratulations! You have successfully saved and load models for\ninference in PyTorch.\n\nLearn More\n----------\n\nTake a look at these other recipes to continue your learning:\n\n- `Saving and loading a general checkpoint in PyTorch `__\n- `Saving and loading multiple models in one file using PyTorch `__\n\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/recipes/recipes/saving_and_loading_models_for_inference.py b/recipes/recipes/saving_and_loading_models_for_inference.py new file mode 100644 index 00000000000..22ae01aec76 --- /dev/null +++ b/recipes/recipes/saving_and_loading_models_for_inference.py @@ -0,0 +1,168 @@ +""" +Saving and loading models for inference in PyTorch +================================================== +There are two approaches for saving and loading models for inference in +PyTorch. The first is saving and loading the ``state_dict``, and the +second is saving and loading the entire model. + +Introduction +------------ +Saving the model’s ``state_dict`` with the ``torch.save()`` function +will give you the most flexibility for restoring the model later. This +is the recommended method for saving models, because it is only really +necessary to save the trained model’s learned parameters. +When saving and loading an entire model, you save the entire module +using Python’s +`pickle `__ module. Using +this approach yields the most intuitive syntax and involves the least +amount of code. The disadvantage of this approach is that the serialized +data is bound to the specific classes and the exact directory structure +used when the model is saved. The reason for this is because pickle does +not save the model class itself. Rather, it saves a path to the file +containing the class, which is used during load time. Because of this, +your code can break in various ways when used in other projects or after +refactors. +In this recipe, we will explore both ways on how to save and load models +for inference. + +Setup +----- +Before we begin, we need to install ``torch`` if it isn’t already +available. + + +:: + + pip install torch + + +""" + + +###################################################################### +# Steps +# ----- +# +# 1. Import all necessary libraries for loading our data +# 2. Define and intialize the neural network +# 3. Initialize the optimizer +# 4. Save and load the model via ``state_dict`` +# 5. Save and load the entire model +# +# 1. Import necessary libraries for loading our data +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` +# and ``torch.optim``. +# + +import torch +import torch.nn as nn +import torch.optim as optim + + +###################################################################### +# 2. Define and intialize the neural network +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# For sake of example, we will create a neural network for training +# images. To learn more see the Defining a Neural Network recipe. +# + +class Net(nn.Module): + def __init__(self): + super(Net, self).__init__() + self.conv1 = nn.Conv2d(3, 6, 5) + self.pool = nn.MaxPool2d(2, 2) + self.conv2 = nn.Conv2d(6, 16, 5) + self.fc1 = nn.Linear(16 * 5 * 5, 120) + self.fc2 = nn.Linear(120, 84) + self.fc3 = nn.Linear(84, 10) + + def forward(self, x): + x = self.pool(F.relu(self.conv1(x))) + x = self.pool(F.relu(self.conv2(x))) + x = x.view(-1, 16 * 5 * 5) + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + x = self.fc3(x) + return x + +net = Net() +print(net) + + +###################################################################### +# 3. Initialize the optimizer +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# We will use SGD with momentum. +# + +optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) + + +###################################################################### +# 4. Save and load the model via ``state_dict`` +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Let’s save and load our model using just ``state_dict``. +# + +# Specify a path +PATH = "state_dict_model.pt" + +# Save +torch.save(net.state_dict(), PATH) + +# Load +model = Net() +model.load_state_dict(torch.load(PATH)) +model.eval() + + +###################################################################### +# A common PyTorch convention is to save models using either a ``.pt`` or +# ``.pth`` file extension. +# +# Notice that the ``load_state_dict()`` function takes a dictionary +# object, NOT a path to a saved object. This means that you must +# deserialize the saved state_dict before you pass it to the +# ``load_state_dict()`` function. For example, you CANNOT load using +# ``model.load_state_dict(PATH)``. +# +# Remember too, that you must call ``model.eval()`` to set dropout and +# batch normalization layers to evaluation mode before running inference. +# Failing to do this will yield inconsistent inference results. +# +# 5. Save and load entire model +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Now let’s try the same thing with the entire model. +# + +# Specify a path +PATH = "entire_model.pt" + +# Save +torch.save(net, PATH) + +# Load +model = torch.load(PATH) +model.eval() + + +###################################################################### +# Again here, remember that you must call model.eval() to set dropout and +# batch normalization layers to evaluation mode before running inference. +# +# Congratulations! You have successfully saved and load models for +# inference in PyTorch. +# +# Learn More +# ---------- +# +# Take a look at these other recipes to continue your learning: +# +# - `Saving and loading a general checkpoint in PyTorch `__ +# - `Saving and loading multiple models in one file using PyTorch `__ diff --git a/recipes/recipes/saving_and_loading_models_for_inference.rst b/recipes/recipes/saving_and_loading_models_for_inference.rst new file mode 100644 index 00000000000..8766c3edda9 --- /dev/null +++ b/recipes/recipes/saving_and_loading_models_for_inference.rst @@ -0,0 +1,223 @@ +.. note:: + :class: sphx-glr-download-link-note + + Click :ref:`here ` to download the full example code +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_recipes_recipes_saving_and_loading_models_for_inference.py: + + +Saving and loading models for inference in PyTorch +================================================== +There are two approaches for saving and loading models for inference in +PyTorch. The first is saving and loading the ``state_dict``, and the +second is saving and loading the entire model. + +Introduction +------------ +Saving the model’s ``state_dict`` with the ``torch.save()`` function +will give you the most flexibility for restoring the model later. This +is the recommended method for saving models, because it is only really +necessary to save the trained model’s learned parameters. +When saving and loading an entire model, you save the entire module +using Python’s +`pickle `__ module. Using +this approach yields the most intuitive syntax and involves the least +amount of code. The disadvantage of this approach is that the serialized +data is bound to the specific classes and the exact directory structure +used when the model is saved. The reason for this is because pickle does +not save the model class itself. Rather, it saves a path to the file +containing the class, which is used during load time. Because of this, +your code can break in various ways when used in other projects or after +refactors. +In this recipe, we will explore both ways on how to save and load models +for inference. + +Setup +----- +Before we begin, we need to install ``torch`` if it isn’t already +available. + + +:: + + pip install torch +Steps +----- + +1. Import all necessary libraries for loading our data +2. Define and intialize the neural network +3. Initialize the optimizer +4. Save and load the model via ``state_dict`` +5. Save and load the entire model + +1. Import necessary libraries for loading our data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` +and ``torch.optim``. + + + +.. code-block:: default + + + import torch + import torch.nn as nn + import torch.optim as optim + + + +2. Define and intialize the neural network +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For sake of example, we will create a neural network for training +images. To learn more see the Defining a Neural Network recipe. + + + +.. code-block:: default + + + class Net(nn.Module): + def __init__(self): + super(Net, self).__init__() + self.conv1 = nn.Conv2d(3, 6, 5) + self.pool = nn.MaxPool2d(2, 2) + self.conv2 = nn.Conv2d(6, 16, 5) + self.fc1 = nn.Linear(16 * 5 * 5, 120) + self.fc2 = nn.Linear(120, 84) + self.fc3 = nn.Linear(84, 10) + + def forward(self, x): + x = self.pool(F.relu(self.conv1(x))) + x = self.pool(F.relu(self.conv2(x))) + x = x.view(-1, 16 * 5 * 5) + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + x = self.fc3(x) + return x + + net = Net() + print(net) + + + +3. Initialize the optimizer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We will use SGD with momentum. + + + +.. code-block:: default + + + optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) + + + +4. Save and load the model via ``state_dict`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Let’s save and load our model using just ``state_dict``. + + + +.. code-block:: default + + + # Specify a path + PATH = "state_dict_model.pt" + + # Save + torch.save(net.state_dict(), PATH) + + # Load + model = Net() + model.load_state_dict(torch.load(PATH)) + model.eval() + + + +A common PyTorch convention is to save models using either a ``.pt`` or +``.pth`` file extension. + +Notice that the ``load_state_dict()`` function takes a dictionary +object, NOT a path to a saved object. This means that you must +deserialize the saved state_dict before you pass it to the +``load_state_dict()`` function. For example, you CANNOT load using +``model.load_state_dict(PATH)``. + +Remember too, that you must call ``model.eval()`` to set dropout and +batch normalization layers to evaluation mode before running inference. +Failing to do this will yield inconsistent inference results. + +5. Save and load entire model +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Now let’s try the same thing with the entire model. + + + +.. code-block:: default + + + # Specify a path + PATH = "entire_model.pt" + + # Save + torch.save(net, PATH) + + # Load + model = torch.load(PATH) + model.eval() + + + +Again here, remember that you must call model.eval() to set dropout and +batch normalization layers to evaluation mode before running inference. + +Congratulations! You have successfully saved and load models for +inference in PyTorch. + +Learn More +---------- + +Take a look at these other recipes to continue your learning: + +- `Saving and loading a general checkpoint in PyTorch `__ +- `Saving and loading multiple models in one file using PyTorch `__ + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** ( 0 minutes 0.000 seconds) + + +.. _sphx_glr_download_recipes_recipes_saving_and_loading_models_for_inference.py: + + +.. only :: html + + .. container:: sphx-glr-footer + :class: sphx-glr-footer-example + + + + .. container:: sphx-glr-download + + :download:`Download Python source code: saving_and_loading_models_for_inference.py ` + + + + .. container:: sphx-glr-download + + :download:`Download Jupyter notebook: saving_and_loading_models_for_inference.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/saving_multiple_models_in_one_file.ipynb b/recipes/recipes/saving_multiple_models_in_one_file.ipynb new file mode 100644 index 00000000000..c2aaaaaa148 --- /dev/null +++ b/recipes/recipes/saving_multiple_models_in_one_file.ipynb @@ -0,0 +1,140 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\nSaving and loading multiple models in one file using PyTorch\n============================================================\nSaving and loading multiple models can be helpful for reusing models\nthat you have previously trained.\n\nIntroduction\n------------\nWhen saving a model comprised of multiple ``torch.nn.Modules``, such as\na GAN, a sequence-to-sequence model, or an ensemble of models, you must\nsave a dictionary of each model\u2019s state_dict and corresponding\noptimizer. You can also save any other items that may aid you in\nresuming training by simply appending them to the dictionary.\nTo load the models, first initialize the models and optimizers, then\nload the dictionary locally using ``torch.load()``. From here, you can\neasily access the saved items by simply querying the dictionary as you\nwould expect.\nIn this recipe, we will demonstrate how to save multiple models to one\nfile using PyTorch.\n\nSetup\n-----\nBefore we begin, we need to install ``torch`` if it isn\u2019t already\navailable.\n\n::\n\n pip install torch\n \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Steps\n-----\n\n1. Import all necessary libraries for loading our data\n2. Define and intialize the neural network\n3. Initialize the optimizer\n4. Save multiple models\n5. Load multiple models\n\n1. Import necessary libraries for loading our data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor this recipe, we will use ``torch`` and its subsidiaries ``torch.nn``\nand ``torch.optim``.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import torch\nimport torch.nn as nn\nimport torch.optim as optim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2. Define and intialize the neural network\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor sake of example, we will create a neural network for training\nimages. To learn more see the Defining a Neural Network recipe. Build\ntwo variables for the models to eventually save.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "class Net(nn.Module):\n def __init__(self):\n super(Net, self).__init__()\n self.conv1 = nn.Conv2d(3, 6, 5)\n self.pool = nn.MaxPool2d(2, 2)\n self.conv2 = nn.Conv2d(6, 16, 5)\n self.fc1 = nn.Linear(16 * 5 * 5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, 10)\n\n def forward(self, x):\n x = self.pool(F.relu(self.conv1(x)))\n x = self.pool(F.relu(self.conv2(x)))\n x = x.view(-1, 16 * 5 * 5)\n x = F.relu(self.fc1(x))\n x = F.relu(self.fc2(x))\n x = self.fc3(x)\n return x\n\nnetA = Net()\nnetB = Net()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3. Initialize the optimizer\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWe will use SGD with momentum to build an optimizer for each model we\ncreated.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "optimizerA = optim.SGD(netA.parameters(), lr=0.001, momentum=0.9)\noptimizerB = optim.SGD(netB.parameters(), lr=0.001, momentum=0.9)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4. Save multiple models\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nCollect all relevant information and build your dictionary.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Specify a path to save to\nPATH = \"model.pt\"\n\ntorch.save({\n 'modelA_state_dict': netA.state_dict(),\n 'modelB_state_dict': netB.state_dict(),\n 'optimizerA_state_dict': optimizerA.state_dict(),\n 'optimizerB_state_dict': optimizerB.state_dict(),\n }, PATH)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4. Load multiple models\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nRemember to first initialize the models and optimizers, then load the\ndictionary locally.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "modelA = Net()\nmodelB = Net()\noptimModelA = optim.SGD(modelA.parameters(), lr=0.001, momentum=0.9)\noptimModelB = optim.SGD(modelB.parameters(), lr=0.001, momentum=0.9)\n\ncheckpoint = torch.load(PATH)\nmodelA.load_state_dict(checkpoint['modelA_state_dict'])\nmodelB.load_state_dict(checkpoint['modelB_state_dict'])\noptimizerA.load_state_dict(checkpoint['optimizerA_state_dict'])\noptimizerB.load_state_dict(checkpoint['optimizerB_state_dict'])\n\nmodelA.eval()\nmodelB.eval()\n# - or -\nmodelA.train()\nmodelB.train()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You must call ``model.eval()`` to set dropout and batch normalization\nlayers to evaluation mode before running inference. Failing to do this\nwill yield inconsistent inference results.\n\nIf you wish to resuming training, call ``model.train()`` to ensure these\nlayers are in training mode.\n\nCongratulations! You have successfully saved and loaded multiple models\nin PyTorch.\n\nLearn More\n----------\n\nTake a look at these other recipes to continue your learning:\n\n- TBD\n- TBD\n\n\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/recipes/recipes/saving_multiple_models_in_one_file.py b/recipes/recipes/saving_multiple_models_in_one_file.py new file mode 100644 index 00000000000..b2f38247b4f --- /dev/null +++ b/recipes/recipes/saving_multiple_models_in_one_file.py @@ -0,0 +1,162 @@ +""" +Saving and loading multiple models in one file using PyTorch +============================================================ +Saving and loading multiple models can be helpful for reusing models +that you have previously trained. + +Introduction +------------ +When saving a model comprised of multiple ``torch.nn.Modules``, such as +a GAN, a sequence-to-sequence model, or an ensemble of models, you must +save a dictionary of each model’s state_dict and corresponding +optimizer. You can also save any other items that may aid you in +resuming training by simply appending them to the dictionary. +To load the models, first initialize the models and optimizers, then +load the dictionary locally using ``torch.load()``. From here, you can +easily access the saved items by simply querying the dictionary as you +would expect. +In this recipe, we will demonstrate how to save multiple models to one +file using PyTorch. + +Setup +----- +Before we begin, we need to install ``torch`` if it isn’t already +available. + +:: + + pip install torch + +""" + + + +###################################################################### +# Steps +# ----- +# +# 1. Import all necessary libraries for loading our data +# 2. Define and intialize the neural network +# 3. Initialize the optimizer +# 4. Save multiple models +# 5. Load multiple models +# +# 1. Import necessary libraries for loading our data +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` +# and ``torch.optim``. +# + +import torch +import torch.nn as nn +import torch.optim as optim + + +###################################################################### +# 2. Define and intialize the neural network +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# For sake of example, we will create a neural network for training +# images. To learn more see the Defining a Neural Network recipe. Build +# two variables for the models to eventually save. +# + +class Net(nn.Module): + def __init__(self): + super(Net, self).__init__() + self.conv1 = nn.Conv2d(3, 6, 5) + self.pool = nn.MaxPool2d(2, 2) + self.conv2 = nn.Conv2d(6, 16, 5) + self.fc1 = nn.Linear(16 * 5 * 5, 120) + self.fc2 = nn.Linear(120, 84) + self.fc3 = nn.Linear(84, 10) + + def forward(self, x): + x = self.pool(F.relu(self.conv1(x))) + x = self.pool(F.relu(self.conv2(x))) + x = x.view(-1, 16 * 5 * 5) + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + x = self.fc3(x) + return x + +netA = Net() +netB = Net() + + +###################################################################### +# 3. Initialize the optimizer +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# We will use SGD with momentum to build an optimizer for each model we +# created. +# + +optimizerA = optim.SGD(netA.parameters(), lr=0.001, momentum=0.9) +optimizerB = optim.SGD(netB.parameters(), lr=0.001, momentum=0.9) + + +###################################################################### +# 4. Save multiple models +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Collect all relevant information and build your dictionary. +# + +# Specify a path to save to +PATH = "model.pt" + +torch.save({ + 'modelA_state_dict': netA.state_dict(), + 'modelB_state_dict': netB.state_dict(), + 'optimizerA_state_dict': optimizerA.state_dict(), + 'optimizerB_state_dict': optimizerB.state_dict(), + }, PATH) + + +###################################################################### +# 4. Load multiple models +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Remember to first initialize the models and optimizers, then load the +# dictionary locally. +# + +modelA = Net() +modelB = Net() +optimModelA = optim.SGD(modelA.parameters(), lr=0.001, momentum=0.9) +optimModelB = optim.SGD(modelB.parameters(), lr=0.001, momentum=0.9) + +checkpoint = torch.load(PATH) +modelA.load_state_dict(checkpoint['modelA_state_dict']) +modelB.load_state_dict(checkpoint['modelB_state_dict']) +optimizerA.load_state_dict(checkpoint['optimizerA_state_dict']) +optimizerB.load_state_dict(checkpoint['optimizerB_state_dict']) + +modelA.eval() +modelB.eval() +# - or - +modelA.train() +modelB.train() + + +###################################################################### +# You must call ``model.eval()`` to set dropout and batch normalization +# layers to evaluation mode before running inference. Failing to do this +# will yield inconsistent inference results. +# +# If you wish to resuming training, call ``model.train()`` to ensure these +# layers are in training mode. +# +# Congratulations! You have successfully saved and loaded multiple models +# in PyTorch. +# +# Learn More +# ---------- +# +# Take a look at these other recipes to continue your learning: +# +# - TBD +# - TBD +# diff --git a/recipes/recipes/saving_multiple_models_in_one_file.rst b/recipes/recipes/saving_multiple_models_in_one_file.rst new file mode 100644 index 00000000000..1e73a7c8783 --- /dev/null +++ b/recipes/recipes/saving_multiple_models_in_one_file.rst @@ -0,0 +1,218 @@ +.. note:: + :class: sphx-glr-download-link-note + + Click :ref:`here ` to download the full example code +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_recipes_recipes_saving_multiple_models_in_one_file.py: + + +Saving and loading multiple models in one file using PyTorch +============================================================ +Saving and loading multiple models can be helpful for reusing models +that you have previously trained. + +Introduction +------------ +When saving a model comprised of multiple ``torch.nn.Modules``, such as +a GAN, a sequence-to-sequence model, or an ensemble of models, you must +save a dictionary of each model’s state_dict and corresponding +optimizer. You can also save any other items that may aid you in +resuming training by simply appending them to the dictionary. +To load the models, first initialize the models and optimizers, then +load the dictionary locally using ``torch.load()``. From here, you can +easily access the saved items by simply querying the dictionary as you +would expect. +In this recipe, we will demonstrate how to save multiple models to one +file using PyTorch. + +Setup +----- +Before we begin, we need to install ``torch`` if it isn’t already +available. + +:: + + pip install torch + +Steps +----- + +1. Import all necessary libraries for loading our data +2. Define and intialize the neural network +3. Initialize the optimizer +4. Save multiple models +5. Load multiple models + +1. Import necessary libraries for loading our data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` +and ``torch.optim``. + + + +.. code-block:: default + + + import torch + import torch.nn as nn + import torch.optim as optim + + + +2. Define and intialize the neural network +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For sake of example, we will create a neural network for training +images. To learn more see the Defining a Neural Network recipe. Build +two variables for the models to eventually save. + + + +.. code-block:: default + + + class Net(nn.Module): + def __init__(self): + super(Net, self).__init__() + self.conv1 = nn.Conv2d(3, 6, 5) + self.pool = nn.MaxPool2d(2, 2) + self.conv2 = nn.Conv2d(6, 16, 5) + self.fc1 = nn.Linear(16 * 5 * 5, 120) + self.fc2 = nn.Linear(120, 84) + self.fc3 = nn.Linear(84, 10) + + def forward(self, x): + x = self.pool(F.relu(self.conv1(x))) + x = self.pool(F.relu(self.conv2(x))) + x = x.view(-1, 16 * 5 * 5) + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + x = self.fc3(x) + return x + + netA = Net() + netB = Net() + + + +3. Initialize the optimizer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We will use SGD with momentum to build an optimizer for each model we +created. + + + +.. code-block:: default + + + optimizerA = optim.SGD(netA.parameters(), lr=0.001, momentum=0.9) + optimizerB = optim.SGD(netB.parameters(), lr=0.001, momentum=0.9) + + + +4. Save multiple models +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Collect all relevant information and build your dictionary. + + + +.. code-block:: default + + + # Specify a path to save to + PATH = "model.pt" + + torch.save({ + 'modelA_state_dict': netA.state_dict(), + 'modelB_state_dict': netB.state_dict(), + 'optimizerA_state_dict': optimizerA.state_dict(), + 'optimizerB_state_dict': optimizerB.state_dict(), + }, PATH) + + + +4. Load multiple models +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Remember to first initialize the models and optimizers, then load the +dictionary locally. + + + +.. code-block:: default + + + modelA = Net() + modelB = Net() + optimModelA = optim.SGD(modelA.parameters(), lr=0.001, momentum=0.9) + optimModelB = optim.SGD(modelB.parameters(), lr=0.001, momentum=0.9) + + checkpoint = torch.load(PATH) + modelA.load_state_dict(checkpoint['modelA_state_dict']) + modelB.load_state_dict(checkpoint['modelB_state_dict']) + optimizerA.load_state_dict(checkpoint['optimizerA_state_dict']) + optimizerB.load_state_dict(checkpoint['optimizerB_state_dict']) + + modelA.eval() + modelB.eval() + # - or - + modelA.train() + modelB.train() + + + +You must call ``model.eval()`` to set dropout and batch normalization +layers to evaluation mode before running inference. Failing to do this +will yield inconsistent inference results. + +If you wish to resuming training, call ``model.train()`` to ensure these +layers are in training mode. + +Congratulations! You have successfully saved and loaded multiple models +in PyTorch. + +Learn More +---------- + +Take a look at these other recipes to continue your learning: + +- TBD +- TBD + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** ( 0 minutes 0.000 seconds) + + +.. _sphx_glr_download_recipes_recipes_saving_multiple_models_in_one_file.py: + + +.. only :: html + + .. container:: sphx-glr-footer + :class: sphx-glr-footer-example + + + + .. container:: sphx-glr-download + + :download:`Download Python source code: saving_multiple_models_in_one_file.py ` + + + + .. container:: sphx-glr-download + + :download:`Download Jupyter notebook: saving_multiple_models_in_one_file.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/tensorboard_with_pytorch.ipynb b/recipes/recipes/tensorboard_with_pytorch.ipynb new file mode 100644 index 00000000000..0aca76ce383 --- /dev/null +++ b/recipes/recipes/tensorboard_with_pytorch.ipynb @@ -0,0 +1,125 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\nHow to use TensorBoard with PyTorch\n===================================\nTensorBoard is a visualization toolkit for machine learning experimentation. \nTensorBoard allows tracking and visualizing metrics such as loss and accuracy, \nvisualizing the model graph, viewing histograms, displaying images and much more. \nIn this tutorial we are going to cover TensorBoard installation, \nbasic usage with PyTorch, and how to visualize data you logged in TensorBoard UI.\n\nInstallation\n----------------------\nPyTorch should be installed to log models and metrics into TensorBoard log \ndirectory. The following command will install PyTorch 1.4+ via \nAnaconda (recommended):\n\n::\n\n $ conda install pytorch torchvision -c pytorch \n \n\nor pip\n\n::\n\n $ pip install torch torchvision\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Using TensorBoard in PyTorch\n-----\n\nLet\u2019s now try using TensorBoard with PyTorch! Before logging anything, \nwe need to create a ``SummaryWriter`` instance.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import torch\nfrom torch.utils.tensorboard import SummaryWriter\nwriter = SummaryWriter()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Writer will output to ``./runs/`` directory by default.\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Log scalars\n-----\n\nIn machine learning, it\u2019s important to understand key metrics such as \nloss and how they change during training. Scalar helps to save \nthe loss value of each training step, or the accuracy after each epoch. \n\nTo log a scalar value, use \n``add_scalar(tag, scalar_value, global_step=None, walltime=None)``. \nFor example, lets create a simple linear regression training, and \nlog loss value using ``add_scalar``\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "x = torch.arange(-5, 5, 0.1).view(-1, 1)\ny = -5 * x + 0.1 * torch.randn(x.size())\n\nmodel = torch.nn.Linear(1, 1)\ncriterion = torch.nn.MSELoss()\noptimizer = torch.optim.SGD(model.parameters(), lr = 0.1)\n\ndef train_model(iter):\n for epoch in range(iter):\n y1 = model(x)\n loss = criterion(y1, y)\n writer.add_scalar(\"Loss/train\", loss, epoch)\n optimizer.zero_grad()\n loss.backward()\n optimizer.step()\n \ntrain_model(10)\nwriter.flush()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Call ``flush()`` method to make sure that all pending events \nhave been written to disk.\n\nSee `torch.utils.tensorboard tutorials `_ \nto find more TensorBoard visualization types you can log.\n\nIf you do not need the summary writer anymore, call ``close()`` method.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "writer.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Run TensorBoard\n-----\n\nInstall TensorBoard through the command line to visualize data you logged\n\n::\n\n $ pip install tensorboard\n\n\nNow, start TensorBoard, specifying the root log directory you used above. \nArgument ``logdir`` points to directory where TensorBoard will look to find \nevent files that it can display. TensorBoard will recursively walk \nthe directory structure rooted at logdir, looking for .*tfevents.* files.\n\n::\n\n $ tensorboard --logdir=runs\n\nGo to the URL it provides OR to `http://localhost:6006/ `_\n\n![](../../_static/img/thumbnails/tensorboard_scalars.png)\n\n :scale: 40 %\n\nThis dashboard shows how the loss and accuracy change with every epoch. \nYou can use it to also track training speed, learning rate, and other \nscalar values. It\u2019s helpful to compare these metrics across different \ntraining runs to improve your model.\n\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Share TensorBoard dashboards\n-----\n\n`TensorBoard.dev `_ lets you upload and share \nyour ML experiment results with anyone. Use TensorBoard.dev to host, \ntrack, and share your TensorBoard dashboards.\n\nInstall the latest version of TensorBoard to use the uploader.\n\n:: \n\n $ pip install tensorboard --upgrade\n\nUse a simple command to upload and share your TensorBoard.\n\n:: \n\n $ tensorboard dev upload --logdir runs \\\n --name \"My latest experiment\" \\ # optional\n --description \"Simple comparison of several hyperparameters\" # optional\n\nFor help, run ``$ tensorboard dev --help``.\n\n**Note:** Uploaded TensorBoards are public and visible to everyone. \nDo not upload sensitive data.\n\nView your TensorBoard live at URL provided in your terminal. \nE.g. `https://tensorboard.dev/experiment/AdYd1TgeTlaLWXx6I8JUbA `_\n\n\n![](../../_static/img/thumbnails/tensorboard_dev.png)\n\n :scale: 40 %\n\n\n

Note

TensorBoard.dev currently supports only scalars dashboard.

\n\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Learn More\n----------------------------\n\n- `torch.utils.tensorboard `_ docs\n- `Visualizing models, data, and training with TensorBoard `_ tutorial\n\n\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/recipes/recipes/tensorboard_with_pytorch.py b/recipes/recipes/tensorboard_with_pytorch.py new file mode 100644 index 00000000000..c51a24728ad --- /dev/null +++ b/recipes/recipes/tensorboard_with_pytorch.py @@ -0,0 +1,168 @@ +""" +How to use TensorBoard with PyTorch +=================================== +TensorBoard is a visualization toolkit for machine learning experimentation. +TensorBoard allows tracking and visualizing metrics such as loss and accuracy, +visualizing the model graph, viewing histograms, displaying images and much more. +In this tutorial we are going to cover TensorBoard installation, +basic usage with PyTorch, and how to visualize data you logged in TensorBoard UI. + +Installation +---------------------- +PyTorch should be installed to log models and metrics into TensorBoard log +directory. The following command will install PyTorch 1.4+ via +Anaconda (recommended): + +:: + + $ conda install pytorch torchvision -c pytorch + + +or pip + +:: + + $ pip install torch torchvision + +""" + +###################################################################### +# Using TensorBoard in PyTorch +# ----- +# +# Let’s now try using TensorBoard with PyTorch! Before logging anything, +# we need to create a ``SummaryWriter`` instance. +# + +import torch +from torch.utils.tensorboard import SummaryWriter +writer = SummaryWriter() + +###################################################################### +# Writer will output to ``./runs/`` directory by default. +# + + +###################################################################### +# Log scalars +# ----- +# +# In machine learning, it’s important to understand key metrics such as +# loss and how they change during training. Scalar helps to save +# the loss value of each training step, or the accuracy after each epoch. +# +# To log a scalar value, use +# ``add_scalar(tag, scalar_value, global_step=None, walltime=None)``. +# For example, lets create a simple linear regression training, and +# log loss value using ``add_scalar`` +# + +x = torch.arange(-5, 5, 0.1).view(-1, 1) +y = -5 * x + 0.1 * torch.randn(x.size()) + +model = torch.nn.Linear(1, 1) +criterion = torch.nn.MSELoss() +optimizer = torch.optim.SGD(model.parameters(), lr = 0.1) + +def train_model(iter): + for epoch in range(iter): + y1 = model(x) + loss = criterion(y1, y) + writer.add_scalar("Loss/train", loss, epoch) + optimizer.zero_grad() + loss.backward() + optimizer.step() + +train_model(10) +writer.flush() + + +###################################################################### +# Call ``flush()`` method to make sure that all pending events +# have been written to disk. +# +# See `torch.utils.tensorboard tutorials `_ +# to find more TensorBoard visualization types you can log. +# +# If you do not need the summary writer anymore, call ``close()`` method. +# + +writer.close() + +###################################################################### +# Run TensorBoard +# ----- +# +# Install TensorBoard through the command line to visualize data you logged +# +# :: +# +# $ pip install tensorboard +# +# +# Now, start TensorBoard, specifying the root log directory you used above. +# Argument ``logdir`` points to directory where TensorBoard will look to find +# event files that it can display. TensorBoard will recursively walk +# the directory structure rooted at logdir, looking for .*tfevents.* files. +# +# :: +# +# $ tensorboard --logdir=runs +# +# Go to the URL it provides OR to `http://localhost:6006/ `_ +# +# .. image:: ../../_static/img/thumbnails/tensorboard_scalars.png +# :scale: 40 % +# +# This dashboard shows how the loss and accuracy change with every epoch. +# You can use it to also track training speed, learning rate, and other +# scalar values. It’s helpful to compare these metrics across different +# training runs to improve your model. +# + + +###################################################################### +# Share TensorBoard dashboards +# ----- +# +# `TensorBoard.dev `_ lets you upload and share +# your ML experiment results with anyone. Use TensorBoard.dev to host, +# track, and share your TensorBoard dashboards. +# +# Install the latest version of TensorBoard to use the uploader. +# +# :: +# +# $ pip install tensorboard --upgrade +# +# Use a simple command to upload and share your TensorBoard. +# +# :: +# +# $ tensorboard dev upload --logdir runs \ +# --name "My latest experiment" \ # optional +# --description "Simple comparison of several hyperparameters" # optional +# +# For help, run ``$ tensorboard dev --help``. +# +# **Note:** Uploaded TensorBoards are public and visible to everyone. +# Do not upload sensitive data. +# +# View your TensorBoard live at URL provided in your terminal. +# E.g. `https://tensorboard.dev/experiment/AdYd1TgeTlaLWXx6I8JUbA `_ +# +# +# .. image:: ../../_static/img/thumbnails/tensorboard_dev.png +# :scale: 40 % +# +# +# .. note:: +# TensorBoard.dev currently supports only scalars dashboard. + +######################################################################## +# Learn More +# ---------------------------- +# +# - `torch.utils.tensorboard `_ docs +# - `Visualizing models, data, and training with TensorBoard `_ tutorial +# diff --git a/recipes/recipes/tensorboard_with_pytorch.rst b/recipes/recipes/tensorboard_with_pytorch.rst new file mode 100644 index 00000000000..c7e7013323f --- /dev/null +++ b/recipes/recipes/tensorboard_with_pytorch.rst @@ -0,0 +1,212 @@ +.. note:: + :class: sphx-glr-download-link-note + + Click :ref:`here ` to download the full example code +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_recipes_recipes_tensorboard_with_pytorch.py: + + +How to use TensorBoard with PyTorch +=================================== +TensorBoard is a visualization toolkit for machine learning experimentation. +TensorBoard allows tracking and visualizing metrics such as loss and accuracy, +visualizing the model graph, viewing histograms, displaying images and much more. +In this tutorial we are going to cover TensorBoard installation, +basic usage with PyTorch, and how to visualize data you logged in TensorBoard UI. + +Installation +---------------------- +PyTorch should be installed to log models and metrics into TensorBoard log +directory. The following command will install PyTorch 1.4+ via +Anaconda (recommended): + +:: + + $ conda install pytorch torchvision -c pytorch + + +or pip + +:: + + $ pip install torch torchvision +Using TensorBoard in PyTorch +----- + +Let’s now try using TensorBoard with PyTorch! Before logging anything, +we need to create a ``SummaryWriter`` instance. + + + +.. code-block:: default + + + import torch + from torch.utils.tensorboard import SummaryWriter + writer = SummaryWriter() + + +Writer will output to ``./runs/`` directory by default. + + +Log scalars +----- + +In machine learning, it’s important to understand key metrics such as +loss and how they change during training. Scalar helps to save +the loss value of each training step, or the accuracy after each epoch. + +To log a scalar value, use +``add_scalar(tag, scalar_value, global_step=None, walltime=None)``. +For example, lets create a simple linear regression training, and +log loss value using ``add_scalar`` + + + +.. code-block:: default + + + x = torch.arange(-5, 5, 0.1).view(-1, 1) + y = -5 * x + 0.1 * torch.randn(x.size()) + + model = torch.nn.Linear(1, 1) + criterion = torch.nn.MSELoss() + optimizer = torch.optim.SGD(model.parameters(), lr = 0.1) + + def train_model(iter): + for epoch in range(iter): + y1 = model(x) + loss = criterion(y1, y) + writer.add_scalar("Loss/train", loss, epoch) + optimizer.zero_grad() + loss.backward() + optimizer.step() + + train_model(10) + writer.flush() + + + +Call ``flush()`` method to make sure that all pending events +have been written to disk. + +See `torch.utils.tensorboard tutorials `_ +to find more TensorBoard visualization types you can log. + +If you do not need the summary writer anymore, call ``close()`` method. + + + +.. code-block:: default + + + writer.close() + + +Run TensorBoard +----- + +Install TensorBoard through the command line to visualize data you logged + +:: + + $ pip install tensorboard + + +Now, start TensorBoard, specifying the root log directory you used above. +Argument ``logdir`` points to directory where TensorBoard will look to find +event files that it can display. TensorBoard will recursively walk +the directory structure rooted at logdir, looking for .*tfevents.* files. + +:: + + $ tensorboard --logdir=runs + +Go to the URL it provides OR to `http://localhost:6006/ `_ + +.. image:: ../../_static/img/thumbnails/tensorboard_scalars.png + :scale: 40 % + +This dashboard shows how the loss and accuracy change with every epoch. +You can use it to also track training speed, learning rate, and other +scalar values. It’s helpful to compare these metrics across different +training runs to improve your model. + + +Share TensorBoard dashboards +----- + +`TensorBoard.dev `_ lets you upload and share +your ML experiment results with anyone. Use TensorBoard.dev to host, +track, and share your TensorBoard dashboards. + +Install the latest version of TensorBoard to use the uploader. + +:: + + $ pip install tensorboard --upgrade + +Use a simple command to upload and share your TensorBoard. + +:: + + $ tensorboard dev upload --logdir runs \ + --name "My latest experiment" \ # optional + --description "Simple comparison of several hyperparameters" # optional + +For help, run ``$ tensorboard dev --help``. + +**Note:** Uploaded TensorBoards are public and visible to everyone. +Do not upload sensitive data. + +View your TensorBoard live at URL provided in your terminal. +E.g. `https://tensorboard.dev/experiment/AdYd1TgeTlaLWXx6I8JUbA `_ + + +.. image:: ../../_static/img/thumbnails/tensorboard_dev.png + :scale: 40 % + + +.. note:: + TensorBoard.dev currently supports only scalars dashboard. + +Learn More +---------------------------- + +- `torch.utils.tensorboard `_ docs +- `Visualizing models, data, and training with TensorBoard `_ tutorial + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** ( 0 minutes 0.000 seconds) + + +.. _sphx_glr_download_recipes_recipes_tensorboard_with_pytorch.py: + + +.. only :: html + + .. container:: sphx-glr-footer + :class: sphx-glr-footer-example + + + + .. container:: sphx-glr-download + + :download:`Download Python source code: tensorboard_with_pytorch.py ` + + + + .. container:: sphx-glr-download + + :download:`Download Jupyter notebook: tensorboard_with_pytorch.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.ipynb b/recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.ipynb new file mode 100644 index 00000000000..a32b3603529 --- /dev/null +++ b/recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.ipynb @@ -0,0 +1,122 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\nWarmstarting model using parameters from a different model in PyTorch\n=====================================================================\nPartially loading a model or loading a partial model are common\nscenarios when transfer learning or training a new complex model.\nLeveraging trained parameters, even if only a few are usable, will help\nto warmstart the training process and hopefully help your model converge\nmuch faster than training from scratch.\n\nIntroduction\n------------\nWhether you are loading from a partial ``state_dict``, which is missing\nsome keys, or loading a ``state_dict`` with more keys than the model\nthat you are loading into, you can set the strict argument to ``False``\nin the ``load_state_dict()`` function to ignore non-matching keys.\nIn this recipe, we will experiment with warmstarting a model using\nparameters of a different model.\n\nSetup\n-----\nBefore we begin, we need to install ``torch`` if it isn\u2019t already\navailable.\n\n::\n\n pip install torch\n \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Steps\n-----\n\n1. Import all necessary libraries for loading our data\n2. Define and intialize the neural network A and B\n3. Save model A\n4. Load into model B\n\n1. Import necessary libraries for loading our data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor this recipe, we will use ``torch`` and its subsidiaries ``torch.nn``\nand ``torch.optim``.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import torch\nimport torch.nn as nn\nimport torch.optim as optim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2. Define and intialize the neural network A and B\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor sake of example, we will create a neural network for training\nimages. To learn more see the Defining a Neural Network recipe. We will\ncreate two neural networks for sake of loading one parameter of type A\ninto type B.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "class NetA(nn.Module):\n def __init__(self):\n super(NetA, self).__init__()\n self.conv1 = nn.Conv2d(3, 6, 5)\n self.pool = nn.MaxPool2d(2, 2)\n self.conv2 = nn.Conv2d(6, 16, 5)\n self.fc1 = nn.Linear(16 * 5 * 5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, 10)\n\n def forward(self, x):\n x = self.pool(F.relu(self.conv1(x)))\n x = self.pool(F.relu(self.conv2(x)))\n x = x.view(-1, 16 * 5 * 5)\n x = F.relu(self.fc1(x))\n x = F.relu(self.fc2(x))\n x = self.fc3(x)\n return x\n\nnetA = NetA()\n\nclass NetB(nn.Module):\n def __init__(self):\n super(NetB, self).__init__()\n self.conv1 = nn.Conv2d(3, 6, 5)\n self.pool = nn.MaxPool2d(2, 2)\n self.conv2 = nn.Conv2d(6, 16, 5)\n self.fc1 = nn.Linear(16 * 5 * 5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, 10)\n\n def forward(self, x):\n x = self.pool(F.relu(self.conv1(x)))\n x = self.pool(F.relu(self.conv2(x)))\n x = x.view(-1, 16 * 5 * 5)\n x = F.relu(self.fc1(x))\n x = F.relu(self.fc2(x))\n x = self.fc3(x)\n return x\n\nnetB = NetB()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3. Save model A\n~~~~~~~~~~~~~~~~~~~\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Specify a path to save to\nPATH = \"model.pt\"\n\ntorch.save(netA.state_dict(), PATH)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4. Load into model B\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf you want to load parameters from one layer to another, but some keys\ndo not match, simply change the name of the parameter keys in the\nstate_dict that you are loading to match the keys in the model that you\nare loading into.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "netB.load_state_dict(torch.load(PATH), strict=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can see that all keys matched successfully!\n\nCongratulations! You have successfully warmstarted a model using\nparameters from a different model in PyTorch.\n\nLearn More\n----------\n\nTake a look at these other recipes to continue your learning:\n\n- `Saving and loading multiple models in one file using PyTorch `__\n- `Saving and loading models across devices in PyTorch `__\n\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.py b/recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.py new file mode 100644 index 00000000000..410bc992c6e --- /dev/null +++ b/recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.py @@ -0,0 +1,142 @@ +""" +Warmstarting model using parameters from a different model in PyTorch +===================================================================== +Partially loading a model or loading a partial model are common +scenarios when transfer learning or training a new complex model. +Leveraging trained parameters, even if only a few are usable, will help +to warmstart the training process and hopefully help your model converge +much faster than training from scratch. + +Introduction +------------ +Whether you are loading from a partial ``state_dict``, which is missing +some keys, or loading a ``state_dict`` with more keys than the model +that you are loading into, you can set the strict argument to ``False`` +in the ``load_state_dict()`` function to ignore non-matching keys. +In this recipe, we will experiment with warmstarting a model using +parameters of a different model. + +Setup +----- +Before we begin, we need to install ``torch`` if it isn’t already +available. + +:: + + pip install torch + +""" + + + +###################################################################### +# Steps +# ----- +# +# 1. Import all necessary libraries for loading our data +# 2. Define and intialize the neural network A and B +# 3. Save model A +# 4. Load into model B +# +# 1. Import necessary libraries for loading our data +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` +# and ``torch.optim``. +# + +import torch +import torch.nn as nn +import torch.optim as optim + + +###################################################################### +# 2. Define and intialize the neural network A and B +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# For sake of example, we will create a neural network for training +# images. To learn more see the Defining a Neural Network recipe. We will +# create two neural networks for sake of loading one parameter of type A +# into type B. +# + +class NetA(nn.Module): + def __init__(self): + super(NetA, self).__init__() + self.conv1 = nn.Conv2d(3, 6, 5) + self.pool = nn.MaxPool2d(2, 2) + self.conv2 = nn.Conv2d(6, 16, 5) + self.fc1 = nn.Linear(16 * 5 * 5, 120) + self.fc2 = nn.Linear(120, 84) + self.fc3 = nn.Linear(84, 10) + + def forward(self, x): + x = self.pool(F.relu(self.conv1(x))) + x = self.pool(F.relu(self.conv2(x))) + x = x.view(-1, 16 * 5 * 5) + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + x = self.fc3(x) + return x + +netA = NetA() + +class NetB(nn.Module): + def __init__(self): + super(NetB, self).__init__() + self.conv1 = nn.Conv2d(3, 6, 5) + self.pool = nn.MaxPool2d(2, 2) + self.conv2 = nn.Conv2d(6, 16, 5) + self.fc1 = nn.Linear(16 * 5 * 5, 120) + self.fc2 = nn.Linear(120, 84) + self.fc3 = nn.Linear(84, 10) + + def forward(self, x): + x = self.pool(F.relu(self.conv1(x))) + x = self.pool(F.relu(self.conv2(x))) + x = x.view(-1, 16 * 5 * 5) + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + x = self.fc3(x) + return x + +netB = NetB() + + +###################################################################### +# 3. Save model A +# ~~~~~~~~~~~~~~~~~~~ +# + +# Specify a path to save to +PATH = "model.pt" + +torch.save(netA.state_dict(), PATH) + + +###################################################################### +# 4. Load into model B +# ~~~~~~~~~~~~~~~~~~~~~~~~ +# +# If you want to load parameters from one layer to another, but some keys +# do not match, simply change the name of the parameter keys in the +# state_dict that you are loading to match the keys in the model that you +# are loading into. +# + +netB.load_state_dict(torch.load(PATH), strict=False) + + +###################################################################### +# You can see that all keys matched successfully! +# +# Congratulations! You have successfully warmstarted a model using +# parameters from a different model in PyTorch. +# +# Learn More +# ---------- +# +# Take a look at these other recipes to continue your learning: +# +# - `Saving and loading multiple models in one file using PyTorch `__ +# - `Saving and loading models across devices in PyTorch `__ diff --git a/recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.rst b/recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.rst new file mode 100644 index 00000000000..a2308a4d566 --- /dev/null +++ b/recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.rst @@ -0,0 +1,194 @@ +.. note:: + :class: sphx-glr-download-link-note + + Click :ref:`here ` to download the full example code +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_recipes_recipes_warmstarting_model_using_parameters_from_a_different_model.py: + + +Warmstarting model using parameters from a different model in PyTorch +===================================================================== +Partially loading a model or loading a partial model are common +scenarios when transfer learning or training a new complex model. +Leveraging trained parameters, even if only a few are usable, will help +to warmstart the training process and hopefully help your model converge +much faster than training from scratch. + +Introduction +------------ +Whether you are loading from a partial ``state_dict``, which is missing +some keys, or loading a ``state_dict`` with more keys than the model +that you are loading into, you can set the strict argument to ``False`` +in the ``load_state_dict()`` function to ignore non-matching keys. +In this recipe, we will experiment with warmstarting a model using +parameters of a different model. + +Setup +----- +Before we begin, we need to install ``torch`` if it isn’t already +available. + +:: + + pip install torch + +Steps +----- + +1. Import all necessary libraries for loading our data +2. Define and intialize the neural network A and B +3. Save model A +4. Load into model B + +1. Import necessary libraries for loading our data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` +and ``torch.optim``. + + + +.. code-block:: default + + + import torch + import torch.nn as nn + import torch.optim as optim + + + +2. Define and intialize the neural network A and B +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For sake of example, we will create a neural network for training +images. To learn more see the Defining a Neural Network recipe. We will +create two neural networks for sake of loading one parameter of type A +into type B. + + + +.. code-block:: default + + + class NetA(nn.Module): + def __init__(self): + super(NetA, self).__init__() + self.conv1 = nn.Conv2d(3, 6, 5) + self.pool = nn.MaxPool2d(2, 2) + self.conv2 = nn.Conv2d(6, 16, 5) + self.fc1 = nn.Linear(16 * 5 * 5, 120) + self.fc2 = nn.Linear(120, 84) + self.fc3 = nn.Linear(84, 10) + + def forward(self, x): + x = self.pool(F.relu(self.conv1(x))) + x = self.pool(F.relu(self.conv2(x))) + x = x.view(-1, 16 * 5 * 5) + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + x = self.fc3(x) + return x + + netA = NetA() + + class NetB(nn.Module): + def __init__(self): + super(NetB, self).__init__() + self.conv1 = nn.Conv2d(3, 6, 5) + self.pool = nn.MaxPool2d(2, 2) + self.conv2 = nn.Conv2d(6, 16, 5) + self.fc1 = nn.Linear(16 * 5 * 5, 120) + self.fc2 = nn.Linear(120, 84) + self.fc3 = nn.Linear(84, 10) + + def forward(self, x): + x = self.pool(F.relu(self.conv1(x))) + x = self.pool(F.relu(self.conv2(x))) + x = x.view(-1, 16 * 5 * 5) + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + x = self.fc3(x) + return x + + netB = NetB() + + + +3. Save model A +~~~~~~~~~~~~~~~~~~~ + + + +.. code-block:: default + + + # Specify a path to save to + PATH = "model.pt" + + torch.save(netA.state_dict(), PATH) + + + +4. Load into model B +~~~~~~~~~~~~~~~~~~~~~~~~ + +If you want to load parameters from one layer to another, but some keys +do not match, simply change the name of the parameter keys in the +state_dict that you are loading to match the keys in the model that you +are loading into. + + + +.. code-block:: default + + + netB.load_state_dict(torch.load(PATH), strict=False) + + + +You can see that all keys matched successfully! + +Congratulations! You have successfully warmstarted a model using +parameters from a different model in PyTorch. + +Learn More +---------- + +Take a look at these other recipes to continue your learning: + +- `Saving and loading multiple models in one file using PyTorch `__ +- `Saving and loading models across devices in PyTorch `__ + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** ( 0 minutes 0.000 seconds) + + +.. _sphx_glr_download_recipes_recipes_warmstarting_model_using_parameters_from_a_different_model.py: + + +.. only :: html + + .. container:: sphx-glr-footer + :class: sphx-glr-footer-example + + + + .. container:: sphx-glr-download + + :download:`Download Python source code: warmstarting_model_using_parameters_from_a_different_model.py ` + + + + .. container:: sphx-glr-download + + :download:`Download Jupyter notebook: warmstarting_model_using_parameters_from_a_different_model.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/what_is_state_dict.ipynb b/recipes/recipes/what_is_state_dict.ipynb new file mode 100644 index 00000000000..d597dbcd0c9 --- /dev/null +++ b/recipes/recipes/what_is_state_dict.ipynb @@ -0,0 +1,122 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\nWhat is a state_dict in PyTorch\n===============================\nIn PyTorch, the learnable parameters (i.e. weights and biases) of a\n``torch.nn.Module`` model are contained in the model\u2019s parameters\n(accessed with ``model.parameters()``). A ``state_dict`` is simply a\nPython dictionary object that maps each layer to its parameter tensor.\n\nIntroduction\n------------\nA ``state_dict`` is an integral entity if you are interested in saving\nor loading models from PyTorch.\nBecause ``state_dict`` objects are Python dictionaries, they can be\neasily saved, updated, altered, and restored, adding a great deal of\nmodularity to PyTorch models and optimizers.\nNote that only layers with learnable parameters (convolutional layers,\nlinear layers, etc.) and registered buffers (batchnorm\u2019s running_mean)\nhave entries in the model\u2019s ``state_dict``. Optimizer objects\n(``torch.optim``) also have a ``state_dict``, which contains information\nabout the optimizer\u2019s state, as well as the hyperparameters used.\nIn this recipe, we will see how ``state_dict`` is used with a simple\nmodel.\n\nSetup\n-----\nBefore we begin, we need to install ``torch`` if it isn\u2019t already\navailable.\n\n::\n\n pip install torchaudio\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Steps\n-----\n\n1. Import all necessary libraries for loading our data\n2. Define and intialize the neural network\n3. Initialize the optimizer\n4. Access the model and optimizer ``state_dict``\n\n1. Import necessary libraries for loading our data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor this recipe, we will use ``torch`` and its subsidiaries ``torch.nn``\nand ``torch.optim``.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import torch\nimport torch.nn as nn\nimport torch.optim as optim" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2. Define and intialize the neural network\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor sake of example, we will create a neural network for training\nimages. To learn more see the Defining a Neural Network recipe.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "class Net(nn.Module):\n def __init__(self):\n super(Net, self).__init__()\n self.conv1 = nn.Conv2d(3, 6, 5)\n self.pool = nn.MaxPool2d(2, 2)\n self.conv2 = nn.Conv2d(6, 16, 5)\n self.fc1 = nn.Linear(16 * 5 * 5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, 10)\n\n def forward(self, x):\n x = self.pool(F.relu(self.conv1(x)))\n x = self.pool(F.relu(self.conv2(x)))\n x = x.view(-1, 16 * 5 * 5)\n x = F.relu(self.fc1(x))\n x = F.relu(self.fc2(x))\n x = self.fc3(x)\n return x\n\nnet = Net()\nprint(net)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3. Initialize the optimizer\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWe will use SGD with momentum.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4. Access the model and optimizer ``state_dict``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nNow that we have constructed our model and optimizer, we can understand\nwhat is preserved in their respective ``state_dict`` properties.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Print model's state_dict\nprint(\"Model's state_dict:\")\nfor param_tensor in net.state_dict():\n print(param_tensor, \"\\t\", net.state_dict()[param_tensor].size())\n\nprint()\n\n# Print optimizer's state_dict\nprint(\"Optimizer's state_dict:\")\nfor var_name in optimizer.state_dict():\n print(var_name, \"\\t\", optimizer.state_dict()[var_name])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This information is relevant for saving and loading the model and\noptimizers for future use.\n\nCongratulations! You have successfully used ``state_dict`` in PyTorch.\n\nLearn More\n----------\n\nTake a look at these other recipes to continue your learning:\n\n- `Saving and loading models for inference in PyTorch `__\n- `Saving and loading a general checkpoint in PyTorch `__\n\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/recipes/recipes/what_is_state_dict.py b/recipes/recipes/what_is_state_dict.py new file mode 100644 index 00000000000..8e718e9071e --- /dev/null +++ b/recipes/recipes/what_is_state_dict.py @@ -0,0 +1,132 @@ +""" +What is a state_dict in PyTorch +=============================== +In PyTorch, the learnable parameters (i.e. weights and biases) of a +``torch.nn.Module`` model are contained in the model’s parameters +(accessed with ``model.parameters()``). A ``state_dict`` is simply a +Python dictionary object that maps each layer to its parameter tensor. + +Introduction +------------ +A ``state_dict`` is an integral entity if you are interested in saving +or loading models from PyTorch. +Because ``state_dict`` objects are Python dictionaries, they can be +easily saved, updated, altered, and restored, adding a great deal of +modularity to PyTorch models and optimizers. +Note that only layers with learnable parameters (convolutional layers, +linear layers, etc.) and registered buffers (batchnorm’s running_mean) +have entries in the model’s ``state_dict``. Optimizer objects +(``torch.optim``) also have a ``state_dict``, which contains information +about the optimizer’s state, as well as the hyperparameters used. +In this recipe, we will see how ``state_dict`` is used with a simple +model. + +Setup +----- +Before we begin, we need to install ``torch`` if it isn’t already +available. + +:: + + pip install torchaudio + +""" + + + +###################################################################### +# Steps +# ----- +# +# 1. Import all necessary libraries for loading our data +# 2. Define and intialize the neural network +# 3. Initialize the optimizer +# 4. Access the model and optimizer ``state_dict`` +# +# 1. Import necessary libraries for loading our data +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` +# and ``torch.optim``. +# + +import torch +import torch.nn as nn +import torch.optim as optim + + +###################################################################### +# 2. Define and intialize the neural network +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# For sake of example, we will create a neural network for training +# images. To learn more see the Defining a Neural Network recipe. +# + +class Net(nn.Module): + def __init__(self): + super(Net, self).__init__() + self.conv1 = nn.Conv2d(3, 6, 5) + self.pool = nn.MaxPool2d(2, 2) + self.conv2 = nn.Conv2d(6, 16, 5) + self.fc1 = nn.Linear(16 * 5 * 5, 120) + self.fc2 = nn.Linear(120, 84) + self.fc3 = nn.Linear(84, 10) + + def forward(self, x): + x = self.pool(F.relu(self.conv1(x))) + x = self.pool(F.relu(self.conv2(x))) + x = x.view(-1, 16 * 5 * 5) + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + x = self.fc3(x) + return x + +net = Net() +print(net) + + +###################################################################### +# 3. Initialize the optimizer +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# We will use SGD with momentum. +# + +optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) + + +###################################################################### +# 4. Access the model and optimizer ``state_dict`` +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Now that we have constructed our model and optimizer, we can understand +# what is preserved in their respective ``state_dict`` properties. +# + +# Print model's state_dict +print("Model's state_dict:") +for param_tensor in net.state_dict(): + print(param_tensor, "\t", net.state_dict()[param_tensor].size()) + +print() + +# Print optimizer's state_dict +print("Optimizer's state_dict:") +for var_name in optimizer.state_dict(): + print(var_name, "\t", optimizer.state_dict()[var_name]) + + +###################################################################### +# This information is relevant for saving and loading the model and +# optimizers for future use. +# +# Congratulations! You have successfully used ``state_dict`` in PyTorch. +# +# Learn More +# ---------- +# +# Take a look at these other recipes to continue your learning: +# +# - `Saving and loading models for inference in PyTorch `__ +# - `Saving and loading a general checkpoint in PyTorch `__ diff --git a/recipes/recipes/what_is_state_dict.rst b/recipes/recipes/what_is_state_dict.rst new file mode 100644 index 00000000000..223a0556cbc --- /dev/null +++ b/recipes/recipes/what_is_state_dict.rst @@ -0,0 +1,183 @@ +.. note:: + :class: sphx-glr-download-link-note + + Click :ref:`here ` to download the full example code +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_recipes_recipes_what_is_state_dict.py: + + +What is a state_dict in PyTorch +=============================== +In PyTorch, the learnable parameters (i.e. weights and biases) of a +``torch.nn.Module`` model are contained in the model’s parameters +(accessed with ``model.parameters()``). A ``state_dict`` is simply a +Python dictionary object that maps each layer to its parameter tensor. + +Introduction +------------ +A ``state_dict`` is an integral entity if you are interested in saving +or loading models from PyTorch. +Because ``state_dict`` objects are Python dictionaries, they can be +easily saved, updated, altered, and restored, adding a great deal of +modularity to PyTorch models and optimizers. +Note that only layers with learnable parameters (convolutional layers, +linear layers, etc.) and registered buffers (batchnorm’s running_mean) +have entries in the model’s ``state_dict``. Optimizer objects +(``torch.optim``) also have a ``state_dict``, which contains information +about the optimizer’s state, as well as the hyperparameters used. +In this recipe, we will see how ``state_dict`` is used with a simple +model. + +Setup +----- +Before we begin, we need to install ``torch`` if it isn’t already +available. + +:: + + pip install torchaudio +Steps +----- + +1. Import all necessary libraries for loading our data +2. Define and intialize the neural network +3. Initialize the optimizer +4. Access the model and optimizer ``state_dict`` + +1. Import necessary libraries for loading our data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` +and ``torch.optim``. + + + +.. code-block:: default + + + import torch + import torch.nn as nn + import torch.optim as optim + + + +2. Define and intialize the neural network +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For sake of example, we will create a neural network for training +images. To learn more see the Defining a Neural Network recipe. + + + +.. code-block:: default + + + class Net(nn.Module): + def __init__(self): + super(Net, self).__init__() + self.conv1 = nn.Conv2d(3, 6, 5) + self.pool = nn.MaxPool2d(2, 2) + self.conv2 = nn.Conv2d(6, 16, 5) + self.fc1 = nn.Linear(16 * 5 * 5, 120) + self.fc2 = nn.Linear(120, 84) + self.fc3 = nn.Linear(84, 10) + + def forward(self, x): + x = self.pool(F.relu(self.conv1(x))) + x = self.pool(F.relu(self.conv2(x))) + x = x.view(-1, 16 * 5 * 5) + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + x = self.fc3(x) + return x + + net = Net() + print(net) + + + +3. Initialize the optimizer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We will use SGD with momentum. + + + +.. code-block:: default + + + optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) + + + +4. Access the model and optimizer ``state_dict`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Now that we have constructed our model and optimizer, we can understand +what is preserved in their respective ``state_dict`` properties. + + + +.. code-block:: default + + + # Print model's state_dict + print("Model's state_dict:") + for param_tensor in net.state_dict(): + print(param_tensor, "\t", net.state_dict()[param_tensor].size()) + + print() + + # Print optimizer's state_dict + print("Optimizer's state_dict:") + for var_name in optimizer.state_dict(): + print(var_name, "\t", optimizer.state_dict()[var_name]) + + + +This information is relevant for saving and loading the model and +optimizers for future use. + +Congratulations! You have successfully used ``state_dict`` in PyTorch. + +Learn More +---------- + +Take a look at these other recipes to continue your learning: + +- `Saving and loading models for inference in PyTorch `__ +- `Saving and loading a general checkpoint in PyTorch `__ + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** ( 0 minutes 0.000 seconds) + + +.. _sphx_glr_download_recipes_recipes_what_is_state_dict.py: + + +.. only :: html + + .. container:: sphx-glr-footer + :class: sphx-glr-footer-example + + + + .. container:: sphx-glr-download + + :download:`Download Python source code: what_is_state_dict.py ` + + + + .. container:: sphx-glr-download + + :download:`Download Jupyter notebook: what_is_state_dict.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/zeroing_out_gradients.ipynb b/recipes/recipes/zeroing_out_gradients.ipynb new file mode 100644 index 00000000000..3314d6c622e --- /dev/null +++ b/recipes/recipes/zeroing_out_gradients.ipynb @@ -0,0 +1,140 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\nZeroing out gradients in PyTorch\n================================\nIt is beneficial to zero out gradients when building a neural network.\nThis is because by default, gradients are accumulated in buffers (i.e,\nnot overwritten) whenever ``.backward()`` is called.\n\nIntroduction\n------------\nWhen training your neural network, models are able to increase their\naccuracy through gradient decent. In short, gradient descent is the\nprocess of minimizing our loss (or error) by tweaking the weights and\nbiases in our model.\n\n``torch.Tensor`` is the central class of PyTorch. When you create a\ntensor, if you set its attribute ``.requires_grad`` as ``True``, the\npackage tracks all operations on it. This happens on subsequent backward\npasses. The gradient for this tensor will be accumulated into ``.grad``\nattribute. The accumulation (or sum) of all the gradients is calculated\nwhen .backward() is called on the loss tensor.\n\nThere are cases where it may be necessary to zero-out the gradients of a\ntensor. For example: when you start your training loop, you should zero\nout the gradients so that you can perform this tracking correctly.\nIn this recipe, we will learn how to zero out gradients using the\nPyTorch library. We will demonstrate how to do this by training a neural\nnetwork on the ``CIFAR10`` dataset built into PyTorch.\n\nSetup\n-----\nSince we will be training data in this recipe, if you are in a runable\nnotebook, it is best to switch the runtime to GPU or TPU.\nBefore we begin, we need to install ``torch`` and ``torchvision`` if\nthey aren\u2019t already available.\n\n::\n\n pip install torchvision\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Steps\n-----\n\nSteps 1 through 4 set up our data and neural network for training. The\nprocess of zeroing out the gradients happens in step 5. If you already\nhave your data and neural network built, skip to 5.\n\n1. Import all necessary libraries for loading our data\n2. Load and normalize the dataset\n3. Build the neural network\n4. Define the loss function\n5. Zero the gradients while training the network\n\n1. Import necessary libraries for loading our data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor this recipe, we will just be using ``torch`` and ``torchvision`` to\naccess the dataset.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import torch\n\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nimport torch.optim as optim\n\nimport torchvision\nimport torchvision.transforms as transforms" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2. Load and normalize the dataset\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPyTorch features various built-in datasets (see the Loading Data recipe\nfor more information).\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "transform = transforms.Compose(\n [transforms.ToTensor(),\n transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])\n\ntrainset = torchvision.datasets.CIFAR10(root='./data', train=True,\n download=True, transform=transform)\ntrainloader = torch.utils.data.DataLoader(trainset, batch_size=4,\n shuffle=True, num_workers=2)\n\ntestset = torchvision.datasets.CIFAR10(root='./data', train=False,\n download=True, transform=transform)\ntestloader = torch.utils.data.DataLoader(testset, batch_size=4,\n shuffle=False, num_workers=2)\n\nclasses = ('plane', 'car', 'bird', 'cat',\n 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3. Build the neural network\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWe will use a convolutional neural network. To learn more see the\nDefining a Neural Network recipe.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "class Net(nn.Module):\n def __init__(self):\n super(Net, self).__init__()\n self.conv1 = nn.Conv2d(3, 6, 5)\n self.pool = nn.MaxPool2d(2, 2)\n self.conv2 = nn.Conv2d(6, 16, 5)\n self.fc1 = nn.Linear(16 * 5 * 5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, 10)\n\n def forward(self, x):\n x = self.pool(F.relu(self.conv1(x)))\n x = self.pool(F.relu(self.conv2(x)))\n x = x.view(-1, 16 * 5 * 5)\n x = F.relu(self.fc1(x))\n x = F.relu(self.fc2(x))\n x = self.fc3(x)\n return x" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4. Define a Loss function and optimizer\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nLet\u2019s use a Classification Cross-Entropy loss and SGD with momentum.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "net = Net()\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "5. Zero the gradients while training the network\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis is when things start to get interesting. We simply have to loop\nover our data iterator, and feed the inputs to the network and optimize.\n\nNotice that for each entity of data, we zero out the gradients. This is\nto ensure that we aren\u2019t tracking any unnecessary information when we\ntrain our neural network.\n\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "for epoch in range(2): # loop over the dataset multiple times\n\n running_loss = 0.0\n for i, data in enumerate(trainloader, 0):\n # get the inputs; data is a list of [inputs, labels]\n inputs, labels = data\n\n # zero the parameter gradients\n optimizer.zero_grad()\n\n # forward + backward + optimize\n outputs = net(inputs)\n loss = criterion(outputs, labels)\n loss.backward()\n optimizer.step()\n\n # print statistics\n running_loss += loss.item()\n if i % 2000 == 1999: # print every 2000 mini-batches\n print('[%d, %5d] loss: %.3f' %\n (epoch + 1, i + 1, running_loss / 2000))\n running_loss = 0.0\n\nprint('Finished Training')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can also use ``model.zero_grad()``. This is the same as using\n``optimizer.zero_grad()`` as long as all your model parameters are in\nthat optimizer. Use your best judgement to decide which one to use.\n\nCongratulations! You have successfully zeroed out gradients PyTorch.\n\nLearn More\n----------\n\nTake a look at these other recipes to continue your learning:\n\n- `Loading data in PyTorch `__\n- `Saving and loading models across devices in PyTorch `__\n\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/recipes/recipes/zeroing_out_gradients.py b/recipes/recipes/zeroing_out_gradients.py new file mode 100644 index 00000000000..687120dcfcb --- /dev/null +++ b/recipes/recipes/zeroing_out_gradients.py @@ -0,0 +1,193 @@ +""" +Zeroing out gradients in PyTorch +================================ +It is beneficial to zero out gradients when building a neural network. +This is because by default, gradients are accumulated in buffers (i.e, +not overwritten) whenever ``.backward()`` is called. + +Introduction +------------ +When training your neural network, models are able to increase their +accuracy through gradient decent. In short, gradient descent is the +process of minimizing our loss (or error) by tweaking the weights and +biases in our model. + +``torch.Tensor`` is the central class of PyTorch. When you create a +tensor, if you set its attribute ``.requires_grad`` as ``True``, the +package tracks all operations on it. This happens on subsequent backward +passes. The gradient for this tensor will be accumulated into ``.grad`` +attribute. The accumulation (or sum) of all the gradients is calculated +when .backward() is called on the loss tensor. + +There are cases where it may be necessary to zero-out the gradients of a +tensor. For example: when you start your training loop, you should zero +out the gradients so that you can perform this tracking correctly. +In this recipe, we will learn how to zero out gradients using the +PyTorch library. We will demonstrate how to do this by training a neural +network on the ``CIFAR10`` dataset built into PyTorch. + +Setup +----- +Since we will be training data in this recipe, if you are in a runable +notebook, it is best to switch the runtime to GPU or TPU. +Before we begin, we need to install ``torch`` and ``torchvision`` if +they aren’t already available. + +:: + + pip install torchvision + + +""" + + +###################################################################### +# Steps +# ----- +# +# Steps 1 through 4 set up our data and neural network for training. The +# process of zeroing out the gradients happens in step 5. If you already +# have your data and neural network built, skip to 5. +# +# 1. Import all necessary libraries for loading our data +# 2. Load and normalize the dataset +# 3. Build the neural network +# 4. Define the loss function +# 5. Zero the gradients while training the network +# +# 1. Import necessary libraries for loading our data +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# For this recipe, we will just be using ``torch`` and ``torchvision`` to +# access the dataset. +# + +import torch + +import torch.nn as nn +import torch.nn.functional as F + +import torch.optim as optim + +import torchvision +import torchvision.transforms as transforms + + +###################################################################### +# 2. Load and normalize the dataset +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# PyTorch features various built-in datasets (see the Loading Data recipe +# for more information). +# + +transform = transforms.Compose( + [transforms.ToTensor(), + transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) + +trainset = torchvision.datasets.CIFAR10(root='./data', train=True, + download=True, transform=transform) +trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, + shuffle=True, num_workers=2) + +testset = torchvision.datasets.CIFAR10(root='./data', train=False, + download=True, transform=transform) +testloader = torch.utils.data.DataLoader(testset, batch_size=4, + shuffle=False, num_workers=2) + +classes = ('plane', 'car', 'bird', 'cat', + 'deer', 'dog', 'frog', 'horse', 'ship', 'truck') + + +###################################################################### +# 3. Build the neural network +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# We will use a convolutional neural network. To learn more see the +# Defining a Neural Network recipe. +# + +class Net(nn.Module): + def __init__(self): + super(Net, self).__init__() + self.conv1 = nn.Conv2d(3, 6, 5) + self.pool = nn.MaxPool2d(2, 2) + self.conv2 = nn.Conv2d(6, 16, 5) + self.fc1 = nn.Linear(16 * 5 * 5, 120) + self.fc2 = nn.Linear(120, 84) + self.fc3 = nn.Linear(84, 10) + + def forward(self, x): + x = self.pool(F.relu(self.conv1(x))) + x = self.pool(F.relu(self.conv2(x))) + x = x.view(-1, 16 * 5 * 5) + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + x = self.fc3(x) + return x + + +###################################################################### +# 4. Define a Loss function and optimizer +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Let’s use a Classification Cross-Entropy loss and SGD with momentum. +# + +net = Net() +criterion = nn.CrossEntropyLoss() +optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) + + +###################################################################### +# 5. Zero the gradients while training the network +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# This is when things start to get interesting. We simply have to loop +# over our data iterator, and feed the inputs to the network and optimize. +# +# Notice that for each entity of data, we zero out the gradients. This is +# to ensure that we aren’t tracking any unnecessary information when we +# train our neural network. +# + +for epoch in range(2): # loop over the dataset multiple times + + running_loss = 0.0 + for i, data in enumerate(trainloader, 0): + # get the inputs; data is a list of [inputs, labels] + inputs, labels = data + + # zero the parameter gradients + optimizer.zero_grad() + + # forward + backward + optimize + outputs = net(inputs) + loss = criterion(outputs, labels) + loss.backward() + optimizer.step() + + # print statistics + running_loss += loss.item() + if i % 2000 == 1999: # print every 2000 mini-batches + print('[%d, %5d] loss: %.3f' % + (epoch + 1, i + 1, running_loss / 2000)) + running_loss = 0.0 + +print('Finished Training') + + +###################################################################### +# You can also use ``model.zero_grad()``. This is the same as using +# ``optimizer.zero_grad()`` as long as all your model parameters are in +# that optimizer. Use your best judgement to decide which one to use. +# +# Congratulations! You have successfully zeroed out gradients PyTorch. +# +# Learn More +# ---------- +# +# Take a look at these other recipes to continue your learning: +# +# - `Loading data in PyTorch `__ +# - `Saving and loading models across devices in PyTorch `__ diff --git a/recipes/recipes/zeroing_out_gradients.rst b/recipes/recipes/zeroing_out_gradients.rst new file mode 100644 index 00000000000..ecfcf7f4a02 --- /dev/null +++ b/recipes/recipes/zeroing_out_gradients.rst @@ -0,0 +1,248 @@ +.. note:: + :class: sphx-glr-download-link-note + + Click :ref:`here ` to download the full example code +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_recipes_recipes_zeroing_out_gradients.py: + + +Zeroing out gradients in PyTorch +================================ +It is beneficial to zero out gradients when building a neural network. +This is because by default, gradients are accumulated in buffers (i.e, +not overwritten) whenever ``.backward()`` is called. + +Introduction +------------ +When training your neural network, models are able to increase their +accuracy through gradient decent. In short, gradient descent is the +process of minimizing our loss (or error) by tweaking the weights and +biases in our model. + +``torch.Tensor`` is the central class of PyTorch. When you create a +tensor, if you set its attribute ``.requires_grad`` as ``True``, the +package tracks all operations on it. This happens on subsequent backward +passes. The gradient for this tensor will be accumulated into ``.grad`` +attribute. The accumulation (or sum) of all the gradients is calculated +when .backward() is called on the loss tensor. + +There are cases where it may be necessary to zero-out the gradients of a +tensor. For example: when you start your training loop, you should zero +out the gradients so that you can perform this tracking correctly. +In this recipe, we will learn how to zero out gradients using the +PyTorch library. We will demonstrate how to do this by training a neural +network on the ``CIFAR10`` dataset built into PyTorch. + +Setup +----- +Since we will be training data in this recipe, if you are in a runable +notebook, it is best to switch the runtime to GPU or TPU. +Before we begin, we need to install ``torch`` and ``torchvision`` if +they aren’t already available. + +:: + + pip install torchvision +Steps +----- + +Steps 1 through 4 set up our data and neural network for training. The +process of zeroing out the gradients happens in step 5. If you already +have your data and neural network built, skip to 5. + +1. Import all necessary libraries for loading our data +2. Load and normalize the dataset +3. Build the neural network +4. Define the loss function +5. Zero the gradients while training the network + +1. Import necessary libraries for loading our data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For this recipe, we will just be using ``torch`` and ``torchvision`` to +access the dataset. + + + +.. code-block:: default + + + import torch + + import torch.nn as nn + import torch.nn.functional as F + + import torch.optim as optim + + import torchvision + import torchvision.transforms as transforms + + + +2. Load and normalize the dataset +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +PyTorch features various built-in datasets (see the Loading Data recipe +for more information). + + + +.. code-block:: default + + + transform = transforms.Compose( + [transforms.ToTensor(), + transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) + + trainset = torchvision.datasets.CIFAR10(root='./data', train=True, + download=True, transform=transform) + trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, + shuffle=True, num_workers=2) + + testset = torchvision.datasets.CIFAR10(root='./data', train=False, + download=True, transform=transform) + testloader = torch.utils.data.DataLoader(testset, batch_size=4, + shuffle=False, num_workers=2) + + classes = ('plane', 'car', 'bird', 'cat', + 'deer', 'dog', 'frog', 'horse', 'ship', 'truck') + + + +3. Build the neural network +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We will use a convolutional neural network. To learn more see the +Defining a Neural Network recipe. + + + +.. code-block:: default + + + class Net(nn.Module): + def __init__(self): + super(Net, self).__init__() + self.conv1 = nn.Conv2d(3, 6, 5) + self.pool = nn.MaxPool2d(2, 2) + self.conv2 = nn.Conv2d(6, 16, 5) + self.fc1 = nn.Linear(16 * 5 * 5, 120) + self.fc2 = nn.Linear(120, 84) + self.fc3 = nn.Linear(84, 10) + + def forward(self, x): + x = self.pool(F.relu(self.conv1(x))) + x = self.pool(F.relu(self.conv2(x))) + x = x.view(-1, 16 * 5 * 5) + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + x = self.fc3(x) + return x + + + +4. Define a Loss function and optimizer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Let’s use a Classification Cross-Entropy loss and SGD with momentum. + + + +.. code-block:: default + + + net = Net() + criterion = nn.CrossEntropyLoss() + optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) + + + +5. Zero the gradients while training the network +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This is when things start to get interesting. We simply have to loop +over our data iterator, and feed the inputs to the network and optimize. + +Notice that for each entity of data, we zero out the gradients. This is +to ensure that we aren’t tracking any unnecessary information when we +train our neural network. + + + +.. code-block:: default + + + for epoch in range(2): # loop over the dataset multiple times + + running_loss = 0.0 + for i, data in enumerate(trainloader, 0): + # get the inputs; data is a list of [inputs, labels] + inputs, labels = data + + # zero the parameter gradients + optimizer.zero_grad() + + # forward + backward + optimize + outputs = net(inputs) + loss = criterion(outputs, labels) + loss.backward() + optimizer.step() + + # print statistics + running_loss += loss.item() + if i % 2000 == 1999: # print every 2000 mini-batches + print('[%d, %5d] loss: %.3f' % + (epoch + 1, i + 1, running_loss / 2000)) + running_loss = 0.0 + + print('Finished Training') + + + +You can also use ``model.zero_grad()``. This is the same as using +``optimizer.zero_grad()`` as long as all your model parameters are in +that optimizer. Use your best judgement to decide which one to use. + +Congratulations! You have successfully zeroed out gradients PyTorch. + +Learn More +---------- + +Take a look at these other recipes to continue your learning: + +- `Loading data in PyTorch `__ +- `Saving and loading models across devices in PyTorch `__ + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** ( 0 minutes 0.000 seconds) + + +.. _sphx_glr_download_recipes_recipes_zeroing_out_gradients.py: + + +.. only :: html + + .. container:: sphx-glr-footer + :class: sphx-glr-footer-example + + + + .. container:: sphx-glr-download + + :download:`Download Python source code: zeroing_out_gradients.py ` + + + + .. container:: sphx-glr-download + + :download:`Download Jupyter notebook: zeroing_out_gradients.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes_index.rst b/recipes/recipes_index.rst new file mode 100644 index 00000000000..251db43297b --- /dev/null +++ b/recipes/recipes_index.rst @@ -0,0 +1,197 @@ +PyTorch Recipes +--------------------------------------------- +Recipes are bite-sized bite-sized, actionable examples of how to use specific PyTorch features, different from our full-length tutorials. + +.. raw:: html + + + + +
+ + + +
+ +
+ +
+
+ +.. Add recipe cards below this line + +.. Basics + +.. customcarditem:: + :header: Loading data in PyTorch + :card_description: Learn how to use PyTorch packages to prepare and load common datasets for your model. + :image: ../_static/img/thumbnails/cropped/loading-data.PNG + :link: ../recipes/recipes/loading_data_recipe.html + :tags: Basics + + +.. customcarditem:: + :header: Defining a Neural Network + :card_description: Learn how to use PyTorch's torch.nn package to create and define a neural network the MNIST dataset. + :image: ../_static/img/thumbnails/cropped/defining-a-network.PNG + :link: ../recipes/recipes/defining_a_neural_network.html + :tags: Basics + +.. customcarditem:: + :header: What is a state_dict in PyTorch + :card_description: Learn how state_dict objects, Python dictionaries, are used in saving or loading models from PyTorch. + :image: ../_static/img/thumbnails/cropped/what-is-a-state-dict.PNG + :link: ../recipes/recipes/what_is_state_dict.html + :tags: Basics + +.. customcarditem:: + :header: Saving and loading models for inference in PyTorch + :card_description: Learn about the two approaches for saving and loading models for inference in PyTorch - via the state_dict and via the entire model. + :image: ../_static/img/thumbnails/cropped/saving-and-loading-models-for-inference.PNG + :link: ../recipes/recipes/saving_and_loading_models_for_inference.html + :tags: Basics + + +.. customcarditem:: + :header: Saving and loading a general checkpoint in PyTorch + :card_description: Saving and loading a general checkpoint model for inference or resuming training can be helpful for picking up where you last left off. In this recipe, explore how to save and load multiple checkpoints. + :image: ../_static/img/thumbnails/cropped/saving-and-loading-general-checkpoint.PNG + :link: ../recipes/recipes/saving_and_loading_a_general_checkpoint.html + :tags: Basics + +.. customcarditem:: + :header: Saving and loading multiple models in one file using PyTorch + :card_description: In this recipe, learn how saving and loading multiple models can be helpful for reusing models that you have previously trained. + :image: ../_static/img/thumbnails/cropped/saving-multiple-models.PNG + :link: ../recipes/recipes/saving_multiple_models_in_one_file.html + :tags: Basics + +.. customcarditem:: + :header: Warmstarting model using parameters from a different model in PyTorch + :card_description: Learn how warmstarting the training process by partially loading a model or loading a partial model can help your model converge much faster than training from scratch. + :image: ../_static/img/thumbnails/cropped/warmstarting-models.PNG + :link: ../recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.html + :tags: Basics + +.. customcarditem:: + :header: Saving and loading models across devices in PyTorch + :card_description: Learn how saving and loading models across devices (CPUs and GPUs) is relatively straightforward using PyTorch. + :image: ../_static/img/thumbnails/cropped/saving-and-loading-models-across-devices.PNG + :link: ../recipes/recipes/save_load_across_devices.html + :tags: Basics + +.. customcarditem:: + :header: Zeroing out gradients in PyTorch + :card_description: Learn when you should zero out graidents and how doing so can help increase the accuracy of your model. + :image: ../_static/img/thumbnails/cropped/zeroing-out-gradients.PNG + :link: ../recipes/recipes/zeroing_out_gradients.html + :tags: Basics + +.. customcarditem:: + :header: PyTorch Profiler + :card_description: Learn how to use PyTorch's profiler to measure operators time and memory consumption + :image: ../_static/img/thumbnails/cropped/profiler.png + :link: ../recipes/recipes/profiler.html + :tags: Basics + +.. Customization + +.. customcarditem:: + :header: Custom Datasets, Transforms & Dataloaders + :card_description: Learn how to leverage the PyTorch dataset API to easily create a custom dataset and custom dataloader. + :image: ../_static/img/thumbnails/cropped/custom-datasets-transforms-and-dataloaders.png + :link: ../recipes/recipes/custom_dataset_transforms_loader.html + :tags: Data-Customization + +.. Interpretability + +.. customcarditem:: + :header: Model Interpretability using Captum + :card_description: Learn how to use Captum attribute the predictions of an image classifier to their corresponding image features and visualize the attribution results. + :image: ../_static/img/thumbnails/cropped/model-interpretability-using-captum.png + :link: ../recipes/recipes/Captum_Recipe.html + :tags: Interpretability,Captum + +.. customcarditem:: + :header: How to use TensorBoard with PyTorch + :card_description: Learn basic usage of TensorBoard with PyTorch, and how to visualize data in TensorBoard UI + :image: ../_static/img/thumbnails/tensorboard_scalars.png + :link: ../recipes/recipes/tensorboard_with_pytorch.html + :tags: Visualization,TensorBoard + +.. Quantization + +.. customcarditem:: + :header: Dynamic Quantization + :card_description: Apply dynamic quantization to a simple LSTM model. + :image: ../_static/img/thumbnails/cropped/using-dynamic-post-training-quantization.png + :link: ../recipes/recipes/dynamic_quantization.html + :tags: Quantization,Text,Model-Optimization + + +.. Production Development + +.. customcarditem:: + :header: TorchScript for Deployment + :card_description: Learn how to export your trained model in TorchScript format and how to load your TorchScript model in C++ and do inference. + :image: ../_static/img/thumbnails/cropped/torchscript_overview.png + :link: ../recipes/torchscript_inference.html + :tags: TorchScript + +.. customcarditem:: + :header: Deploying with Flask + :card_description: Learn how to use Flask, a lightweight web server, to quickly setup a web API from your trained PyTorch model. + :image: ../_static/img/thumbnails/cropped/using-flask-create-restful-api.png + :link: ../recipes/deployment_with_flask.html + :tags: Production,TorchScript + +.. customcarditem:: + :header: PyTorch Mobile Performance Recipes + :card_description: List of recipes for performance optimizations for using PyTorch on Mobile. + :image: ../_static/img/thumbnails/cropped/zeroing-out-gradients.PNG + :link: ../recipes/mobile_perf.html + :tags: Mobile,Model-Optimization + + +.. End of tutorial card section + +.. raw:: html + +
+ + + +
+ +
+ +.. ----------------------------------------- +.. Page TOC +.. ----------------------------------------- +.. toctree:: + :hidden: + + /recipes/recipes/loading_data_recipe + /recipes/recipes/defining_a_neural_network + /recipes/recipes/what_is_state_dict + /recipes/recipes/saving_and_loading_models_for_inference + /recipes/recipes/saving_and_loading_a_general_checkpoint + /recipes/recipes/saving_multiple_models_in_one_file + /recipes/recipes/warmstarting_model_using_parameters_from_a_different_model + /recipes/recipes/save_load_across_devices + /recipes/recipes/zeroing_out_gradients + /recipes/recipes/profiler + /recipes/recipes/custom_dataset_transforms_loader + /recipes/recipes/Captum_Recipe + /recipes/recipes/tensorboard_with_pytorch + /recipes/recipes/dynamic_quantization + /recipes/torchscript_inference + /recipes/deployment_with_flask diff --git a/recipes/torchscript_inference.rst b/recipes/torchscript_inference.rst new file mode 100644 index 00000000000..54068e70723 --- /dev/null +++ b/recipes/torchscript_inference.rst @@ -0,0 +1,197 @@ +TorchScript for Deployment +========================== + +In this recipe, you will learn: + +- What TorchScript is +- How to export your trained model in TorchScript format +- How to load your TorchScript model in C++ and do inference + +Requirements +------------ + +- PyTorch 1.5 +- TorchVision 0.6.0 +- libtorch 1.5 +- C++ compiler + +The instructions for installing the three PyTorch components are +available at `pytorch.org`_. The C++ compiler will depend on your +platform. + +What is TorchScript? +-------------------- + +**TorchScript** is an intermediate representation of a PyTorch model +(subclass of ``nn.Module``) that can then be run in a high-performance +environment like C++. It’s a high-performance subset of Python that is +meant to be consumed by the **PyTorch JIT Compiler,** which performs +run-time optimization on your model’s computation. TorchScript is the +recommended model format for doing scaled inference with PyTorch models. +For more information, see the PyTorch `Introduction to TorchScript +tutorial`_, the `Loading A TorchScript Model in C++ tutorial`_, and the +`full TorchScript documentation`_, all of which are available on +`pytorch.org`_. + +How to Export Your Model +------------------------ + +As an example, let’s take a pretrained vision model. All of the +pretrained models in TorchVision are compatible with TorchScript. + +Run the following Python 3 code, either in a script or from the REPL: + +.. code:: python3 + + import torch + import torch.nn.functional as F + import torchvision.models as models + + r18 = models.resnet18(pretrained=True) # We now have an instance of the pretrained model + r18_scripted = torch.jit.script(r18) # *** This is the TorchScript export + dummy_input = torch.rand(1, 3, 224, 224) # We should run a quick test + +Let’s do a sanity check on the equivalence of the two models: + +:: + + unscripted_output = r18(dummy_input) # Get the unscripted model's prediction... + scripted_output = r18_scripted(dummy_input) # ...and do the same for the scripted version + + unscripted_top5 = F.softmax(unscripted_output, dim=1).topk(5).indices + scripted_top5 = F.softmax(scripted_output, dim=1).topk(5).indices + + print('Python model top 5 results:\n {}'.format(unscripted_top5)) + print('TorchScript model top 5 results:\n {}'.format(scripted_top5)) + +You should see that both versions of the model give the same results: + +:: + + Python model top 5 results: + tensor([[463, 600, 731, 899, 898]]) + TorchScript model top 5 results: + tensor([[463, 600, 731, 899, 898]]) + +With that check confirmed, go ahead and save the model: + +:: + + r18_scripted.save('r18_scripted.pt') + +Loading TorchScript Models in C++ +--------------------------------- + +Create the following C++ file and name it ``ts-infer.cpp``: + +.. code:: cpp + + #include + #include + + + int main(int argc, const char* argv[]) { + if (argc != 2) { + std::cerr << "usage: ts-infer \n"; + return -1; + } + + std::cout << "Loading model...\n"; + + // deserialize ScriptModule + torch::jit::script::Module module; + try { + module = torch::jit::load(argv[1]); + } catch (const c10::Error& e) { + std::cerr << "Error loading model\n"; + std::cerr << e.msg_without_backtrace(); + return -1; + } + + std::cout << "Model loaded successfully\n"; + + torch::NoGradGuard no_grad; // ensures that autograd is off + module.eval(); // turn off dropout and other training-time layers/functions + + // create an input "image" + std::vector inputs; + inputs.push_back(torch::rand({1, 3, 224, 224})); + + // execute model and package output as tensor + at::Tensor output = module.forward(inputs).toTensor(); + + namespace F = torch::nn::functional; + at::Tensor output_sm = F::softmax(output, F::SoftmaxFuncOptions(1)); + std::tuple top5_tensor = output_sm.topk(5); + at::Tensor top5 = std::get<1>(top5_tensor); + + std::cout << top5[0] << "\n"; + + std::cout << "\nDONE\n"; + return 0; + } + +This program: + +- Loads the model you specify on the command line +- Creates a dummy “image” input tensor +- Performs inference on the input + +Also, notice that there is no dependency on TorchVision in this code. +The saved version of your TorchScript model has your learning weights +*and* your computation graph - nothing else is needed. + +Building and Running Your C++ Inference Engine +---------------------------------------------- + +Create the following ``CMakeLists.txt`` file: + +:: + + cmake_minimum_required(VERSION 3.0 FATAL_ERROR) + project(custom_ops) + + find_package(Torch REQUIRED) + + add_executable(ts-infer ts-infer.cpp) + target_link_libraries(ts-infer "${TORCH_LIBRARIES}") + set_property(TARGET ts-infer PROPERTY CXX_STANDARD 11) + +Make the program: + +:: + + cmake -DCMAKE_PREFIX_PATH= + make + +Now, we can run inference in C++, and verify that we get a result: + +:: + + $ ./ts-infer r18_scripted.pt + Loading model... + Model loaded successfully + 418 + 845 + 111 + 892 + 644 + [ CPULongType{5} ] + + DONE + +Important Resources +------------------- + +- `pytorch.org`_ for installation instructions, and more documentation + and tutorials. +- `Introduction to TorchScript tutorial`_ for a deeper initial + exposition of TorchScript +- `Full TorchScript documentation`_ for complete TorchScript language + and API reference + +.. _pytorch.org: https://pytorch.org/ +.. _Introduction to TorchScript tutorial: https://pytorch.org/tutorials/beginner/Intro_to_TorchScript_tutorial.html +.. _Full TorchScript documentation: https://pytorch.org/docs/stable/jit.html +.. _Loading A TorchScript Model in C++ tutorial: https://pytorch.org/tutorials/advanced/cpp_export.html +.. _full TorchScript documentation: https://pytorch.org/docs/stable/jit.html diff --git a/recipes_source/mobile_perf.rst b/recipes_source/mobile_perf.rst index d0fbf3cb6fb..366318e7533 100644 --- a/recipes_source/mobile_perf.rst +++ b/recipes_source/mobile_perf.rst @@ -2,24 +2,29 @@ Pytorch Mobile Performance Recipes ================================== Introduction ------------- +---------------- Performance (aka latency) is crucial to most, if not all, applications and use-cases of ML model inference on mobile devices. Today, PyTorch executes the models on the CPU backend pending availability of other hardware backends such as GPU, DSP, and NPU. +In this recipe, you will learn: + +- How to optimize your model to help decrease execution time (higher performance, lower latency) on the mobile device. +- How to benchmark (to check if optimizations helped your use case). + Model preparation ----------------- -Next recipes you can take (offline) while preparing the model -to have an optimized model that will probably have shorter execution time +We will start with preparing to optimize your model to help decrease execution time (higher performance, lower latency) on the mobile device. Setup -_____ +####### + First we need to installed pytorch using conda or pip with version at least 1.5.0. :: @@ -63,7 +68,7 @@ Code your model: 1. Fuse operators using ``torch.quantization.fuse_modules`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +############################################################# Do not be confused that fuse_modules is in the quantization package. It works for all ``torcn.nn.Module``. @@ -84,7 +89,7 @@ This script will fuse Convolution, Batch Normalization and Relu in previously de 2. Quantize your model -~~~~~~~~~~~~~~~~~~~~~~ +############################################################# You can find more about PyTorch quantization in `the dedicated tutorial `_. @@ -109,7 +114,7 @@ This code does quantization, using stub for model calibration function, you can 3. Use torch.utils.mobile_optimizer -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +############################################################# Torch mobile_optimizer package does several optimizations with the scripted model, which will help to conv2d and linear operations. @@ -131,7 +136,7 @@ Next we call ``optimize_for_mobile`` and save model on the disk. 4. Android. Reusing tensors for forward. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +############################################################# This recipe is Android only. Memory is a critical resource for android performance, especially on old devices. @@ -180,14 +185,14 @@ and buffer is refilled using ``org.pytorch.torchvision.TensorImageUtils.imageYUV Benchmarking ------------ -The best way to benchmark (to check if optimizations helped your use case) - to measure your particular use case that you want to optimize, as performance behavior can vary in different environments. +The best way to benchmark (to check if optimizations helped your use case) - is to measure your particular use case that you want to optimize, as performance behavior can vary in different environments. PyTorch distribution provides a way to benchmark naked binary that runs the model forward, this approach can give more stable measurements rather than testing inside the application. -Android -------- +Android - Benchmarking Setup +############################# For this you first need to build benchmark binary: @@ -219,5 +224,3 @@ Now we are ready to benchmark your model: Running warmup runs. Main runs. Main run finished. Microseconds per iter: 121318. Iters per second: 8.24281 - - From d64b9aaf1a9bc89840d67b7d087a608449c59168 Mon Sep 17 00:00:00 2001 From: Jessica Lin Date: Mon, 29 Jun 2020 17:42:33 -0700 Subject: [PATCH 06/33] Remove built files --- recipes/deployment_with_flask.rst | 284 --------- recipes/index.rst | 391 ------------- recipes/mobile_perf.rst | 226 -------- recipes/recipes/Captum_Recipe.ipynb | 160 ------ recipes/recipes/Captum_Recipe.py | 189 ------ recipes/recipes/Captum_Recipe.rst | 220 ------- .../custom_dataset_transforms_loader.ipynb | 261 --------- .../custom_dataset_transforms_loader.py | 480 ---------------- .../custom_dataset_transforms_loader.rst | 539 ------------------ .../recipes/defining_a_neural_network.ipynb | 122 ---- recipes/recipes/defining_a_neural_network.py | 183 ------ recipes/recipes/defining_a_neural_network.rst | 234 -------- recipes/recipes/dynamic_quantization.ipynb | 133 ----- recipes/recipes/dynamic_quantization.py | 306 ---------- recipes/recipes/dynamic_quantization.rst | 364 ------------ .../thumb/sphx_glr_Captum_Recipe_thumb.png | Bin 26786 -> 0 bytes ...custom_dataset_transforms_loader_thumb.png | Bin 26786 -> 0 bytes ...hx_glr_defining_a_neural_network_thumb.png | Bin 26786 -> 0 bytes .../sphx_glr_dynamic_quantization_thumb.png | Bin 26786 -> 0 bytes .../sphx_glr_loading_data_recipe_thumb.png | Bin 26786 -> 0 bytes .../images/thumb/sphx_glr_profiler_thumb.png | Bin 26786 -> 0 bytes ...phx_glr_save_load_across_devices_thumb.png | Bin 26786 -> 0 bytes ...and_loading_a_general_checkpoint_thumb.png | Bin 26786 -> 0 bytes ...and_loading_models_for_inference_thumb.png | Bin 26786 -> 0 bytes ...ving_multiple_models_in_one_file_thumb.png | Bin 26786 -> 0 bytes ...phx_glr_tensorboard_with_pytorch_thumb.png | Bin 26786 -> 0 bytes ...arameters_from_a_different_model_thumb.png | Bin 26786 -> 0 bytes .../sphx_glr_what_is_state_dict_thumb.png | Bin 26786 -> 0 bytes .../sphx_glr_zeroing_out_gradients_thumb.png | Bin 26786 -> 0 bytes recipes/recipes/loading_data_recipe.ipynb | 140 ----- recipes/recipes/loading_data_recipe.py | 171 ------ recipes/recipes/loading_data_recipe.rst | 221 ------- recipes/recipes/profiler.ipynb | 201 ------- recipes/recipes/profiler.py | 215 ------- recipes/recipes/profiler.rst | 281 --------- .../recipes/save_load_across_devices.ipynb | 158 ----- recipes/recipes/save_load_across_devices.py | 190 ------ recipes/recipes/save_load_across_devices.rst | 250 -------- ...ing_and_loading_a_general_checkpoint.ipynb | 140 ----- ...saving_and_loading_a_general_checkpoint.py | 162 ------ ...aving_and_loading_a_general_checkpoint.rst | 216 ------- ...ing_and_loading_models_for_inference.ipynb | 140 ----- ...saving_and_loading_models_for_inference.py | 168 ------ ...aving_and_loading_models_for_inference.rst | 223 -------- .../saving_multiple_models_in_one_file.ipynb | 140 ----- .../saving_multiple_models_in_one_file.py | 162 ------ .../saving_multiple_models_in_one_file.rst | 218 ------- .../recipes/tensorboard_with_pytorch.ipynb | 125 ---- recipes/recipes/tensorboard_with_pytorch.py | 168 ------ recipes/recipes/tensorboard_with_pytorch.rst | 212 ------- ...ng_parameters_from_a_different_model.ipynb | 122 ---- ...using_parameters_from_a_different_model.py | 142 ----- ...sing_parameters_from_a_different_model.rst | 194 ------- recipes/recipes/what_is_state_dict.ipynb | 122 ---- recipes/recipes/what_is_state_dict.py | 132 ----- recipes/recipes/what_is_state_dict.rst | 183 ------ recipes/recipes/zeroing_out_gradients.ipynb | 140 ----- recipes/recipes/zeroing_out_gradients.py | 193 ------- recipes/recipes/zeroing_out_gradients.rst | 248 -------- recipes/recipes_index.rst | 197 ------- recipes/torchscript_inference.rst | 197 ------- 61 files changed, 9863 deletions(-) delete mode 100644 recipes/deployment_with_flask.rst delete mode 100644 recipes/index.rst delete mode 100644 recipes/mobile_perf.rst delete mode 100644 recipes/recipes/Captum_Recipe.ipynb delete mode 100644 recipes/recipes/Captum_Recipe.py delete mode 100644 recipes/recipes/Captum_Recipe.rst delete mode 100644 recipes/recipes/custom_dataset_transforms_loader.ipynb delete mode 100644 recipes/recipes/custom_dataset_transforms_loader.py delete mode 100644 recipes/recipes/custom_dataset_transforms_loader.rst delete mode 100644 recipes/recipes/defining_a_neural_network.ipynb delete mode 100644 recipes/recipes/defining_a_neural_network.py delete mode 100644 recipes/recipes/defining_a_neural_network.rst delete mode 100644 recipes/recipes/dynamic_quantization.ipynb delete mode 100644 recipes/recipes/dynamic_quantization.py delete mode 100644 recipes/recipes/dynamic_quantization.rst delete mode 100644 recipes/recipes/images/thumb/sphx_glr_Captum_Recipe_thumb.png delete mode 100644 recipes/recipes/images/thumb/sphx_glr_custom_dataset_transforms_loader_thumb.png delete mode 100644 recipes/recipes/images/thumb/sphx_glr_defining_a_neural_network_thumb.png delete mode 100644 recipes/recipes/images/thumb/sphx_glr_dynamic_quantization_thumb.png delete mode 100644 recipes/recipes/images/thumb/sphx_glr_loading_data_recipe_thumb.png delete mode 100644 recipes/recipes/images/thumb/sphx_glr_profiler_thumb.png delete mode 100644 recipes/recipes/images/thumb/sphx_glr_save_load_across_devices_thumb.png delete mode 100644 recipes/recipes/images/thumb/sphx_glr_saving_and_loading_a_general_checkpoint_thumb.png delete mode 100644 recipes/recipes/images/thumb/sphx_glr_saving_and_loading_models_for_inference_thumb.png delete mode 100644 recipes/recipes/images/thumb/sphx_glr_saving_multiple_models_in_one_file_thumb.png delete mode 100644 recipes/recipes/images/thumb/sphx_glr_tensorboard_with_pytorch_thumb.png delete mode 100644 recipes/recipes/images/thumb/sphx_glr_warmstarting_model_using_parameters_from_a_different_model_thumb.png delete mode 100644 recipes/recipes/images/thumb/sphx_glr_what_is_state_dict_thumb.png delete mode 100644 recipes/recipes/images/thumb/sphx_glr_zeroing_out_gradients_thumb.png delete mode 100644 recipes/recipes/loading_data_recipe.ipynb delete mode 100644 recipes/recipes/loading_data_recipe.py delete mode 100644 recipes/recipes/loading_data_recipe.rst delete mode 100644 recipes/recipes/profiler.ipynb delete mode 100644 recipes/recipes/profiler.py delete mode 100644 recipes/recipes/profiler.rst delete mode 100644 recipes/recipes/save_load_across_devices.ipynb delete mode 100644 recipes/recipes/save_load_across_devices.py delete mode 100644 recipes/recipes/save_load_across_devices.rst delete mode 100644 recipes/recipes/saving_and_loading_a_general_checkpoint.ipynb delete mode 100644 recipes/recipes/saving_and_loading_a_general_checkpoint.py delete mode 100644 recipes/recipes/saving_and_loading_a_general_checkpoint.rst delete mode 100644 recipes/recipes/saving_and_loading_models_for_inference.ipynb delete mode 100644 recipes/recipes/saving_and_loading_models_for_inference.py delete mode 100644 recipes/recipes/saving_and_loading_models_for_inference.rst delete mode 100644 recipes/recipes/saving_multiple_models_in_one_file.ipynb delete mode 100644 recipes/recipes/saving_multiple_models_in_one_file.py delete mode 100644 recipes/recipes/saving_multiple_models_in_one_file.rst delete mode 100644 recipes/recipes/tensorboard_with_pytorch.ipynb delete mode 100644 recipes/recipes/tensorboard_with_pytorch.py delete mode 100644 recipes/recipes/tensorboard_with_pytorch.rst delete mode 100644 recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.ipynb delete mode 100644 recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.py delete mode 100644 recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.rst delete mode 100644 recipes/recipes/what_is_state_dict.ipynb delete mode 100644 recipes/recipes/what_is_state_dict.py delete mode 100644 recipes/recipes/what_is_state_dict.rst delete mode 100644 recipes/recipes/zeroing_out_gradients.ipynb delete mode 100644 recipes/recipes/zeroing_out_gradients.py delete mode 100644 recipes/recipes/zeroing_out_gradients.rst delete mode 100644 recipes/recipes_index.rst delete mode 100644 recipes/torchscript_inference.rst diff --git a/recipes/deployment_with_flask.rst b/recipes/deployment_with_flask.rst deleted file mode 100644 index 0d1291c8d89..00000000000 --- a/recipes/deployment_with_flask.rst +++ /dev/null @@ -1,284 +0,0 @@ -Deploying with Flask -==================== - -In this recipe, you will learn: - -- How to wrap your trained PyTorch model in a Flask container to expose - it via a web API -- How to translate incoming web requests into PyTorch tensors for your - model -- How to package your model’s output for an HTTP response - -Requirements ------------- - -You will need a Python 3 environment with the following packages (and -their dependencies) installed: - -- PyTorch 1.5 -- TorchVision 0.6.0 -- Flask 1.1 - -Optionally, to get some of the supporting files, you'll need git. - -The instructions for installing PyTorch and TorchVision are available at -`pytorch.org`_. Instructions for installing Flask are available on `the -Flask site`_. - -What is Flask? --------------- - -Flask is a lightweight web server written in Python. It provides a -convenient way for you to quickly set up a web API for predictions from -your trained PyTorch model, either for direct use, or as a web service -within a larger system. - -Setup and Supporting Files --------------------------- - -We're going to create a web service that takes in images, and maps them -to one of the 1000 classes of the ImageNet dataset. To do this, you'll -need an image file for testing. Optionally, you can also get a file that -will map the class index output by the model to a human-readable class -name. - -Option 1: To Get Both Files Quickly -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can pull both of the supporting files quickly by checking out the -TorchServe repository and copying them to your working folder. *(NB: -There is no dependency on TorchServe for this tutorial - it's just a -quick way to get the files.)* Issue the following commands from your -shell prompt: - -:: - - git clone https://github.com/pytorch/serve - cp serve/examples/image_classifier/kitten.jpg . - cp serve/examples/image_classifier/index_to_name.json . - -And you've got them! - -Option 2: Bring Your Own Image -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``index_to_name.json`` file is optional in the Flask service below. -You can test your service with your own image - just make sure it's a -3-color JPEG. - -Building Your Flask Service ---------------------------- - -The full Python script for the Flask service is shown at the end of this -recipe; you can copy and paste that into your own ``app.py`` file. Below -we'll look at individual sections to make their functions clear. - -Imports -~~~~~~~ - -:: - - import torchvision.models as models - import torchvision.transforms as transforms - from PIL import Image - from flask import Flask, jsonify, request - -In order: - -- We'll be using a pre-trained DenseNet model from - ``torchvision.models`` -- ``torchvision.transforms`` contains tools for manipulating your image - data -- Pillow (``PIL``) is what we'll use to load the image file initially -- And of course we'll need classes from ``flask`` - -Pre-Processing -~~~~~~~~~~~~~~ - -:: - - def transform_image(infile): - input_transforms = [transforms.Resize(255), - transforms.CenterCrop(224), - transforms.ToTensor(), - transforms.Normalize([0.485, 0.456, 0.406], - [0.229, 0.224, 0.225])] - my_transforms = transforms.Compose(input_transforms) - image = Image.open(infile) - timg = my_transforms(image) - timg.unsqueeze_(0) - return timg - -The web request gave us an image file, but our model expects a PyTorch -tensor of shape (N, 3, 224, 224) where *N* is the number of items in the -input batch. (We will just have a batch size of 1.) The first thing we -do is compose a set of TorchVision transforms that resize and crop the -image, convert it to a tensor, then normalize the values in the tensor. -(For more information on this normalization, see the documentation for -``torchvision.models_``.) - -After that, we open the file and apply the transforms. The transforms -return a tensor of shape (3, 224, 224) - the 3 color channels of a -224x224 image. Because we need to make this single image a batch, we use -the ``unsqueeze_(0)`` call to modify the tensor in place by adding a new -first dimension. The tensor contains the same data, but now has shape -(1, 3, 224, 224). - -In general, even if you're not working with image data, you will need to -transform the input from your HTTP request into a tensor that PyTorch -can consume. - -Inference -~~~~~~~~~ - -:: - - def get_prediction(input_tensor): - outputs = model.forward(input_tensor) - _, y_hat = outputs.max(1) - prediction = y_hat.item() - return prediction - -The inference itself is the simplest part: When we pass the input tensor -to them model, we get back a tensor of values that represent the model's -estimated likelihood that the image belongs to a particular class. The -``max()`` call finds the class with the maximum likelihood value, and -returns that value with the ImageNet class index. Finally, we extract -that class index from the tensor containing it with the ``item()`` call, and -return it. - -Post-Processing -~~~~~~~~~~~~~~~ - -:: - - def render_prediction(prediction_idx): - stridx = str(prediction_idx) - class_name = 'Unknown' - if img_class_map is not None: - if stridx in img_class_map is not None: - class_name = img_class_map[stridx][1] - - return prediction_idx, class_name - -The ``render_prediction()`` method maps the predicted class index to a -human-readable class label. It's typical, after getting the prediction -from your model, to perform post-processing to make the prediction ready -for either human consumption, or for another piece of software. - -Running The Full Flask App --------------------------- - -Paste the following into a file called ``app.py``: - -:: - - import io - import json - import os - - import torchvision.models as models - import torchvision.transforms as transforms - from PIL import Image - from flask import Flask, jsonify, request - - - app = Flask(__name__) - model = models.densenet121(pretrained=True) # Trained on 1000 classes from ImageNet - model.eval() # Turns off autograd and - - - - img_class_map = None - mapping_file_path = 'index_to_name.json' # Human-readable names for Imagenet classes - if os.path.isfile(mapping_file_path): - with open (mapping_file_path) as f: - img_class_map = json.load(f) - - - - # Transform input into the form our model expects - def transform_image(infile): - input_transforms = [transforms.Resize(255), # We use multiple TorchVision transforms to ready the image - transforms.CenterCrop(224), - transforms.ToTensor(), - transforms.Normalize([0.485, 0.456, 0.406], # Standard normalization for ImageNet model input - [0.229, 0.224, 0.225])] - my_transforms = transforms.Compose(input_transforms) - image = Image.open(infile) # Open the image file - timg = my_transforms(image) # Transform PIL image to appropriately-shaped PyTorch tensor - timg.unsqueeze_(0) # PyTorch models expect batched input; create a batch of 1 - return timg - - - # Get a prediction - def get_prediction(input_tensor): - outputs = model.forward(input_tensor) # Get likelihoods for all ImageNet classes - _, y_hat = outputs.max(1) # Extract the most likely class - prediction = y_hat.item() # Extract the int value from the PyTorch tensor - return prediction - - # Make the prediction human-readable - def render_prediction(prediction_idx): - stridx = str(prediction_idx) - class_name = 'Unknown' - if img_class_map is not None: - if stridx in img_class_map is not None: - class_name = img_class_map[stridx][1] - - return prediction_idx, class_name - - - @app.route('/', methods=['GET']) - def root(): - return jsonify({'msg' : 'Try POSTing to the /predict endpoint with an RGB image attachment'}) - - - @app.route('/predict', methods=['POST']) - def predict(): - if request.method == 'POST': - file = request.files['file'] - if file is not None: - input_tensor = transform_image(file) - prediction_idx = get_prediction(input_tensor) - class_id, class_name = render_prediction(prediction_idx) - return jsonify({'class_id': class_id, 'class_name': class_name}) - - - if __name__ == '__main__': - app.run() - -To start the server from your shell prompt, issue the following command: - -:: - - FLASK_APP=app.py flask run - -By default, your Flask server is listening on port 5000. Once the server -is running, open another terminal window, and test your new inference -server: - -:: - - curl -X POST -H "Content-Type: multipart/form-data" http://localhost:5000/predict -F "file=@kitten.jpg" - -If everything is set up correctly, you should recevie a response similar -to the following: - -:: - - {"class_id":285,"class_name":"Egyptian_cat"} - -Important Resources -------------------- - -- `pytorch.org`_ for installation instructions, and more documentation - and tutorials -- The `Flask site`_ has a `Quick Start guide`_ that goes into more - detail on setting up a simple Flask service - -.. _pytorch.org: https://pytorch.org -.. _Flask site: https://flask.palletsprojects.com/en/1.1.x/ -.. _Quick Start guide: https://flask.palletsprojects.com/en/1.1.x/quickstart/ -.. _torchvision.models: https://pytorch.org/docs/stable/torchvision/models.html -.. _the Flask site: https://flask.palletsprojects.com/en/1.1.x/installation/ diff --git a/recipes/index.rst b/recipes/index.rst deleted file mode 100644 index d30ee3e4e29..00000000000 --- a/recipes/index.rst +++ /dev/null @@ -1,391 +0,0 @@ -:orphan: - - - -.. _sphx_glr_recipes: - -Recipes ------------------- -1. recipes/* and recipes_index.rst - PyTorch Recipes - https://pytorch.org/tutorials/recipes/recipes_index.html - - - - -.. raw:: html - -
- - - -.. _sphx_glr_recipes_recipes: - -PyTorch Recipes ---------------------------------------------- -1. loading_data_recipe.py - Loading Data in PyTorch - https://pytorch.org/tutorials/recipes/recipes/loading_data_recipe.html - -2. defining_a_neural_network.py - Defining a Neural Network in PyTorch - https://pytorch.org/tutorials/recipes/recipes/defining_a_neural_network.html - -3. what_is_state_dict.py - What is a state_dict in PyTorch - https://pytorch.org/tutorials/recipes/recipes/what_is_state_dict.html - -4. saving_and_loading_models_for_inference.py - Saving and loading models for inference in PyTorch - https://pytorch.org/tutorials/recipes/recipes/saving_and_loading_models_for_inference.html - -5. custom_dataset_transforms_loader.py - Developing Custom PyTorch Dataloaders - https://pytorch.org/tutorials/recipes/recipes/custom_dataset_transforms_loader.html - - -6. Captum_Recipe.py - Model Interpretability using Captum - https://pytorch.org/tutorials/recipes/recipes/Captum_Recipe.html - -7. dynamic_quantization.py - Dynamic Quantization - https://pytorch.org/tutorials/recipes/recipes/dynamic_quantization.html - -8. save_load_across_devices.py - Saving and loading models across devices in PyTorch - https://pytorch.org/tutorials/recipes/recipes/save_load_across_devices.html - -9. saving_and_loading_a_general_checkpoint.py - Saving and loading a general checkpoint in PyTorch - https://pytorch.org/tutorials/recipes/recipes/saving_and_loading_a_general_checkpoint.html - -10. saving_and_loading_models_for_inference.py - Saving and loading models for inference in PyTorch - https://pytorch.org/tutorials/recipes/recipes/saving_and_loading_models_for_inference.html - -11. saving_multiple_models_in_one_file.py - Saving and loading multiple models in one file using PyTorch - https://pytorch.org/tutorials/recipes/recipes/saving_multiple_models_in_one_file.html - -12. warmstarting_model_using_parameters_from_a_different_model.py - Warmstarting models using parameters from different model - https://pytorch.org/tutorials/recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.html - -13. zeroing_out_gradients.py - Zeroing out gradients - https://pytorch.org/tutorials/recipes/recipes/zeroing_out_gradients.html - -14. mobile_perf.py - PyTorch Mobile Performance Recipes - https://pytorch.org/tutorials/recipes/mobile_perf.html - - - -.. raw:: html - -
- -.. only:: html - - .. figure:: /recipes/recipes/images/thumb/sphx_glr_tensorboard_with_pytorch_thumb.png - - :ref:`sphx_glr_recipes_recipes_tensorboard_with_pytorch.py` - -.. raw:: html - -
- - -.. toctree:: - :hidden: - - /recipes/recipes/tensorboard_with_pytorch - -.. raw:: html - -
- -.. only:: html - - .. figure:: /recipes/recipes/images/thumb/sphx_glr_saving_and_loading_models_for_inference_thumb.png - - :ref:`sphx_glr_recipes_recipes_saving_and_loading_models_for_inference.py` - -.. raw:: html - -
- - -.. toctree:: - :hidden: - - /recipes/recipes/saving_and_loading_models_for_inference - -.. raw:: html - -
- -.. only:: html - - .. figure:: /recipes/recipes/images/thumb/sphx_glr_what_is_state_dict_thumb.png - - :ref:`sphx_glr_recipes_recipes_what_is_state_dict.py` - -.. raw:: html - -
- - -.. toctree:: - :hidden: - - /recipes/recipes/what_is_state_dict - -.. raw:: html - -
- -.. only:: html - - .. figure:: /recipes/recipes/images/thumb/sphx_glr_saving_and_loading_a_general_checkpoint_thumb.png - - :ref:`sphx_glr_recipes_recipes_saving_and_loading_a_general_checkpoint.py` - -.. raw:: html - -
- - -.. toctree:: - :hidden: - - /recipes/recipes/saving_and_loading_a_general_checkpoint - -.. raw:: html - -
- -.. only:: html - - .. figure:: /recipes/recipes/images/thumb/sphx_glr_warmstarting_model_using_parameters_from_a_different_model_thumb.png - - :ref:`sphx_glr_recipes_recipes_warmstarting_model_using_parameters_from_a_different_model.py` - -.. raw:: html - -
- - -.. toctree:: - :hidden: - - /recipes/recipes/warmstarting_model_using_parameters_from_a_different_model - -.. raw:: html - -
- -.. only:: html - - .. figure:: /recipes/recipes/images/thumb/sphx_glr_loading_data_recipe_thumb.png - - :ref:`sphx_glr_recipes_recipes_loading_data_recipe.py` - -.. raw:: html - -
- - -.. toctree:: - :hidden: - - /recipes/recipes/loading_data_recipe - -.. raw:: html - -
- -.. only:: html - - .. figure:: /recipes/recipes/images/thumb/sphx_glr_save_load_across_devices_thumb.png - - :ref:`sphx_glr_recipes_recipes_save_load_across_devices.py` - -.. raw:: html - -
- - -.. toctree:: - :hidden: - - /recipes/recipes/save_load_across_devices - -.. raw:: html - -
- -.. only:: html - - .. figure:: /recipes/recipes/images/thumb/sphx_glr_saving_multiple_models_in_one_file_thumb.png - - :ref:`sphx_glr_recipes_recipes_saving_multiple_models_in_one_file.py` - -.. raw:: html - -
- - -.. toctree:: - :hidden: - - /recipes/recipes/saving_multiple_models_in_one_file - -.. raw:: html - -
- -.. only:: html - - .. figure:: /recipes/recipes/images/thumb/sphx_glr_defining_a_neural_network_thumb.png - - :ref:`sphx_glr_recipes_recipes_defining_a_neural_network.py` - -.. raw:: html - -
- - -.. toctree:: - :hidden: - - /recipes/recipes/defining_a_neural_network - -.. raw:: html - -
- -.. only:: html - - .. figure:: /recipes/recipes/images/thumb/sphx_glr_zeroing_out_gradients_thumb.png - - :ref:`sphx_glr_recipes_recipes_zeroing_out_gradients.py` - -.. raw:: html - -
- - -.. toctree:: - :hidden: - - /recipes/recipes/zeroing_out_gradients - -.. raw:: html - -
- -.. only:: html - - .. figure:: /recipes/recipes/images/thumb/sphx_glr_dynamic_quantization_thumb.png - - :ref:`sphx_glr_recipes_recipes_dynamic_quantization.py` - -.. raw:: html - -
- - -.. toctree:: - :hidden: - - /recipes/recipes/dynamic_quantization - -.. raw:: html - -
- -.. only:: html - - .. figure:: /recipes/recipes/images/thumb/sphx_glr_Captum_Recipe_thumb.png - - :ref:`sphx_glr_recipes_recipes_Captum_Recipe.py` - -.. raw:: html - -
- - -.. toctree:: - :hidden: - - /recipes/recipes/Captum_Recipe - -.. raw:: html - -
- -.. only:: html - - .. figure:: /recipes/recipes/images/thumb/sphx_glr_profiler_thumb.png - - :ref:`sphx_glr_recipes_recipes_profiler.py` - -.. raw:: html - -
- - -.. toctree:: - :hidden: - - /recipes/recipes/profiler - -.. raw:: html - -
- -.. only:: html - - .. figure:: /recipes/recipes/images/thumb/sphx_glr_custom_dataset_transforms_loader_thumb.png - - :ref:`sphx_glr_recipes_recipes_custom_dataset_transforms_loader.py` - -.. raw:: html - -
- - -.. toctree:: - :hidden: - - /recipes/recipes/custom_dataset_transforms_loader -.. raw:: html - -
- - - -.. only :: html - - .. container:: sphx-glr-footer - :class: sphx-glr-footer-gallery - - - .. container:: sphx-glr-download - - :download:`Download all examples in Python source code: recipes_python.zip ` - - - - .. container:: sphx-glr-download - - :download:`Download all examples in Jupyter notebooks: recipes_jupyter.zip ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/mobile_perf.rst b/recipes/mobile_perf.rst deleted file mode 100644 index 366318e7533..00000000000 --- a/recipes/mobile_perf.rst +++ /dev/null @@ -1,226 +0,0 @@ -Pytorch Mobile Performance Recipes -================================== - -Introduction ----------------- -Performance (aka latency) is crucial to most, if not all, -applications and use-cases of ML model inference on mobile devices. - -Today, PyTorch executes the models on the CPU backend pending availability -of other hardware backends such as GPU, DSP, and NPU. - -In this recipe, you will learn: - -- How to optimize your model to help decrease execution time (higher performance, lower latency) on the mobile device. -- How to benchmark (to check if optimizations helped your use case). - - -Model preparation ------------------ - -We will start with preparing to optimize your model to help decrease execution time -(higher performance, lower latency) on the mobile device. - - -Setup -####### - -First we need to installed pytorch using conda or pip with version at least 1.5.0. - -:: - - conda install pytorch torchvision -c pytorch - -or - -:: - - pip install torch torchvision - -Code your model: - -:: - - import torch - from torch.utils.mobile_optimizer import optimize_for_mobile - - class AnnotatedConvBnReLUModel(torch.nn.Module): - def __init__(self): - super(AnnotatedConvBnReLUModel, self).__init__() - self.conv = torch.nn.Conv2d(3, 5, 3, bias=False).to(dtype=torch.float) - self.bn = torch.nn.BatchNorm2d(5).to(dtype=torch.float) - self.relu = torch.nn.ReLU(inplace=True) - self.quant = torch.quantization.QuantStub() - self.dequant = torch.quantization.DeQuantStub() - - def forward(self, x): - x = self.quant(x) - x = self.conv(x) - x = self.bn(x) - x = self.relu(x) - x = self.dequant(x) - return x - - model = AnnotatedConvBnReLUModel() - - -``torch.quantization.QuantStub`` and ``torch.quantization.DeQuantStub()`` are no-op stubs, which will be used for quantization step. - - -1. Fuse operators using ``torch.quantization.fuse_modules`` -############################################################# - -Do not be confused that fuse_modules is in the quantization package. -It works for all ``torcn.nn.Module``. - -``torch.quantization.fuse_modules`` fuses a list of modules into a single module. -It fuses only the following sequence of modules: - -- Convolution, Batch normalization -- Convolution, Batch normalization, Relu -- Convolution, Relu -- Linear, Relu - -This script will fuse Convolution, Batch Normalization and Relu in previously declared model. - -:: - - torch.quantization.fuse_modules(model, [['conv', 'bn', 'relu']], inplace=True) - - -2. Quantize your model -############################################################# - -You can find more about PyTorch quantization in -`the dedicated tutorial `_. - -Quantization of the model not only moves computation to int8, -but also reduces the size of your model on a disk. -That size reduction helps to reduce disk read operations during the first load of the model and decreases the amount of RAM. -Both of those resources can be crucial for the performance of mobile applications. -This code does quantization, using stub for model calibration function, you can find more about it `here `__. - -:: - - model.qconfig = torch.quantization.get_default_qconfig('qnnpack') - torch.quantization.prepare(model, inplace=True) - # Calibrate your model - def calibrate(model, calibration_data): - # Your calibration code here - return - calibrate(model, []) - torch.quantization.convert(model, inplace=True) - - - -3. Use torch.utils.mobile_optimizer -############################################################# - -Torch mobile_optimizer package does several optimizations with the scripted model, -which will help to conv2d and linear operations. -It pre-packs model weights in an optimized format and fuses ops above with relu -if it is the next operation. - -First we script the result model from previous step: - -:: - - torchscript_model = torch.jit.script(model) - -Next we call ``optimize_for_mobile`` and save model on the disk. - -:: - - torchscript_model_optimized = optimize_for_mobile(torchscript_model) - torch.jit.save(torchscript_model_optimized, "model.pt") - - -4. Android. Reusing tensors for forward. -############################################################# - -This recipe is Android only. -Memory is a critical resource for android performance, especially on old devices. -Tensors can need a significant amount of memory. -For example, standard computer vision tensor contains 1*3*224*224 elements, -assuming that data type is float and will need 588Kb of memory. - -:: - - FloatBuffer buffer = Tensor.allocateFloatBuffer(1*3*224*224); - Tensor tensor = Tensor.fromBlob(buffer, new long[]{1, 3, 224, 224}); - - -Here we allocate native memory as ``java.nio.FloatBuffer`` and creating ``org.pytorch.Tensor`` which storage will be pointing to the memory of the allocated buffer. - -For most of the use cases, we do not do model forward only once, repeating it with some frequency or as fast as possible. - -If we are doing new memory allocation for every module forward - that will be suboptimal. -Instead of this, we can reuse the same memory that we allocated on the previous step, fill it with new data, and run module forward again on the same tensor object. - -You can check how it looks in code in `pytorch android application example `_. - -:: - - protected AnalysisResult analyzeImage(ImageProxy image, int rotationDegrees) { - if (mModule == null) { - mModule = Module.load(moduleFileAbsoluteFilePath); - mInputTensorBuffer = - Tensor.allocateFloatBuffer(3 * 224 * 224); - mInputTensor = Tensor.fromBlob(mInputTensorBuffer, new long[]{1, 3, 224, 224}); - } - - TensorImageUtils.imageYUV420CenterCropToFloatBuffer( - image.getImage(), rotationDegrees, - 224, 224, - TensorImageUtils.TORCHVISION_NORM_MEAN_RGB, - TensorImageUtils.TORCHVISION_NORM_STD_RGB, - mInputTensorBuffer, 0); - - Tensor outputTensor = mModule.forward(IValue.from(mInputTensor)).toTensor(); - } - -Member fields ``mModule``, ``mInputTensorBuffer`` and ``mInputTensor`` are initialized only once -and buffer is refilled using ``org.pytorch.torchvision.TensorImageUtils.imageYUV420CenterCropToFloatBuffer``. - -Benchmarking ------------- - -The best way to benchmark (to check if optimizations helped your use case) - is to measure your particular use case that you want to optimize, as performance behavior can vary in different environments. - -PyTorch distribution provides a way to benchmark naked binary that runs the model forward, -this approach can give more stable measurements rather than testing inside the application. - - -Android - Benchmarking Setup -############################# - -For this you first need to build benchmark binary: - -:: - - - rm -rf build_android - BUILD_PYTORCH_MOBILE=1 ANDROID_ABI=arm64-v8a ./scripts/build_android.sh -DBUILD_BINARY=ON - -You should have arm64 binary at: ``build_android/bin/speed_benchmark_torch``. -This binary takes ``--model=``, ``--input_dim="1,3,224,224"`` as dimension information for the input and ``--input_type="float"`` as the type of the input as arguments. - -Once you have your android device connected, -push speedbenchark_torch binary and your model to the phone: - -:: - - adb push /data/local/tmp - adb push /data/local/tmp - - -Now we are ready to benchmark your model: - -:: - - adb shell "/data/local/tmp/speed_benchmark_torch --model="/data/local/tmp/model.pt" --input_dims="1,3,224,224" --input_type="float" - ----- output ----- - Starting benchmark. - Running warmup runs. - Main runs. - Main run finished. Microseconds per iter: 121318. Iters per second: 8.24281 diff --git a/recipes/recipes/Captum_Recipe.ipynb b/recipes/recipes/Captum_Recipe.ipynb deleted file mode 100644 index 9feb2f0bc4a..00000000000 --- a/recipes/recipes/Captum_Recipe.ipynb +++ /dev/null @@ -1,160 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\nModel Interpretability using Captum\n===================================\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Captum helps you understand how the data features impact your model\npredictions or neuron activations, shedding light on how your model\noperates.\n\nUsing Captum, you can apply a wide range of state-of-the-art feature\nattribution algorithms such as \\ ``Guided GradCam``\\ and\n\\ ``Integrated Gradients``\\ in a unified way.\n\nIn this recipe you will learn how to use Captum to: \\* attribute the\npredictions of an image classifier to their corresponding image\nfeatures. \\* visualize the attribution results.\n\n\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Before you begin\n----------------\n\n\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Make sure Captum is installed in your active Python environment. Captum\nis available both on GitHub, as a ``pip`` package, or as a ``conda``\npackage. For detailed instructions, consult the installation guide at\nhttps://captum.ai/\n\n\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For a model, we use a built-in image classifier in PyTorch. Captum can\nreveal which parts of a sample image support certain predictions made by\nthe model.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import torchvision\nfrom torchvision import transforms\nfrom PIL import Image\nimport requests\nfrom io import BytesIO\n\nmodel = torchvision.models.resnet18(pretrained=True).eval()\n\nresponse = requests.get(\"https://image.freepik.com/free-photo/two-beautiful-puppies-cat-dog_58409-6024.jpg\")\nimg = Image.open(BytesIO(response.content))\n\ncenter_crop = transforms.Compose([\n transforms.Resize(256),\n transforms.CenterCrop(224),\n])\n\nnormalize = transforms.Compose([\n transforms.ToTensor(), # converts the image to a tensor with values between 0 and 1\n transforms.Normalize( # normalize to follow 0-centered imagenet pixel rgb distribution\n mean=[0.485, 0.456, 0.406],\n std=[0.229, 0.224, 0.225]\n )\n])\ninput_img = normalize(center_crop(img)).unsqueeze(0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Computing Attribution\n---------------------\n\n\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Among the top-3 predictions of the models are classes 208 and 283 which\ncorrespond to dog and cat.\n\nLet us attribute each of these predictions to the corresponding part of\nthe input, using Captum\u2019s \\ ``Occlusion``\\ algorithm.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "from captum.attr import Occlusion \n\nocclusion = Occlusion(model)\n\nstrides = (3, 9, 9) # smaller = more fine-grained attribution but slower\ntarget=208, # Labrador index in ImageNet \nsliding_window_shapes=(3,45, 45) # choose size enough to change object appearance\nbaselines = 0 # values to occlude the image with. 0 corresponds to gray\n\nattribution_dog = occlusion.attribute(input_img,\n strides = strides,\n target=target,\n sliding_window_shapes=sliding_window_shapes,\n baselines=baselines)\n\n\ntarget=283, # Persian cat index in ImageNet \nattribution_cat = occlusion.attribute(input_img,\n strides = strides,\n target=target,\n sliding_window_shapes=sliding_window_shapes,\n baselines=0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Besides ``Occlusion``, Captum features many algorithms such as\n\\ ``Integrated Gradients``\\ , \\ ``Deconvolution``\\ ,\n\\ ``GuidedBackprop``\\ , \\ ``Guided GradCam``\\ , \\ ``DeepLift``\\ , and\n\\ ``GradientShap``\\ . All of these algorithms are subclasses of\n``Attribution`` which expects your model as a callable ``forward_func``\nupon initialization and has an ``attribute(...)`` method which returns\nthe attribution result in a unified format.\n\nLet us visualize the computed attribution results in case of images.\n\n\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Visualizing the Results\n-----------------------\n\n\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Captum\u2019s \\ ``visualization``\\ utility provides out-of-the-box methods\nto visualize attribution results both for pictorial and for textual\ninputs.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import numpy as np\nfrom captum.attr import visualization as viz\n\n# Convert the compute attribution tensor into an image-like numpy array\nattribution_dog = np.transpose(attribution_dog.squeeze().cpu().detach().numpy(), (1,2,0))\n\nvis_types = [\"heat_map\", \"original_image\"]\nvis_signs = [\"all\", \"all\"], # \"positive\", \"negative\", or \"all\" to show both\n# positive attribution indicates that the presence of the area increases the prediction score\n# negative attribution indicates distractor areas whose absence increases the score\n\n_ = viz.visualize_image_attr_multiple(attribution_dog,\n center_crop(img),\n vis_types,\n vis_signs,\n [\"attribution for dog\", \"image\"],\n show_colorbar = True\n )\n\n\nattribution_cat = np.transpose(attribution_cat.squeeze().cpu().detach().numpy(), (1,2,0))\n\n_ = viz.visualize_image_attr_multiple(attribution_cat,\n center_crop(img),\n [\"heat_map\", \"original_image\"], \n [\"all\", \"all\"], # positive/negative attribution or all\n [\"attribution for cat\", \"image\"],\n show_colorbar = True\n )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If your data is textual, ``visualization.visualize_text()`` offers a\ndedicated view to explore attribution on top of the input text. Find out\nmore at http://captum.ai/tutorials/IMDB_TorchText_Interpret\n\n\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Final Notes\n-----------\n\n\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Captum can handle most model types in PyTorch across modalities\nincluding vision, text, and more. With Captum you can: \\* Attribute a\nspecific output to the model input as illustrated above. \\* Attribute a\nspecific output to a hidden-layer neuron (see Captum API reference). \\*\nAttribute a hidden-layer neuron response to the model input (see Captum\nAPI reference).\n\nFor complete API of the supported methods and a list of tutorials,\nconsult our website http://captum.ai\n\nAnother useful post by Gilbert Tanner:\nhttps://gilberttanner.com/blog/interpreting-pytorch-models-with-captum\n\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.4" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/recipes/recipes/Captum_Recipe.py b/recipes/recipes/Captum_Recipe.py deleted file mode 100644 index 105c8cd5f67..00000000000 --- a/recipes/recipes/Captum_Recipe.py +++ /dev/null @@ -1,189 +0,0 @@ -""" -Model Interpretability using Captum -=================================== - -""" - - -###################################################################### -# Captum helps you understand how the data features impact your model -# predictions or neuron activations, shedding light on how your model -# operates. -# -# Using Captum, you can apply a wide range of state-of-the-art feature -# attribution algorithms such as \ ``Guided GradCam``\ and -# \ ``Integrated Gradients``\ in a unified way. -# -# In this recipe you will learn how to use Captum to: \* attribute the -# predictions of an image classifier to their corresponding image -# features. \* visualize the attribution results. -# - - -###################################################################### -# Before you begin -# ---------------- -# - - -###################################################################### -# Make sure Captum is installed in your active Python environment. Captum -# is available both on GitHub, as a ``pip`` package, or as a ``conda`` -# package. For detailed instructions, consult the installation guide at -# https://captum.ai/ -# - - -###################################################################### -# For a model, we use a built-in image classifier in PyTorch. Captum can -# reveal which parts of a sample image support certain predictions made by -# the model. -# - -import torchvision -from torchvision import transforms -from PIL import Image -import requests -from io import BytesIO - -model = torchvision.models.resnet18(pretrained=True).eval() - -response = requests.get("https://image.freepik.com/free-photo/two-beautiful-puppies-cat-dog_58409-6024.jpg") -img = Image.open(BytesIO(response.content)) - -center_crop = transforms.Compose([ - transforms.Resize(256), - transforms.CenterCrop(224), -]) - -normalize = transforms.Compose([ - transforms.ToTensor(), # converts the image to a tensor with values between 0 and 1 - transforms.Normalize( # normalize to follow 0-centered imagenet pixel rgb distribution - mean=[0.485, 0.456, 0.406], - std=[0.229, 0.224, 0.225] - ) -]) -input_img = normalize(center_crop(img)).unsqueeze(0) - - -###################################################################### -# Computing Attribution -# --------------------- -# - - -###################################################################### -# Among the top-3 predictions of the models are classes 208 and 283 which -# correspond to dog and cat. -# -# Let us attribute each of these predictions to the corresponding part of -# the input, using Captum’s \ ``Occlusion``\ algorithm. -# - -from captum.attr import Occlusion - -occlusion = Occlusion(model) - -strides = (3, 9, 9) # smaller = more fine-grained attribution but slower -target=208, # Labrador index in ImageNet -sliding_window_shapes=(3,45, 45) # choose size enough to change object appearance -baselines = 0 # values to occlude the image with. 0 corresponds to gray - -attribution_dog = occlusion.attribute(input_img, - strides = strides, - target=target, - sliding_window_shapes=sliding_window_shapes, - baselines=baselines) - - -target=283, # Persian cat index in ImageNet -attribution_cat = occlusion.attribute(input_img, - strides = strides, - target=target, - sliding_window_shapes=sliding_window_shapes, - baselines=0) - - -###################################################################### -# Besides ``Occlusion``, Captum features many algorithms such as -# \ ``Integrated Gradients``\ , \ ``Deconvolution``\ , -# \ ``GuidedBackprop``\ , \ ``Guided GradCam``\ , \ ``DeepLift``\ , and -# \ ``GradientShap``\ . All of these algorithms are subclasses of -# ``Attribution`` which expects your model as a callable ``forward_func`` -# upon initialization and has an ``attribute(...)`` method which returns -# the attribution result in a unified format. -# -# Let us visualize the computed attribution results in case of images. -# - - -###################################################################### -# Visualizing the Results -# ----------------------- -# - - -###################################################################### -# Captum’s \ ``visualization``\ utility provides out-of-the-box methods -# to visualize attribution results both for pictorial and for textual -# inputs. -# - -import numpy as np -from captum.attr import visualization as viz - -# Convert the compute attribution tensor into an image-like numpy array -attribution_dog = np.transpose(attribution_dog.squeeze().cpu().detach().numpy(), (1,2,0)) - -vis_types = ["heat_map", "original_image"] -vis_signs = ["all", "all"], # "positive", "negative", or "all" to show both -# positive attribution indicates that the presence of the area increases the prediction score -# negative attribution indicates distractor areas whose absence increases the score - -_ = viz.visualize_image_attr_multiple(attribution_dog, - center_crop(img), - vis_types, - vis_signs, - ["attribution for dog", "image"], - show_colorbar = True - ) - - -attribution_cat = np.transpose(attribution_cat.squeeze().cpu().detach().numpy(), (1,2,0)) - -_ = viz.visualize_image_attr_multiple(attribution_cat, - center_crop(img), - ["heat_map", "original_image"], - ["all", "all"], # positive/negative attribution or all - ["attribution for cat", "image"], - show_colorbar = True - ) - - -###################################################################### -# If your data is textual, ``visualization.visualize_text()`` offers a -# dedicated view to explore attribution on top of the input text. Find out -# more at http://captum.ai/tutorials/IMDB_TorchText_Interpret -# - - -###################################################################### -# Final Notes -# ----------- -# - - -###################################################################### -# Captum can handle most model types in PyTorch across modalities -# including vision, text, and more. With Captum you can: \* Attribute a -# specific output to the model input as illustrated above. \* Attribute a -# specific output to a hidden-layer neuron (see Captum API reference). \* -# Attribute a hidden-layer neuron response to the model input (see Captum -# API reference). -# -# For complete API of the supported methods and a list of tutorials, -# consult our website http://captum.ai -# -# Another useful post by Gilbert Tanner: -# https://gilberttanner.com/blog/interpreting-pytorch-models-with-captum -# \ No newline at end of file diff --git a/recipes/recipes/Captum_Recipe.rst b/recipes/recipes/Captum_Recipe.rst deleted file mode 100644 index aff05ebd305..00000000000 --- a/recipes/recipes/Captum_Recipe.rst +++ /dev/null @@ -1,220 +0,0 @@ -.. note:: - :class: sphx-glr-download-link-note - - Click :ref:`here ` to download the full example code -.. rst-class:: sphx-glr-example-title - -.. _sphx_glr_recipes_recipes_Captum_Recipe.py: - - -Model Interpretability using Captum -=================================== -Captum helps you understand how the data features impact your model -predictions or neuron activations, shedding light on how your model -operates. - -Using Captum, you can apply a wide range of state-of-the-art feature -attribution algorithms such as \ ``Guided GradCam``\ and -\ ``Integrated Gradients``\ in a unified way. - -In this recipe you will learn how to use Captum to: \* attribute the -predictions of an image classifier to their corresponding image -features. \* visualize the attribution results. - - -Before you begin ----------------- - - -Make sure Captum is installed in your active Python environment. Captum -is available both on GitHub, as a ``pip`` package, or as a ``conda`` -package. For detailed instructions, consult the installation guide at -https://captum.ai/ - - -For a model, we use a built-in image classifier in PyTorch. Captum can -reveal which parts of a sample image support certain predictions made by -the model. - - - -.. code-block:: default - - - import torchvision - from torchvision import transforms - from PIL import Image - import requests - from io import BytesIO - - model = torchvision.models.resnet18(pretrained=True).eval() - - response = requests.get("https://image.freepik.com/free-photo/two-beautiful-puppies-cat-dog_58409-6024.jpg") - img = Image.open(BytesIO(response.content)) - - center_crop = transforms.Compose([ - transforms.Resize(256), - transforms.CenterCrop(224), - ]) - - normalize = transforms.Compose([ - transforms.ToTensor(), # converts the image to a tensor with values between 0 and 1 - transforms.Normalize( # normalize to follow 0-centered imagenet pixel rgb distribution - mean=[0.485, 0.456, 0.406], - std=[0.229, 0.224, 0.225] - ) - ]) - input_img = normalize(center_crop(img)).unsqueeze(0) - - - -Computing Attribution ---------------------- - - -Among the top-3 predictions of the models are classes 208 and 283 which -correspond to dog and cat. - -Let us attribute each of these predictions to the corresponding part of -the input, using Captum’s \ ``Occlusion``\ algorithm. - - - -.. code-block:: default - - - from captum.attr import Occlusion - - occlusion = Occlusion(model) - - strides = (3, 9, 9) # smaller = more fine-grained attribution but slower - target=208, # Labrador index in ImageNet - sliding_window_shapes=(3,45, 45) # choose size enough to change object appearance - baselines = 0 # values to occlude the image with. 0 corresponds to gray - - attribution_dog = occlusion.attribute(input_img, - strides = strides, - target=target, - sliding_window_shapes=sliding_window_shapes, - baselines=baselines) - - - target=283, # Persian cat index in ImageNet - attribution_cat = occlusion.attribute(input_img, - strides = strides, - target=target, - sliding_window_shapes=sliding_window_shapes, - baselines=0) - - - -Besides ``Occlusion``, Captum features many algorithms such as -\ ``Integrated Gradients``\ , \ ``Deconvolution``\ , -\ ``GuidedBackprop``\ , \ ``Guided GradCam``\ , \ ``DeepLift``\ , and -\ ``GradientShap``\ . All of these algorithms are subclasses of -``Attribution`` which expects your model as a callable ``forward_func`` -upon initialization and has an ``attribute(...)`` method which returns -the attribution result in a unified format. - -Let us visualize the computed attribution results in case of images. - - -Visualizing the Results ------------------------ - - -Captum’s \ ``visualization``\ utility provides out-of-the-box methods -to visualize attribution results both for pictorial and for textual -inputs. - - - -.. code-block:: default - - - import numpy as np - from captum.attr import visualization as viz - - # Convert the compute attribution tensor into an image-like numpy array - attribution_dog = np.transpose(attribution_dog.squeeze().cpu().detach().numpy(), (1,2,0)) - - vis_types = ["heat_map", "original_image"] - vis_signs = ["all", "all"], # "positive", "negative", or "all" to show both - # positive attribution indicates that the presence of the area increases the prediction score - # negative attribution indicates distractor areas whose absence increases the score - - _ = viz.visualize_image_attr_multiple(attribution_dog, - center_crop(img), - vis_types, - vis_signs, - ["attribution for dog", "image"], - show_colorbar = True - ) - - - attribution_cat = np.transpose(attribution_cat.squeeze().cpu().detach().numpy(), (1,2,0)) - - _ = viz.visualize_image_attr_multiple(attribution_cat, - center_crop(img), - ["heat_map", "original_image"], - ["all", "all"], # positive/negative attribution or all - ["attribution for cat", "image"], - show_colorbar = True - ) - - - -If your data is textual, ``visualization.visualize_text()`` offers a -dedicated view to explore attribution on top of the input text. Find out -more at http://captum.ai/tutorials/IMDB_TorchText_Interpret - - -Final Notes ------------ - - -Captum can handle most model types in PyTorch across modalities -including vision, text, and more. With Captum you can: \* Attribute a -specific output to the model input as illustrated above. \* Attribute a -specific output to a hidden-layer neuron (see Captum API reference). \* -Attribute a hidden-layer neuron response to the model input (see Captum -API reference). - -For complete API of the supported methods and a list of tutorials, -consult our website http://captum.ai - -Another useful post by Gilbert Tanner: -https://gilberttanner.com/blog/interpreting-pytorch-models-with-captum - - -.. rst-class:: sphx-glr-timing - - **Total running time of the script:** ( 0 minutes 0.000 seconds) - - -.. _sphx_glr_download_recipes_recipes_Captum_Recipe.py: - - -.. only :: html - - .. container:: sphx-glr-footer - :class: sphx-glr-footer-example - - - - .. container:: sphx-glr-download - - :download:`Download Python source code: Captum_Recipe.py ` - - - - .. container:: sphx-glr-download - - :download:`Download Jupyter notebook: Captum_Recipe.ipynb ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/custom_dataset_transforms_loader.ipynb b/recipes/recipes/custom_dataset_transforms_loader.ipynb deleted file mode 100644 index 207d56dc868..00000000000 --- a/recipes/recipes/custom_dataset_transforms_loader.ipynb +++ /dev/null @@ -1,261 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\nDeveloping Custom PyTorch Dataloaders\n=====================================\n\nA significant amount of the effort applied to developing machine\nlearning algorithms is related to data preparation. PyTorch provides\nmany tools to make data loading easy and hopefully, makes your code more\nreadable. In this recipe, you will learn how to:\n\n 1. Create a custom dataset leveraging the PyTorch dataset APIs;\n 2. Create callable custom transforms that can be composable; and\n 3. Put these components together to create a custom dataloader.\n\nPlease note, to run this tutorial, ensure the following packages are\ninstalled:\n - ``scikit-image``: For image io and transforms\n - ``pandas``: For easier csv parsing\n\nAs a point of attribution, this recipe is based on the original tutorial\nfrom `Sasank Chilamkurthy `__ and was later\nedited by `Joe Spisak `__.\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Setup\n----------------------\nFirst let\u2019s import all of the needed libraries for this recipe.\n\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "from __future__ import print_function, division\nimport os\nimport torch\nimport pandas as pd\nfrom skimage import io, transform\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms, utils\n\n# Ignore warnings\nimport warnings\nwarnings.filterwarnings(\"ignore\")\n\nplt.ion() # interactive mode" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Part 1: The Dataset\n-------------------\n\n\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The dataset we are going to deal with is that of facial pose. Overall,\n68 different landmark points are annotated for each face.\n\nAs a next step, please download the dataset from\n`here `_ so that the\nimages are in a directory named \u2018data/faces/\u2019.\n\n**Note:** This dataset was actually generated by applying\n`dlib's pose estimation `_\non images from the imagenet dataset containing the \u2018face\u2019 tag.\n\n::\n\n !wget https://download.pytorch.org/tutorial/faces.zip\n !mkdir data/faces/\n import zipfile\n with zipfile.ZipFile(\"faces.zip\",\"r\") as zip_ref:\n zip_ref.extractall(\"/data/faces/\")\n %cd /data/faces/\n\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The dataset comes with a csv file with annotations which looks like\nthis:\n\n::\n\n image_name,part_0_x,part_0_y,part_1_x,part_1_y,part_2_x, ... ,part_67_x,part_67_y\n 0805personali01.jpg,27,83,27,98, ... 84,134\n 1084239450_e76e00b7e7.jpg,70,236,71,257, ... ,128,312\n\nLet\u2019s quickly read the CSV and get the annotations in an (N, 2) array\nwhere N is the number of landmarks.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "landmarks_frame = pd.read_csv('faces/face_landmarks.csv')\n\nn = 65\nimg_name = landmarks_frame.iloc[n, 0]\nlandmarks = landmarks_frame.iloc[n, 1:]\nlandmarks = np.asarray(landmarks)\nlandmarks = landmarks.astype('float').reshape(-1, 2)\n\nprint('Image name: {}'.format(img_name))\nprint('Landmarks shape: {}'.format(landmarks.shape))\nprint('First 4 Landmarks: {}'.format(landmarks[:4]))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "1.1 Write a simple helper function to show an image\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nNext let\u2019s write a simple helper function to show an image, its landmarks and use it to show a sample.\n\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "def show_landmarks(image, landmarks):\n \"\"\"Show image with landmarks\"\"\"\n plt.imshow(image)\n plt.scatter(landmarks[:, 0], landmarks[:, 1], s=10, marker='.', c='r')\n plt.pause(0.001) # pause a bit so that plots are updated\n\nplt.figure()\nshow_landmarks(io.imread(os.path.join('faces/', img_name)),\n landmarks)\nplt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "1.2 Create a dataset class\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nNow lets talk about the PyTorch dataset class\n\n\n\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "``torch.utils.data.Dataset`` is an abstract class representing a\ndataset. Your custom dataset should inherit ``Dataset`` and override the\nfollowing methods:\n\n- ``__len__`` so that ``len(dataset)`` returns the size of the dataset.\n- ``__getitem__`` to support indexing such that ``dataset[i]`` can be\n used to get $`i$`\u00a0th sample\n\nLet\u2019s create a dataset class for our face landmarks dataset. We will\nread the csv in ``__init__`` but leave the reading of images to\n``__getitem__``. This is memory efficient because all the images are not\nstored in the memory at once but read as required.\n\nHere we show a sample of our dataset in the forma of a dict\n``{'image': image, 'landmarks': landmarks}``. Our dataset will take an\noptional argument ``transform`` so that any required processing can be\napplied on the sample. We will see the usefulness of ``transform`` in\nanother recipe.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "class FaceLandmarksDataset(Dataset):\n \"\"\"Face Landmarks dataset.\"\"\"\n\n def __init__(self, csv_file, root_dir, transform=None):\n \"\"\"\n Args:\n csv_file (string): Path to the csv file with annotations.\n root_dir (string): Directory with all the images.\n transform (callable, optional): Optional transform to be applied\n on a sample.\n \"\"\"\n self.landmarks_frame = pd.read_csv(csv_file)\n self.root_dir = root_dir\n self.transform = transform\n\n def __len__(self):\n return len(self.landmarks_frame)\n\n def __getitem__(self, idx):\n if torch.is_tensor(idx):\n idx = idx.tolist()\n\n img_name = os.path.join(self.root_dir,\n self.landmarks_frame.iloc[idx, 0])\n image = io.imread(img_name)\n landmarks = self.landmarks_frame.iloc[idx, 1:]\n landmarks = np.array([landmarks])\n landmarks = landmarks.astype('float').reshape(-1, 2)\n sample = {'image': image, 'landmarks': landmarks}\n\n if self.transform:\n sample = self.transform(sample)\n\n return sample" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "1.3 Iterate through data samples\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next let\u2019s instantiate this class and iterate through the data samples.\nWe will print the sizes of first 4 samples and show their landmarks.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "face_dataset = FaceLandmarksDataset(csv_file='faces/face_landmarks.csv',\n root_dir='faces/')\n\nfig = plt.figure()\n\nfor i in range(len(face_dataset)):\n sample = face_dataset[i]\n\n print(i, sample['image'].shape, sample['landmarks'].shape)\n\n ax = plt.subplot(1, 4, i + 1)\n plt.tight_layout()\n ax.set_title('Sample #{}'.format(i))\n ax.axis('off')\n show_landmarks(**sample)\n\n if i == 3:\n plt.show()\n break" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Part 2: Data Tranformations\n---------------------------\n\n\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that we have a dataset to work with and have done some level of\ncustomization, we can move to creating custom transformations. In\ncomputer vision, these come in handy to help generalize algorithms and\nimprove accuracy. A suite of transformations used at training time is\ntypically referred to as data augmentation and is a common practice for\nmodern model development.\n\nOne issue common in handling datasets is that the samples may not all be\nthe same size. Most neural networks expect the images of a fixed size.\nTherefore, we will need to write some prepocessing code. Let\u2019s create\nthree transforms:\n\n- ``Rescale``: to scale the image\n- ``RandomCrop``: to crop from image randomly. This is data\n augmentation.\n- ``ToTensor``: to convert the numpy images to torch images (we need to\n swap axes).\n\nWe will write them as callable classes instead of simple functions so\nthat parameters of the transform need not be passed everytime it\u2019s\ncalled. For this, we just need to implement ``__call__`` method and if\nrequired, ``__init__`` method. We can then use a transform like this:\n\n::\n\n tsfm = Transform(params)\n transformed_sample = tsfm(sample)\n\nObserve below how these transforms had to be applied both on the image\nand landmarks.\n\n\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "2.1 Create callable classes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nLet\u2019s start with creating callable classes for each transform\n\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "class Rescale(object):\n \"\"\"Rescale the image in a sample to a given size.\n\n Args:\n output_size (tuple or int): Desired output size. If tuple, output is\n matched to output_size. If int, smaller of image edges is matched\n to output_size keeping aspect ratio the same.\n \"\"\"\n\n def __init__(self, output_size):\n assert isinstance(output_size, (int, tuple))\n self.output_size = output_size\n\n def __call__(self, sample):\n image, landmarks = sample['image'], sample['landmarks']\n\n h, w = image.shape[:2]\n if isinstance(self.output_size, int):\n if h > w:\n new_h, new_w = self.output_size * h / w, self.output_size\n else:\n new_h, new_w = self.output_size, self.output_size * w / h\n else:\n new_h, new_w = self.output_size\n\n new_h, new_w = int(new_h), int(new_w)\n\n img = transform.resize(image, (new_h, new_w))\n\n # h and w are swapped for landmarks because for images,\n # x and y axes are axis 1 and 0 respectively\n landmarks = landmarks * [new_w / w, new_h / h]\n\n return {'image': img, 'landmarks': landmarks}\n\n\nclass RandomCrop(object):\n \"\"\"Crop randomly the image in a sample.\n\n Args:\n output_size (tuple or int): Desired output size. If int, square crop\n is made.\n \"\"\"\n\n def __init__(self, output_size):\n assert isinstance(output_size, (int, tuple))\n if isinstance(output_size, int):\n self.output_size = (output_size, output_size)\n else:\n assert len(output_size) == 2\n self.output_size = output_size\n\n def __call__(self, sample):\n image, landmarks = sample['image'], sample['landmarks']\n\n h, w = image.shape[:2]\n new_h, new_w = self.output_size\n\n top = np.random.randint(0, h - new_h)\n left = np.random.randint(0, w - new_w)\n\n image = image[top: top + new_h,\n left: left + new_w]\n\n landmarks = landmarks - [left, top]\n\n return {'image': image, 'landmarks': landmarks}\n\n\nclass ToTensor(object):\n \"\"\"Convert ndarrays in sample to Tensors.\"\"\"\n\n def __call__(self, sample):\n image, landmarks = sample['image'], sample['landmarks']\n\n # swap color axis because\n # numpy image: H x W x C\n # torch image: C X H X W\n image = image.transpose((2, 0, 1))\n return {'image': torch.from_numpy(image),\n 'landmarks': torch.from_numpy(landmarks)}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "2.2 Compose transforms and apply to a sample\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nNext let\u2019s compose these transforms and apply to a sample\n\n\nLet\u2019s say we want to rescale the shorter side of the image to 256 and\nthen randomly crop a square of size 224 from it. i.e, we want to compose\n``Rescale`` and ``RandomCrop`` transforms.\n``torchvision.transforms.Compose`` is a simple callable class which\nallows us to do this.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "scale = Rescale(256)\ncrop = RandomCrop(128)\ncomposed = transforms.Compose([Rescale(256),\n RandomCrop(224)])\n\n# Apply each of the above transforms on sample.\nfig = plt.figure()\nsample = face_dataset[65]\nfor i, tsfrm in enumerate([scale, crop, composed]):\n transformed_sample = tsfrm(sample)\n\n ax = plt.subplot(1, 3, i + 1)\n plt.tight_layout()\n ax.set_title(type(tsfrm).__name__)\n show_landmarks(**transformed_sample)\n\nplt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "2.3 Iterate through the dataset\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nNext we will iterate through the dataset\n\n\nLet\u2019s put this all together to create a dataset with composed\ntransforms. To summarize, every time this dataset is sampled:\n\n- An image is read from the file on the fly\n- Transforms are applied on the read image\n- Since one of the transforms is random, data is augmentated on\n sampling\n\nWe can iterate over the created dataset with a ``for i in range`` loop\nas before.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "transformed_dataset = FaceLandmarksDataset(csv_file='faces/face_landmarks.csv',\n root_dir='faces/',\n transform=transforms.Compose([\n Rescale(256),\n RandomCrop(224),\n ToTensor()\n ]))\n\nfor i in range(len(transformed_dataset)):\n sample = transformed_dataset[i]\n\n print(i, sample['image'].size(), sample['landmarks'].size())\n\n if i == 3:\n break" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Part 3: The Dataloader\n----------------------\n\n\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "By operating on the dataset directly, we are losing out on a lot of\nfeatures by using a simple ``for`` loop to iterate over the data. In\nparticular, we are missing out on:\n\n- Batching the data\n- Shuffling the data\n- Load the data in parallel using ``multiprocessing`` workers.\n\n``torch.utils.data.DataLoader`` is an iterator which provides all these\nfeatures. Parameters used below should be clear. One parameter of\ninterest is ``collate_fn``. You can specify how exactly the samples need\nto be batched using ``collate_fn``. However, default collate should work\nfine for most use cases.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "dataloader = DataLoader(transformed_dataset, batch_size=4,\n shuffle=True, num_workers=4)\n\n\n# Helper function to show a batch\ndef show_landmarks_batch(sample_batched):\n \"\"\"Show image with landmarks for a batch of samples.\"\"\"\n images_batch, landmarks_batch = \\\n sample_batched['image'], sample_batched['landmarks']\n batch_size = len(images_batch)\n im_size = images_batch.size(2)\n\n grid = utils.make_grid(images_batch)\n plt.imshow(grid.numpy().transpose((1, 2, 0)))\n\n for i in range(batch_size):\n plt.scatter(landmarks_batch[i, :, 0].numpy() + i * im_size,\n landmarks_batch[i, :, 1].numpy(),\n s=10, marker='.', c='r')\n\n plt.title('Batch from dataloader')\n\nfor i_batch, sample_batched in enumerate(dataloader):\n print(i_batch, sample_batched['image'].size(),\n sample_batched['landmarks'].size())\n\n # observe 4th batch and stop.\n if i_batch == 3:\n plt.figure()\n show_landmarks_batch(sample_batched)\n plt.axis('off')\n plt.ioff()\n plt.show()\n break" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that you\u2019ve learned how to create a custom dataloader with PyTorch,\nwe recommend diving deeper into the docs and customizing your workflow\neven further. You can learn more in the ``torch.utils.data`` docs\n`here `__.\n\n\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.4" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/recipes/recipes/custom_dataset_transforms_loader.py b/recipes/recipes/custom_dataset_transforms_loader.py deleted file mode 100644 index 077dcee8001..00000000000 --- a/recipes/recipes/custom_dataset_transforms_loader.py +++ /dev/null @@ -1,480 +0,0 @@ -""" -Developing Custom PyTorch Dataloaders -===================================== - -A significant amount of the effort applied to developing machine -learning algorithms is related to data preparation. PyTorch provides -many tools to make data loading easy and hopefully, makes your code more -readable. In this recipe, you will learn how to: - - 1. Create a custom dataset leveraging the PyTorch dataset APIs; - 2. Create callable custom transforms that can be composable; and - 3. Put these components together to create a custom dataloader. - -Please note, to run this tutorial, ensure the following packages are -installed: - - ``scikit-image``: For image io and transforms - - ``pandas``: For easier csv parsing - -As a point of attribution, this recipe is based on the original tutorial -from `Sasank Chilamkurthy `__ and was later -edited by `Joe Spisak `__. -""" - - -###################################################################### -# Setup -# ---------------------- -# First let’s import all of the needed libraries for this recipe. -# -# - -from __future__ import print_function, division -import os -import torch -import pandas as pd -from skimage import io, transform -import numpy as np -import matplotlib.pyplot as plt -from torch.utils.data import Dataset, DataLoader -from torchvision import transforms, utils - -# Ignore warnings -import warnings -warnings.filterwarnings("ignore") - -plt.ion() # interactive mode - - -###################################################################### -# Part 1: The Dataset -# ------------------- -# - -###################################################################### -# The dataset we are going to deal with is that of facial pose. Overall, -# 68 different landmark points are annotated for each face. -# -# As a next step, please download the dataset from -# `here `_ so that the -# images are in a directory named ‘data/faces/’. -# -# **Note:** This dataset was actually generated by applying -# `dlib's pose estimation `_ -# on images from the imagenet dataset containing the ‘face’ tag. -# -# :: -# -# !wget https://download.pytorch.org/tutorial/faces.zip -# !mkdir data/faces/ -# import zipfile -# with zipfile.ZipFile("faces.zip","r") as zip_ref: -# zip_ref.extractall("/data/faces/") -# %cd /data/faces/ - - - -###################################################################### -# The dataset comes with a csv file with annotations which looks like -# this: -# -# :: -# -# image_name,part_0_x,part_0_y,part_1_x,part_1_y,part_2_x, ... ,part_67_x,part_67_y -# 0805personali01.jpg,27,83,27,98, ... 84,134 -# 1084239450_e76e00b7e7.jpg,70,236,71,257, ... ,128,312 -# -# Let’s quickly read the CSV and get the annotations in an (N, 2) array -# where N is the number of landmarks. -# - - -landmarks_frame = pd.read_csv('faces/face_landmarks.csv') - -n = 65 -img_name = landmarks_frame.iloc[n, 0] -landmarks = landmarks_frame.iloc[n, 1:] -landmarks = np.asarray(landmarks) -landmarks = landmarks.astype('float').reshape(-1, 2) - -print('Image name: {}'.format(img_name)) -print('Landmarks shape: {}'.format(landmarks.shape)) -print('First 4 Landmarks: {}'.format(landmarks[:4])) - -###################################################################### -# 1.1 Write a simple helper function to show an image -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# Next let’s write a simple helper function to show an image, its landmarks and use it to show a sample. -# -# - -def show_landmarks(image, landmarks): - """Show image with landmarks""" - plt.imshow(image) - plt.scatter(landmarks[:, 0], landmarks[:, 1], s=10, marker='.', c='r') - plt.pause(0.001) # pause a bit so that plots are updated - -plt.figure() -show_landmarks(io.imread(os.path.join('faces/', img_name)), - landmarks) -plt.show() - - -###################################################################### -# 1.2 Create a dataset class -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# Now lets talk about the PyTorch dataset class -# -# - - -###################################################################### -# ``torch.utils.data.Dataset`` is an abstract class representing a -# dataset. Your custom dataset should inherit ``Dataset`` and override the -# following methods: -# -# - ``__len__`` so that ``len(dataset)`` returns the size of the dataset. -# - ``__getitem__`` to support indexing such that ``dataset[i]`` can be -# used to get :math:``i`` th sample -# -# Let’s create a dataset class for our face landmarks dataset. We will -# read the csv in ``__init__`` but leave the reading of images to -# ``__getitem__``. This is memory efficient because all the images are not -# stored in the memory at once but read as required. -# -# Here we show a sample of our dataset in the forma of a dict -# ``{'image': image, 'landmarks': landmarks}``. Our dataset will take an -# optional argument ``transform`` so that any required processing can be -# applied on the sample. We will see the usefulness of ``transform`` in -# another recipe. -# - -class FaceLandmarksDataset(Dataset): - """Face Landmarks dataset.""" - - def __init__(self, csv_file, root_dir, transform=None): - """ - Args: - csv_file (string): Path to the csv file with annotations. - root_dir (string): Directory with all the images. - transform (callable, optional): Optional transform to be applied - on a sample. - """ - self.landmarks_frame = pd.read_csv(csv_file) - self.root_dir = root_dir - self.transform = transform - - def __len__(self): - return len(self.landmarks_frame) - - def __getitem__(self, idx): - if torch.is_tensor(idx): - idx = idx.tolist() - - img_name = os.path.join(self.root_dir, - self.landmarks_frame.iloc[idx, 0]) - image = io.imread(img_name) - landmarks = self.landmarks_frame.iloc[idx, 1:] - landmarks = np.array([landmarks]) - landmarks = landmarks.astype('float').reshape(-1, 2) - sample = {'image': image, 'landmarks': landmarks} - - if self.transform: - sample = self.transform(sample) - - return sample - - -###################################################################### -# 1.3 Iterate through data samples -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# - - -###################################################################### -# Next let’s instantiate this class and iterate through the data samples. -# We will print the sizes of first 4 samples and show their landmarks. -# - -face_dataset = FaceLandmarksDataset(csv_file='faces/face_landmarks.csv', - root_dir='faces/') - -fig = plt.figure() - -for i in range(len(face_dataset)): - sample = face_dataset[i] - - print(i, sample['image'].shape, sample['landmarks'].shape) - - ax = plt.subplot(1, 4, i + 1) - plt.tight_layout() - ax.set_title('Sample #{}'.format(i)) - ax.axis('off') - show_landmarks(**sample) - - if i == 3: - plt.show() - break - - -###################################################################### -# Part 2: Data Tranformations -# --------------------------- -# - - -###################################################################### -# Now that we have a dataset to work with and have done some level of -# customization, we can move to creating custom transformations. In -# computer vision, these come in handy to help generalize algorithms and -# improve accuracy. A suite of transformations used at training time is -# typically referred to as data augmentation and is a common practice for -# modern model development. -# -# One issue common in handling datasets is that the samples may not all be -# the same size. Most neural networks expect the images of a fixed size. -# Therefore, we will need to write some prepocessing code. Let’s create -# three transforms: -# -# - ``Rescale``: to scale the image -# - ``RandomCrop``: to crop from image randomly. This is data -# augmentation. -# - ``ToTensor``: to convert the numpy images to torch images (we need to -# swap axes). -# -# We will write them as callable classes instead of simple functions so -# that parameters of the transform need not be passed everytime it’s -# called. For this, we just need to implement ``__call__`` method and if -# required, ``__init__`` method. We can then use a transform like this: -# -# :: -# -# tsfm = Transform(params) -# transformed_sample = tsfm(sample) -# -# Observe below how these transforms had to be applied both on the image -# and landmarks. -# - - -###################################################################### -# 2.1 Create callable classes -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# Let’s start with creating callable classes for each transform -# -# - -class Rescale(object): - """Rescale the image in a sample to a given size. - - Args: - output_size (tuple or int): Desired output size. If tuple, output is - matched to output_size. If int, smaller of image edges is matched - to output_size keeping aspect ratio the same. - """ - - def __init__(self, output_size): - assert isinstance(output_size, (int, tuple)) - self.output_size = output_size - - def __call__(self, sample): - image, landmarks = sample['image'], sample['landmarks'] - - h, w = image.shape[:2] - if isinstance(self.output_size, int): - if h > w: - new_h, new_w = self.output_size * h / w, self.output_size - else: - new_h, new_w = self.output_size, self.output_size * w / h - else: - new_h, new_w = self.output_size - - new_h, new_w = int(new_h), int(new_w) - - img = transform.resize(image, (new_h, new_w)) - - # h and w are swapped for landmarks because for images, - # x and y axes are axis 1 and 0 respectively - landmarks = landmarks * [new_w / w, new_h / h] - - return {'image': img, 'landmarks': landmarks} - - -class RandomCrop(object): - """Crop randomly the image in a sample. - - Args: - output_size (tuple or int): Desired output size. If int, square crop - is made. - """ - - def __init__(self, output_size): - assert isinstance(output_size, (int, tuple)) - if isinstance(output_size, int): - self.output_size = (output_size, output_size) - else: - assert len(output_size) == 2 - self.output_size = output_size - - def __call__(self, sample): - image, landmarks = sample['image'], sample['landmarks'] - - h, w = image.shape[:2] - new_h, new_w = self.output_size - - top = np.random.randint(0, h - new_h) - left = np.random.randint(0, w - new_w) - - image = image[top: top + new_h, - left: left + new_w] - - landmarks = landmarks - [left, top] - - return {'image': image, 'landmarks': landmarks} - - -class ToTensor(object): - """Convert ndarrays in sample to Tensors.""" - - def __call__(self, sample): - image, landmarks = sample['image'], sample['landmarks'] - - # swap color axis because - # numpy image: H x W x C - # torch image: C X H X W - image = image.transpose((2, 0, 1)) - return {'image': torch.from_numpy(image), - 'landmarks': torch.from_numpy(landmarks)} - - -###################################################################### -# 2.2 Compose transforms and apply to a sample -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# Next let’s compose these transforms and apply to a sample -# -# -# Let’s say we want to rescale the shorter side of the image to 256 and -# then randomly crop a square of size 224 from it. i.e, we want to compose -# ``Rescale`` and ``RandomCrop`` transforms. -# ``torchvision.transforms.Compose`` is a simple callable class which -# allows us to do this. -# - -scale = Rescale(256) -crop = RandomCrop(128) -composed = transforms.Compose([Rescale(256), - RandomCrop(224)]) - -# Apply each of the above transforms on sample. -fig = plt.figure() -sample = face_dataset[65] -for i, tsfrm in enumerate([scale, crop, composed]): - transformed_sample = tsfrm(sample) - - ax = plt.subplot(1, 3, i + 1) - plt.tight_layout() - ax.set_title(type(tsfrm).__name__) - show_landmarks(**transformed_sample) - -plt.show() - - -###################################################################### -# 2.3 Iterate through the dataset -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# Next we will iterate through the dataset -# -# -# Let’s put this all together to create a dataset with composed -# transforms. To summarize, every time this dataset is sampled: -# -# - An image is read from the file on the fly -# - Transforms are applied on the read image -# - Since one of the transforms is random, data is augmentated on -# sampling -# -# We can iterate over the created dataset with a ``for i in range`` loop -# as before. -# - -transformed_dataset = FaceLandmarksDataset(csv_file='faces/face_landmarks.csv', - root_dir='faces/', - transform=transforms.Compose([ - Rescale(256), - RandomCrop(224), - ToTensor() - ])) - -for i in range(len(transformed_dataset)): - sample = transformed_dataset[i] - - print(i, sample['image'].size(), sample['landmarks'].size()) - - if i == 3: - break - - -###################################################################### -# Part 3: The Dataloader -# ---------------------- -# - - -###################################################################### -# By operating on the dataset directly, we are losing out on a lot of -# features by using a simple ``for`` loop to iterate over the data. In -# particular, we are missing out on: -# -# - Batching the data -# - Shuffling the data -# - Load the data in parallel using ``multiprocessing`` workers. -# -# ``torch.utils.data.DataLoader`` is an iterator which provides all these -# features. Parameters used below should be clear. One parameter of -# interest is ``collate_fn``. You can specify how exactly the samples need -# to be batched using ``collate_fn``. However, default collate should work -# fine for most use cases. -# - -dataloader = DataLoader(transformed_dataset, batch_size=4, - shuffle=True, num_workers=4) - - -# Helper function to show a batch -def show_landmarks_batch(sample_batched): - """Show image with landmarks for a batch of samples.""" - images_batch, landmarks_batch = \ - sample_batched['image'], sample_batched['landmarks'] - batch_size = len(images_batch) - im_size = images_batch.size(2) - - grid = utils.make_grid(images_batch) - plt.imshow(grid.numpy().transpose((1, 2, 0))) - - for i in range(batch_size): - plt.scatter(landmarks_batch[i, :, 0].numpy() + i * im_size, - landmarks_batch[i, :, 1].numpy(), - s=10, marker='.', c='r') - - plt.title('Batch from dataloader') - -for i_batch, sample_batched in enumerate(dataloader): - print(i_batch, sample_batched['image'].size(), - sample_batched['landmarks'].size()) - - # observe 4th batch and stop. - if i_batch == 3: - plt.figure() - show_landmarks_batch(sample_batched) - plt.axis('off') - plt.ioff() - plt.show() - break - - -###################################################################### -# Now that you’ve learned how to create a custom dataloader with PyTorch, -# we recommend diving deeper into the docs and customizing your workflow -# even further. You can learn more in the ``torch.utils.data`` docs -# `here `__. -# diff --git a/recipes/recipes/custom_dataset_transforms_loader.rst b/recipes/recipes/custom_dataset_transforms_loader.rst deleted file mode 100644 index 5122e4ad931..00000000000 --- a/recipes/recipes/custom_dataset_transforms_loader.rst +++ /dev/null @@ -1,539 +0,0 @@ -.. note:: - :class: sphx-glr-download-link-note - - Click :ref:`here ` to download the full example code -.. rst-class:: sphx-glr-example-title - -.. _sphx_glr_recipes_recipes_custom_dataset_transforms_loader.py: - - -Developing Custom PyTorch Dataloaders -===================================== - -A significant amount of the effort applied to developing machine -learning algorithms is related to data preparation. PyTorch provides -many tools to make data loading easy and hopefully, makes your code more -readable. In this recipe, you will learn how to: - - 1. Create a custom dataset leveraging the PyTorch dataset APIs; - 2. Create callable custom transforms that can be composable; and - 3. Put these components together to create a custom dataloader. - -Please note, to run this tutorial, ensure the following packages are -installed: - - ``scikit-image``: For image io and transforms - - ``pandas``: For easier csv parsing - -As a point of attribution, this recipe is based on the original tutorial -from `Sasank Chilamkurthy `__ and was later -edited by `Joe Spisak `__. -Setup ----------------------- -First let’s import all of the needed libraries for this recipe. - - - - -.. code-block:: default - - - from __future__ import print_function, division - import os - import torch - import pandas as pd - from skimage import io, transform - import numpy as np - import matplotlib.pyplot as plt - from torch.utils.data import Dataset, DataLoader - from torchvision import transforms, utils - - # Ignore warnings - import warnings - warnings.filterwarnings("ignore") - - plt.ion() # interactive mode - - - -Part 1: The Dataset -------------------- - - -The dataset we are going to deal with is that of facial pose. Overall, -68 different landmark points are annotated for each face. - -As a next step, please download the dataset from -`here `_ so that the -images are in a directory named ‘data/faces/’. - -**Note:** This dataset was actually generated by applying -`dlib's pose estimation `_ -on images from the imagenet dataset containing the ‘face’ tag. - -:: - - !wget https://download.pytorch.org/tutorial/faces.zip - !mkdir data/faces/ - import zipfile - with zipfile.ZipFile("faces.zip","r") as zip_ref: - zip_ref.extractall("/data/faces/") - %cd /data/faces/ - -The dataset comes with a csv file with annotations which looks like -this: - -:: - - image_name,part_0_x,part_0_y,part_1_x,part_1_y,part_2_x, ... ,part_67_x,part_67_y - 0805personali01.jpg,27,83,27,98, ... 84,134 - 1084239450_e76e00b7e7.jpg,70,236,71,257, ... ,128,312 - -Let’s quickly read the CSV and get the annotations in an (N, 2) array -where N is the number of landmarks. - - - -.. code-block:: default - - - - landmarks_frame = pd.read_csv('faces/face_landmarks.csv') - - n = 65 - img_name = landmarks_frame.iloc[n, 0] - landmarks = landmarks_frame.iloc[n, 1:] - landmarks = np.asarray(landmarks) - landmarks = landmarks.astype('float').reshape(-1, 2) - - print('Image name: {}'.format(img_name)) - print('Landmarks shape: {}'.format(landmarks.shape)) - print('First 4 Landmarks: {}'.format(landmarks[:4])) - - -1.1 Write a simple helper function to show an image -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Next let’s write a simple helper function to show an image, its landmarks and use it to show a sample. - - - - -.. code-block:: default - - - def show_landmarks(image, landmarks): - """Show image with landmarks""" - plt.imshow(image) - plt.scatter(landmarks[:, 0], landmarks[:, 1], s=10, marker='.', c='r') - plt.pause(0.001) # pause a bit so that plots are updated - - plt.figure() - show_landmarks(io.imread(os.path.join('faces/', img_name)), - landmarks) - plt.show() - - - -1.2 Create a dataset class -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Now lets talk about the PyTorch dataset class - - - -``torch.utils.data.Dataset`` is an abstract class representing a -dataset. Your custom dataset should inherit ``Dataset`` and override the -following methods: - -- ``__len__`` so that ``len(dataset)`` returns the size of the dataset. -- ``__getitem__`` to support indexing such that ``dataset[i]`` can be - used to get :math:``i`` th sample - -Let’s create a dataset class for our face landmarks dataset. We will -read the csv in ``__init__`` but leave the reading of images to -``__getitem__``. This is memory efficient because all the images are not -stored in the memory at once but read as required. - -Here we show a sample of our dataset in the forma of a dict -``{'image': image, 'landmarks': landmarks}``. Our dataset will take an -optional argument ``transform`` so that any required processing can be -applied on the sample. We will see the usefulness of ``transform`` in -another recipe. - - - -.. code-block:: default - - - class FaceLandmarksDataset(Dataset): - """Face Landmarks dataset.""" - - def __init__(self, csv_file, root_dir, transform=None): - """ - Args: - csv_file (string): Path to the csv file with annotations. - root_dir (string): Directory with all the images. - transform (callable, optional): Optional transform to be applied - on a sample. - """ - self.landmarks_frame = pd.read_csv(csv_file) - self.root_dir = root_dir - self.transform = transform - - def __len__(self): - return len(self.landmarks_frame) - - def __getitem__(self, idx): - if torch.is_tensor(idx): - idx = idx.tolist() - - img_name = os.path.join(self.root_dir, - self.landmarks_frame.iloc[idx, 0]) - image = io.imread(img_name) - landmarks = self.landmarks_frame.iloc[idx, 1:] - landmarks = np.array([landmarks]) - landmarks = landmarks.astype('float').reshape(-1, 2) - sample = {'image': image, 'landmarks': landmarks} - - if self.transform: - sample = self.transform(sample) - - return sample - - - -1.3 Iterate through data samples -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -Next let’s instantiate this class and iterate through the data samples. -We will print the sizes of first 4 samples and show their landmarks. - - - -.. code-block:: default - - - face_dataset = FaceLandmarksDataset(csv_file='faces/face_landmarks.csv', - root_dir='faces/') - - fig = plt.figure() - - for i in range(len(face_dataset)): - sample = face_dataset[i] - - print(i, sample['image'].shape, sample['landmarks'].shape) - - ax = plt.subplot(1, 4, i + 1) - plt.tight_layout() - ax.set_title('Sample #{}'.format(i)) - ax.axis('off') - show_landmarks(**sample) - - if i == 3: - plt.show() - break - - - -Part 2: Data Tranformations ---------------------------- - - -Now that we have a dataset to work with and have done some level of -customization, we can move to creating custom transformations. In -computer vision, these come in handy to help generalize algorithms and -improve accuracy. A suite of transformations used at training time is -typically referred to as data augmentation and is a common practice for -modern model development. - -One issue common in handling datasets is that the samples may not all be -the same size. Most neural networks expect the images of a fixed size. -Therefore, we will need to write some prepocessing code. Let’s create -three transforms: - -- ``Rescale``: to scale the image -- ``RandomCrop``: to crop from image randomly. This is data - augmentation. -- ``ToTensor``: to convert the numpy images to torch images (we need to - swap axes). - -We will write them as callable classes instead of simple functions so -that parameters of the transform need not be passed everytime it’s -called. For this, we just need to implement ``__call__`` method and if -required, ``__init__`` method. We can then use a transform like this: - -:: - - tsfm = Transform(params) - transformed_sample = tsfm(sample) - -Observe below how these transforms had to be applied both on the image -and landmarks. - - -2.1 Create callable classes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Let’s start with creating callable classes for each transform - - - - -.. code-block:: default - - - class Rescale(object): - """Rescale the image in a sample to a given size. - - Args: - output_size (tuple or int): Desired output size. If tuple, output is - matched to output_size. If int, smaller of image edges is matched - to output_size keeping aspect ratio the same. - """ - - def __init__(self, output_size): - assert isinstance(output_size, (int, tuple)) - self.output_size = output_size - - def __call__(self, sample): - image, landmarks = sample['image'], sample['landmarks'] - - h, w = image.shape[:2] - if isinstance(self.output_size, int): - if h > w: - new_h, new_w = self.output_size * h / w, self.output_size - else: - new_h, new_w = self.output_size, self.output_size * w / h - else: - new_h, new_w = self.output_size - - new_h, new_w = int(new_h), int(new_w) - - img = transform.resize(image, (new_h, new_w)) - - # h and w are swapped for landmarks because for images, - # x and y axes are axis 1 and 0 respectively - landmarks = landmarks * [new_w / w, new_h / h] - - return {'image': img, 'landmarks': landmarks} - - - class RandomCrop(object): - """Crop randomly the image in a sample. - - Args: - output_size (tuple or int): Desired output size. If int, square crop - is made. - """ - - def __init__(self, output_size): - assert isinstance(output_size, (int, tuple)) - if isinstance(output_size, int): - self.output_size = (output_size, output_size) - else: - assert len(output_size) == 2 - self.output_size = output_size - - def __call__(self, sample): - image, landmarks = sample['image'], sample['landmarks'] - - h, w = image.shape[:2] - new_h, new_w = self.output_size - - top = np.random.randint(0, h - new_h) - left = np.random.randint(0, w - new_w) - - image = image[top: top + new_h, - left: left + new_w] - - landmarks = landmarks - [left, top] - - return {'image': image, 'landmarks': landmarks} - - - class ToTensor(object): - """Convert ndarrays in sample to Tensors.""" - - def __call__(self, sample): - image, landmarks = sample['image'], sample['landmarks'] - - # swap color axis because - # numpy image: H x W x C - # torch image: C X H X W - image = image.transpose((2, 0, 1)) - return {'image': torch.from_numpy(image), - 'landmarks': torch.from_numpy(landmarks)} - - - -2.2 Compose transforms and apply to a sample -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Next let’s compose these transforms and apply to a sample - - -Let’s say we want to rescale the shorter side of the image to 256 and -then randomly crop a square of size 224 from it. i.e, we want to compose -``Rescale`` and ``RandomCrop`` transforms. -``torchvision.transforms.Compose`` is a simple callable class which -allows us to do this. - - - -.. code-block:: default - - - scale = Rescale(256) - crop = RandomCrop(128) - composed = transforms.Compose([Rescale(256), - RandomCrop(224)]) - - # Apply each of the above transforms on sample. - fig = plt.figure() - sample = face_dataset[65] - for i, tsfrm in enumerate([scale, crop, composed]): - transformed_sample = tsfrm(sample) - - ax = plt.subplot(1, 3, i + 1) - plt.tight_layout() - ax.set_title(type(tsfrm).__name__) - show_landmarks(**transformed_sample) - - plt.show() - - - -2.3 Iterate through the dataset -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Next we will iterate through the dataset - - -Let’s put this all together to create a dataset with composed -transforms. To summarize, every time this dataset is sampled: - -- An image is read from the file on the fly -- Transforms are applied on the read image -- Since one of the transforms is random, data is augmentated on - sampling - -We can iterate over the created dataset with a ``for i in range`` loop -as before. - - - -.. code-block:: default - - - transformed_dataset = FaceLandmarksDataset(csv_file='faces/face_landmarks.csv', - root_dir='faces/', - transform=transforms.Compose([ - Rescale(256), - RandomCrop(224), - ToTensor() - ])) - - for i in range(len(transformed_dataset)): - sample = transformed_dataset[i] - - print(i, sample['image'].size(), sample['landmarks'].size()) - - if i == 3: - break - - - -Part 3: The Dataloader ----------------------- - - -By operating on the dataset directly, we are losing out on a lot of -features by using a simple ``for`` loop to iterate over the data. In -particular, we are missing out on: - -- Batching the data -- Shuffling the data -- Load the data in parallel using ``multiprocessing`` workers. - -``torch.utils.data.DataLoader`` is an iterator which provides all these -features. Parameters used below should be clear. One parameter of -interest is ``collate_fn``. You can specify how exactly the samples need -to be batched using ``collate_fn``. However, default collate should work -fine for most use cases. - - - -.. code-block:: default - - - dataloader = DataLoader(transformed_dataset, batch_size=4, - shuffle=True, num_workers=4) - - - # Helper function to show a batch - def show_landmarks_batch(sample_batched): - """Show image with landmarks for a batch of samples.""" - images_batch, landmarks_batch = \ - sample_batched['image'], sample_batched['landmarks'] - batch_size = len(images_batch) - im_size = images_batch.size(2) - - grid = utils.make_grid(images_batch) - plt.imshow(grid.numpy().transpose((1, 2, 0))) - - for i in range(batch_size): - plt.scatter(landmarks_batch[i, :, 0].numpy() + i * im_size, - landmarks_batch[i, :, 1].numpy(), - s=10, marker='.', c='r') - - plt.title('Batch from dataloader') - - for i_batch, sample_batched in enumerate(dataloader): - print(i_batch, sample_batched['image'].size(), - sample_batched['landmarks'].size()) - - # observe 4th batch and stop. - if i_batch == 3: - plt.figure() - show_landmarks_batch(sample_batched) - plt.axis('off') - plt.ioff() - plt.show() - break - - - -Now that you’ve learned how to create a custom dataloader with PyTorch, -we recommend diving deeper into the docs and customizing your workflow -even further. You can learn more in the ``torch.utils.data`` docs -`here `__. - - - -.. rst-class:: sphx-glr-timing - - **Total running time of the script:** ( 0 minutes 0.000 seconds) - - -.. _sphx_glr_download_recipes_recipes_custom_dataset_transforms_loader.py: - - -.. only :: html - - .. container:: sphx-glr-footer - :class: sphx-glr-footer-example - - - - .. container:: sphx-glr-download - - :download:`Download Python source code: custom_dataset_transforms_loader.py ` - - - - .. container:: sphx-glr-download - - :download:`Download Jupyter notebook: custom_dataset_transforms_loader.ipynb ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/defining_a_neural_network.ipynb b/recipes/recipes/defining_a_neural_network.ipynb deleted file mode 100644 index 7709cfaf1c5..00000000000 --- a/recipes/recipes/defining_a_neural_network.ipynb +++ /dev/null @@ -1,122 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\nDefining a Neural Network in PyTorch\n====================================\nDeep learning uses artificial neural networks (models), which are\ncomputing systems that are composed of many layers of interconnected\nunits. By passing data through these interconnected units, a neural\nnetwork is able to learn how to approximate the computations required to\ntransform inputs into outputs. In PyTorch, neural networks can be\nconstructed using the ``torch.nn`` package.\n\nIntroduction\n------------\nPyTorch provides the elegantly designed modules and classes, including\n``torch.nn``, to help you create and train neural networks. An\n``nn.Module`` contains layers, and a method ``forward(input)`` that\nreturns the ``output``.\n\nIn this recipe, we will use ``torch.nn`` to define a neural network\nintended for the `MNIST\ndataset `__.\n\nSetup\n-----\nBefore we begin, we need to install ``torch`` if it isn\u2019t already\navailable.\n\n::\n\n pip install torchaudio\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Steps\n-----\n\n1. Import all necessary libraries for loading our data\n2. Define and intialize the neural network\n3. Specify how data will pass through your model\n4. [Optional] Pass data through your model to test\n\n1. Import necessary libraries for loading our data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor this recipe, we will use ``torch`` and its subsidiaries ``torch.nn``\nand ``torch.nn.functional``.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import torch\nimport torch.nn as nn\nimport torch.nn.functional as F" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "2. Define and intialize the neural network\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nOur network will recognize images. We will use a process built into\nPyTorch called convolution. Convolution adds each element of an image to\nits local neighbors, weighted by a kernel, or a small martrix, that\nhelps us extract certain features (like edge detection, sharpness,\nblurriness, etc.) from the input image.\n\nThere are two requirements for defining the ``Net`` class of your model.\nThe first is writing an ``__init__`` function that references\n``nn.Module``. This function is where you define the fully connected\nlayers in your neural network.\n\nUsing convolution, we will define our model to take 1 input image\nchannel, and output match our target of 10 labels representing numbers 0\nthrough 9. This algorithm is yours to create, we will follow a standard\nMNIST algorithm.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "class Net(nn.Module):\n def __init__(self):\n super(Net, self).__init__()\n\n # First 2D convolutional layer, taking in 1 input channel (image),\n # outputting 32 convolutional features, with a square kernel size of 3\n self.conv1 = nn.Conv2d(1, 32, 3, 1)\n # Second 2D convolutional layer, taking in the 32 input layers,\n # outputting 64 convolutional features, with a square kernel size of 3\n self.conv2 = nn.Conv2d(32, 64, 3, 1)\n\n # Designed to ensure that adjacent pixels are either all 0s or all active\n # with an input probability\n self.dropout1 = nn.Dropout2d(0.25)\n self.dropout2 = nn.Dropout2d(0.5)\n\n # First fully connected layer\n self.fc1 = nn.Linear(9216, 128)\n # Second fully connected layer that outputs our 10 labels\n self.fc2 = nn.Linear(128, 10)\n\nmy_nn = Net()\nprint(my_nn)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We have finished defining our neural network, now we have to define how\nour data will pass through it.\n\n3. Specify how data will pass through your model\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhen you use PyTorch to build a model, you just have to define the\n``forward`` function, that will pass the data into the computation graph\n(i.e. our neural network). This will represent our feed-forward\nalgorithm.\n\nYou can use any of the Tensor operations in the ``forward`` function.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "class Net(nn.Module):\n def __init__(self):\n super(Net, self).__init__()\n self.conv1 = nn.Conv2d(1, 32, 3, 1)\n self.conv2 = nn.Conv2d(32, 64, 3, 1)\n self.dropout1 = nn.Dropout2d(0.25)\n self.dropout2 = nn.Dropout2d(0.5)\n self.fc1 = nn.Linear(9216, 128)\n self.fc2 = nn.Linear(128, 10)\n\n # x represents our data\n def forward(self, x):\n # Pass data through conv1\n x = self.conv1(x)\n # Use the rectified-linear activation function over x\n x = F.relu(x)\n\n x = self.conv2(x)\n x = F.relu(x)\n\n # Run max pooling over x\n x = F.max_pool2d(x, 2)\n # Pass data through dropout1\n x = self.dropout1(x)\n # Flatten x with start_dim=1\n x = torch.flatten(x, 1)\n # Pass data through fc1\n x = self.fc1(x)\n x = F.relu(x)\n x = self.dropout2(x)\n x = self.fc2(x)\n\n # Apply softmax to x \n output = F.log_softmax(x, dim=1)\n return output" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "4. [Optional] Pass data through your model to test\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nTo ensure we receive our desired output, let\u2019s test our model by passing\nsome random data through it.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# Equates to one random 28x28 image\nrandom_data = torch.rand((1, 1, 28, 28))\n\nmy_nn = Net()\nresult = my_nn(random_data)\nprint (result)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Each number in this resulting tensor equates to the prediction of the\nlabel the random tensor is associated to.\n\nCongratulations! You have successfully defined a neural network in\nPyTorch.\n\nLearn More\n----------\n\nTake a look at these other recipes to continue your learning:\n\n- `What is a state_dict in PyTorch `__\n- `Saving and loading models for inference in PyTorch `__\n\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.4" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/recipes/recipes/defining_a_neural_network.py b/recipes/recipes/defining_a_neural_network.py deleted file mode 100644 index 42e2d3370ca..00000000000 --- a/recipes/recipes/defining_a_neural_network.py +++ /dev/null @@ -1,183 +0,0 @@ -""" -Defining a Neural Network in PyTorch -==================================== -Deep learning uses artificial neural networks (models), which are -computing systems that are composed of many layers of interconnected -units. By passing data through these interconnected units, a neural -network is able to learn how to approximate the computations required to -transform inputs into outputs. In PyTorch, neural networks can be -constructed using the ``torch.nn`` package. - -Introduction ------------- -PyTorch provides the elegantly designed modules and classes, including -``torch.nn``, to help you create and train neural networks. An -``nn.Module`` contains layers, and a method ``forward(input)`` that -returns the ``output``. - -In this recipe, we will use ``torch.nn`` to define a neural network -intended for the `MNIST -dataset `__. - -Setup ------ -Before we begin, we need to install ``torch`` if it isn’t already -available. - -:: - - pip install torchaudio - - -""" - - -###################################################################### -# Steps -# ----- -# -# 1. Import all necessary libraries for loading our data -# 2. Define and intialize the neural network -# 3. Specify how data will pass through your model -# 4. [Optional] Pass data through your model to test -# -# 1. Import necessary libraries for loading our data -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` -# and ``torch.nn.functional``. -# - -import torch -import torch.nn as nn -import torch.nn.functional as F - - -###################################################################### -# 2. Define and intialize the neural network -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Our network will recognize images. We will use a process built into -# PyTorch called convolution. Convolution adds each element of an image to -# its local neighbors, weighted by a kernel, or a small martrix, that -# helps us extract certain features (like edge detection, sharpness, -# blurriness, etc.) from the input image. -# -# There are two requirements for defining the ``Net`` class of your model. -# The first is writing an ``__init__`` function that references -# ``nn.Module``. This function is where you define the fully connected -# layers in your neural network. -# -# Using convolution, we will define our model to take 1 input image -# channel, and output match our target of 10 labels representing numbers 0 -# through 9. This algorithm is yours to create, we will follow a standard -# MNIST algorithm. -# - -class Net(nn.Module): - def __init__(self): - super(Net, self).__init__() - - # First 2D convolutional layer, taking in 1 input channel (image), - # outputting 32 convolutional features, with a square kernel size of 3 - self.conv1 = nn.Conv2d(1, 32, 3, 1) - # Second 2D convolutional layer, taking in the 32 input layers, - # outputting 64 convolutional features, with a square kernel size of 3 - self.conv2 = nn.Conv2d(32, 64, 3, 1) - - # Designed to ensure that adjacent pixels are either all 0s or all active - # with an input probability - self.dropout1 = nn.Dropout2d(0.25) - self.dropout2 = nn.Dropout2d(0.5) - - # First fully connected layer - self.fc1 = nn.Linear(9216, 128) - # Second fully connected layer that outputs our 10 labels - self.fc2 = nn.Linear(128, 10) - -my_nn = Net() -print(my_nn) - - -###################################################################### -# We have finished defining our neural network, now we have to define how -# our data will pass through it. -# -# 3. Specify how data will pass through your model -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# When you use PyTorch to build a model, you just have to define the -# ``forward`` function, that will pass the data into the computation graph -# (i.e. our neural network). This will represent our feed-forward -# algorithm. -# -# You can use any of the Tensor operations in the ``forward`` function. -# - -class Net(nn.Module): - def __init__(self): - super(Net, self).__init__() - self.conv1 = nn.Conv2d(1, 32, 3, 1) - self.conv2 = nn.Conv2d(32, 64, 3, 1) - self.dropout1 = nn.Dropout2d(0.25) - self.dropout2 = nn.Dropout2d(0.5) - self.fc1 = nn.Linear(9216, 128) - self.fc2 = nn.Linear(128, 10) - - # x represents our data - def forward(self, x): - # Pass data through conv1 - x = self.conv1(x) - # Use the rectified-linear activation function over x - x = F.relu(x) - - x = self.conv2(x) - x = F.relu(x) - - # Run max pooling over x - x = F.max_pool2d(x, 2) - # Pass data through dropout1 - x = self.dropout1(x) - # Flatten x with start_dim=1 - x = torch.flatten(x, 1) - # Pass data through fc1 - x = self.fc1(x) - x = F.relu(x) - x = self.dropout2(x) - x = self.fc2(x) - - # Apply softmax to x - output = F.log_softmax(x, dim=1) - return output - - -###################################################################### -# 4. [Optional] Pass data through your model to test -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# To ensure we receive our desired output, let’s test our model by passing -# some random data through it. -# - -# Equates to one random 28x28 image -random_data = torch.rand((1, 1, 28, 28)) - -my_nn = Net() -result = my_nn(random_data) -print (result) - - -###################################################################### -# Each number in this resulting tensor equates to the prediction of the -# label the random tensor is associated to. -# -# Congratulations! You have successfully defined a neural network in -# PyTorch. -# -# Learn More -# ---------- -# -# Take a look at these other recipes to continue your learning: -# -# - `What is a state_dict in PyTorch `__ -# - `Saving and loading models for inference in PyTorch `__ diff --git a/recipes/recipes/defining_a_neural_network.rst b/recipes/recipes/defining_a_neural_network.rst deleted file mode 100644 index 6097b49d295..00000000000 --- a/recipes/recipes/defining_a_neural_network.rst +++ /dev/null @@ -1,234 +0,0 @@ -.. note:: - :class: sphx-glr-download-link-note - - Click :ref:`here ` to download the full example code -.. rst-class:: sphx-glr-example-title - -.. _sphx_glr_recipes_recipes_defining_a_neural_network.py: - - -Defining a Neural Network in PyTorch -==================================== -Deep learning uses artificial neural networks (models), which are -computing systems that are composed of many layers of interconnected -units. By passing data through these interconnected units, a neural -network is able to learn how to approximate the computations required to -transform inputs into outputs. In PyTorch, neural networks can be -constructed using the ``torch.nn`` package. - -Introduction ------------- -PyTorch provides the elegantly designed modules and classes, including -``torch.nn``, to help you create and train neural networks. An -``nn.Module`` contains layers, and a method ``forward(input)`` that -returns the ``output``. - -In this recipe, we will use ``torch.nn`` to define a neural network -intended for the `MNIST -dataset `__. - -Setup ------ -Before we begin, we need to install ``torch`` if it isn’t already -available. - -:: - - pip install torchaudio -Steps ------ - -1. Import all necessary libraries for loading our data -2. Define and intialize the neural network -3. Specify how data will pass through your model -4. [Optional] Pass data through your model to test - -1. Import necessary libraries for loading our data -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` -and ``torch.nn.functional``. - - - -.. code-block:: default - - - import torch - import torch.nn as nn - import torch.nn.functional as F - - - -2. Define and intialize the neural network -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Our network will recognize images. We will use a process built into -PyTorch called convolution. Convolution adds each element of an image to -its local neighbors, weighted by a kernel, or a small martrix, that -helps us extract certain features (like edge detection, sharpness, -blurriness, etc.) from the input image. - -There are two requirements for defining the ``Net`` class of your model. -The first is writing an ``__init__`` function that references -``nn.Module``. This function is where you define the fully connected -layers in your neural network. - -Using convolution, we will define our model to take 1 input image -channel, and output match our target of 10 labels representing numbers 0 -through 9. This algorithm is yours to create, we will follow a standard -MNIST algorithm. - - - -.. code-block:: default - - - class Net(nn.Module): - def __init__(self): - super(Net, self).__init__() - - # First 2D convolutional layer, taking in 1 input channel (image), - # outputting 32 convolutional features, with a square kernel size of 3 - self.conv1 = nn.Conv2d(1, 32, 3, 1) - # Second 2D convolutional layer, taking in the 32 input layers, - # outputting 64 convolutional features, with a square kernel size of 3 - self.conv2 = nn.Conv2d(32, 64, 3, 1) - - # Designed to ensure that adjacent pixels are either all 0s or all active - # with an input probability - self.dropout1 = nn.Dropout2d(0.25) - self.dropout2 = nn.Dropout2d(0.5) - - # First fully connected layer - self.fc1 = nn.Linear(9216, 128) - # Second fully connected layer that outputs our 10 labels - self.fc2 = nn.Linear(128, 10) - - my_nn = Net() - print(my_nn) - - - -We have finished defining our neural network, now we have to define how -our data will pass through it. - -3. Specify how data will pass through your model -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When you use PyTorch to build a model, you just have to define the -``forward`` function, that will pass the data into the computation graph -(i.e. our neural network). This will represent our feed-forward -algorithm. - -You can use any of the Tensor operations in the ``forward`` function. - - - -.. code-block:: default - - - class Net(nn.Module): - def __init__(self): - super(Net, self).__init__() - self.conv1 = nn.Conv2d(1, 32, 3, 1) - self.conv2 = nn.Conv2d(32, 64, 3, 1) - self.dropout1 = nn.Dropout2d(0.25) - self.dropout2 = nn.Dropout2d(0.5) - self.fc1 = nn.Linear(9216, 128) - self.fc2 = nn.Linear(128, 10) - - # x represents our data - def forward(self, x): - # Pass data through conv1 - x = self.conv1(x) - # Use the rectified-linear activation function over x - x = F.relu(x) - - x = self.conv2(x) - x = F.relu(x) - - # Run max pooling over x - x = F.max_pool2d(x, 2) - # Pass data through dropout1 - x = self.dropout1(x) - # Flatten x with start_dim=1 - x = torch.flatten(x, 1) - # Pass data through fc1 - x = self.fc1(x) - x = F.relu(x) - x = self.dropout2(x) - x = self.fc2(x) - - # Apply softmax to x - output = F.log_softmax(x, dim=1) - return output - - - -4. [Optional] Pass data through your model to test -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To ensure we receive our desired output, let’s test our model by passing -some random data through it. - - - -.. code-block:: default - - - # Equates to one random 28x28 image - random_data = torch.rand((1, 1, 28, 28)) - - my_nn = Net() - result = my_nn(random_data) - print (result) - - - -Each number in this resulting tensor equates to the prediction of the -label the random tensor is associated to. - -Congratulations! You have successfully defined a neural network in -PyTorch. - -Learn More ----------- - -Take a look at these other recipes to continue your learning: - -- `What is a state_dict in PyTorch `__ -- `Saving and loading models for inference in PyTorch `__ - - -.. rst-class:: sphx-glr-timing - - **Total running time of the script:** ( 0 minutes 0.000 seconds) - - -.. _sphx_glr_download_recipes_recipes_defining_a_neural_network.py: - - -.. only :: html - - .. container:: sphx-glr-footer - :class: sphx-glr-footer-example - - - - .. container:: sphx-glr-download - - :download:`Download Python source code: defining_a_neural_network.py ` - - - - .. container:: sphx-glr-download - - :download:`Download Jupyter notebook: defining_a_neural_network.ipynb ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/dynamic_quantization.ipynb b/recipes/recipes/dynamic_quantization.ipynb deleted file mode 100644 index f341da5275a..00000000000 --- a/recipes/recipes/dynamic_quantization.ipynb +++ /dev/null @@ -1,133 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\nDynamic Quantization\n====================\n\nIn this recipe you will see how to take advantage of Dynamic\nQuantization to accelerate inference on an LSTM-style recurrent neural\nnetwork. This reduces the size of the model weights and speeds up model\nexecution.\n\nIntroduction\n-------------\n\nThere are a number of trade-offs that can be made when designing neural\nnetworks. During model developmenet and training you can alter the\nnumber of layers and number of parameters in a recurrent neural network\nand trade-off accuracy against model size and/or model latency or\nthroughput. Such changes can take lot of time and compute resources\nbecause you are iterating over the model training. Quantization gives\nyou a way to make a similar trade off between performance and model\naccuracy with a known model after training is completed.\n\nYou can give it a try in a single session and you will certainly reduce\nyour model size significantly and may get a significant latency\nreduction without losing a lot of accuracy.\n\nWhat is dynamic quantization?\n-------------\n\nQuantizing a network means converting it to use a reduced precision\ninteger representation for the weights and/or activations. This saves on\nmodel size and allows the use of higher throughput math operations on\nyour CPU or GPU.\n\nWhen converting from floating point to integer values you are\nessentially multiplying the floating point value by some scale factor\nand rounding the result to a whole number. The various quantization\napproaches differ in the way they approach determining that scale\nfactor.\n\nThe key idea with dynamic quantization as described here is that we are\ngoing to determine the scale factor for activations dynamically based on\nthe data range observed at runtime. This ensures that the scale factor\nis \"tuned\" so that as much signal as possible about each observed\ndataset is preserved.\n\nThe model parameters on the other hand are known during model conversion\nand they are converted ahead of time and stored in INT8 form.\n\nArithmetic in the quantized model is done using vectorized INT8\ninstructions. Accumulation is typically done with INT16 or INT32 to\navoid overflow. This higher precision value is scaled back to INT8 if\nthe next layer is quantized or converted to FP32 for output.\n\nDynamic quantization is relatively free of tuning parameters which makes\nit well suited to be added into production pipelines as a standard part\nof converting LSTM models to deployment.\n\n\n\n

Note

Limitations on the approach taken here\n\n\n This recipe provides a quick introduction to the dynamic quantization\n features in PyTorch and the workflow for using it. Our focus is on\n explaining the specific functions used to convert the model. We will\n make a number of significant simplifications in the interest of brevity\n and clarity

\n\n\n1. You will start with a minimal LSTM network\n2. You are simply going to initialize the network with a random hidden\n state\n3. You are going to test the network with random inputs\n4. You are not going to train the network in this tutorial\n5. You will see that the quantized form of this network is smaller and\n runs faster than the floating point network we started with\n6. You will see that the output values are generally in the same\n ballpark as the output of the FP32 network, but we are not\n demonstrating here the expected accuracy loss on a real trained\n network\n\nYou will see how dynamic quantization is done and be able to see\nsuggestive reductions in memory use and latency times. Providing a\ndemonstration that the technique can preserve high levels of model\naccuracy on a trained LSTM is left to a more advanced tutorial. If you\nwant to move right away to that more rigorous treatment please proceed\nto the `advanced dynamic quantization\ntutorial `__.\n\nSteps\n-------------\n\nThis recipe has 5 steps.\n\n1. Set Up - Here you define a very simple LSTM, import modules, and establish\n some random input tensors.\n\n2. Do the Quantization - Here you instantiate a floating point model and then create quantized\n version of it.\n\n3. Look at Model Size - Here you show that the model size gets smaller.\n\n4. Look at Latency - Here you run the two models and compare model runtime (latency).\n\n5. Look at Accuracy - Here you run the two models and compare outputs.\n\n\n1: Set Up\n~~~~~~~~~~~~~~~\nThis is a straightfoward bit of code to set up for the rest of the\nrecipe.\n\nThe unique module we are importing here is torch.quantization which\nincludes PyTorch's quantized operators and conversion functions. We also\ndefine a very simple LSTM model and set up some inputs.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# import the modules used here in this recipe\nimport torch\nimport torch.quantization\nimport torch.nn as nn\nimport copy\nimport os\nimport time\n\n# define a very, very simple LSTM for demonstration purposes\n# in this case, we are wrapping nn.LSTM, one layer, no pre or post processing\n# inspired by \n# https://pytorch.org/tutorials/beginner/nlp/sequence_models_tutorial.html, by Robert Guthrie\n# and https://pytorch.org/tutorials/advanced/dynamic_quantization_tutorial.html\nclass lstm_for_demonstration(nn.Module):\n \"\"\"Elementary Long Short Term Memory style model which simply wraps nn.LSTM\n Not to be used for anything other than demonstration. \n \"\"\" \n def __init__(self,in_dim,out_dim,depth):\n super(lstm_for_demonstration,self).__init__()\n self.lstm = nn.LSTM(in_dim,out_dim,depth)\n\n def forward(self,inputs,hidden):\n out,hidden = self.lstm(inputs,hidden)\n return out, hidden\n\n \ntorch.manual_seed(29592) # set the seed for reproducibility\n\n#shape parameters\nmodel_dimension=8\nsequence_length=20\nbatch_size=1\nlstm_depth=1\n\n# random data for input\ninputs = torch.randn(sequence_length,batch_size,model_dimension)\n# hidden is actually is a tuple of the initial hidden state and the initial cell state\nhidden = (torch.randn(lstm_depth,batch_size,model_dimension), torch.randn(lstm_depth,batch_size,model_dimension))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "2: Do the Quantization\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nNow we get to the fun part. First we create an instance of the model\ncalled float\\_lstm then we are going to quantize it. We're going to use\nthe\n\n::\n\n torch.quantization.quantize_dynamic()\n\nfunction here (`see\ndocumentation `__)\nwhich takes the model, then a list of the submodules which we want to\nhave quantized if they appear, then the datatype we are targeting. This\nfunction returns a quantized version of the original model as a new\nmodule.\n\nThat's all it takes.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# here is our floating point instance \nfloat_lstm = lstm_for_demonstration(model_dimension, model_dimension,lstm_depth)\n\n# this is the call that does the work\nquantized_lstm = torch.quantization.quantize_dynamic(\n float_lstm, {nn.LSTM, nn.Linear}, dtype=torch.qint8\n)\n\n# show the changes that were made\nprint('Here is the floating point version of this module:')\nprint(float_lstm)\nprint('')\nprint('and now the quantized version:')\nprint(quantized_lstm)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "3. Look at Model Size\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nOk, so we've quantized the model. What does that get us? Well the first\nbenefit is that we've replaced the FP32 model parameters with INT8\nvalues (and some recorded scale factors). This means about 75% less data\nto store and move around. With the default values the reduction shown\nbelow will be less than 75% but if you increase the model size above\n(for example you can set model dimension to something like 80) this will\nconverge towards 4x smaller as the stored model size dominated more and\nmore by the parameter values.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "def print_size_of_model(model, label=\"\"):\n torch.save(model.state_dict(), \"temp.p\")\n size=os.path.getsize(\"temp.p\")\n print(\"model: \",label,' \\t','Size (KB):', size/1e3)\n os.remove('temp.p')\n return size\n\n# compare the sizes\nf=print_size_of_model(float_lstm,\"fp32\")\nq=print_size_of_model(quantized_lstm,\"int8\")\nprint(\"{0:.2f} times smaller\".format(f/q))\n\n# note that this value is wrong in PyTorch 1.4 due to https://github.com/pytorch/pytorch/issues/31468\n# this will be fixed in 1.5 with https://github.com/pytorch/pytorch/pull/31540" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "4. Look at Latency\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nThe second benefit is that the quantized model will typically run\nfaster. This is due to a combinations of effects including at least:\n\n1. Less time spent moving parameter data in\n2. Faster INT8 operations\n\nAs you will see the quantized version of this super-simple network runs\nfaster. This will generally be true of more complex networks but as they\nsay \"your milage may vary\" depending on a number of factors including\nthe structure of the model and the hardware you are running on.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# compare the performance\nprint(\"Floating point FP32\")\n# %timeit float_lstm.forward(inputs, hidden)\n\nprint(\"Quantized INT8\")\n# %timeit quantized_lstm.forward(inputs,hidden)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "5: Look at Accuracy\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nWe are not going to do a careful look at accuracy here because we are\nworking with a randomly initialized network rather than a properly\ntrained one. However, I think it is worth quickly showing that the\nquantized network does produce output tensors that are \"in the same\nballpark\" as the original one.\n\nFor a more detailed analysis please see the more advanced tutorials\nreferenced at the end of this recipe.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# run the float model\nout1, hidden1 = float_lstm(inputs, hidden)\nmag1 = torch.mean(abs(out1)).item()\nprint('mean absolute value of output tensor values in the FP32 model is {0:.5f} '.format(mag1))\n\n# run the quantized model\nout2, hidden2 = quantized_lstm(inputs, hidden)\nmag2 = torch.mean(abs(out2)).item()\nprint('mean absolute value of output tensor values in the INT8 model is {0:.5f}'.format(mag2))\n\n# compare them \nmag3 = torch.mean(abs(out1-out2)).item()\nprint('mean absolute value of the difference between the output tensors is {0:.5f} or {1:.2f} percent'.format(mag3,mag3/mag1*100))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Learn More\n------------\nWe've explained what dynamic quantization is, what benefits it brings,\nand you have used the ``torch.quantization.quantize_dynamic()`` function\nto quickly quantize a simple LSTM model.\n\nThis was a fast and high level treatment of this material; for more\ndetail please continue learning with `(experimental) Dynamic Quantization on an LSTM Word Language Model Tutorial `_.\n\n\nAdditional Resources\n=========\nDocumentation\n~~~~~~~~~~~~~~\n\n`Quantization API Documentaion `_\n\nTutorials\n~~~~~~~~~~~~~~\n\n`(experimental) Dynamic Quantization on BERT `_\n\n`(experimental) Dynamic Quantization on an LSTM Word Language Model `_\n\nBlogs\n~~~~~~~~~~~~~~\n` Introduction to Quantization on PyTorch `_\n\n\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.4" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/recipes/recipes/dynamic_quantization.py b/recipes/recipes/dynamic_quantization.py deleted file mode 100644 index 78dc1f5408a..00000000000 --- a/recipes/recipes/dynamic_quantization.py +++ /dev/null @@ -1,306 +0,0 @@ -""" -Dynamic Quantization -==================== - -In this recipe you will see how to take advantage of Dynamic -Quantization to accelerate inference on an LSTM-style recurrent neural -network. This reduces the size of the model weights and speeds up model -execution. - -Introduction -------------- - -There are a number of trade-offs that can be made when designing neural -networks. During model developmenet and training you can alter the -number of layers and number of parameters in a recurrent neural network -and trade-off accuracy against model size and/or model latency or -throughput. Such changes can take lot of time and compute resources -because you are iterating over the model training. Quantization gives -you a way to make a similar trade off between performance and model -accuracy with a known model after training is completed. - -You can give it a try in a single session and you will certainly reduce -your model size significantly and may get a significant latency -reduction without losing a lot of accuracy. - -What is dynamic quantization? -------------- - -Quantizing a network means converting it to use a reduced precision -integer representation for the weights and/or activations. This saves on -model size and allows the use of higher throughput math operations on -your CPU or GPU. - -When converting from floating point to integer values you are -essentially multiplying the floating point value by some scale factor -and rounding the result to a whole number. The various quantization -approaches differ in the way they approach determining that scale -factor. - -The key idea with dynamic quantization as described here is that we are -going to determine the scale factor for activations dynamically based on -the data range observed at runtime. This ensures that the scale factor -is "tuned" so that as much signal as possible about each observed -dataset is preserved. - -The model parameters on the other hand are known during model conversion -and they are converted ahead of time and stored in INT8 form. - -Arithmetic in the quantized model is done using vectorized INT8 -instructions. Accumulation is typically done with INT16 or INT32 to -avoid overflow. This higher precision value is scaled back to INT8 if -the next layer is quantized or converted to FP32 for output. - -Dynamic quantization is relatively free of tuning parameters which makes -it well suited to be added into production pipelines as a standard part -of converting LSTM models to deployment. - - - -.. note:: - Limitations on the approach taken here - - - This recipe provides a quick introduction to the dynamic quantization - features in PyTorch and the workflow for using it. Our focus is on - explaining the specific functions used to convert the model. We will - make a number of significant simplifications in the interest of brevity - and clarity - - -1. You will start with a minimal LSTM network -2. You are simply going to initialize the network with a random hidden - state -3. You are going to test the network with random inputs -4. You are not going to train the network in this tutorial -5. You will see that the quantized form of this network is smaller and - runs faster than the floating point network we started with -6. You will see that the output values are generally in the same - ballpark as the output of the FP32 network, but we are not - demonstrating here the expected accuracy loss on a real trained - network - -You will see how dynamic quantization is done and be able to see -suggestive reductions in memory use and latency times. Providing a -demonstration that the technique can preserve high levels of model -accuracy on a trained LSTM is left to a more advanced tutorial. If you -want to move right away to that more rigorous treatment please proceed -to the `advanced dynamic quantization -tutorial `__. - -Steps -------------- - -This recipe has 5 steps. - -1. Set Up - Here you define a very simple LSTM, import modules, and establish - some random input tensors. - -2. Do the Quantization - Here you instantiate a floating point model and then create quantized - version of it. - -3. Look at Model Size - Here you show that the model size gets smaller. - -4. Look at Latency - Here you run the two models and compare model runtime (latency). - -5. Look at Accuracy - Here you run the two models and compare outputs. - - -1: Set Up -~~~~~~~~~~~~~~~ -This is a straightfoward bit of code to set up for the rest of the -recipe. - -The unique module we are importing here is torch.quantization which -includes PyTorch's quantized operators and conversion functions. We also -define a very simple LSTM model and set up some inputs. - -""" - -# import the modules used here in this recipe -import torch -import torch.quantization -import torch.nn as nn -import copy -import os -import time - -# define a very, very simple LSTM for demonstration purposes -# in this case, we are wrapping nn.LSTM, one layer, no pre or post processing -# inspired by -# https://pytorch.org/tutorials/beginner/nlp/sequence_models_tutorial.html, by Robert Guthrie -# and https://pytorch.org/tutorials/advanced/dynamic_quantization_tutorial.html -class lstm_for_demonstration(nn.Module): - """Elementary Long Short Term Memory style model which simply wraps nn.LSTM - Not to be used for anything other than demonstration. - """ - def __init__(self,in_dim,out_dim,depth): - super(lstm_for_demonstration,self).__init__() - self.lstm = nn.LSTM(in_dim,out_dim,depth) - - def forward(self,inputs,hidden): - out,hidden = self.lstm(inputs,hidden) - return out, hidden - - -torch.manual_seed(29592) # set the seed for reproducibility - -#shape parameters -model_dimension=8 -sequence_length=20 -batch_size=1 -lstm_depth=1 - -# random data for input -inputs = torch.randn(sequence_length,batch_size,model_dimension) -# hidden is actually is a tuple of the initial hidden state and the initial cell state -hidden = (torch.randn(lstm_depth,batch_size,model_dimension), torch.randn(lstm_depth,batch_size,model_dimension)) - - -###################################################################### -# 2: Do the Quantization -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Now we get to the fun part. First we create an instance of the model -# called float\_lstm then we are going to quantize it. We're going to use -# the -# -# :: -# -# torch.quantization.quantize_dynamic() -# -# function here (`see -# documentation `__) -# which takes the model, then a list of the submodules which we want to -# have quantized if they appear, then the datatype we are targeting. This -# function returns a quantized version of the original model as a new -# module. -# -# That's all it takes. -# - - # here is our floating point instance -float_lstm = lstm_for_demonstration(model_dimension, model_dimension,lstm_depth) - -# this is the call that does the work -quantized_lstm = torch.quantization.quantize_dynamic( - float_lstm, {nn.LSTM, nn.Linear}, dtype=torch.qint8 -) - -# show the changes that were made -print('Here is the floating point version of this module:') -print(float_lstm) -print('') -print('and now the quantized version:') -print(quantized_lstm) - - -###################################################################### -# 3. Look at Model Size -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# Ok, so we've quantized the model. What does that get us? Well the first -# benefit is that we've replaced the FP32 model parameters with INT8 -# values (and some recorded scale factors). This means about 75% less data -# to store and move around. With the default values the reduction shown -# below will be less than 75% but if you increase the model size above -# (for example you can set model dimension to something like 80) this will -# converge towards 4x smaller as the stored model size dominated more and -# more by the parameter values. -# - -def print_size_of_model(model, label=""): - torch.save(model.state_dict(), "temp.p") - size=os.path.getsize("temp.p") - print("model: ",label,' \t','Size (KB):', size/1e3) - os.remove('temp.p') - return size - -# compare the sizes -f=print_size_of_model(float_lstm,"fp32") -q=print_size_of_model(quantized_lstm,"int8") -print("{0:.2f} times smaller".format(f/q)) - -# note that this value is wrong in PyTorch 1.4 due to https://github.com/pytorch/pytorch/issues/31468 -# this will be fixed in 1.5 with https://github.com/pytorch/pytorch/pull/31540 - - -###################################################################### -# 4. Look at Latency -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# The second benefit is that the quantized model will typically run -# faster. This is due to a combinations of effects including at least: -# -# 1. Less time spent moving parameter data in -# 2. Faster INT8 operations -# -# As you will see the quantized version of this super-simple network runs -# faster. This will generally be true of more complex networks but as they -# say "your milage may vary" depending on a number of factors including -# the structure of the model and the hardware you are running on. -# - -# compare the performance -print("Floating point FP32") -# %timeit float_lstm.forward(inputs, hidden) - -print("Quantized INT8") -# %timeit quantized_lstm.forward(inputs,hidden) - - -###################################################################### -# 5: Look at Accuracy -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# We are not going to do a careful look at accuracy here because we are -# working with a randomly initialized network rather than a properly -# trained one. However, I think it is worth quickly showing that the -# quantized network does produce output tensors that are "in the same -# ballpark" as the original one. -# -# For a more detailed analysis please see the more advanced tutorials -# referenced at the end of this recipe. -# - -# run the float model -out1, hidden1 = float_lstm(inputs, hidden) -mag1 = torch.mean(abs(out1)).item() -print('mean absolute value of output tensor values in the FP32 model is {0:.5f} '.format(mag1)) - -# run the quantized model -out2, hidden2 = quantized_lstm(inputs, hidden) -mag2 = torch.mean(abs(out2)).item() -print('mean absolute value of output tensor values in the INT8 model is {0:.5f}'.format(mag2)) - -# compare them -mag3 = torch.mean(abs(out1-out2)).item() -print('mean absolute value of the difference between the output tensors is {0:.5f} or {1:.2f} percent'.format(mag3,mag3/mag1*100)) - - -###################################################################### -# Learn More -# ------------ -# We've explained what dynamic quantization is, what benefits it brings, -# and you have used the ``torch.quantization.quantize_dynamic()`` function -# to quickly quantize a simple LSTM model. -# -# This was a fast and high level treatment of this material; for more -# detail please continue learning with `(experimental) Dynamic Quantization on an LSTM Word Language Model Tutorial `_. -# -# -# Additional Resources -# ========= -# Documentation -# ~~~~~~~~~~~~~~ -# -# `Quantization API Documentaion `_ -# -# Tutorials -# ~~~~~~~~~~~~~~ -# -# `(experimental) Dynamic Quantization on BERT `_ -# -# `(experimental) Dynamic Quantization on an LSTM Word Language Model `_ -# -# Blogs -# ~~~~~~~~~~~~~~ -# ` Introduction to Quantization on PyTorch `_ -# diff --git a/recipes/recipes/dynamic_quantization.rst b/recipes/recipes/dynamic_quantization.rst deleted file mode 100644 index 7657ef80fcf..00000000000 --- a/recipes/recipes/dynamic_quantization.rst +++ /dev/null @@ -1,364 +0,0 @@ -.. note:: - :class: sphx-glr-download-link-note - - Click :ref:`here ` to download the full example code -.. rst-class:: sphx-glr-example-title - -.. _sphx_glr_recipes_recipes_dynamic_quantization.py: - - -Dynamic Quantization -==================== - -In this recipe you will see how to take advantage of Dynamic -Quantization to accelerate inference on an LSTM-style recurrent neural -network. This reduces the size of the model weights and speeds up model -execution. - -Introduction -------------- - -There are a number of trade-offs that can be made when designing neural -networks. During model developmenet and training you can alter the -number of layers and number of parameters in a recurrent neural network -and trade-off accuracy against model size and/or model latency or -throughput. Such changes can take lot of time and compute resources -because you are iterating over the model training. Quantization gives -you a way to make a similar trade off between performance and model -accuracy with a known model after training is completed. - -You can give it a try in a single session and you will certainly reduce -your model size significantly and may get a significant latency -reduction without losing a lot of accuracy. - -What is dynamic quantization? -------------- - -Quantizing a network means converting it to use a reduced precision -integer representation for the weights and/or activations. This saves on -model size and allows the use of higher throughput math operations on -your CPU or GPU. - -When converting from floating point to integer values you are -essentially multiplying the floating point value by some scale factor -and rounding the result to a whole number. The various quantization -approaches differ in the way they approach determining that scale -factor. - -The key idea with dynamic quantization as described here is that we are -going to determine the scale factor for activations dynamically based on -the data range observed at runtime. This ensures that the scale factor -is "tuned" so that as much signal as possible about each observed -dataset is preserved. - -The model parameters on the other hand are known during model conversion -and they are converted ahead of time and stored in INT8 form. - -Arithmetic in the quantized model is done using vectorized INT8 -instructions. Accumulation is typically done with INT16 or INT32 to -avoid overflow. This higher precision value is scaled back to INT8 if -the next layer is quantized or converted to FP32 for output. - -Dynamic quantization is relatively free of tuning parameters which makes -it well suited to be added into production pipelines as a standard part -of converting LSTM models to deployment. - - - -.. note:: - Limitations on the approach taken here - - - This recipe provides a quick introduction to the dynamic quantization - features in PyTorch and the workflow for using it. Our focus is on - explaining the specific functions used to convert the model. We will - make a number of significant simplifications in the interest of brevity - and clarity - - -1. You will start with a minimal LSTM network -2. You are simply going to initialize the network with a random hidden - state -3. You are going to test the network with random inputs -4. You are not going to train the network in this tutorial -5. You will see that the quantized form of this network is smaller and - runs faster than the floating point network we started with -6. You will see that the output values are generally in the same - ballpark as the output of the FP32 network, but we are not - demonstrating here the expected accuracy loss on a real trained - network - -You will see how dynamic quantization is done and be able to see -suggestive reductions in memory use and latency times. Providing a -demonstration that the technique can preserve high levels of model -accuracy on a trained LSTM is left to a more advanced tutorial. If you -want to move right away to that more rigorous treatment please proceed -to the `advanced dynamic quantization -tutorial `__. - -Steps -------------- - -This recipe has 5 steps. - -1. Set Up - Here you define a very simple LSTM, import modules, and establish - some random input tensors. - -2. Do the Quantization - Here you instantiate a floating point model and then create quantized - version of it. - -3. Look at Model Size - Here you show that the model size gets smaller. - -4. Look at Latency - Here you run the two models and compare model runtime (latency). - -5. Look at Accuracy - Here you run the two models and compare outputs. - - -1: Set Up -~~~~~~~~~~~~~~~ -This is a straightfoward bit of code to set up for the rest of the -recipe. - -The unique module we are importing here is torch.quantization which -includes PyTorch's quantized operators and conversion functions. We also -define a very simple LSTM model and set up some inputs. - -.. code-block:: default - - - # import the modules used here in this recipe - import torch - import torch.quantization - import torch.nn as nn - import copy - import os - import time - - # define a very, very simple LSTM for demonstration purposes - # in this case, we are wrapping nn.LSTM, one layer, no pre or post processing - # inspired by - # https://pytorch.org/tutorials/beginner/nlp/sequence_models_tutorial.html, by Robert Guthrie - # and https://pytorch.org/tutorials/advanced/dynamic_quantization_tutorial.html - class lstm_for_demonstration(nn.Module): - """Elementary Long Short Term Memory style model which simply wraps nn.LSTM - Not to be used for anything other than demonstration. - """ - def __init__(self,in_dim,out_dim,depth): - super(lstm_for_demonstration,self).__init__() - self.lstm = nn.LSTM(in_dim,out_dim,depth) - - def forward(self,inputs,hidden): - out,hidden = self.lstm(inputs,hidden) - return out, hidden - - - torch.manual_seed(29592) # set the seed for reproducibility - - #shape parameters - model_dimension=8 - sequence_length=20 - batch_size=1 - lstm_depth=1 - - # random data for input - inputs = torch.randn(sequence_length,batch_size,model_dimension) - # hidden is actually is a tuple of the initial hidden state and the initial cell state - hidden = (torch.randn(lstm_depth,batch_size,model_dimension), torch.randn(lstm_depth,batch_size,model_dimension)) - - - -2: Do the Quantization -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Now we get to the fun part. First we create an instance of the model -called float\_lstm then we are going to quantize it. We're going to use -the - -:: - - torch.quantization.quantize_dynamic() - -function here (`see -documentation `__) -which takes the model, then a list of the submodules which we want to -have quantized if they appear, then the datatype we are targeting. This -function returns a quantized version of the original model as a new -module. - -That's all it takes. - - - -.. code-block:: default - - - # here is our floating point instance - float_lstm = lstm_for_demonstration(model_dimension, model_dimension,lstm_depth) - - # this is the call that does the work - quantized_lstm = torch.quantization.quantize_dynamic( - float_lstm, {nn.LSTM, nn.Linear}, dtype=torch.qint8 - ) - - # show the changes that were made - print('Here is the floating point version of this module:') - print(float_lstm) - print('') - print('and now the quantized version:') - print(quantized_lstm) - - - -3. Look at Model Size -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Ok, so we've quantized the model. What does that get us? Well the first -benefit is that we've replaced the FP32 model parameters with INT8 -values (and some recorded scale factors). This means about 75% less data -to store and move around. With the default values the reduction shown -below will be less than 75% but if you increase the model size above -(for example you can set model dimension to something like 80) this will -converge towards 4x smaller as the stored model size dominated more and -more by the parameter values. - - - -.. code-block:: default - - - def print_size_of_model(model, label=""): - torch.save(model.state_dict(), "temp.p") - size=os.path.getsize("temp.p") - print("model: ",label,' \t','Size (KB):', size/1e3) - os.remove('temp.p') - return size - - # compare the sizes - f=print_size_of_model(float_lstm,"fp32") - q=print_size_of_model(quantized_lstm,"int8") - print("{0:.2f} times smaller".format(f/q)) - - # note that this value is wrong in PyTorch 1.4 due to https://github.com/pytorch/pytorch/issues/31468 - # this will be fixed in 1.5 with https://github.com/pytorch/pytorch/pull/31540 - - - -4. Look at Latency -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The second benefit is that the quantized model will typically run -faster. This is due to a combinations of effects including at least: - -1. Less time spent moving parameter data in -2. Faster INT8 operations - -As you will see the quantized version of this super-simple network runs -faster. This will generally be true of more complex networks but as they -say "your milage may vary" depending on a number of factors including -the structure of the model and the hardware you are running on. - - - -.. code-block:: default - - - # compare the performance - print("Floating point FP32") - # %timeit float_lstm.forward(inputs, hidden) - - print("Quantized INT8") - # %timeit quantized_lstm.forward(inputs,hidden) - - - -5: Look at Accuracy -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -We are not going to do a careful look at accuracy here because we are -working with a randomly initialized network rather than a properly -trained one. However, I think it is worth quickly showing that the -quantized network does produce output tensors that are "in the same -ballpark" as the original one. - -For a more detailed analysis please see the more advanced tutorials -referenced at the end of this recipe. - - - -.. code-block:: default - - - # run the float model - out1, hidden1 = float_lstm(inputs, hidden) - mag1 = torch.mean(abs(out1)).item() - print('mean absolute value of output tensor values in the FP32 model is {0:.5f} '.format(mag1)) - - # run the quantized model - out2, hidden2 = quantized_lstm(inputs, hidden) - mag2 = torch.mean(abs(out2)).item() - print('mean absolute value of output tensor values in the INT8 model is {0:.5f}'.format(mag2)) - - # compare them - mag3 = torch.mean(abs(out1-out2)).item() - print('mean absolute value of the difference between the output tensors is {0:.5f} or {1:.2f} percent'.format(mag3,mag3/mag1*100)) - - - -Learn More ------------- -We've explained what dynamic quantization is, what benefits it brings, -and you have used the ``torch.quantization.quantize_dynamic()`` function -to quickly quantize a simple LSTM model. - -This was a fast and high level treatment of this material; for more -detail please continue learning with `(experimental) Dynamic Quantization on an LSTM Word Language Model Tutorial `_. - - -Additional Resources -========= -Documentation -~~~~~~~~~~~~~~ - -`Quantization API Documentaion `_ - -Tutorials -~~~~~~~~~~~~~~ - -`(experimental) Dynamic Quantization on BERT `_ - -`(experimental) Dynamic Quantization on an LSTM Word Language Model `_ - -Blogs -~~~~~~~~~~~~~~ -` Introduction to Quantization on PyTorch `_ - - - -.. rst-class:: sphx-glr-timing - - **Total running time of the script:** ( 0 minutes 0.000 seconds) - - -.. _sphx_glr_download_recipes_recipes_dynamic_quantization.py: - - -.. only :: html - - .. container:: sphx-glr-footer - :class: sphx-glr-footer-example - - - - .. container:: sphx-glr-download - - :download:`Download Python source code: dynamic_quantization.py ` - - - - .. container:: sphx-glr-download - - :download:`Download Jupyter notebook: dynamic_quantization.ipynb ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/images/thumb/sphx_glr_Captum_Recipe_thumb.png b/recipes/recipes/images/thumb/sphx_glr_Captum_Recipe_thumb.png deleted file mode 100644 index 233f8e605efca4bef384a7c603d53fdc385428bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26786 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 diff --git a/recipes/recipes/images/thumb/sphx_glr_custom_dataset_transforms_loader_thumb.png b/recipes/recipes/images/thumb/sphx_glr_custom_dataset_transforms_loader_thumb.png deleted file mode 100644 index 233f8e605efca4bef384a7c603d53fdc385428bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26786 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 diff --git a/recipes/recipes/images/thumb/sphx_glr_defining_a_neural_network_thumb.png b/recipes/recipes/images/thumb/sphx_glr_defining_a_neural_network_thumb.png deleted file mode 100644 index 233f8e605efca4bef384a7c603d53fdc385428bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26786 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 diff --git a/recipes/recipes/images/thumb/sphx_glr_dynamic_quantization_thumb.png b/recipes/recipes/images/thumb/sphx_glr_dynamic_quantization_thumb.png deleted file mode 100644 index 233f8e605efca4bef384a7c603d53fdc385428bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26786 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 diff --git a/recipes/recipes/images/thumb/sphx_glr_loading_data_recipe_thumb.png b/recipes/recipes/images/thumb/sphx_glr_loading_data_recipe_thumb.png deleted file mode 100644 index 233f8e605efca4bef384a7c603d53fdc385428bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26786 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 diff --git a/recipes/recipes/images/thumb/sphx_glr_profiler_thumb.png b/recipes/recipes/images/thumb/sphx_glr_profiler_thumb.png deleted file mode 100644 index 233f8e605efca4bef384a7c603d53fdc385428bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26786 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 diff --git a/recipes/recipes/images/thumb/sphx_glr_save_load_across_devices_thumb.png b/recipes/recipes/images/thumb/sphx_glr_save_load_across_devices_thumb.png deleted file mode 100644 index 233f8e605efca4bef384a7c603d53fdc385428bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26786 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 diff --git a/recipes/recipes/images/thumb/sphx_glr_saving_and_loading_a_general_checkpoint_thumb.png b/recipes/recipes/images/thumb/sphx_glr_saving_and_loading_a_general_checkpoint_thumb.png deleted file mode 100644 index 233f8e605efca4bef384a7c603d53fdc385428bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26786 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 diff --git a/recipes/recipes/images/thumb/sphx_glr_saving_and_loading_models_for_inference_thumb.png b/recipes/recipes/images/thumb/sphx_glr_saving_and_loading_models_for_inference_thumb.png deleted file mode 100644 index 233f8e605efca4bef384a7c603d53fdc385428bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26786 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 diff --git a/recipes/recipes/images/thumb/sphx_glr_saving_multiple_models_in_one_file_thumb.png b/recipes/recipes/images/thumb/sphx_glr_saving_multiple_models_in_one_file_thumb.png deleted file mode 100644 index 233f8e605efca4bef384a7c603d53fdc385428bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26786 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 diff --git a/recipes/recipes/images/thumb/sphx_glr_tensorboard_with_pytorch_thumb.png b/recipes/recipes/images/thumb/sphx_glr_tensorboard_with_pytorch_thumb.png deleted file mode 100644 index 233f8e605efca4bef384a7c603d53fdc385428bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26786 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 diff --git a/recipes/recipes/images/thumb/sphx_glr_warmstarting_model_using_parameters_from_a_different_model_thumb.png b/recipes/recipes/images/thumb/sphx_glr_warmstarting_model_using_parameters_from_a_different_model_thumb.png deleted file mode 100644 index 233f8e605efca4bef384a7c603d53fdc385428bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26786 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 diff --git a/recipes/recipes/images/thumb/sphx_glr_what_is_state_dict_thumb.png b/recipes/recipes/images/thumb/sphx_glr_what_is_state_dict_thumb.png deleted file mode 100644 index 233f8e605efca4bef384a7c603d53fdc385428bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26786 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 diff --git a/recipes/recipes/images/thumb/sphx_glr_zeroing_out_gradients_thumb.png b/recipes/recipes/images/thumb/sphx_glr_zeroing_out_gradients_thumb.png deleted file mode 100644 index 233f8e605efca4bef384a7c603d53fdc385428bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26786 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 diff --git a/recipes/recipes/loading_data_recipe.ipynb b/recipes/recipes/loading_data_recipe.ipynb deleted file mode 100644 index 8332ae5a58f..00000000000 --- a/recipes/recipes/loading_data_recipe.ipynb +++ /dev/null @@ -1,140 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\nLoading data in PyTorch\n=======================\nPyTorch features extensive neural network building blocks with a simple,\nintuitive, and stable API. PyTorch includes packages to prepare and load\ncommon datasets for your model.\n\nIntroduction\n------------\nAt the heart of PyTorch data loading utility is the\n`torch.utils.data.DataLoader `__\nclass. It represents a Python iterable over a dataset. Libraries in\nPyTorch offer built-in high-quality datasets for you to use in\n`torch.utils.data.Dataset `__.\nThese datasets are currently available in:\n\n* `torchvision `__\n* `torchaudio `__\n* `torchtext `__\n\nwith more to come.\nUsing the Yesno dataset from ``torchaudio.datasets.YESNO``, we will\ndemonstrate how to effectively and efficiently load data from a PyTorch\n``Dataset`` into a PyTorch ``DataLoader``.\n\nSetup\n-----\nBefore we begin, we need to install ``torchaudio`` to have access to the\ndataset.\n\n::\n\n pip install torchaudio\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Steps\n-----\n\n1. Import all necessary libraries for loading our data\n2. Access the data in the dataset\n3. Loading the data\n4. Iterate over the data\n5. [Optional] Visualize the data\n\n\n1. Import necessary libraries for loading our data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor this recipe, we will use ``torch`` and ``torchaudio``. Depending on\nwhat built-in datasets you use, you can also install and import\n``torchvision`` or ``torchtext``.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import torch\nimport torchaudio" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "2. Access the data in the dataset\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe Yesno dataset in ``torchaudio`` features sixty recordings of one\nindividual saying yes or no in Hebrew; with each recording being eight\nwords long (`read more here `__).\n\n``torchaudio.datasets.YESNO`` creates a dataset for YesNo.\n\n::\n\n torchaudio.datasets.YESNO(\n root,\n url='http://www.openslr.org/resources/1/waves_yesno.tar.gz',\n folder_in_archive='waves_yesno',\n download=False,\n transform=None,\n target_transform=None)\n\nEach item in the dataset is a tuple of the form: (waveform, sample_rate,\nlabels).\n\nYou must set a ``root`` for the Yesno dataset, which is where the\ntraining and testing dataset will exist. The other parameters are\noptional, with their default values shown. Here is some additional\nuseful info on the other parameters:\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# * ``download``: If true, downloads the dataset from the internet and puts it in root directory. If dataset is already downloaded, it is not downloaded again.\n# * ``transform``: Using transforms on your data allows you to take it from its source state and transform it into data that\u2019s joined together, de-normalized, and ready for training. Each library in PyTorch supports a growing list of transformations.\n# * ``target_transform``: A function/transform that takes in the target and transforms it.\n# \n# Let\u2019s access our Yesno data:\n# \n\n# A data point in Yesno is a tuple (waveform, sample_rate, labels) where labels \n# is a list of integers with 1 for yes and 0 for no.\nyesno_data_trainset = torchaudio.datasets.YESNO('./', download=True)\n\n# Pick data point number 3 to see an example of the the yesno_data:\nn = 3\nwaveform, sample_rate, labels = yesno_data[n]\nprint(\"Waveform: {}\\nSample rate: {}\\nLabels: {}\".format(waveform, sample_rate, labels))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When using this data in practice, it is best practice to provision the\ndata into a \u201ctraining\u201d dataset and a \u201ctesting\u201d dataset. This ensures\nthat you have out-of-sample data to test the performance of your model.\n\n3. Loading the data\n~~~~~~~~~~~~~~~~~~~~~~~\n\nNow that we have access to the dataset, we must pass it through\n``torch.utils.data.DataLoader``. The ``DataLoader`` combines the dataset\nand a sampler, returning an iterable over the dataset.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "data_loader = torch.utils.data.DataLoader(yesno_data,\n batch_size=1,\n shuffle=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "4. Iterate over the data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nOur data is now iterable using the ``data_loader``. This will be\nnecessary when we begin training our model! You will notice that now\neach data entry in the ``data_loader`` object is converted to a tensor\ncontaining tensors representing our waveform, sample rate, and labels.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "for data in data_loader:\n print(\"Data: \", data)\n print(\"Waveform: {}\\nSample rate: {}\\nLabels: {}\".format(data[0], data[1], data[2]))\n break" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "5. [Optional] Visualize the data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou can optionally visualize your data to further understand the output\nfrom your ``DataLoader``.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n\nprint(data[0][0].numpy())\n\nplt.figure()\nplt.plot(waveform.t().numpy())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Congratulations! You have successfully loaded data in PyTorch.\n\nLearn More\n----------\n\nTake a look at these other recipes to continue your learning:\n\n- `Defining a Neural Network `__\n- `What is a state_dict in PyTorch `__\n\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.4" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/recipes/recipes/loading_data_recipe.py b/recipes/recipes/loading_data_recipe.py deleted file mode 100644 index 99e685897c3..00000000000 --- a/recipes/recipes/loading_data_recipe.py +++ /dev/null @@ -1,171 +0,0 @@ -""" -Loading data in PyTorch -======================= -PyTorch features extensive neural network building blocks with a simple, -intuitive, and stable API. PyTorch includes packages to prepare and load -common datasets for your model. - -Introduction ------------- -At the heart of PyTorch data loading utility is the -`torch.utils.data.DataLoader `__ -class. It represents a Python iterable over a dataset. Libraries in -PyTorch offer built-in high-quality datasets for you to use in -`torch.utils.data.Dataset `__. -These datasets are currently available in: - -* `torchvision `__ -* `torchaudio `__ -* `torchtext `__ - -with more to come. -Using the Yesno dataset from ``torchaudio.datasets.YESNO``, we will -demonstrate how to effectively and efficiently load data from a PyTorch -``Dataset`` into a PyTorch ``DataLoader``. - -Setup ------ -Before we begin, we need to install ``torchaudio`` to have access to the -dataset. - -:: - - pip install torchaudio - - -""" - - - - - - - -###################################################################### -# Steps -# ----- -# -# 1. Import all necessary libraries for loading our data -# 2. Access the data in the dataset -# 3. Loading the data -# 4. Iterate over the data -# 5. [Optional] Visualize the data -# -# -# 1. Import necessary libraries for loading our data -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# For this recipe, we will use ``torch`` and ``torchaudio``. Depending on -# what built-in datasets you use, you can also install and import -# ``torchvision`` or ``torchtext``. -# - -import torch -import torchaudio - - -###################################################################### -# 2. Access the data in the dataset -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# The Yesno dataset in ``torchaudio`` features sixty recordings of one -# individual saying yes or no in Hebrew; with each recording being eight -# words long (`read more here `__). -# -# ``torchaudio.datasets.YESNO`` creates a dataset for YesNo. -# -# :: -# -# torchaudio.datasets.YESNO( -# root, -# url='http://www.openslr.org/resources/1/waves_yesno.tar.gz', -# folder_in_archive='waves_yesno', -# download=False, -# transform=None, -# target_transform=None) -# -# Each item in the dataset is a tuple of the form: (waveform, sample_rate, -# labels). -# -# You must set a ``root`` for the Yesno dataset, which is where the -# training and testing dataset will exist. The other parameters are -# optional, with their default values shown. Here is some additional -# useful info on the other parameters: - -# * ``download``: If true, downloads the dataset from the internet and puts it in root directory. If dataset is already downloaded, it is not downloaded again. -# * ``transform``: Using transforms on your data allows you to take it from its source state and transform it into data that’s joined together, de-normalized, and ready for training. Each library in PyTorch supports a growing list of transformations. -# * ``target_transform``: A function/transform that takes in the target and transforms it. -# -# Let’s access our Yesno data: -# - -# A data point in Yesno is a tuple (waveform, sample_rate, labels) where labels -# is a list of integers with 1 for yes and 0 for no. -yesno_data_trainset = torchaudio.datasets.YESNO('./', download=True) - -# Pick data point number 3 to see an example of the the yesno_data: -n = 3 -waveform, sample_rate, labels = yesno_data[n] -print("Waveform: {}\nSample rate: {}\nLabels: {}".format(waveform, sample_rate, labels)) - - -###################################################################### -# When using this data in practice, it is best practice to provision the -# data into a “training” dataset and a “testing” dataset. This ensures -# that you have out-of-sample data to test the performance of your model. -# -# 3. Loading the data -# ~~~~~~~~~~~~~~~~~~~~~~~ -# -# Now that we have access to the dataset, we must pass it through -# ``torch.utils.data.DataLoader``. The ``DataLoader`` combines the dataset -# and a sampler, returning an iterable over the dataset. -# - -data_loader = torch.utils.data.DataLoader(yesno_data, - batch_size=1, - shuffle=True) - - -###################################################################### -# 4. Iterate over the data -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Our data is now iterable using the ``data_loader``. This will be -# necessary when we begin training our model! You will notice that now -# each data entry in the ``data_loader`` object is converted to a tensor -# containing tensors representing our waveform, sample rate, and labels. -# - -for data in data_loader: - print("Data: ", data) - print("Waveform: {}\nSample rate: {}\nLabels: {}".format(data[0], data[1], data[2])) - break - - -###################################################################### -# 5. [Optional] Visualize the data -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# You can optionally visualize your data to further understand the output -# from your ``DataLoader``. -# - -import matplotlib.pyplot as plt - -print(data[0][0].numpy()) - -plt.figure() -plt.plot(waveform.t().numpy()) - - -###################################################################### -# Congratulations! You have successfully loaded data in PyTorch. -# -# Learn More -# ---------- -# -# Take a look at these other recipes to continue your learning: -# -# - `Defining a Neural Network `__ -# - `What is a state_dict in PyTorch `__ diff --git a/recipes/recipes/loading_data_recipe.rst b/recipes/recipes/loading_data_recipe.rst deleted file mode 100644 index e2d70679b68..00000000000 --- a/recipes/recipes/loading_data_recipe.rst +++ /dev/null @@ -1,221 +0,0 @@ -.. note:: - :class: sphx-glr-download-link-note - - Click :ref:`here ` to download the full example code -.. rst-class:: sphx-glr-example-title - -.. _sphx_glr_recipes_recipes_loading_data_recipe.py: - - -Loading data in PyTorch -======================= -PyTorch features extensive neural network building blocks with a simple, -intuitive, and stable API. PyTorch includes packages to prepare and load -common datasets for your model. - -Introduction ------------- -At the heart of PyTorch data loading utility is the -`torch.utils.data.DataLoader `__ -class. It represents a Python iterable over a dataset. Libraries in -PyTorch offer built-in high-quality datasets for you to use in -`torch.utils.data.Dataset `__. -These datasets are currently available in: - -* `torchvision `__ -* `torchaudio `__ -* `torchtext `__ - -with more to come. -Using the Yesno dataset from ``torchaudio.datasets.YESNO``, we will -demonstrate how to effectively and efficiently load data from a PyTorch -``Dataset`` into a PyTorch ``DataLoader``. - -Setup ------ -Before we begin, we need to install ``torchaudio`` to have access to the -dataset. - -:: - - pip install torchaudio -Steps ------ - -1. Import all necessary libraries for loading our data -2. Access the data in the dataset -3. Loading the data -4. Iterate over the data -5. [Optional] Visualize the data - - -1. Import necessary libraries for loading our data -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For this recipe, we will use ``torch`` and ``torchaudio``. Depending on -what built-in datasets you use, you can also install and import -``torchvision`` or ``torchtext``. - - - -.. code-block:: default - - - import torch - import torchaudio - - - -2. Access the data in the dataset -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The Yesno dataset in ``torchaudio`` features sixty recordings of one -individual saying yes or no in Hebrew; with each recording being eight -words long (`read more here `__). - -``torchaudio.datasets.YESNO`` creates a dataset for YesNo. - -:: - - torchaudio.datasets.YESNO( - root, - url='http://www.openslr.org/resources/1/waves_yesno.tar.gz', - folder_in_archive='waves_yesno', - download=False, - transform=None, - target_transform=None) - -Each item in the dataset is a tuple of the form: (waveform, sample_rate, -labels). - -You must set a ``root`` for the Yesno dataset, which is where the -training and testing dataset will exist. The other parameters are -optional, with their default values shown. Here is some additional -useful info on the other parameters: - - -.. code-block:: default - - - # * ``download``: If true, downloads the dataset from the internet and puts it in root directory. If dataset is already downloaded, it is not downloaded again. - # * ``transform``: Using transforms on your data allows you to take it from its source state and transform it into data that’s joined together, de-normalized, and ready for training. Each library in PyTorch supports a growing list of transformations. - # * ``target_transform``: A function/transform that takes in the target and transforms it. - # - # Let’s access our Yesno data: - # - - # A data point in Yesno is a tuple (waveform, sample_rate, labels) where labels - # is a list of integers with 1 for yes and 0 for no. - yesno_data_trainset = torchaudio.datasets.YESNO('./', download=True) - - # Pick data point number 3 to see an example of the the yesno_data: - n = 3 - waveform, sample_rate, labels = yesno_data[n] - print("Waveform: {}\nSample rate: {}\nLabels: {}".format(waveform, sample_rate, labels)) - - - -When using this data in practice, it is best practice to provision the -data into a “training” dataset and a “testing” dataset. This ensures -that you have out-of-sample data to test the performance of your model. - -3. Loading the data -~~~~~~~~~~~~~~~~~~~~~~~ - -Now that we have access to the dataset, we must pass it through -``torch.utils.data.DataLoader``. The ``DataLoader`` combines the dataset -and a sampler, returning an iterable over the dataset. - - - -.. code-block:: default - - - data_loader = torch.utils.data.DataLoader(yesno_data, - batch_size=1, - shuffle=True) - - - -4. Iterate over the data -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Our data is now iterable using the ``data_loader``. This will be -necessary when we begin training our model! You will notice that now -each data entry in the ``data_loader`` object is converted to a tensor -containing tensors representing our waveform, sample rate, and labels. - - - -.. code-block:: default - - - for data in data_loader: - print("Data: ", data) - print("Waveform: {}\nSample rate: {}\nLabels: {}".format(data[0], data[1], data[2])) - break - - - -5. [Optional] Visualize the data -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can optionally visualize your data to further understand the output -from your ``DataLoader``. - - - -.. code-block:: default - - - import matplotlib.pyplot as plt - - print(data[0][0].numpy()) - - plt.figure() - plt.plot(waveform.t().numpy()) - - - -Congratulations! You have successfully loaded data in PyTorch. - -Learn More ----------- - -Take a look at these other recipes to continue your learning: - -- `Defining a Neural Network `__ -- `What is a state_dict in PyTorch `__ - - -.. rst-class:: sphx-glr-timing - - **Total running time of the script:** ( 0 minutes 0.000 seconds) - - -.. _sphx_glr_download_recipes_recipes_loading_data_recipe.py: - - -.. only :: html - - .. container:: sphx-glr-footer - :class: sphx-glr-footer-example - - - - .. container:: sphx-glr-download - - :download:`Download Python source code: loading_data_recipe.py ` - - - - .. container:: sphx-glr-download - - :download:`Download Jupyter notebook: loading_data_recipe.ipynb ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/profiler.ipynb b/recipes/recipes/profiler.ipynb deleted file mode 100644 index 6d0faeb373d..00000000000 --- a/recipes/recipes/profiler.ipynb +++ /dev/null @@ -1,201 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\nPyTorch Profiler\n====================================\nThis recipe explains how to use PyTorch profiler and measure the time and\nmemory consumption of the model's operators.\n\nIntroduction\n------------\nPyTorch includes a simple profiler API that is useful when user needs\nto determine the most expensive operators in the model.\n\nIn this recipe, we will use a simple Resnet model to demonstrate how to\nuse profiler to analyze model performance.\n\nSetup\n-----\nTo install ``torch`` and ``torchvision`` use the following command:\n\n::\n\n pip install torch torchvision\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Steps\n-----\n\n1. Import all necessary libraries\n2. Instantiate a simple Resnet model\n3. Use profiler to analyze execution time\n4. Use profiler to analyze memory consumption\n5. Using tracing functionality\n\n1. Import all necessary libraries\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIn this recipe we will use ``torch``, ``torchvision.models``\nand ``profiler`` modules:\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import torch\nimport torchvision.models as models\nimport torch.autograd.profiler as profiler" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "2. Instantiate a simple Resnet model\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nLet's create an instance of a Resnet model and prepare an input\nfor it:\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "model = models.resnet18()\ninputs = torch.randn(5, 3, 224, 224)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "3. Use profiler to analyze execution time\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPyTorch profiler is enabled through the context manager and accepts\na number of parameters, some of the most useful are:\n\n- ``record_shapes`` - whether to record shapes of the operator inputs;\n- ``profile_memory`` - whether to report amount of memory consumed by\n model's Tensors;\n- ``use_cuda`` - whether to measure execution time of CUDA kernels.\n\nLet's see how we can use profiler to analyze the execution time:\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "with profiler.profile(record_shapes=True) as prof:\n with profiler.record_function(\"model_inference\"):\n model(inputs)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that we can use ``record_function`` context manager to label\narbitrary code ranges with user provided names\n(``model_inference`` is used as a label in the example above).\nProfiler allows one to check which operators were called during the\nexecution of a code range wrapped with a profiler context manager.\nIf multiple profiler ranges are active at the same time (e.g. in\nparallel PyTorch threads), each profiling context manager tracks only\nthe operators of its corresponding range.\nProfiler also automatically profiles the async tasks launched\nwith ``torch.jit._fork`` and (in case of a backward pass)\nthe backward pass operators launched with ``backward()`` call.\n\nLet's print out the stats for the execution above:\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "print(prof.key_averages().table(sort_by=\"cpu_time_total\", row_limit=10))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The output will look like (omitting some columns):\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# ------------------------- -------------- ---------- ------------ ---------\n# Name Self CPU total CPU total CPU time avg # Calls\n# ------------------------- -------------- ---------- ------------ ---------\n# model_inference 3.541ms 69.571ms 69.571ms 1\n# conv2d 69.122us 40.556ms 2.028ms 20\n# convolution 79.100us 40.487ms 2.024ms 20\n# _convolution 349.533us 40.408ms 2.020ms 20\n# mkldnn_convolution 39.822ms 39.988ms 1.999ms 20\n# batch_norm 105.559us 15.523ms 776.134us 20\n# _batch_norm_impl_index 103.697us 15.417ms 770.856us 20\n# native_batch_norm 9.387ms 15.249ms 762.471us 20\n# max_pool2d 29.400us 7.200ms 7.200ms 1\n# max_pool2d_with_indices 7.154ms 7.170ms 7.170ms 1\n# ------------------------- -------------- ---------- ------------ ---------" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here we see that, as expected, most of the time is spent in convolution (and specifically in ``mkldnn_convolution``\nfor PyTorch compiled with MKL-DNN support).\nNote the difference between self cpu time and cpu time - operators can call other operators, self cpu time exludes time\nspent in children operator calls, while total cpu time includes it.\n\nTo get a finer granularity of results and include operator input shapes, pass ``group_by_input_shape=True``:\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "print(prof.key_averages(group_by_input_shape=True).table(sort_by=\"cpu_time_total\", row_limit=10))\n\n# (omitting some columns)\n# ------------------------- ----------- -------- -------------------------------------\n# Name CPU total # Calls Input Shapes\n# ------------------------- ----------- -------- -------------------------------------\n# model_inference 69.571ms 1 []\n# conv2d 9.019ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []]\n# convolution 9.006ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []]\n# _convolution 8.982ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []]\n# mkldnn_convolution 8.894ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []]\n# max_pool2d 7.200ms 1 [[5, 64, 112, 112]]\n# conv2d 7.189ms 3 [[5, 512, 7, 7], [512, 512, 3, 3], []]\n# convolution 7.180ms 3 [[5, 512, 7, 7], [512, 512, 3, 3], []]\n# _convolution 7.171ms 3 [[5, 512, 7, 7], [512, 512, 3, 3], []]\n# max_pool2d_with_indices 7.170ms 1 [[5, 64, 112, 112]]\n# ------------------------- ----------- -------- --------------------------------------" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "4. Use profiler to analyze memory consumption\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPyTorch profiler can also show the amount of memory (used by the model's tensors)\nthat was allocated (or released) during the execution of the model's operators.\nIn the output below, 'self' memory corresponds to the memory allocated (released)\nby the operator, excluding the children calls to the other operators.\nTo enable memory profiling functionality pass ``profile_memory=True``.\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "with profiler.profile(profile_memory=True, record_shapes=True) as prof:\n model(inputs)\n\nprint(prof.key_averages().table(sort_by=\"self_cpu_memory_usage\", row_limit=10))\n\n# (omitting some columns)\n# --------------------------- --------------- --------------- ---------------\n# Name CPU Mem Self CPU Mem Number of Calls\n# --------------------------- --------------- --------------- ---------------\n# empty 94.79 Mb 94.79 Mb 123\n# resize_ 11.48 Mb 11.48 Mb 2\n# addmm 19.53 Kb 19.53 Kb 1\n# empty_strided 4 b 4 b 1\n# conv2d 47.37 Mb 0 b 20\n# --------------------------- --------------- --------------- ---------------\n\nprint(prof.key_averages().table(sort_by=\"cpu_memory_usage\", row_limit=10))\n\n# (omitting some columns)\n# --------------------------- --------------- --------------- ---------------\n# Name CPU Mem Self CPU Mem Number of Calls\n# --------------------------- --------------- --------------- ---------------\n# empty 94.79 Mb 94.79 Mb 123\n# batch_norm 47.41 Mb 0 b 20\n# _batch_norm_impl_index 47.41 Mb 0 b 20\n# native_batch_norm 47.41 Mb 0 b 20\n# conv2d 47.37 Mb 0 b 20\n# convolution 47.37 Mb 0 b 20\n# _convolution 47.37 Mb 0 b 20\n# mkldnn_convolution 47.37 Mb 0 b 20\n# empty_like 47.37 Mb 0 b 20\n# max_pool2d 11.48 Mb 0 b 1\n# max_pool2d_with_indices 11.48 Mb 0 b 1\n# resize_ 11.48 Mb 11.48 Mb 2\n# addmm 19.53 Kb 19.53 Kb 1\n# adaptive_avg_pool2d 10.00 Kb 0 b 1\n# mean 10.00 Kb 0 b 1\n# --------------------------- --------------- --------------- ---------------" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "5. Using tracing functionality\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nProfiling results can be outputted as a .json trace file:\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "with profiler.profile() as prof:\n with profiler.record_function(\"model_inference\"):\n model(inputs)\n\nprof.export_chrome_trace(\"trace.json\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "User can examine the sequence of profiled operators after loading the trace file\nin Chrome (``chrome://tracing``):\n\n![](../../_static/img/trace_img.png)\n\n :scale: 25 %\n\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Learn More\n----------\n\nTake a look at the following tutorial to learn how to visualize your model with TensorBoard:\n\n- `Visualizing models, data, and training with TensorBoard `_ tutorial\n\n\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.4" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/recipes/recipes/profiler.py b/recipes/recipes/profiler.py deleted file mode 100644 index b3233302176..00000000000 --- a/recipes/recipes/profiler.py +++ /dev/null @@ -1,215 +0,0 @@ -""" -PyTorch Profiler -==================================== -This recipe explains how to use PyTorch profiler and measure the time and -memory consumption of the model's operators. - -Introduction ------------- -PyTorch includes a simple profiler API that is useful when user needs -to determine the most expensive operators in the model. - -In this recipe, we will use a simple Resnet model to demonstrate how to -use profiler to analyze model performance. - -Setup ------ -To install ``torch`` and ``torchvision`` use the following command: - -:: - - pip install torch torchvision - - -""" - - -###################################################################### -# Steps -# ----- -# -# 1. Import all necessary libraries -# 2. Instantiate a simple Resnet model -# 3. Use profiler to analyze execution time -# 4. Use profiler to analyze memory consumption -# 5. Using tracing functionality -# -# 1. Import all necessary libraries -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# In this recipe we will use ``torch``, ``torchvision.models`` -# and ``profiler`` modules: -# - -import torch -import torchvision.models as models -import torch.autograd.profiler as profiler - - -###################################################################### -# 2. Instantiate a simple Resnet model -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Let's create an instance of a Resnet model and prepare an input -# for it: -# - -model = models.resnet18() -inputs = torch.randn(5, 3, 224, 224) - -###################################################################### -# 3. Use profiler to analyze execution time -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# PyTorch profiler is enabled through the context manager and accepts -# a number of parameters, some of the most useful are: -# -# - ``record_shapes`` - whether to record shapes of the operator inputs; -# - ``profile_memory`` - whether to report amount of memory consumed by -# model's Tensors; -# - ``use_cuda`` - whether to measure execution time of CUDA kernels. -# -# Let's see how we can use profiler to analyze the execution time: - -with profiler.profile(record_shapes=True) as prof: - with profiler.record_function("model_inference"): - model(inputs) - -###################################################################### -# Note that we can use ``record_function`` context manager to label -# arbitrary code ranges with user provided names -# (``model_inference`` is used as a label in the example above). -# Profiler allows one to check which operators were called during the -# execution of a code range wrapped with a profiler context manager. -# If multiple profiler ranges are active at the same time (e.g. in -# parallel PyTorch threads), each profiling context manager tracks only -# the operators of its corresponding range. -# Profiler also automatically profiles the async tasks launched -# with ``torch.jit._fork`` and (in case of a backward pass) -# the backward pass operators launched with ``backward()`` call. -# -# Let's print out the stats for the execution above: - -print(prof.key_averages().table(sort_by="cpu_time_total", row_limit=10)) - -###################################################################### -# The output will look like (omitting some columns): - -# ------------------------- -------------- ---------- ------------ --------- -# Name Self CPU total CPU total CPU time avg # Calls -# ------------------------- -------------- ---------- ------------ --------- -# model_inference 3.541ms 69.571ms 69.571ms 1 -# conv2d 69.122us 40.556ms 2.028ms 20 -# convolution 79.100us 40.487ms 2.024ms 20 -# _convolution 349.533us 40.408ms 2.020ms 20 -# mkldnn_convolution 39.822ms 39.988ms 1.999ms 20 -# batch_norm 105.559us 15.523ms 776.134us 20 -# _batch_norm_impl_index 103.697us 15.417ms 770.856us 20 -# native_batch_norm 9.387ms 15.249ms 762.471us 20 -# max_pool2d 29.400us 7.200ms 7.200ms 1 -# max_pool2d_with_indices 7.154ms 7.170ms 7.170ms 1 -# ------------------------- -------------- ---------- ------------ --------- - -###################################################################### -# Here we see that, as expected, most of the time is spent in convolution (and specifically in ``mkldnn_convolution`` -# for PyTorch compiled with MKL-DNN support). -# Note the difference between self cpu time and cpu time - operators can call other operators, self cpu time exludes time -# spent in children operator calls, while total cpu time includes it. -# -# To get a finer granularity of results and include operator input shapes, pass ``group_by_input_shape=True``: - -print(prof.key_averages(group_by_input_shape=True).table(sort_by="cpu_time_total", row_limit=10)) - -# (omitting some columns) -# ------------------------- ----------- -------- ------------------------------------- -# Name CPU total # Calls Input Shapes -# ------------------------- ----------- -------- ------------------------------------- -# model_inference 69.571ms 1 [] -# conv2d 9.019ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []] -# convolution 9.006ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []] -# _convolution 8.982ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []] -# mkldnn_convolution 8.894ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []] -# max_pool2d 7.200ms 1 [[5, 64, 112, 112]] -# conv2d 7.189ms 3 [[5, 512, 7, 7], [512, 512, 3, 3], []] -# convolution 7.180ms 3 [[5, 512, 7, 7], [512, 512, 3, 3], []] -# _convolution 7.171ms 3 [[5, 512, 7, 7], [512, 512, 3, 3], []] -# max_pool2d_with_indices 7.170ms 1 [[5, 64, 112, 112]] -# ------------------------- ----------- -------- -------------------------------------- - - -###################################################################### -# 4. Use profiler to analyze memory consumption -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# PyTorch profiler can also show the amount of memory (used by the model's tensors) -# that was allocated (or released) during the execution of the model's operators. -# In the output below, 'self' memory corresponds to the memory allocated (released) -# by the operator, excluding the children calls to the other operators. -# To enable memory profiling functionality pass ``profile_memory=True``. - -with profiler.profile(profile_memory=True, record_shapes=True) as prof: - model(inputs) - -print(prof.key_averages().table(sort_by="self_cpu_memory_usage", row_limit=10)) - -# (omitting some columns) -# --------------------------- --------------- --------------- --------------- -# Name CPU Mem Self CPU Mem Number of Calls -# --------------------------- --------------- --------------- --------------- -# empty 94.79 Mb 94.79 Mb 123 -# resize_ 11.48 Mb 11.48 Mb 2 -# addmm 19.53 Kb 19.53 Kb 1 -# empty_strided 4 b 4 b 1 -# conv2d 47.37 Mb 0 b 20 -# --------------------------- --------------- --------------- --------------- - -print(prof.key_averages().table(sort_by="cpu_memory_usage", row_limit=10)) - -# (omitting some columns) -# --------------------------- --------------- --------------- --------------- -# Name CPU Mem Self CPU Mem Number of Calls -# --------------------------- --------------- --------------- --------------- -# empty 94.79 Mb 94.79 Mb 123 -# batch_norm 47.41 Mb 0 b 20 -# _batch_norm_impl_index 47.41 Mb 0 b 20 -# native_batch_norm 47.41 Mb 0 b 20 -# conv2d 47.37 Mb 0 b 20 -# convolution 47.37 Mb 0 b 20 -# _convolution 47.37 Mb 0 b 20 -# mkldnn_convolution 47.37 Mb 0 b 20 -# empty_like 47.37 Mb 0 b 20 -# max_pool2d 11.48 Mb 0 b 1 -# max_pool2d_with_indices 11.48 Mb 0 b 1 -# resize_ 11.48 Mb 11.48 Mb 2 -# addmm 19.53 Kb 19.53 Kb 1 -# adaptive_avg_pool2d 10.00 Kb 0 b 1 -# mean 10.00 Kb 0 b 1 -# --------------------------- --------------- --------------- --------------- - -###################################################################### -# 5. Using tracing functionality -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Profiling results can be outputted as a .json trace file: - -with profiler.profile() as prof: - with profiler.record_function("model_inference"): - model(inputs) - -prof.export_chrome_trace("trace.json") - -###################################################################### -# User can examine the sequence of profiled operators after loading the trace file -# in Chrome (``chrome://tracing``): -# -# .. image:: ../../_static/img/trace_img.png -# :scale: 25 % - -###################################################################### -# Learn More -# ---------- -# -# Take a look at the following tutorial to learn how to visualize your model with TensorBoard: -# -# - `Visualizing models, data, and training with TensorBoard `_ tutorial -# diff --git a/recipes/recipes/profiler.rst b/recipes/recipes/profiler.rst deleted file mode 100644 index 76b7e5456c9..00000000000 --- a/recipes/recipes/profiler.rst +++ /dev/null @@ -1,281 +0,0 @@ -.. note:: - :class: sphx-glr-download-link-note - - Click :ref:`here ` to download the full example code -.. rst-class:: sphx-glr-example-title - -.. _sphx_glr_recipes_recipes_profiler.py: - - -PyTorch Profiler -==================================== -This recipe explains how to use PyTorch profiler and measure the time and -memory consumption of the model's operators. - -Introduction ------------- -PyTorch includes a simple profiler API that is useful when user needs -to determine the most expensive operators in the model. - -In this recipe, we will use a simple Resnet model to demonstrate how to -use profiler to analyze model performance. - -Setup ------ -To install ``torch`` and ``torchvision`` use the following command: - -:: - - pip install torch torchvision -Steps ------ - -1. Import all necessary libraries -2. Instantiate a simple Resnet model -3. Use profiler to analyze execution time -4. Use profiler to analyze memory consumption -5. Using tracing functionality - -1. Import all necessary libraries -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In this recipe we will use ``torch``, ``torchvision.models`` -and ``profiler`` modules: - - - -.. code-block:: default - - - import torch - import torchvision.models as models - import torch.autograd.profiler as profiler - - - -2. Instantiate a simple Resnet model -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Let's create an instance of a Resnet model and prepare an input -for it: - - - -.. code-block:: default - - - model = models.resnet18() - inputs = torch.randn(5, 3, 224, 224) - - -3. Use profiler to analyze execution time -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -PyTorch profiler is enabled through the context manager and accepts -a number of parameters, some of the most useful are: - -- ``record_shapes`` - whether to record shapes of the operator inputs; -- ``profile_memory`` - whether to report amount of memory consumed by - model's Tensors; -- ``use_cuda`` - whether to measure execution time of CUDA kernels. - -Let's see how we can use profiler to analyze the execution time: - - -.. code-block:: default - - - with profiler.profile(record_shapes=True) as prof: - with profiler.record_function("model_inference"): - model(inputs) - - -Note that we can use ``record_function`` context manager to label -arbitrary code ranges with user provided names -(``model_inference`` is used as a label in the example above). -Profiler allows one to check which operators were called during the -execution of a code range wrapped with a profiler context manager. -If multiple profiler ranges are active at the same time (e.g. in -parallel PyTorch threads), each profiling context manager tracks only -the operators of its corresponding range. -Profiler also automatically profiles the async tasks launched -with ``torch.jit._fork`` and (in case of a backward pass) -the backward pass operators launched with ``backward()`` call. - -Let's print out the stats for the execution above: - - -.. code-block:: default - - - print(prof.key_averages().table(sort_by="cpu_time_total", row_limit=10)) - - -The output will look like (omitting some columns): - - -.. code-block:: default - - - # ------------------------- -------------- ---------- ------------ --------- - # Name Self CPU total CPU total CPU time avg # Calls - # ------------------------- -------------- ---------- ------------ --------- - # model_inference 3.541ms 69.571ms 69.571ms 1 - # conv2d 69.122us 40.556ms 2.028ms 20 - # convolution 79.100us 40.487ms 2.024ms 20 - # _convolution 349.533us 40.408ms 2.020ms 20 - # mkldnn_convolution 39.822ms 39.988ms 1.999ms 20 - # batch_norm 105.559us 15.523ms 776.134us 20 - # _batch_norm_impl_index 103.697us 15.417ms 770.856us 20 - # native_batch_norm 9.387ms 15.249ms 762.471us 20 - # max_pool2d 29.400us 7.200ms 7.200ms 1 - # max_pool2d_with_indices 7.154ms 7.170ms 7.170ms 1 - # ------------------------- -------------- ---------- ------------ --------- - - -Here we see that, as expected, most of the time is spent in convolution (and specifically in ``mkldnn_convolution`` -for PyTorch compiled with MKL-DNN support). -Note the difference between self cpu time and cpu time - operators can call other operators, self cpu time exludes time -spent in children operator calls, while total cpu time includes it. - -To get a finer granularity of results and include operator input shapes, pass ``group_by_input_shape=True``: - - -.. code-block:: default - - - print(prof.key_averages(group_by_input_shape=True).table(sort_by="cpu_time_total", row_limit=10)) - - # (omitting some columns) - # ------------------------- ----------- -------- ------------------------------------- - # Name CPU total # Calls Input Shapes - # ------------------------- ----------- -------- ------------------------------------- - # model_inference 69.571ms 1 [] - # conv2d 9.019ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []] - # convolution 9.006ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []] - # _convolution 8.982ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []] - # mkldnn_convolution 8.894ms 4 [[5, 64, 56, 56], [64, 64, 3, 3], []] - # max_pool2d 7.200ms 1 [[5, 64, 112, 112]] - # conv2d 7.189ms 3 [[5, 512, 7, 7], [512, 512, 3, 3], []] - # convolution 7.180ms 3 [[5, 512, 7, 7], [512, 512, 3, 3], []] - # _convolution 7.171ms 3 [[5, 512, 7, 7], [512, 512, 3, 3], []] - # max_pool2d_with_indices 7.170ms 1 [[5, 64, 112, 112]] - # ------------------------- ----------- -------- -------------------------------------- - - - -4. Use profiler to analyze memory consumption -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -PyTorch profiler can also show the amount of memory (used by the model's tensors) -that was allocated (or released) during the execution of the model's operators. -In the output below, 'self' memory corresponds to the memory allocated (released) -by the operator, excluding the children calls to the other operators. -To enable memory profiling functionality pass ``profile_memory=True``. - - -.. code-block:: default - - - with profiler.profile(profile_memory=True, record_shapes=True) as prof: - model(inputs) - - print(prof.key_averages().table(sort_by="self_cpu_memory_usage", row_limit=10)) - - # (omitting some columns) - # --------------------------- --------------- --------------- --------------- - # Name CPU Mem Self CPU Mem Number of Calls - # --------------------------- --------------- --------------- --------------- - # empty 94.79 Mb 94.79 Mb 123 - # resize_ 11.48 Mb 11.48 Mb 2 - # addmm 19.53 Kb 19.53 Kb 1 - # empty_strided 4 b 4 b 1 - # conv2d 47.37 Mb 0 b 20 - # --------------------------- --------------- --------------- --------------- - - print(prof.key_averages().table(sort_by="cpu_memory_usage", row_limit=10)) - - # (omitting some columns) - # --------------------------- --------------- --------------- --------------- - # Name CPU Mem Self CPU Mem Number of Calls - # --------------------------- --------------- --------------- --------------- - # empty 94.79 Mb 94.79 Mb 123 - # batch_norm 47.41 Mb 0 b 20 - # _batch_norm_impl_index 47.41 Mb 0 b 20 - # native_batch_norm 47.41 Mb 0 b 20 - # conv2d 47.37 Mb 0 b 20 - # convolution 47.37 Mb 0 b 20 - # _convolution 47.37 Mb 0 b 20 - # mkldnn_convolution 47.37 Mb 0 b 20 - # empty_like 47.37 Mb 0 b 20 - # max_pool2d 11.48 Mb 0 b 1 - # max_pool2d_with_indices 11.48 Mb 0 b 1 - # resize_ 11.48 Mb 11.48 Mb 2 - # addmm 19.53 Kb 19.53 Kb 1 - # adaptive_avg_pool2d 10.00 Kb 0 b 1 - # mean 10.00 Kb 0 b 1 - # --------------------------- --------------- --------------- --------------- - - -5. Using tracing functionality -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Profiling results can be outputted as a .json trace file: - - -.. code-block:: default - - - with profiler.profile() as prof: - with profiler.record_function("model_inference"): - model(inputs) - - prof.export_chrome_trace("trace.json") - - -User can examine the sequence of profiled operators after loading the trace file -in Chrome (``chrome://tracing``): - -.. image:: ../../_static/img/trace_img.png - :scale: 25 % - -Learn More ----------- - -Take a look at the following tutorial to learn how to visualize your model with TensorBoard: - -- `Visualizing models, data, and training with TensorBoard `_ tutorial - - - -.. rst-class:: sphx-glr-timing - - **Total running time of the script:** ( 0 minutes 0.000 seconds) - - -.. _sphx_glr_download_recipes_recipes_profiler.py: - - -.. only :: html - - .. container:: sphx-glr-footer - :class: sphx-glr-footer-example - - - - .. container:: sphx-glr-download - - :download:`Download Python source code: profiler.py ` - - - - .. container:: sphx-glr-download - - :download:`Download Jupyter notebook: profiler.ipynb ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/save_load_across_devices.ipynb b/recipes/recipes/save_load_across_devices.ipynb deleted file mode 100644 index 9ad519d95f5..00000000000 --- a/recipes/recipes/save_load_across_devices.ipynb +++ /dev/null @@ -1,158 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\nSaving and loading models across devices in PyTorch\n===================================================\n\nThere may be instances where you want to save and load your neural\nnetworks across different devices.\n\nIntroduction\n------------\n\nSaving and loading models across devices is relatively straightforward\nusing PyTorch. In this recipe, we will experiment with saving and\nloading models across CPUs and GPUs.\n\nSetup\n-----\n\nIn order for every code block to run properly in this recipe, you must\nfirst change the runtime to \u201cGPU\u201d or higher. Once you do, we need to\ninstall ``torch`` if it isn\u2019t already available.\n\n::\n\n pip install torch\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Steps\n-----\n\n1. Import all necessary libraries for loading our data\n2. Define and intialize the neural network\n3. Save on a GPU, load on a CPU\n4. Save on a GPU, load on a GPU\n5. Save on a CPU, load on a GPU\n6. Saving and loading ``DataParallel`` models\n\n1. Import necessary libraries for loading our data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor this recipe, we will use ``torch`` and its subsidiaries ``torch.nn``\nand ``torch.optim``.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import torch\nimport torch.nn as nn\nimport torch.optim as optim" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "2. Define and intialize the neural network\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor sake of example, we will create a neural network for training\nimages. To learn more see the Defining a Neural Network recipe.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "class Net(nn.Module):\n def __init__(self):\n super(Net, self).__init__()\n self.conv1 = nn.Conv2d(3, 6, 5)\n self.pool = nn.MaxPool2d(2, 2)\n self.conv2 = nn.Conv2d(6, 16, 5)\n self.fc1 = nn.Linear(16 * 5 * 5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, 10)\n\n def forward(self, x):\n x = self.pool(F.relu(self.conv1(x)))\n x = self.pool(F.relu(self.conv2(x)))\n x = x.view(-1, 16 * 5 * 5)\n x = F.relu(self.fc1(x))\n x = F.relu(self.fc2(x))\n x = self.fc3(x)\n return x\n\nnet = Net()\nprint(net)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "3. Save on GPU, Load on CPU\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhen loading a model on a CPU that was trained with a GPU, pass\n``torch.device('cpu')`` to the ``map_location`` argument in the\n``torch.load()`` function.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# Specify a path to save to\nPATH = \"model.pt\"\n\n# Save\ntorch.save(net.state_dict(), PATH)\n\n# Load\ndevice = torch.device('cpu')\nmodel = Net()\nmodel.load_state_dict(torch.load(PATH, map_location=device))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this case, the storages underlying the tensors are dynamically\nremapped to the CPU device using the ``map_location`` argument.\n\n4. Save on GPU, Load on GPU\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhen loading a model on a GPU that was trained and saved on GPU, simply\nconvert the initialized model to a CUDA optimized model using\n``model.to(torch.device('cuda'))``.\n\nBe sure to use the ``.to(torch.device('cuda'))`` function on all model\ninputs to prepare the data for the model.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# Save\ntorch.save(net.state_dict(), PATH)\n\n# Load\ndevice = torch.device(\"cuda\")\nmodel = Net()\nmodel.load_state_dict(torch.load(PATH))\nmodel.to(device)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that calling ``my_tensor.to(device)`` returns a new copy of\n``my_tensor`` on GPU. It does NOT overwrite ``my_tensor``. Therefore,\nremember to manually overwrite tensors:\n``my_tensor = my_tensor.to(torch.device('cuda'))``.\n\n5. Save on CPU, Load on GPU\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhen loading a model on a GPU that was trained and saved on CPU, set the\n``map_location`` argument in the ``torch.load()`` function to\n``cuda:device_id``. This loads the model to a given GPU device.\n\nBe sure to call ``model.to(torch.device('cuda'))`` to convert the\nmodel\u2019s parameter tensors to CUDA tensors.\n\nFinally, also be sure to use the ``.to(torch.device('cuda'))`` function\non all model inputs to prepare the data for the CUDA optimized model.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# Save\ntorch.save(net.state_dict(), PATH)\n\n# Load\ndevice = torch.device(\"cuda\")\nmodel = Net()\n# Choose whatever GPU device number you want\nmodel.load_state_dict(torch.load(PATH, map_location=\"cuda:0\"))\n# Make sure to call input = input.to(device) on any input tensors that you feed to the model\nmodel.to(device)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "6. Saving ``torch.nn.DataParallel`` Models\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n``torch.nn.DataParallel`` is a model wrapper that enables parallel GPU\nutilization.\n\nTo save a ``DataParallel`` model generically, save the\n``model.module.state_dict()``. This way, you have the flexibility to\nload the model any way you want to any device you want.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# Save\ntorch.save(net.module.state_dict(), PATH)\n\n# Load to whatever device you want" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Congratulations! You have successfully saved and loaded models across\ndevices in PyTorch.\n\nLearn More\n----------\n\nTake a look at these other recipes to continue your learning:\n\n- TBD\n- TBD\n\n\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.4" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/recipes/recipes/save_load_across_devices.py b/recipes/recipes/save_load_across_devices.py deleted file mode 100644 index c2d86fbab50..00000000000 --- a/recipes/recipes/save_load_across_devices.py +++ /dev/null @@ -1,190 +0,0 @@ -""" -Saving and loading models across devices in PyTorch -=================================================== - -There may be instances where you want to save and load your neural -networks across different devices. - -Introduction ------------- - -Saving and loading models across devices is relatively straightforward -using PyTorch. In this recipe, we will experiment with saving and -loading models across CPUs and GPUs. - -Setup ------ - -In order for every code block to run properly in this recipe, you must -first change the runtime to “GPU” or higher. Once you do, we need to -install ``torch`` if it isn’t already available. - -:: - - pip install torch - -""" - - -###################################################################### -# Steps -# ----- -# -# 1. Import all necessary libraries for loading our data -# 2. Define and intialize the neural network -# 3. Save on a GPU, load on a CPU -# 4. Save on a GPU, load on a GPU -# 5. Save on a CPU, load on a GPU -# 6. Saving and loading ``DataParallel`` models -# -# 1. Import necessary libraries for loading our data -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` -# and ``torch.optim``. -# - -import torch -import torch.nn as nn -import torch.optim as optim - - -###################################################################### -# 2. Define and intialize the neural network -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# For sake of example, we will create a neural network for training -# images. To learn more see the Defining a Neural Network recipe. -# - -class Net(nn.Module): - def __init__(self): - super(Net, self).__init__() - self.conv1 = nn.Conv2d(3, 6, 5) - self.pool = nn.MaxPool2d(2, 2) - self.conv2 = nn.Conv2d(6, 16, 5) - self.fc1 = nn.Linear(16 * 5 * 5, 120) - self.fc2 = nn.Linear(120, 84) - self.fc3 = nn.Linear(84, 10) - - def forward(self, x): - x = self.pool(F.relu(self.conv1(x))) - x = self.pool(F.relu(self.conv2(x))) - x = x.view(-1, 16 * 5 * 5) - x = F.relu(self.fc1(x)) - x = F.relu(self.fc2(x)) - x = self.fc3(x) - return x - -net = Net() -print(net) - - -###################################################################### -# 3. Save on GPU, Load on CPU -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# When loading a model on a CPU that was trained with a GPU, pass -# ``torch.device('cpu')`` to the ``map_location`` argument in the -# ``torch.load()`` function. -# - -# Specify a path to save to -PATH = "model.pt" - -# Save -torch.save(net.state_dict(), PATH) - -# Load -device = torch.device('cpu') -model = Net() -model.load_state_dict(torch.load(PATH, map_location=device)) - - -###################################################################### -# In this case, the storages underlying the tensors are dynamically -# remapped to the CPU device using the ``map_location`` argument. -# -# 4. Save on GPU, Load on GPU -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# When loading a model on a GPU that was trained and saved on GPU, simply -# convert the initialized model to a CUDA optimized model using -# ``model.to(torch.device('cuda'))``. -# -# Be sure to use the ``.to(torch.device('cuda'))`` function on all model -# inputs to prepare the data for the model. -# - -# Save -torch.save(net.state_dict(), PATH) - -# Load -device = torch.device("cuda") -model = Net() -model.load_state_dict(torch.load(PATH)) -model.to(device) - - -###################################################################### -# Note that calling ``my_tensor.to(device)`` returns a new copy of -# ``my_tensor`` on GPU. It does NOT overwrite ``my_tensor``. Therefore, -# remember to manually overwrite tensors: -# ``my_tensor = my_tensor.to(torch.device('cuda'))``. -# -# 5. Save on CPU, Load on GPU -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# When loading a model on a GPU that was trained and saved on CPU, set the -# ``map_location`` argument in the ``torch.load()`` function to -# ``cuda:device_id``. This loads the model to a given GPU device. -# -# Be sure to call ``model.to(torch.device('cuda'))`` to convert the -# model’s parameter tensors to CUDA tensors. -# -# Finally, also be sure to use the ``.to(torch.device('cuda'))`` function -# on all model inputs to prepare the data for the CUDA optimized model. -# - -# Save -torch.save(net.state_dict(), PATH) - -# Load -device = torch.device("cuda") -model = Net() -# Choose whatever GPU device number you want -model.load_state_dict(torch.load(PATH, map_location="cuda:0")) -# Make sure to call input = input.to(device) on any input tensors that you feed to the model -model.to(device) - - -###################################################################### -# 6. Saving ``torch.nn.DataParallel`` Models -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# ``torch.nn.DataParallel`` is a model wrapper that enables parallel GPU -# utilization. -# -# To save a ``DataParallel`` model generically, save the -# ``model.module.state_dict()``. This way, you have the flexibility to -# load the model any way you want to any device you want. -# - -# Save -torch.save(net.module.state_dict(), PATH) - -# Load to whatever device you want - - -###################################################################### -# Congratulations! You have successfully saved and loaded models across -# devices in PyTorch. -# -# Learn More -# ---------- -# -# Take a look at these other recipes to continue your learning: -# -# - TBD -# - TBD -# diff --git a/recipes/recipes/save_load_across_devices.rst b/recipes/recipes/save_load_across_devices.rst deleted file mode 100644 index 30f23e353e7..00000000000 --- a/recipes/recipes/save_load_across_devices.rst +++ /dev/null @@ -1,250 +0,0 @@ -.. note:: - :class: sphx-glr-download-link-note - - Click :ref:`here ` to download the full example code -.. rst-class:: sphx-glr-example-title - -.. _sphx_glr_recipes_recipes_save_load_across_devices.py: - - -Saving and loading models across devices in PyTorch -=================================================== - -There may be instances where you want to save and load your neural -networks across different devices. - -Introduction ------------- - -Saving and loading models across devices is relatively straightforward -using PyTorch. In this recipe, we will experiment with saving and -loading models across CPUs and GPUs. - -Setup ------ - -In order for every code block to run properly in this recipe, you must -first change the runtime to “GPU” or higher. Once you do, we need to -install ``torch`` if it isn’t already available. - -:: - - pip install torch -Steps ------ - -1. Import all necessary libraries for loading our data -2. Define and intialize the neural network -3. Save on a GPU, load on a CPU -4. Save on a GPU, load on a GPU -5. Save on a CPU, load on a GPU -6. Saving and loading ``DataParallel`` models - -1. Import necessary libraries for loading our data -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` -and ``torch.optim``. - - - -.. code-block:: default - - - import torch - import torch.nn as nn - import torch.optim as optim - - - -2. Define and intialize the neural network -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For sake of example, we will create a neural network for training -images. To learn more see the Defining a Neural Network recipe. - - - -.. code-block:: default - - - class Net(nn.Module): - def __init__(self): - super(Net, self).__init__() - self.conv1 = nn.Conv2d(3, 6, 5) - self.pool = nn.MaxPool2d(2, 2) - self.conv2 = nn.Conv2d(6, 16, 5) - self.fc1 = nn.Linear(16 * 5 * 5, 120) - self.fc2 = nn.Linear(120, 84) - self.fc3 = nn.Linear(84, 10) - - def forward(self, x): - x = self.pool(F.relu(self.conv1(x))) - x = self.pool(F.relu(self.conv2(x))) - x = x.view(-1, 16 * 5 * 5) - x = F.relu(self.fc1(x)) - x = F.relu(self.fc2(x)) - x = self.fc3(x) - return x - - net = Net() - print(net) - - - -3. Save on GPU, Load on CPU -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When loading a model on a CPU that was trained with a GPU, pass -``torch.device('cpu')`` to the ``map_location`` argument in the -``torch.load()`` function. - - - -.. code-block:: default - - - # Specify a path to save to - PATH = "model.pt" - - # Save - torch.save(net.state_dict(), PATH) - - # Load - device = torch.device('cpu') - model = Net() - model.load_state_dict(torch.load(PATH, map_location=device)) - - - -In this case, the storages underlying the tensors are dynamically -remapped to the CPU device using the ``map_location`` argument. - -4. Save on GPU, Load on GPU -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When loading a model on a GPU that was trained and saved on GPU, simply -convert the initialized model to a CUDA optimized model using -``model.to(torch.device('cuda'))``. - -Be sure to use the ``.to(torch.device('cuda'))`` function on all model -inputs to prepare the data for the model. - - - -.. code-block:: default - - - # Save - torch.save(net.state_dict(), PATH) - - # Load - device = torch.device("cuda") - model = Net() - model.load_state_dict(torch.load(PATH)) - model.to(device) - - - -Note that calling ``my_tensor.to(device)`` returns a new copy of -``my_tensor`` on GPU. It does NOT overwrite ``my_tensor``. Therefore, -remember to manually overwrite tensors: -``my_tensor = my_tensor.to(torch.device('cuda'))``. - -5. Save on CPU, Load on GPU -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When loading a model on a GPU that was trained and saved on CPU, set the -``map_location`` argument in the ``torch.load()`` function to -``cuda:device_id``. This loads the model to a given GPU device. - -Be sure to call ``model.to(torch.device('cuda'))`` to convert the -model’s parameter tensors to CUDA tensors. - -Finally, also be sure to use the ``.to(torch.device('cuda'))`` function -on all model inputs to prepare the data for the CUDA optimized model. - - - -.. code-block:: default - - - # Save - torch.save(net.state_dict(), PATH) - - # Load - device = torch.device("cuda") - model = Net() - # Choose whatever GPU device number you want - model.load_state_dict(torch.load(PATH, map_location="cuda:0")) - # Make sure to call input = input.to(device) on any input tensors that you feed to the model - model.to(device) - - - -6. Saving ``torch.nn.DataParallel`` Models -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``torch.nn.DataParallel`` is a model wrapper that enables parallel GPU -utilization. - -To save a ``DataParallel`` model generically, save the -``model.module.state_dict()``. This way, you have the flexibility to -load the model any way you want to any device you want. - - - -.. code-block:: default - - - # Save - torch.save(net.module.state_dict(), PATH) - - # Load to whatever device you want - - - -Congratulations! You have successfully saved and loaded models across -devices in PyTorch. - -Learn More ----------- - -Take a look at these other recipes to continue your learning: - -- TBD -- TBD - - - -.. rst-class:: sphx-glr-timing - - **Total running time of the script:** ( 0 minutes 0.000 seconds) - - -.. _sphx_glr_download_recipes_recipes_save_load_across_devices.py: - - -.. only :: html - - .. container:: sphx-glr-footer - :class: sphx-glr-footer-example - - - - .. container:: sphx-glr-download - - :download:`Download Python source code: save_load_across_devices.py ` - - - - .. container:: sphx-glr-download - - :download:`Download Jupyter notebook: save_load_across_devices.ipynb ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/saving_and_loading_a_general_checkpoint.ipynb b/recipes/recipes/saving_and_loading_a_general_checkpoint.ipynb deleted file mode 100644 index 0952465a851..00000000000 --- a/recipes/recipes/saving_and_loading_a_general_checkpoint.ipynb +++ /dev/null @@ -1,140 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\nSaving and loading a general checkpoint in PyTorch\n==================================================\nSaving and loading a general checkpoint model for inference or \nresuming training can be helpful for picking up where you last left off.\nWhen saving a general checkpoint, you must save more than just the\nmodel\u2019s state_dict. It is important to also save the optimizer\u2019s\nstate_dict, as this contains buffers and parameters that are updated as\nthe model trains. Other items that you may want to save are the epoch\nyou left off on, the latest recorded training loss, external\n``torch.nn.Embedding`` layers, and more, based on your own algorithm.\n\nIntroduction\n------------\nTo save multiple checkpoints, you must organize them in a dictionary and\nuse ``torch.save()`` to serialize the dictionary. A common PyTorch\nconvention is to save these checkpoints using the ``.tar`` file\nextension. To load the items, first initialize the model and optimizer,\nthen load the dictionary locally using torch.load(). From here, you can\neasily access the saved items by simply querying the dictionary as you\nwould expect.\n\nIn this recipe, we will explore how to save and load multiple\ncheckpoints.\n\nSetup\n-----\nBefore we begin, we need to install ``torch`` if it isn\u2019t already\navailable.\n\n::\n\n pip install torch\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Steps\n-----\n\n1. Import all necessary libraries for loading our data\n2. Define and intialize the neural network\n3. Initialize the optimizer\n4. Save the general checkpoint\n5. Load the general checkpoint\n\n1. Import necessary libraries for loading our data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor this recipe, we will use ``torch`` and its subsidiaries ``torch.nn``\nand ``torch.optim``.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import torch\nimport torch.nn as nn\nimport torch.optim as optim" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "2. Define and intialize the neural network\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor sake of example, we will create a neural network for training\nimages. To learn more see the Defining a Neural Network recipe.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "class Net(nn.Module):\n def __init__(self):\n super(Net, self).__init__()\n self.conv1 = nn.Conv2d(3, 6, 5)\n self.pool = nn.MaxPool2d(2, 2)\n self.conv2 = nn.Conv2d(6, 16, 5)\n self.fc1 = nn.Linear(16 * 5 * 5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, 10)\n\n def forward(self, x):\n x = self.pool(F.relu(self.conv1(x)))\n x = self.pool(F.relu(self.conv2(x)))\n x = x.view(-1, 16 * 5 * 5)\n x = F.relu(self.fc1(x))\n x = F.relu(self.fc2(x))\n x = self.fc3(x)\n return x\n\nnet = Net()\nprint(net)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "3. Initialize the optimizer\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWe will use SGD with momentum.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "4. Save the general checkpoint\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nCollect all relevant information and build your dictionary.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# Additional information\nEPOCH = 5\nPATH = \"model.pt\"\nLOSS = 0.4\n\ntorch.save({\n 'epoch': EPOCH,\n 'model_state_dict': net.state_dict(),\n 'optimizer_state_dict': optimizer.state_dict(),\n 'loss': LOSS,\n }, PATH)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "5. Load the general checkpoint\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nRemember to first initialize the model and optimizer, then load the\ndictionary locally.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "model = Net()\noptimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)\n\ncheckpoint = torch.load(PATH)\nmodel.load_state_dict(checkpoint['model_state_dict'])\noptimizer.load_state_dict(checkpoint['optimizer_state_dict'])\nepoch = checkpoint['epoch']\nloss = checkpoint['loss']\n\nmodel.eval()\n# - or -\nmodel.train()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You must call ``model.eval()`` to set dropout and batch normalization\nlayers to evaluation mode before running inference. Failing to do this\nwill yield inconsistent inference results.\n\nIf you wish to resuming training, call ``model.train()`` to ensure these\nlayers are in training mode.\n\nCongratulations! You have successfully saved and loaded a general\ncheckpoint for inference and/or resuming training in PyTorch.\n\nLearn More\n----------\n\nTake a look at these other recipes to continue your learning:\n\n- TBD\n- TBD\n\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.4" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/recipes/recipes/saving_and_loading_a_general_checkpoint.py b/recipes/recipes/saving_and_loading_a_general_checkpoint.py deleted file mode 100644 index 6e0c490ec2a..00000000000 --- a/recipes/recipes/saving_and_loading_a_general_checkpoint.py +++ /dev/null @@ -1,162 +0,0 @@ -""" -Saving and loading a general checkpoint in PyTorch -================================================== -Saving and loading a general checkpoint model for inference or -resuming training can be helpful for picking up where you last left off. -When saving a general checkpoint, you must save more than just the -model’s state_dict. It is important to also save the optimizer’s -state_dict, as this contains buffers and parameters that are updated as -the model trains. Other items that you may want to save are the epoch -you left off on, the latest recorded training loss, external -``torch.nn.Embedding`` layers, and more, based on your own algorithm. - -Introduction ------------- -To save multiple checkpoints, you must organize them in a dictionary and -use ``torch.save()`` to serialize the dictionary. A common PyTorch -convention is to save these checkpoints using the ``.tar`` file -extension. To load the items, first initialize the model and optimizer, -then load the dictionary locally using torch.load(). From here, you can -easily access the saved items by simply querying the dictionary as you -would expect. - -In this recipe, we will explore how to save and load multiple -checkpoints. - -Setup ------ -Before we begin, we need to install ``torch`` if it isn’t already -available. - -:: - - pip install torch - - -""" - - - -###################################################################### -# Steps -# ----- -# -# 1. Import all necessary libraries for loading our data -# 2. Define and intialize the neural network -# 3. Initialize the optimizer -# 4. Save the general checkpoint -# 5. Load the general checkpoint -# -# 1. Import necessary libraries for loading our data -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` -# and ``torch.optim``. -# - -import torch -import torch.nn as nn -import torch.optim as optim - - -###################################################################### -# 2. Define and intialize the neural network -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# For sake of example, we will create a neural network for training -# images. To learn more see the Defining a Neural Network recipe. -# - -class Net(nn.Module): - def __init__(self): - super(Net, self).__init__() - self.conv1 = nn.Conv2d(3, 6, 5) - self.pool = nn.MaxPool2d(2, 2) - self.conv2 = nn.Conv2d(6, 16, 5) - self.fc1 = nn.Linear(16 * 5 * 5, 120) - self.fc2 = nn.Linear(120, 84) - self.fc3 = nn.Linear(84, 10) - - def forward(self, x): - x = self.pool(F.relu(self.conv1(x))) - x = self.pool(F.relu(self.conv2(x))) - x = x.view(-1, 16 * 5 * 5) - x = F.relu(self.fc1(x)) - x = F.relu(self.fc2(x)) - x = self.fc3(x) - return x - -net = Net() -print(net) - - -###################################################################### -# 3. Initialize the optimizer -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# We will use SGD with momentum. -# - -optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) - - -###################################################################### -# 4. Save the general checkpoint -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Collect all relevant information and build your dictionary. -# - -# Additional information -EPOCH = 5 -PATH = "model.pt" -LOSS = 0.4 - -torch.save({ - 'epoch': EPOCH, - 'model_state_dict': net.state_dict(), - 'optimizer_state_dict': optimizer.state_dict(), - 'loss': LOSS, - }, PATH) - - -###################################################################### -# 5. Load the general checkpoint -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Remember to first initialize the model and optimizer, then load the -# dictionary locally. -# - -model = Net() -optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) - -checkpoint = torch.load(PATH) -model.load_state_dict(checkpoint['model_state_dict']) -optimizer.load_state_dict(checkpoint['optimizer_state_dict']) -epoch = checkpoint['epoch'] -loss = checkpoint['loss'] - -model.eval() -# - or - -model.train() - - -###################################################################### -# You must call ``model.eval()`` to set dropout and batch normalization -# layers to evaluation mode before running inference. Failing to do this -# will yield inconsistent inference results. -# -# If you wish to resuming training, call ``model.train()`` to ensure these -# layers are in training mode. -# -# Congratulations! You have successfully saved and loaded a general -# checkpoint for inference and/or resuming training in PyTorch. -# -# Learn More -# ---------- -# -# Take a look at these other recipes to continue your learning: -# -# - TBD -# - TBD diff --git a/recipes/recipes/saving_and_loading_a_general_checkpoint.rst b/recipes/recipes/saving_and_loading_a_general_checkpoint.rst deleted file mode 100644 index 2669f620781..00000000000 --- a/recipes/recipes/saving_and_loading_a_general_checkpoint.rst +++ /dev/null @@ -1,216 +0,0 @@ -.. note:: - :class: sphx-glr-download-link-note - - Click :ref:`here ` to download the full example code -.. rst-class:: sphx-glr-example-title - -.. _sphx_glr_recipes_recipes_saving_and_loading_a_general_checkpoint.py: - - -Saving and loading a general checkpoint in PyTorch -================================================== -Saving and loading a general checkpoint model for inference or -resuming training can be helpful for picking up where you last left off. -When saving a general checkpoint, you must save more than just the -model’s state_dict. It is important to also save the optimizer’s -state_dict, as this contains buffers and parameters that are updated as -the model trains. Other items that you may want to save are the epoch -you left off on, the latest recorded training loss, external -``torch.nn.Embedding`` layers, and more, based on your own algorithm. - -Introduction ------------- -To save multiple checkpoints, you must organize them in a dictionary and -use ``torch.save()`` to serialize the dictionary. A common PyTorch -convention is to save these checkpoints using the ``.tar`` file -extension. To load the items, first initialize the model and optimizer, -then load the dictionary locally using torch.load(). From here, you can -easily access the saved items by simply querying the dictionary as you -would expect. - -In this recipe, we will explore how to save and load multiple -checkpoints. - -Setup ------ -Before we begin, we need to install ``torch`` if it isn’t already -available. - -:: - - pip install torch -Steps ------ - -1. Import all necessary libraries for loading our data -2. Define and intialize the neural network -3. Initialize the optimizer -4. Save the general checkpoint -5. Load the general checkpoint - -1. Import necessary libraries for loading our data -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` -and ``torch.optim``. - - - -.. code-block:: default - - - import torch - import torch.nn as nn - import torch.optim as optim - - - -2. Define and intialize the neural network -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For sake of example, we will create a neural network for training -images. To learn more see the Defining a Neural Network recipe. - - - -.. code-block:: default - - - class Net(nn.Module): - def __init__(self): - super(Net, self).__init__() - self.conv1 = nn.Conv2d(3, 6, 5) - self.pool = nn.MaxPool2d(2, 2) - self.conv2 = nn.Conv2d(6, 16, 5) - self.fc1 = nn.Linear(16 * 5 * 5, 120) - self.fc2 = nn.Linear(120, 84) - self.fc3 = nn.Linear(84, 10) - - def forward(self, x): - x = self.pool(F.relu(self.conv1(x))) - x = self.pool(F.relu(self.conv2(x))) - x = x.view(-1, 16 * 5 * 5) - x = F.relu(self.fc1(x)) - x = F.relu(self.fc2(x)) - x = self.fc3(x) - return x - - net = Net() - print(net) - - - -3. Initialize the optimizer -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -We will use SGD with momentum. - - - -.. code-block:: default - - - optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) - - - -4. Save the general checkpoint -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Collect all relevant information and build your dictionary. - - - -.. code-block:: default - - - # Additional information - EPOCH = 5 - PATH = "model.pt" - LOSS = 0.4 - - torch.save({ - 'epoch': EPOCH, - 'model_state_dict': net.state_dict(), - 'optimizer_state_dict': optimizer.state_dict(), - 'loss': LOSS, - }, PATH) - - - -5. Load the general checkpoint -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Remember to first initialize the model and optimizer, then load the -dictionary locally. - - - -.. code-block:: default - - - model = Net() - optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) - - checkpoint = torch.load(PATH) - model.load_state_dict(checkpoint['model_state_dict']) - optimizer.load_state_dict(checkpoint['optimizer_state_dict']) - epoch = checkpoint['epoch'] - loss = checkpoint['loss'] - - model.eval() - # - or - - model.train() - - - -You must call ``model.eval()`` to set dropout and batch normalization -layers to evaluation mode before running inference. Failing to do this -will yield inconsistent inference results. - -If you wish to resuming training, call ``model.train()`` to ensure these -layers are in training mode. - -Congratulations! You have successfully saved and loaded a general -checkpoint for inference and/or resuming training in PyTorch. - -Learn More ----------- - -Take a look at these other recipes to continue your learning: - -- TBD -- TBD - - -.. rst-class:: sphx-glr-timing - - **Total running time of the script:** ( 0 minutes 0.000 seconds) - - -.. _sphx_glr_download_recipes_recipes_saving_and_loading_a_general_checkpoint.py: - - -.. only :: html - - .. container:: sphx-glr-footer - :class: sphx-glr-footer-example - - - - .. container:: sphx-glr-download - - :download:`Download Python source code: saving_and_loading_a_general_checkpoint.py ` - - - - .. container:: sphx-glr-download - - :download:`Download Jupyter notebook: saving_and_loading_a_general_checkpoint.ipynb ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/saving_and_loading_models_for_inference.ipynb b/recipes/recipes/saving_and_loading_models_for_inference.ipynb deleted file mode 100644 index 7fc3fa2e3c3..00000000000 --- a/recipes/recipes/saving_and_loading_models_for_inference.ipynb +++ /dev/null @@ -1,140 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\nSaving and loading models for inference in PyTorch\n==================================================\nThere are two approaches for saving and loading models for inference in\nPyTorch. The first is saving and loading the ``state_dict``, and the\nsecond is saving and loading the entire model.\n\nIntroduction\n------------\nSaving the model\u2019s ``state_dict`` with the ``torch.save()`` function\nwill give you the most flexibility for restoring the model later. This\nis the recommended method for saving models, because it is only really\nnecessary to save the trained model\u2019s learned parameters.\nWhen saving and loading an entire model, you save the entire module\nusing Python\u2019s\n`pickle `__ module. Using\nthis approach yields the most intuitive syntax and involves the least\namount of code. The disadvantage of this approach is that the serialized\ndata is bound to the specific classes and the exact directory structure\nused when the model is saved. The reason for this is because pickle does\nnot save the model class itself. Rather, it saves a path to the file\ncontaining the class, which is used during load time. Because of this,\nyour code can break in various ways when used in other projects or after\nrefactors.\nIn this recipe, we will explore both ways on how to save and load models\nfor inference.\n\nSetup\n-----\nBefore we begin, we need to install ``torch`` if it isn\u2019t already\navailable.\n\n\n::\n\n pip install torch\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Steps\n-----\n\n1. Import all necessary libraries for loading our data\n2. Define and intialize the neural network\n3. Initialize the optimizer\n4. Save and load the model via ``state_dict``\n5. Save and load the entire model\n\n1. Import necessary libraries for loading our data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor this recipe, we will use ``torch`` and its subsidiaries ``torch.nn``\nand ``torch.optim``.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import torch\nimport torch.nn as nn\nimport torch.optim as optim" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "2. Define and intialize the neural network\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor sake of example, we will create a neural network for training\nimages. To learn more see the Defining a Neural Network recipe.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "class Net(nn.Module):\n def __init__(self):\n super(Net, self).__init__()\n self.conv1 = nn.Conv2d(3, 6, 5)\n self.pool = nn.MaxPool2d(2, 2)\n self.conv2 = nn.Conv2d(6, 16, 5)\n self.fc1 = nn.Linear(16 * 5 * 5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, 10)\n\n def forward(self, x):\n x = self.pool(F.relu(self.conv1(x)))\n x = self.pool(F.relu(self.conv2(x)))\n x = x.view(-1, 16 * 5 * 5)\n x = F.relu(self.fc1(x))\n x = F.relu(self.fc2(x))\n x = self.fc3(x)\n return x\n\nnet = Net()\nprint(net)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "3. Initialize the optimizer\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWe will use SGD with momentum.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "4. Save and load the model via ``state_dict``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nLet\u2019s save and load our model using just ``state_dict``.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# Specify a path\nPATH = \"state_dict_model.pt\"\n\n# Save\ntorch.save(net.state_dict(), PATH)\n\n# Load\nmodel = Net()\nmodel.load_state_dict(torch.load(PATH))\nmodel.eval()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A common PyTorch convention is to save models using either a ``.pt`` or\n``.pth`` file extension.\n\nNotice that the ``load_state_dict()`` function takes a dictionary\nobject, NOT a path to a saved object. This means that you must\ndeserialize the saved state_dict before you pass it to the\n``load_state_dict()`` function. For example, you CANNOT load using\n``model.load_state_dict(PATH)``.\n\nRemember too, that you must call ``model.eval()`` to set dropout and\nbatch normalization layers to evaluation mode before running inference.\nFailing to do this will yield inconsistent inference results.\n\n5. Save and load entire model\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nNow let\u2019s try the same thing with the entire model.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# Specify a path\nPATH = \"entire_model.pt\"\n\n# Save\ntorch.save(net, PATH)\n\n# Load\nmodel = torch.load(PATH)\nmodel.eval()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Again here, remember that you must call model.eval() to set dropout and\nbatch normalization layers to evaluation mode before running inference.\n\nCongratulations! You have successfully saved and load models for\ninference in PyTorch.\n\nLearn More\n----------\n\nTake a look at these other recipes to continue your learning:\n\n- `Saving and loading a general checkpoint in PyTorch `__\n- `Saving and loading multiple models in one file using PyTorch `__\n\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.4" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/recipes/recipes/saving_and_loading_models_for_inference.py b/recipes/recipes/saving_and_loading_models_for_inference.py deleted file mode 100644 index 22ae01aec76..00000000000 --- a/recipes/recipes/saving_and_loading_models_for_inference.py +++ /dev/null @@ -1,168 +0,0 @@ -""" -Saving and loading models for inference in PyTorch -================================================== -There are two approaches for saving and loading models for inference in -PyTorch. The first is saving and loading the ``state_dict``, and the -second is saving and loading the entire model. - -Introduction ------------- -Saving the model’s ``state_dict`` with the ``torch.save()`` function -will give you the most flexibility for restoring the model later. This -is the recommended method for saving models, because it is only really -necessary to save the trained model’s learned parameters. -When saving and loading an entire model, you save the entire module -using Python’s -`pickle `__ module. Using -this approach yields the most intuitive syntax and involves the least -amount of code. The disadvantage of this approach is that the serialized -data is bound to the specific classes and the exact directory structure -used when the model is saved. The reason for this is because pickle does -not save the model class itself. Rather, it saves a path to the file -containing the class, which is used during load time. Because of this, -your code can break in various ways when used in other projects or after -refactors. -In this recipe, we will explore both ways on how to save and load models -for inference. - -Setup ------ -Before we begin, we need to install ``torch`` if it isn’t already -available. - - -:: - - pip install torch - - -""" - - -###################################################################### -# Steps -# ----- -# -# 1. Import all necessary libraries for loading our data -# 2. Define and intialize the neural network -# 3. Initialize the optimizer -# 4. Save and load the model via ``state_dict`` -# 5. Save and load the entire model -# -# 1. Import necessary libraries for loading our data -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` -# and ``torch.optim``. -# - -import torch -import torch.nn as nn -import torch.optim as optim - - -###################################################################### -# 2. Define and intialize the neural network -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# For sake of example, we will create a neural network for training -# images. To learn more see the Defining a Neural Network recipe. -# - -class Net(nn.Module): - def __init__(self): - super(Net, self).__init__() - self.conv1 = nn.Conv2d(3, 6, 5) - self.pool = nn.MaxPool2d(2, 2) - self.conv2 = nn.Conv2d(6, 16, 5) - self.fc1 = nn.Linear(16 * 5 * 5, 120) - self.fc2 = nn.Linear(120, 84) - self.fc3 = nn.Linear(84, 10) - - def forward(self, x): - x = self.pool(F.relu(self.conv1(x))) - x = self.pool(F.relu(self.conv2(x))) - x = x.view(-1, 16 * 5 * 5) - x = F.relu(self.fc1(x)) - x = F.relu(self.fc2(x)) - x = self.fc3(x) - return x - -net = Net() -print(net) - - -###################################################################### -# 3. Initialize the optimizer -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# We will use SGD with momentum. -# - -optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) - - -###################################################################### -# 4. Save and load the model via ``state_dict`` -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Let’s save and load our model using just ``state_dict``. -# - -# Specify a path -PATH = "state_dict_model.pt" - -# Save -torch.save(net.state_dict(), PATH) - -# Load -model = Net() -model.load_state_dict(torch.load(PATH)) -model.eval() - - -###################################################################### -# A common PyTorch convention is to save models using either a ``.pt`` or -# ``.pth`` file extension. -# -# Notice that the ``load_state_dict()`` function takes a dictionary -# object, NOT a path to a saved object. This means that you must -# deserialize the saved state_dict before you pass it to the -# ``load_state_dict()`` function. For example, you CANNOT load using -# ``model.load_state_dict(PATH)``. -# -# Remember too, that you must call ``model.eval()`` to set dropout and -# batch normalization layers to evaluation mode before running inference. -# Failing to do this will yield inconsistent inference results. -# -# 5. Save and load entire model -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Now let’s try the same thing with the entire model. -# - -# Specify a path -PATH = "entire_model.pt" - -# Save -torch.save(net, PATH) - -# Load -model = torch.load(PATH) -model.eval() - - -###################################################################### -# Again here, remember that you must call model.eval() to set dropout and -# batch normalization layers to evaluation mode before running inference. -# -# Congratulations! You have successfully saved and load models for -# inference in PyTorch. -# -# Learn More -# ---------- -# -# Take a look at these other recipes to continue your learning: -# -# - `Saving and loading a general checkpoint in PyTorch `__ -# - `Saving and loading multiple models in one file using PyTorch `__ diff --git a/recipes/recipes/saving_and_loading_models_for_inference.rst b/recipes/recipes/saving_and_loading_models_for_inference.rst deleted file mode 100644 index 8766c3edda9..00000000000 --- a/recipes/recipes/saving_and_loading_models_for_inference.rst +++ /dev/null @@ -1,223 +0,0 @@ -.. note:: - :class: sphx-glr-download-link-note - - Click :ref:`here ` to download the full example code -.. rst-class:: sphx-glr-example-title - -.. _sphx_glr_recipes_recipes_saving_and_loading_models_for_inference.py: - - -Saving and loading models for inference in PyTorch -================================================== -There are two approaches for saving and loading models for inference in -PyTorch. The first is saving and loading the ``state_dict``, and the -second is saving and loading the entire model. - -Introduction ------------- -Saving the model’s ``state_dict`` with the ``torch.save()`` function -will give you the most flexibility for restoring the model later. This -is the recommended method for saving models, because it is only really -necessary to save the trained model’s learned parameters. -When saving and loading an entire model, you save the entire module -using Python’s -`pickle `__ module. Using -this approach yields the most intuitive syntax and involves the least -amount of code. The disadvantage of this approach is that the serialized -data is bound to the specific classes and the exact directory structure -used when the model is saved. The reason for this is because pickle does -not save the model class itself. Rather, it saves a path to the file -containing the class, which is used during load time. Because of this, -your code can break in various ways when used in other projects or after -refactors. -In this recipe, we will explore both ways on how to save and load models -for inference. - -Setup ------ -Before we begin, we need to install ``torch`` if it isn’t already -available. - - -:: - - pip install torch -Steps ------ - -1. Import all necessary libraries for loading our data -2. Define and intialize the neural network -3. Initialize the optimizer -4. Save and load the model via ``state_dict`` -5. Save and load the entire model - -1. Import necessary libraries for loading our data -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` -and ``torch.optim``. - - - -.. code-block:: default - - - import torch - import torch.nn as nn - import torch.optim as optim - - - -2. Define and intialize the neural network -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For sake of example, we will create a neural network for training -images. To learn more see the Defining a Neural Network recipe. - - - -.. code-block:: default - - - class Net(nn.Module): - def __init__(self): - super(Net, self).__init__() - self.conv1 = nn.Conv2d(3, 6, 5) - self.pool = nn.MaxPool2d(2, 2) - self.conv2 = nn.Conv2d(6, 16, 5) - self.fc1 = nn.Linear(16 * 5 * 5, 120) - self.fc2 = nn.Linear(120, 84) - self.fc3 = nn.Linear(84, 10) - - def forward(self, x): - x = self.pool(F.relu(self.conv1(x))) - x = self.pool(F.relu(self.conv2(x))) - x = x.view(-1, 16 * 5 * 5) - x = F.relu(self.fc1(x)) - x = F.relu(self.fc2(x)) - x = self.fc3(x) - return x - - net = Net() - print(net) - - - -3. Initialize the optimizer -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -We will use SGD with momentum. - - - -.. code-block:: default - - - optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) - - - -4. Save and load the model via ``state_dict`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Let’s save and load our model using just ``state_dict``. - - - -.. code-block:: default - - - # Specify a path - PATH = "state_dict_model.pt" - - # Save - torch.save(net.state_dict(), PATH) - - # Load - model = Net() - model.load_state_dict(torch.load(PATH)) - model.eval() - - - -A common PyTorch convention is to save models using either a ``.pt`` or -``.pth`` file extension. - -Notice that the ``load_state_dict()`` function takes a dictionary -object, NOT a path to a saved object. This means that you must -deserialize the saved state_dict before you pass it to the -``load_state_dict()`` function. For example, you CANNOT load using -``model.load_state_dict(PATH)``. - -Remember too, that you must call ``model.eval()`` to set dropout and -batch normalization layers to evaluation mode before running inference. -Failing to do this will yield inconsistent inference results. - -5. Save and load entire model -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Now let’s try the same thing with the entire model. - - - -.. code-block:: default - - - # Specify a path - PATH = "entire_model.pt" - - # Save - torch.save(net, PATH) - - # Load - model = torch.load(PATH) - model.eval() - - - -Again here, remember that you must call model.eval() to set dropout and -batch normalization layers to evaluation mode before running inference. - -Congratulations! You have successfully saved and load models for -inference in PyTorch. - -Learn More ----------- - -Take a look at these other recipes to continue your learning: - -- `Saving and loading a general checkpoint in PyTorch `__ -- `Saving and loading multiple models in one file using PyTorch `__ - - -.. rst-class:: sphx-glr-timing - - **Total running time of the script:** ( 0 minutes 0.000 seconds) - - -.. _sphx_glr_download_recipes_recipes_saving_and_loading_models_for_inference.py: - - -.. only :: html - - .. container:: sphx-glr-footer - :class: sphx-glr-footer-example - - - - .. container:: sphx-glr-download - - :download:`Download Python source code: saving_and_loading_models_for_inference.py ` - - - - .. container:: sphx-glr-download - - :download:`Download Jupyter notebook: saving_and_loading_models_for_inference.ipynb ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/saving_multiple_models_in_one_file.ipynb b/recipes/recipes/saving_multiple_models_in_one_file.ipynb deleted file mode 100644 index c2aaaaaa148..00000000000 --- a/recipes/recipes/saving_multiple_models_in_one_file.ipynb +++ /dev/null @@ -1,140 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\nSaving and loading multiple models in one file using PyTorch\n============================================================\nSaving and loading multiple models can be helpful for reusing models\nthat you have previously trained.\n\nIntroduction\n------------\nWhen saving a model comprised of multiple ``torch.nn.Modules``, such as\na GAN, a sequence-to-sequence model, or an ensemble of models, you must\nsave a dictionary of each model\u2019s state_dict and corresponding\noptimizer. You can also save any other items that may aid you in\nresuming training by simply appending them to the dictionary.\nTo load the models, first initialize the models and optimizers, then\nload the dictionary locally using ``torch.load()``. From here, you can\neasily access the saved items by simply querying the dictionary as you\nwould expect.\nIn this recipe, we will demonstrate how to save multiple models to one\nfile using PyTorch.\n\nSetup\n-----\nBefore we begin, we need to install ``torch`` if it isn\u2019t already\navailable.\n\n::\n\n pip install torch\n \n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Steps\n-----\n\n1. Import all necessary libraries for loading our data\n2. Define and intialize the neural network\n3. Initialize the optimizer\n4. Save multiple models\n5. Load multiple models\n\n1. Import necessary libraries for loading our data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor this recipe, we will use ``torch`` and its subsidiaries ``torch.nn``\nand ``torch.optim``.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import torch\nimport torch.nn as nn\nimport torch.optim as optim" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "2. Define and intialize the neural network\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor sake of example, we will create a neural network for training\nimages. To learn more see the Defining a Neural Network recipe. Build\ntwo variables for the models to eventually save.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "class Net(nn.Module):\n def __init__(self):\n super(Net, self).__init__()\n self.conv1 = nn.Conv2d(3, 6, 5)\n self.pool = nn.MaxPool2d(2, 2)\n self.conv2 = nn.Conv2d(6, 16, 5)\n self.fc1 = nn.Linear(16 * 5 * 5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, 10)\n\n def forward(self, x):\n x = self.pool(F.relu(self.conv1(x)))\n x = self.pool(F.relu(self.conv2(x)))\n x = x.view(-1, 16 * 5 * 5)\n x = F.relu(self.fc1(x))\n x = F.relu(self.fc2(x))\n x = self.fc3(x)\n return x\n\nnetA = Net()\nnetB = Net()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "3. Initialize the optimizer\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWe will use SGD with momentum to build an optimizer for each model we\ncreated.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "optimizerA = optim.SGD(netA.parameters(), lr=0.001, momentum=0.9)\noptimizerB = optim.SGD(netB.parameters(), lr=0.001, momentum=0.9)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "4. Save multiple models\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nCollect all relevant information and build your dictionary.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# Specify a path to save to\nPATH = \"model.pt\"\n\ntorch.save({\n 'modelA_state_dict': netA.state_dict(),\n 'modelB_state_dict': netB.state_dict(),\n 'optimizerA_state_dict': optimizerA.state_dict(),\n 'optimizerB_state_dict': optimizerB.state_dict(),\n }, PATH)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "4. Load multiple models\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nRemember to first initialize the models and optimizers, then load the\ndictionary locally.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "modelA = Net()\nmodelB = Net()\noptimModelA = optim.SGD(modelA.parameters(), lr=0.001, momentum=0.9)\noptimModelB = optim.SGD(modelB.parameters(), lr=0.001, momentum=0.9)\n\ncheckpoint = torch.load(PATH)\nmodelA.load_state_dict(checkpoint['modelA_state_dict'])\nmodelB.load_state_dict(checkpoint['modelB_state_dict'])\noptimizerA.load_state_dict(checkpoint['optimizerA_state_dict'])\noptimizerB.load_state_dict(checkpoint['optimizerB_state_dict'])\n\nmodelA.eval()\nmodelB.eval()\n# - or -\nmodelA.train()\nmodelB.train()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You must call ``model.eval()`` to set dropout and batch normalization\nlayers to evaluation mode before running inference. Failing to do this\nwill yield inconsistent inference results.\n\nIf you wish to resuming training, call ``model.train()`` to ensure these\nlayers are in training mode.\n\nCongratulations! You have successfully saved and loaded multiple models\nin PyTorch.\n\nLearn More\n----------\n\nTake a look at these other recipes to continue your learning:\n\n- TBD\n- TBD\n\n\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.4" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/recipes/recipes/saving_multiple_models_in_one_file.py b/recipes/recipes/saving_multiple_models_in_one_file.py deleted file mode 100644 index b2f38247b4f..00000000000 --- a/recipes/recipes/saving_multiple_models_in_one_file.py +++ /dev/null @@ -1,162 +0,0 @@ -""" -Saving and loading multiple models in one file using PyTorch -============================================================ -Saving and loading multiple models can be helpful for reusing models -that you have previously trained. - -Introduction ------------- -When saving a model comprised of multiple ``torch.nn.Modules``, such as -a GAN, a sequence-to-sequence model, or an ensemble of models, you must -save a dictionary of each model’s state_dict and corresponding -optimizer. You can also save any other items that may aid you in -resuming training by simply appending them to the dictionary. -To load the models, first initialize the models and optimizers, then -load the dictionary locally using ``torch.load()``. From here, you can -easily access the saved items by simply querying the dictionary as you -would expect. -In this recipe, we will demonstrate how to save multiple models to one -file using PyTorch. - -Setup ------ -Before we begin, we need to install ``torch`` if it isn’t already -available. - -:: - - pip install torch - -""" - - - -###################################################################### -# Steps -# ----- -# -# 1. Import all necessary libraries for loading our data -# 2. Define and intialize the neural network -# 3. Initialize the optimizer -# 4. Save multiple models -# 5. Load multiple models -# -# 1. Import necessary libraries for loading our data -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` -# and ``torch.optim``. -# - -import torch -import torch.nn as nn -import torch.optim as optim - - -###################################################################### -# 2. Define and intialize the neural network -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# For sake of example, we will create a neural network for training -# images. To learn more see the Defining a Neural Network recipe. Build -# two variables for the models to eventually save. -# - -class Net(nn.Module): - def __init__(self): - super(Net, self).__init__() - self.conv1 = nn.Conv2d(3, 6, 5) - self.pool = nn.MaxPool2d(2, 2) - self.conv2 = nn.Conv2d(6, 16, 5) - self.fc1 = nn.Linear(16 * 5 * 5, 120) - self.fc2 = nn.Linear(120, 84) - self.fc3 = nn.Linear(84, 10) - - def forward(self, x): - x = self.pool(F.relu(self.conv1(x))) - x = self.pool(F.relu(self.conv2(x))) - x = x.view(-1, 16 * 5 * 5) - x = F.relu(self.fc1(x)) - x = F.relu(self.fc2(x)) - x = self.fc3(x) - return x - -netA = Net() -netB = Net() - - -###################################################################### -# 3. Initialize the optimizer -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# We will use SGD with momentum to build an optimizer for each model we -# created. -# - -optimizerA = optim.SGD(netA.parameters(), lr=0.001, momentum=0.9) -optimizerB = optim.SGD(netB.parameters(), lr=0.001, momentum=0.9) - - -###################################################################### -# 4. Save multiple models -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Collect all relevant information and build your dictionary. -# - -# Specify a path to save to -PATH = "model.pt" - -torch.save({ - 'modelA_state_dict': netA.state_dict(), - 'modelB_state_dict': netB.state_dict(), - 'optimizerA_state_dict': optimizerA.state_dict(), - 'optimizerB_state_dict': optimizerB.state_dict(), - }, PATH) - - -###################################################################### -# 4. Load multiple models -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Remember to first initialize the models and optimizers, then load the -# dictionary locally. -# - -modelA = Net() -modelB = Net() -optimModelA = optim.SGD(modelA.parameters(), lr=0.001, momentum=0.9) -optimModelB = optim.SGD(modelB.parameters(), lr=0.001, momentum=0.9) - -checkpoint = torch.load(PATH) -modelA.load_state_dict(checkpoint['modelA_state_dict']) -modelB.load_state_dict(checkpoint['modelB_state_dict']) -optimizerA.load_state_dict(checkpoint['optimizerA_state_dict']) -optimizerB.load_state_dict(checkpoint['optimizerB_state_dict']) - -modelA.eval() -modelB.eval() -# - or - -modelA.train() -modelB.train() - - -###################################################################### -# You must call ``model.eval()`` to set dropout and batch normalization -# layers to evaluation mode before running inference. Failing to do this -# will yield inconsistent inference results. -# -# If you wish to resuming training, call ``model.train()`` to ensure these -# layers are in training mode. -# -# Congratulations! You have successfully saved and loaded multiple models -# in PyTorch. -# -# Learn More -# ---------- -# -# Take a look at these other recipes to continue your learning: -# -# - TBD -# - TBD -# diff --git a/recipes/recipes/saving_multiple_models_in_one_file.rst b/recipes/recipes/saving_multiple_models_in_one_file.rst deleted file mode 100644 index 1e73a7c8783..00000000000 --- a/recipes/recipes/saving_multiple_models_in_one_file.rst +++ /dev/null @@ -1,218 +0,0 @@ -.. note:: - :class: sphx-glr-download-link-note - - Click :ref:`here ` to download the full example code -.. rst-class:: sphx-glr-example-title - -.. _sphx_glr_recipes_recipes_saving_multiple_models_in_one_file.py: - - -Saving and loading multiple models in one file using PyTorch -============================================================ -Saving and loading multiple models can be helpful for reusing models -that you have previously trained. - -Introduction ------------- -When saving a model comprised of multiple ``torch.nn.Modules``, such as -a GAN, a sequence-to-sequence model, or an ensemble of models, you must -save a dictionary of each model’s state_dict and corresponding -optimizer. You can also save any other items that may aid you in -resuming training by simply appending them to the dictionary. -To load the models, first initialize the models and optimizers, then -load the dictionary locally using ``torch.load()``. From here, you can -easily access the saved items by simply querying the dictionary as you -would expect. -In this recipe, we will demonstrate how to save multiple models to one -file using PyTorch. - -Setup ------ -Before we begin, we need to install ``torch`` if it isn’t already -available. - -:: - - pip install torch - -Steps ------ - -1. Import all necessary libraries for loading our data -2. Define and intialize the neural network -3. Initialize the optimizer -4. Save multiple models -5. Load multiple models - -1. Import necessary libraries for loading our data -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` -and ``torch.optim``. - - - -.. code-block:: default - - - import torch - import torch.nn as nn - import torch.optim as optim - - - -2. Define and intialize the neural network -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For sake of example, we will create a neural network for training -images. To learn more see the Defining a Neural Network recipe. Build -two variables for the models to eventually save. - - - -.. code-block:: default - - - class Net(nn.Module): - def __init__(self): - super(Net, self).__init__() - self.conv1 = nn.Conv2d(3, 6, 5) - self.pool = nn.MaxPool2d(2, 2) - self.conv2 = nn.Conv2d(6, 16, 5) - self.fc1 = nn.Linear(16 * 5 * 5, 120) - self.fc2 = nn.Linear(120, 84) - self.fc3 = nn.Linear(84, 10) - - def forward(self, x): - x = self.pool(F.relu(self.conv1(x))) - x = self.pool(F.relu(self.conv2(x))) - x = x.view(-1, 16 * 5 * 5) - x = F.relu(self.fc1(x)) - x = F.relu(self.fc2(x)) - x = self.fc3(x) - return x - - netA = Net() - netB = Net() - - - -3. Initialize the optimizer -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -We will use SGD with momentum to build an optimizer for each model we -created. - - - -.. code-block:: default - - - optimizerA = optim.SGD(netA.parameters(), lr=0.001, momentum=0.9) - optimizerB = optim.SGD(netB.parameters(), lr=0.001, momentum=0.9) - - - -4. Save multiple models -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Collect all relevant information and build your dictionary. - - - -.. code-block:: default - - - # Specify a path to save to - PATH = "model.pt" - - torch.save({ - 'modelA_state_dict': netA.state_dict(), - 'modelB_state_dict': netB.state_dict(), - 'optimizerA_state_dict': optimizerA.state_dict(), - 'optimizerB_state_dict': optimizerB.state_dict(), - }, PATH) - - - -4. Load multiple models -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Remember to first initialize the models and optimizers, then load the -dictionary locally. - - - -.. code-block:: default - - - modelA = Net() - modelB = Net() - optimModelA = optim.SGD(modelA.parameters(), lr=0.001, momentum=0.9) - optimModelB = optim.SGD(modelB.parameters(), lr=0.001, momentum=0.9) - - checkpoint = torch.load(PATH) - modelA.load_state_dict(checkpoint['modelA_state_dict']) - modelB.load_state_dict(checkpoint['modelB_state_dict']) - optimizerA.load_state_dict(checkpoint['optimizerA_state_dict']) - optimizerB.load_state_dict(checkpoint['optimizerB_state_dict']) - - modelA.eval() - modelB.eval() - # - or - - modelA.train() - modelB.train() - - - -You must call ``model.eval()`` to set dropout and batch normalization -layers to evaluation mode before running inference. Failing to do this -will yield inconsistent inference results. - -If you wish to resuming training, call ``model.train()`` to ensure these -layers are in training mode. - -Congratulations! You have successfully saved and loaded multiple models -in PyTorch. - -Learn More ----------- - -Take a look at these other recipes to continue your learning: - -- TBD -- TBD - - - -.. rst-class:: sphx-glr-timing - - **Total running time of the script:** ( 0 minutes 0.000 seconds) - - -.. _sphx_glr_download_recipes_recipes_saving_multiple_models_in_one_file.py: - - -.. only :: html - - .. container:: sphx-glr-footer - :class: sphx-glr-footer-example - - - - .. container:: sphx-glr-download - - :download:`Download Python source code: saving_multiple_models_in_one_file.py ` - - - - .. container:: sphx-glr-download - - :download:`Download Jupyter notebook: saving_multiple_models_in_one_file.ipynb ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/tensorboard_with_pytorch.ipynb b/recipes/recipes/tensorboard_with_pytorch.ipynb deleted file mode 100644 index 0aca76ce383..00000000000 --- a/recipes/recipes/tensorboard_with_pytorch.ipynb +++ /dev/null @@ -1,125 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\nHow to use TensorBoard with PyTorch\n===================================\nTensorBoard is a visualization toolkit for machine learning experimentation. \nTensorBoard allows tracking and visualizing metrics such as loss and accuracy, \nvisualizing the model graph, viewing histograms, displaying images and much more. \nIn this tutorial we are going to cover TensorBoard installation, \nbasic usage with PyTorch, and how to visualize data you logged in TensorBoard UI.\n\nInstallation\n----------------------\nPyTorch should be installed to log models and metrics into TensorBoard log \ndirectory. The following command will install PyTorch 1.4+ via \nAnaconda (recommended):\n\n::\n\n $ conda install pytorch torchvision -c pytorch \n \n\nor pip\n\n::\n\n $ pip install torch torchvision\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Using TensorBoard in PyTorch\n-----\n\nLet\u2019s now try using TensorBoard with PyTorch! Before logging anything, \nwe need to create a ``SummaryWriter`` instance.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import torch\nfrom torch.utils.tensorboard import SummaryWriter\nwriter = SummaryWriter()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Writer will output to ``./runs/`` directory by default.\n\n\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Log scalars\n-----\n\nIn machine learning, it\u2019s important to understand key metrics such as \nloss and how they change during training. Scalar helps to save \nthe loss value of each training step, or the accuracy after each epoch. \n\nTo log a scalar value, use \n``add_scalar(tag, scalar_value, global_step=None, walltime=None)``. \nFor example, lets create a simple linear regression training, and \nlog loss value using ``add_scalar``\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "x = torch.arange(-5, 5, 0.1).view(-1, 1)\ny = -5 * x + 0.1 * torch.randn(x.size())\n\nmodel = torch.nn.Linear(1, 1)\ncriterion = torch.nn.MSELoss()\noptimizer = torch.optim.SGD(model.parameters(), lr = 0.1)\n\ndef train_model(iter):\n for epoch in range(iter):\n y1 = model(x)\n loss = criterion(y1, y)\n writer.add_scalar(\"Loss/train\", loss, epoch)\n optimizer.zero_grad()\n loss.backward()\n optimizer.step()\n \ntrain_model(10)\nwriter.flush()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Call ``flush()`` method to make sure that all pending events \nhave been written to disk.\n\nSee `torch.utils.tensorboard tutorials `_ \nto find more TensorBoard visualization types you can log.\n\nIf you do not need the summary writer anymore, call ``close()`` method.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "writer.close()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Run TensorBoard\n-----\n\nInstall TensorBoard through the command line to visualize data you logged\n\n::\n\n $ pip install tensorboard\n\n\nNow, start TensorBoard, specifying the root log directory you used above. \nArgument ``logdir`` points to directory where TensorBoard will look to find \nevent files that it can display. TensorBoard will recursively walk \nthe directory structure rooted at logdir, looking for .*tfevents.* files.\n\n::\n\n $ tensorboard --logdir=runs\n\nGo to the URL it provides OR to `http://localhost:6006/ `_\n\n![](../../_static/img/thumbnails/tensorboard_scalars.png)\n\n :scale: 40 %\n\nThis dashboard shows how the loss and accuracy change with every epoch. \nYou can use it to also track training speed, learning rate, and other \nscalar values. It\u2019s helpful to compare these metrics across different \ntraining runs to improve your model.\n\n\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Share TensorBoard dashboards\n-----\n\n`TensorBoard.dev `_ lets you upload and share \nyour ML experiment results with anyone. Use TensorBoard.dev to host, \ntrack, and share your TensorBoard dashboards.\n\nInstall the latest version of TensorBoard to use the uploader.\n\n:: \n\n $ pip install tensorboard --upgrade\n\nUse a simple command to upload and share your TensorBoard.\n\n:: \n\n $ tensorboard dev upload --logdir runs \\\n --name \"My latest experiment\" \\ # optional\n --description \"Simple comparison of several hyperparameters\" # optional\n\nFor help, run ``$ tensorboard dev --help``.\n\n**Note:** Uploaded TensorBoards are public and visible to everyone. \nDo not upload sensitive data.\n\nView your TensorBoard live at URL provided in your terminal. \nE.g. `https://tensorboard.dev/experiment/AdYd1TgeTlaLWXx6I8JUbA `_\n\n\n![](../../_static/img/thumbnails/tensorboard_dev.png)\n\n :scale: 40 %\n\n\n

Note

TensorBoard.dev currently supports only scalars dashboard.

\n\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Learn More\n----------------------------\n\n- `torch.utils.tensorboard `_ docs\n- `Visualizing models, data, and training with TensorBoard `_ tutorial\n\n\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.4" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/recipes/recipes/tensorboard_with_pytorch.py b/recipes/recipes/tensorboard_with_pytorch.py deleted file mode 100644 index c51a24728ad..00000000000 --- a/recipes/recipes/tensorboard_with_pytorch.py +++ /dev/null @@ -1,168 +0,0 @@ -""" -How to use TensorBoard with PyTorch -=================================== -TensorBoard is a visualization toolkit for machine learning experimentation. -TensorBoard allows tracking and visualizing metrics such as loss and accuracy, -visualizing the model graph, viewing histograms, displaying images and much more. -In this tutorial we are going to cover TensorBoard installation, -basic usage with PyTorch, and how to visualize data you logged in TensorBoard UI. - -Installation ----------------------- -PyTorch should be installed to log models and metrics into TensorBoard log -directory. The following command will install PyTorch 1.4+ via -Anaconda (recommended): - -:: - - $ conda install pytorch torchvision -c pytorch - - -or pip - -:: - - $ pip install torch torchvision - -""" - -###################################################################### -# Using TensorBoard in PyTorch -# ----- -# -# Let’s now try using TensorBoard with PyTorch! Before logging anything, -# we need to create a ``SummaryWriter`` instance. -# - -import torch -from torch.utils.tensorboard import SummaryWriter -writer = SummaryWriter() - -###################################################################### -# Writer will output to ``./runs/`` directory by default. -# - - -###################################################################### -# Log scalars -# ----- -# -# In machine learning, it’s important to understand key metrics such as -# loss and how they change during training. Scalar helps to save -# the loss value of each training step, or the accuracy after each epoch. -# -# To log a scalar value, use -# ``add_scalar(tag, scalar_value, global_step=None, walltime=None)``. -# For example, lets create a simple linear regression training, and -# log loss value using ``add_scalar`` -# - -x = torch.arange(-5, 5, 0.1).view(-1, 1) -y = -5 * x + 0.1 * torch.randn(x.size()) - -model = torch.nn.Linear(1, 1) -criterion = torch.nn.MSELoss() -optimizer = torch.optim.SGD(model.parameters(), lr = 0.1) - -def train_model(iter): - for epoch in range(iter): - y1 = model(x) - loss = criterion(y1, y) - writer.add_scalar("Loss/train", loss, epoch) - optimizer.zero_grad() - loss.backward() - optimizer.step() - -train_model(10) -writer.flush() - - -###################################################################### -# Call ``flush()`` method to make sure that all pending events -# have been written to disk. -# -# See `torch.utils.tensorboard tutorials `_ -# to find more TensorBoard visualization types you can log. -# -# If you do not need the summary writer anymore, call ``close()`` method. -# - -writer.close() - -###################################################################### -# Run TensorBoard -# ----- -# -# Install TensorBoard through the command line to visualize data you logged -# -# :: -# -# $ pip install tensorboard -# -# -# Now, start TensorBoard, specifying the root log directory you used above. -# Argument ``logdir`` points to directory where TensorBoard will look to find -# event files that it can display. TensorBoard will recursively walk -# the directory structure rooted at logdir, looking for .*tfevents.* files. -# -# :: -# -# $ tensorboard --logdir=runs -# -# Go to the URL it provides OR to `http://localhost:6006/ `_ -# -# .. image:: ../../_static/img/thumbnails/tensorboard_scalars.png -# :scale: 40 % -# -# This dashboard shows how the loss and accuracy change with every epoch. -# You can use it to also track training speed, learning rate, and other -# scalar values. It’s helpful to compare these metrics across different -# training runs to improve your model. -# - - -###################################################################### -# Share TensorBoard dashboards -# ----- -# -# `TensorBoard.dev `_ lets you upload and share -# your ML experiment results with anyone. Use TensorBoard.dev to host, -# track, and share your TensorBoard dashboards. -# -# Install the latest version of TensorBoard to use the uploader. -# -# :: -# -# $ pip install tensorboard --upgrade -# -# Use a simple command to upload and share your TensorBoard. -# -# :: -# -# $ tensorboard dev upload --logdir runs \ -# --name "My latest experiment" \ # optional -# --description "Simple comparison of several hyperparameters" # optional -# -# For help, run ``$ tensorboard dev --help``. -# -# **Note:** Uploaded TensorBoards are public and visible to everyone. -# Do not upload sensitive data. -# -# View your TensorBoard live at URL provided in your terminal. -# E.g. `https://tensorboard.dev/experiment/AdYd1TgeTlaLWXx6I8JUbA `_ -# -# -# .. image:: ../../_static/img/thumbnails/tensorboard_dev.png -# :scale: 40 % -# -# -# .. note:: -# TensorBoard.dev currently supports only scalars dashboard. - -######################################################################## -# Learn More -# ---------------------------- -# -# - `torch.utils.tensorboard `_ docs -# - `Visualizing models, data, and training with TensorBoard `_ tutorial -# diff --git a/recipes/recipes/tensorboard_with_pytorch.rst b/recipes/recipes/tensorboard_with_pytorch.rst deleted file mode 100644 index c7e7013323f..00000000000 --- a/recipes/recipes/tensorboard_with_pytorch.rst +++ /dev/null @@ -1,212 +0,0 @@ -.. note:: - :class: sphx-glr-download-link-note - - Click :ref:`here ` to download the full example code -.. rst-class:: sphx-glr-example-title - -.. _sphx_glr_recipes_recipes_tensorboard_with_pytorch.py: - - -How to use TensorBoard with PyTorch -=================================== -TensorBoard is a visualization toolkit for machine learning experimentation. -TensorBoard allows tracking and visualizing metrics such as loss and accuracy, -visualizing the model graph, viewing histograms, displaying images and much more. -In this tutorial we are going to cover TensorBoard installation, -basic usage with PyTorch, and how to visualize data you logged in TensorBoard UI. - -Installation ----------------------- -PyTorch should be installed to log models and metrics into TensorBoard log -directory. The following command will install PyTorch 1.4+ via -Anaconda (recommended): - -:: - - $ conda install pytorch torchvision -c pytorch - - -or pip - -:: - - $ pip install torch torchvision -Using TensorBoard in PyTorch ------ - -Let’s now try using TensorBoard with PyTorch! Before logging anything, -we need to create a ``SummaryWriter`` instance. - - - -.. code-block:: default - - - import torch - from torch.utils.tensorboard import SummaryWriter - writer = SummaryWriter() - - -Writer will output to ``./runs/`` directory by default. - - -Log scalars ------ - -In machine learning, it’s important to understand key metrics such as -loss and how they change during training. Scalar helps to save -the loss value of each training step, or the accuracy after each epoch. - -To log a scalar value, use -``add_scalar(tag, scalar_value, global_step=None, walltime=None)``. -For example, lets create a simple linear regression training, and -log loss value using ``add_scalar`` - - - -.. code-block:: default - - - x = torch.arange(-5, 5, 0.1).view(-1, 1) - y = -5 * x + 0.1 * torch.randn(x.size()) - - model = torch.nn.Linear(1, 1) - criterion = torch.nn.MSELoss() - optimizer = torch.optim.SGD(model.parameters(), lr = 0.1) - - def train_model(iter): - for epoch in range(iter): - y1 = model(x) - loss = criterion(y1, y) - writer.add_scalar("Loss/train", loss, epoch) - optimizer.zero_grad() - loss.backward() - optimizer.step() - - train_model(10) - writer.flush() - - - -Call ``flush()`` method to make sure that all pending events -have been written to disk. - -See `torch.utils.tensorboard tutorials `_ -to find more TensorBoard visualization types you can log. - -If you do not need the summary writer anymore, call ``close()`` method. - - - -.. code-block:: default - - - writer.close() - - -Run TensorBoard ------ - -Install TensorBoard through the command line to visualize data you logged - -:: - - $ pip install tensorboard - - -Now, start TensorBoard, specifying the root log directory you used above. -Argument ``logdir`` points to directory where TensorBoard will look to find -event files that it can display. TensorBoard will recursively walk -the directory structure rooted at logdir, looking for .*tfevents.* files. - -:: - - $ tensorboard --logdir=runs - -Go to the URL it provides OR to `http://localhost:6006/ `_ - -.. image:: ../../_static/img/thumbnails/tensorboard_scalars.png - :scale: 40 % - -This dashboard shows how the loss and accuracy change with every epoch. -You can use it to also track training speed, learning rate, and other -scalar values. It’s helpful to compare these metrics across different -training runs to improve your model. - - -Share TensorBoard dashboards ------ - -`TensorBoard.dev `_ lets you upload and share -your ML experiment results with anyone. Use TensorBoard.dev to host, -track, and share your TensorBoard dashboards. - -Install the latest version of TensorBoard to use the uploader. - -:: - - $ pip install tensorboard --upgrade - -Use a simple command to upload and share your TensorBoard. - -:: - - $ tensorboard dev upload --logdir runs \ - --name "My latest experiment" \ # optional - --description "Simple comparison of several hyperparameters" # optional - -For help, run ``$ tensorboard dev --help``. - -**Note:** Uploaded TensorBoards are public and visible to everyone. -Do not upload sensitive data. - -View your TensorBoard live at URL provided in your terminal. -E.g. `https://tensorboard.dev/experiment/AdYd1TgeTlaLWXx6I8JUbA `_ - - -.. image:: ../../_static/img/thumbnails/tensorboard_dev.png - :scale: 40 % - - -.. note:: - TensorBoard.dev currently supports only scalars dashboard. - -Learn More ----------------------------- - -- `torch.utils.tensorboard `_ docs -- `Visualizing models, data, and training with TensorBoard `_ tutorial - - - -.. rst-class:: sphx-glr-timing - - **Total running time of the script:** ( 0 minutes 0.000 seconds) - - -.. _sphx_glr_download_recipes_recipes_tensorboard_with_pytorch.py: - - -.. only :: html - - .. container:: sphx-glr-footer - :class: sphx-glr-footer-example - - - - .. container:: sphx-glr-download - - :download:`Download Python source code: tensorboard_with_pytorch.py ` - - - - .. container:: sphx-glr-download - - :download:`Download Jupyter notebook: tensorboard_with_pytorch.ipynb ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.ipynb b/recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.ipynb deleted file mode 100644 index a32b3603529..00000000000 --- a/recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.ipynb +++ /dev/null @@ -1,122 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\nWarmstarting model using parameters from a different model in PyTorch\n=====================================================================\nPartially loading a model or loading a partial model are common\nscenarios when transfer learning or training a new complex model.\nLeveraging trained parameters, even if only a few are usable, will help\nto warmstart the training process and hopefully help your model converge\nmuch faster than training from scratch.\n\nIntroduction\n------------\nWhether you are loading from a partial ``state_dict``, which is missing\nsome keys, or loading a ``state_dict`` with more keys than the model\nthat you are loading into, you can set the strict argument to ``False``\nin the ``load_state_dict()`` function to ignore non-matching keys.\nIn this recipe, we will experiment with warmstarting a model using\nparameters of a different model.\n\nSetup\n-----\nBefore we begin, we need to install ``torch`` if it isn\u2019t already\navailable.\n\n::\n\n pip install torch\n \n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Steps\n-----\n\n1. Import all necessary libraries for loading our data\n2. Define and intialize the neural network A and B\n3. Save model A\n4. Load into model B\n\n1. Import necessary libraries for loading our data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor this recipe, we will use ``torch`` and its subsidiaries ``torch.nn``\nand ``torch.optim``.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import torch\nimport torch.nn as nn\nimport torch.optim as optim" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "2. Define and intialize the neural network A and B\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor sake of example, we will create a neural network for training\nimages. To learn more see the Defining a Neural Network recipe. We will\ncreate two neural networks for sake of loading one parameter of type A\ninto type B.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "class NetA(nn.Module):\n def __init__(self):\n super(NetA, self).__init__()\n self.conv1 = nn.Conv2d(3, 6, 5)\n self.pool = nn.MaxPool2d(2, 2)\n self.conv2 = nn.Conv2d(6, 16, 5)\n self.fc1 = nn.Linear(16 * 5 * 5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, 10)\n\n def forward(self, x):\n x = self.pool(F.relu(self.conv1(x)))\n x = self.pool(F.relu(self.conv2(x)))\n x = x.view(-1, 16 * 5 * 5)\n x = F.relu(self.fc1(x))\n x = F.relu(self.fc2(x))\n x = self.fc3(x)\n return x\n\nnetA = NetA()\n\nclass NetB(nn.Module):\n def __init__(self):\n super(NetB, self).__init__()\n self.conv1 = nn.Conv2d(3, 6, 5)\n self.pool = nn.MaxPool2d(2, 2)\n self.conv2 = nn.Conv2d(6, 16, 5)\n self.fc1 = nn.Linear(16 * 5 * 5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, 10)\n\n def forward(self, x):\n x = self.pool(F.relu(self.conv1(x)))\n x = self.pool(F.relu(self.conv2(x)))\n x = x.view(-1, 16 * 5 * 5)\n x = F.relu(self.fc1(x))\n x = F.relu(self.fc2(x))\n x = self.fc3(x)\n return x\n\nnetB = NetB()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "3. Save model A\n~~~~~~~~~~~~~~~~~~~\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# Specify a path to save to\nPATH = \"model.pt\"\n\ntorch.save(netA.state_dict(), PATH)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "4. Load into model B\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf you want to load parameters from one layer to another, but some keys\ndo not match, simply change the name of the parameter keys in the\nstate_dict that you are loading to match the keys in the model that you\nare loading into.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "netB.load_state_dict(torch.load(PATH), strict=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can see that all keys matched successfully!\n\nCongratulations! You have successfully warmstarted a model using\nparameters from a different model in PyTorch.\n\nLearn More\n----------\n\nTake a look at these other recipes to continue your learning:\n\n- `Saving and loading multiple models in one file using PyTorch `__\n- `Saving and loading models across devices in PyTorch `__\n\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.4" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.py b/recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.py deleted file mode 100644 index 410bc992c6e..00000000000 --- a/recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.py +++ /dev/null @@ -1,142 +0,0 @@ -""" -Warmstarting model using parameters from a different model in PyTorch -===================================================================== -Partially loading a model or loading a partial model are common -scenarios when transfer learning or training a new complex model. -Leveraging trained parameters, even if only a few are usable, will help -to warmstart the training process and hopefully help your model converge -much faster than training from scratch. - -Introduction ------------- -Whether you are loading from a partial ``state_dict``, which is missing -some keys, or loading a ``state_dict`` with more keys than the model -that you are loading into, you can set the strict argument to ``False`` -in the ``load_state_dict()`` function to ignore non-matching keys. -In this recipe, we will experiment with warmstarting a model using -parameters of a different model. - -Setup ------ -Before we begin, we need to install ``torch`` if it isn’t already -available. - -:: - - pip install torch - -""" - - - -###################################################################### -# Steps -# ----- -# -# 1. Import all necessary libraries for loading our data -# 2. Define and intialize the neural network A and B -# 3. Save model A -# 4. Load into model B -# -# 1. Import necessary libraries for loading our data -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` -# and ``torch.optim``. -# - -import torch -import torch.nn as nn -import torch.optim as optim - - -###################################################################### -# 2. Define and intialize the neural network A and B -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# For sake of example, we will create a neural network for training -# images. To learn more see the Defining a Neural Network recipe. We will -# create two neural networks for sake of loading one parameter of type A -# into type B. -# - -class NetA(nn.Module): - def __init__(self): - super(NetA, self).__init__() - self.conv1 = nn.Conv2d(3, 6, 5) - self.pool = nn.MaxPool2d(2, 2) - self.conv2 = nn.Conv2d(6, 16, 5) - self.fc1 = nn.Linear(16 * 5 * 5, 120) - self.fc2 = nn.Linear(120, 84) - self.fc3 = nn.Linear(84, 10) - - def forward(self, x): - x = self.pool(F.relu(self.conv1(x))) - x = self.pool(F.relu(self.conv2(x))) - x = x.view(-1, 16 * 5 * 5) - x = F.relu(self.fc1(x)) - x = F.relu(self.fc2(x)) - x = self.fc3(x) - return x - -netA = NetA() - -class NetB(nn.Module): - def __init__(self): - super(NetB, self).__init__() - self.conv1 = nn.Conv2d(3, 6, 5) - self.pool = nn.MaxPool2d(2, 2) - self.conv2 = nn.Conv2d(6, 16, 5) - self.fc1 = nn.Linear(16 * 5 * 5, 120) - self.fc2 = nn.Linear(120, 84) - self.fc3 = nn.Linear(84, 10) - - def forward(self, x): - x = self.pool(F.relu(self.conv1(x))) - x = self.pool(F.relu(self.conv2(x))) - x = x.view(-1, 16 * 5 * 5) - x = F.relu(self.fc1(x)) - x = F.relu(self.fc2(x)) - x = self.fc3(x) - return x - -netB = NetB() - - -###################################################################### -# 3. Save model A -# ~~~~~~~~~~~~~~~~~~~ -# - -# Specify a path to save to -PATH = "model.pt" - -torch.save(netA.state_dict(), PATH) - - -###################################################################### -# 4. Load into model B -# ~~~~~~~~~~~~~~~~~~~~~~~~ -# -# If you want to load parameters from one layer to another, but some keys -# do not match, simply change the name of the parameter keys in the -# state_dict that you are loading to match the keys in the model that you -# are loading into. -# - -netB.load_state_dict(torch.load(PATH), strict=False) - - -###################################################################### -# You can see that all keys matched successfully! -# -# Congratulations! You have successfully warmstarted a model using -# parameters from a different model in PyTorch. -# -# Learn More -# ---------- -# -# Take a look at these other recipes to continue your learning: -# -# - `Saving and loading multiple models in one file using PyTorch `__ -# - `Saving and loading models across devices in PyTorch `__ diff --git a/recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.rst b/recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.rst deleted file mode 100644 index a2308a4d566..00000000000 --- a/recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.rst +++ /dev/null @@ -1,194 +0,0 @@ -.. note:: - :class: sphx-glr-download-link-note - - Click :ref:`here ` to download the full example code -.. rst-class:: sphx-glr-example-title - -.. _sphx_glr_recipes_recipes_warmstarting_model_using_parameters_from_a_different_model.py: - - -Warmstarting model using parameters from a different model in PyTorch -===================================================================== -Partially loading a model or loading a partial model are common -scenarios when transfer learning or training a new complex model. -Leveraging trained parameters, even if only a few are usable, will help -to warmstart the training process and hopefully help your model converge -much faster than training from scratch. - -Introduction ------------- -Whether you are loading from a partial ``state_dict``, which is missing -some keys, or loading a ``state_dict`` with more keys than the model -that you are loading into, you can set the strict argument to ``False`` -in the ``load_state_dict()`` function to ignore non-matching keys. -In this recipe, we will experiment with warmstarting a model using -parameters of a different model. - -Setup ------ -Before we begin, we need to install ``torch`` if it isn’t already -available. - -:: - - pip install torch - -Steps ------ - -1. Import all necessary libraries for loading our data -2. Define and intialize the neural network A and B -3. Save model A -4. Load into model B - -1. Import necessary libraries for loading our data -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` -and ``torch.optim``. - - - -.. code-block:: default - - - import torch - import torch.nn as nn - import torch.optim as optim - - - -2. Define and intialize the neural network A and B -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For sake of example, we will create a neural network for training -images. To learn more see the Defining a Neural Network recipe. We will -create two neural networks for sake of loading one parameter of type A -into type B. - - - -.. code-block:: default - - - class NetA(nn.Module): - def __init__(self): - super(NetA, self).__init__() - self.conv1 = nn.Conv2d(3, 6, 5) - self.pool = nn.MaxPool2d(2, 2) - self.conv2 = nn.Conv2d(6, 16, 5) - self.fc1 = nn.Linear(16 * 5 * 5, 120) - self.fc2 = nn.Linear(120, 84) - self.fc3 = nn.Linear(84, 10) - - def forward(self, x): - x = self.pool(F.relu(self.conv1(x))) - x = self.pool(F.relu(self.conv2(x))) - x = x.view(-1, 16 * 5 * 5) - x = F.relu(self.fc1(x)) - x = F.relu(self.fc2(x)) - x = self.fc3(x) - return x - - netA = NetA() - - class NetB(nn.Module): - def __init__(self): - super(NetB, self).__init__() - self.conv1 = nn.Conv2d(3, 6, 5) - self.pool = nn.MaxPool2d(2, 2) - self.conv2 = nn.Conv2d(6, 16, 5) - self.fc1 = nn.Linear(16 * 5 * 5, 120) - self.fc2 = nn.Linear(120, 84) - self.fc3 = nn.Linear(84, 10) - - def forward(self, x): - x = self.pool(F.relu(self.conv1(x))) - x = self.pool(F.relu(self.conv2(x))) - x = x.view(-1, 16 * 5 * 5) - x = F.relu(self.fc1(x)) - x = F.relu(self.fc2(x)) - x = self.fc3(x) - return x - - netB = NetB() - - - -3. Save model A -~~~~~~~~~~~~~~~~~~~ - - - -.. code-block:: default - - - # Specify a path to save to - PATH = "model.pt" - - torch.save(netA.state_dict(), PATH) - - - -4. Load into model B -~~~~~~~~~~~~~~~~~~~~~~~~ - -If you want to load parameters from one layer to another, but some keys -do not match, simply change the name of the parameter keys in the -state_dict that you are loading to match the keys in the model that you -are loading into. - - - -.. code-block:: default - - - netB.load_state_dict(torch.load(PATH), strict=False) - - - -You can see that all keys matched successfully! - -Congratulations! You have successfully warmstarted a model using -parameters from a different model in PyTorch. - -Learn More ----------- - -Take a look at these other recipes to continue your learning: - -- `Saving and loading multiple models in one file using PyTorch `__ -- `Saving and loading models across devices in PyTorch `__ - - -.. rst-class:: sphx-glr-timing - - **Total running time of the script:** ( 0 minutes 0.000 seconds) - - -.. _sphx_glr_download_recipes_recipes_warmstarting_model_using_parameters_from_a_different_model.py: - - -.. only :: html - - .. container:: sphx-glr-footer - :class: sphx-glr-footer-example - - - - .. container:: sphx-glr-download - - :download:`Download Python source code: warmstarting_model_using_parameters_from_a_different_model.py ` - - - - .. container:: sphx-glr-download - - :download:`Download Jupyter notebook: warmstarting_model_using_parameters_from_a_different_model.ipynb ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/what_is_state_dict.ipynb b/recipes/recipes/what_is_state_dict.ipynb deleted file mode 100644 index d597dbcd0c9..00000000000 --- a/recipes/recipes/what_is_state_dict.ipynb +++ /dev/null @@ -1,122 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\nWhat is a state_dict in PyTorch\n===============================\nIn PyTorch, the learnable parameters (i.e. weights and biases) of a\n``torch.nn.Module`` model are contained in the model\u2019s parameters\n(accessed with ``model.parameters()``). A ``state_dict`` is simply a\nPython dictionary object that maps each layer to its parameter tensor.\n\nIntroduction\n------------\nA ``state_dict`` is an integral entity if you are interested in saving\nor loading models from PyTorch.\nBecause ``state_dict`` objects are Python dictionaries, they can be\neasily saved, updated, altered, and restored, adding a great deal of\nmodularity to PyTorch models and optimizers.\nNote that only layers with learnable parameters (convolutional layers,\nlinear layers, etc.) and registered buffers (batchnorm\u2019s running_mean)\nhave entries in the model\u2019s ``state_dict``. Optimizer objects\n(``torch.optim``) also have a ``state_dict``, which contains information\nabout the optimizer\u2019s state, as well as the hyperparameters used.\nIn this recipe, we will see how ``state_dict`` is used with a simple\nmodel.\n\nSetup\n-----\nBefore we begin, we need to install ``torch`` if it isn\u2019t already\navailable.\n\n::\n\n pip install torchaudio\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Steps\n-----\n\n1. Import all necessary libraries for loading our data\n2. Define and intialize the neural network\n3. Initialize the optimizer\n4. Access the model and optimizer ``state_dict``\n\n1. Import necessary libraries for loading our data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor this recipe, we will use ``torch`` and its subsidiaries ``torch.nn``\nand ``torch.optim``.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import torch\nimport torch.nn as nn\nimport torch.optim as optim" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "2. Define and intialize the neural network\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor sake of example, we will create a neural network for training\nimages. To learn more see the Defining a Neural Network recipe.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "class Net(nn.Module):\n def __init__(self):\n super(Net, self).__init__()\n self.conv1 = nn.Conv2d(3, 6, 5)\n self.pool = nn.MaxPool2d(2, 2)\n self.conv2 = nn.Conv2d(6, 16, 5)\n self.fc1 = nn.Linear(16 * 5 * 5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, 10)\n\n def forward(self, x):\n x = self.pool(F.relu(self.conv1(x)))\n x = self.pool(F.relu(self.conv2(x)))\n x = x.view(-1, 16 * 5 * 5)\n x = F.relu(self.fc1(x))\n x = F.relu(self.fc2(x))\n x = self.fc3(x)\n return x\n\nnet = Net()\nprint(net)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "3. Initialize the optimizer\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWe will use SGD with momentum.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "4. Access the model and optimizer ``state_dict``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nNow that we have constructed our model and optimizer, we can understand\nwhat is preserved in their respective ``state_dict`` properties.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "# Print model's state_dict\nprint(\"Model's state_dict:\")\nfor param_tensor in net.state_dict():\n print(param_tensor, \"\\t\", net.state_dict()[param_tensor].size())\n\nprint()\n\n# Print optimizer's state_dict\nprint(\"Optimizer's state_dict:\")\nfor var_name in optimizer.state_dict():\n print(var_name, \"\\t\", optimizer.state_dict()[var_name])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This information is relevant for saving and loading the model and\noptimizers for future use.\n\nCongratulations! You have successfully used ``state_dict`` in PyTorch.\n\nLearn More\n----------\n\nTake a look at these other recipes to continue your learning:\n\n- `Saving and loading models for inference in PyTorch `__\n- `Saving and loading a general checkpoint in PyTorch `__\n\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.4" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/recipes/recipes/what_is_state_dict.py b/recipes/recipes/what_is_state_dict.py deleted file mode 100644 index 8e718e9071e..00000000000 --- a/recipes/recipes/what_is_state_dict.py +++ /dev/null @@ -1,132 +0,0 @@ -""" -What is a state_dict in PyTorch -=============================== -In PyTorch, the learnable parameters (i.e. weights and biases) of a -``torch.nn.Module`` model are contained in the model’s parameters -(accessed with ``model.parameters()``). A ``state_dict`` is simply a -Python dictionary object that maps each layer to its parameter tensor. - -Introduction ------------- -A ``state_dict`` is an integral entity if you are interested in saving -or loading models from PyTorch. -Because ``state_dict`` objects are Python dictionaries, they can be -easily saved, updated, altered, and restored, adding a great deal of -modularity to PyTorch models and optimizers. -Note that only layers with learnable parameters (convolutional layers, -linear layers, etc.) and registered buffers (batchnorm’s running_mean) -have entries in the model’s ``state_dict``. Optimizer objects -(``torch.optim``) also have a ``state_dict``, which contains information -about the optimizer’s state, as well as the hyperparameters used. -In this recipe, we will see how ``state_dict`` is used with a simple -model. - -Setup ------ -Before we begin, we need to install ``torch`` if it isn’t already -available. - -:: - - pip install torchaudio - -""" - - - -###################################################################### -# Steps -# ----- -# -# 1. Import all necessary libraries for loading our data -# 2. Define and intialize the neural network -# 3. Initialize the optimizer -# 4. Access the model and optimizer ``state_dict`` -# -# 1. Import necessary libraries for loading our data -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` -# and ``torch.optim``. -# - -import torch -import torch.nn as nn -import torch.optim as optim - - -###################################################################### -# 2. Define and intialize the neural network -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# For sake of example, we will create a neural network for training -# images. To learn more see the Defining a Neural Network recipe. -# - -class Net(nn.Module): - def __init__(self): - super(Net, self).__init__() - self.conv1 = nn.Conv2d(3, 6, 5) - self.pool = nn.MaxPool2d(2, 2) - self.conv2 = nn.Conv2d(6, 16, 5) - self.fc1 = nn.Linear(16 * 5 * 5, 120) - self.fc2 = nn.Linear(120, 84) - self.fc3 = nn.Linear(84, 10) - - def forward(self, x): - x = self.pool(F.relu(self.conv1(x))) - x = self.pool(F.relu(self.conv2(x))) - x = x.view(-1, 16 * 5 * 5) - x = F.relu(self.fc1(x)) - x = F.relu(self.fc2(x)) - x = self.fc3(x) - return x - -net = Net() -print(net) - - -###################################################################### -# 3. Initialize the optimizer -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# We will use SGD with momentum. -# - -optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) - - -###################################################################### -# 4. Access the model and optimizer ``state_dict`` -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Now that we have constructed our model and optimizer, we can understand -# what is preserved in their respective ``state_dict`` properties. -# - -# Print model's state_dict -print("Model's state_dict:") -for param_tensor in net.state_dict(): - print(param_tensor, "\t", net.state_dict()[param_tensor].size()) - -print() - -# Print optimizer's state_dict -print("Optimizer's state_dict:") -for var_name in optimizer.state_dict(): - print(var_name, "\t", optimizer.state_dict()[var_name]) - - -###################################################################### -# This information is relevant for saving and loading the model and -# optimizers for future use. -# -# Congratulations! You have successfully used ``state_dict`` in PyTorch. -# -# Learn More -# ---------- -# -# Take a look at these other recipes to continue your learning: -# -# - `Saving and loading models for inference in PyTorch `__ -# - `Saving and loading a general checkpoint in PyTorch `__ diff --git a/recipes/recipes/what_is_state_dict.rst b/recipes/recipes/what_is_state_dict.rst deleted file mode 100644 index 223a0556cbc..00000000000 --- a/recipes/recipes/what_is_state_dict.rst +++ /dev/null @@ -1,183 +0,0 @@ -.. note:: - :class: sphx-glr-download-link-note - - Click :ref:`here ` to download the full example code -.. rst-class:: sphx-glr-example-title - -.. _sphx_glr_recipes_recipes_what_is_state_dict.py: - - -What is a state_dict in PyTorch -=============================== -In PyTorch, the learnable parameters (i.e. weights and biases) of a -``torch.nn.Module`` model are contained in the model’s parameters -(accessed with ``model.parameters()``). A ``state_dict`` is simply a -Python dictionary object that maps each layer to its parameter tensor. - -Introduction ------------- -A ``state_dict`` is an integral entity if you are interested in saving -or loading models from PyTorch. -Because ``state_dict`` objects are Python dictionaries, they can be -easily saved, updated, altered, and restored, adding a great deal of -modularity to PyTorch models and optimizers. -Note that only layers with learnable parameters (convolutional layers, -linear layers, etc.) and registered buffers (batchnorm’s running_mean) -have entries in the model’s ``state_dict``. Optimizer objects -(``torch.optim``) also have a ``state_dict``, which contains information -about the optimizer’s state, as well as the hyperparameters used. -In this recipe, we will see how ``state_dict`` is used with a simple -model. - -Setup ------ -Before we begin, we need to install ``torch`` if it isn’t already -available. - -:: - - pip install torchaudio -Steps ------ - -1. Import all necessary libraries for loading our data -2. Define and intialize the neural network -3. Initialize the optimizer -4. Access the model and optimizer ``state_dict`` - -1. Import necessary libraries for loading our data -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For this recipe, we will use ``torch`` and its subsidiaries ``torch.nn`` -and ``torch.optim``. - - - -.. code-block:: default - - - import torch - import torch.nn as nn - import torch.optim as optim - - - -2. Define and intialize the neural network -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For sake of example, we will create a neural network for training -images. To learn more see the Defining a Neural Network recipe. - - - -.. code-block:: default - - - class Net(nn.Module): - def __init__(self): - super(Net, self).__init__() - self.conv1 = nn.Conv2d(3, 6, 5) - self.pool = nn.MaxPool2d(2, 2) - self.conv2 = nn.Conv2d(6, 16, 5) - self.fc1 = nn.Linear(16 * 5 * 5, 120) - self.fc2 = nn.Linear(120, 84) - self.fc3 = nn.Linear(84, 10) - - def forward(self, x): - x = self.pool(F.relu(self.conv1(x))) - x = self.pool(F.relu(self.conv2(x))) - x = x.view(-1, 16 * 5 * 5) - x = F.relu(self.fc1(x)) - x = F.relu(self.fc2(x)) - x = self.fc3(x) - return x - - net = Net() - print(net) - - - -3. Initialize the optimizer -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -We will use SGD with momentum. - - - -.. code-block:: default - - - optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) - - - -4. Access the model and optimizer ``state_dict`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Now that we have constructed our model and optimizer, we can understand -what is preserved in their respective ``state_dict`` properties. - - - -.. code-block:: default - - - # Print model's state_dict - print("Model's state_dict:") - for param_tensor in net.state_dict(): - print(param_tensor, "\t", net.state_dict()[param_tensor].size()) - - print() - - # Print optimizer's state_dict - print("Optimizer's state_dict:") - for var_name in optimizer.state_dict(): - print(var_name, "\t", optimizer.state_dict()[var_name]) - - - -This information is relevant for saving and loading the model and -optimizers for future use. - -Congratulations! You have successfully used ``state_dict`` in PyTorch. - -Learn More ----------- - -Take a look at these other recipes to continue your learning: - -- `Saving and loading models for inference in PyTorch `__ -- `Saving and loading a general checkpoint in PyTorch `__ - - -.. rst-class:: sphx-glr-timing - - **Total running time of the script:** ( 0 minutes 0.000 seconds) - - -.. _sphx_glr_download_recipes_recipes_what_is_state_dict.py: - - -.. only :: html - - .. container:: sphx-glr-footer - :class: sphx-glr-footer-example - - - - .. container:: sphx-glr-download - - :download:`Download Python source code: what_is_state_dict.py ` - - - - .. container:: sphx-glr-download - - :download:`Download Jupyter notebook: what_is_state_dict.ipynb ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes/zeroing_out_gradients.ipynb b/recipes/recipes/zeroing_out_gradients.ipynb deleted file mode 100644 index 3314d6c622e..00000000000 --- a/recipes/recipes/zeroing_out_gradients.ipynb +++ /dev/null @@ -1,140 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\nZeroing out gradients in PyTorch\n================================\nIt is beneficial to zero out gradients when building a neural network.\nThis is because by default, gradients are accumulated in buffers (i.e,\nnot overwritten) whenever ``.backward()`` is called.\n\nIntroduction\n------------\nWhen training your neural network, models are able to increase their\naccuracy through gradient decent. In short, gradient descent is the\nprocess of minimizing our loss (or error) by tweaking the weights and\nbiases in our model.\n\n``torch.Tensor`` is the central class of PyTorch. When you create a\ntensor, if you set its attribute ``.requires_grad`` as ``True``, the\npackage tracks all operations on it. This happens on subsequent backward\npasses. The gradient for this tensor will be accumulated into ``.grad``\nattribute. The accumulation (or sum) of all the gradients is calculated\nwhen .backward() is called on the loss tensor.\n\nThere are cases where it may be necessary to zero-out the gradients of a\ntensor. For example: when you start your training loop, you should zero\nout the gradients so that you can perform this tracking correctly.\nIn this recipe, we will learn how to zero out gradients using the\nPyTorch library. We will demonstrate how to do this by training a neural\nnetwork on the ``CIFAR10`` dataset built into PyTorch.\n\nSetup\n-----\nSince we will be training data in this recipe, if you are in a runable\nnotebook, it is best to switch the runtime to GPU or TPU.\nBefore we begin, we need to install ``torch`` and ``torchvision`` if\nthey aren\u2019t already available.\n\n::\n\n pip install torchvision\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Steps\n-----\n\nSteps 1 through 4 set up our data and neural network for training. The\nprocess of zeroing out the gradients happens in step 5. If you already\nhave your data and neural network built, skip to 5.\n\n1. Import all necessary libraries for loading our data\n2. Load and normalize the dataset\n3. Build the neural network\n4. Define the loss function\n5. Zero the gradients while training the network\n\n1. Import necessary libraries for loading our data\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor this recipe, we will just be using ``torch`` and ``torchvision`` to\naccess the dataset.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import torch\n\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nimport torch.optim as optim\n\nimport torchvision\nimport torchvision.transforms as transforms" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "2. Load and normalize the dataset\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPyTorch features various built-in datasets (see the Loading Data recipe\nfor more information).\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "transform = transforms.Compose(\n [transforms.ToTensor(),\n transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])\n\ntrainset = torchvision.datasets.CIFAR10(root='./data', train=True,\n download=True, transform=transform)\ntrainloader = torch.utils.data.DataLoader(trainset, batch_size=4,\n shuffle=True, num_workers=2)\n\ntestset = torchvision.datasets.CIFAR10(root='./data', train=False,\n download=True, transform=transform)\ntestloader = torch.utils.data.DataLoader(testset, batch_size=4,\n shuffle=False, num_workers=2)\n\nclasses = ('plane', 'car', 'bird', 'cat',\n 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "3. Build the neural network\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWe will use a convolutional neural network. To learn more see the\nDefining a Neural Network recipe.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "class Net(nn.Module):\n def __init__(self):\n super(Net, self).__init__()\n self.conv1 = nn.Conv2d(3, 6, 5)\n self.pool = nn.MaxPool2d(2, 2)\n self.conv2 = nn.Conv2d(6, 16, 5)\n self.fc1 = nn.Linear(16 * 5 * 5, 120)\n self.fc2 = nn.Linear(120, 84)\n self.fc3 = nn.Linear(84, 10)\n\n def forward(self, x):\n x = self.pool(F.relu(self.conv1(x)))\n x = self.pool(F.relu(self.conv2(x)))\n x = x.view(-1, 16 * 5 * 5)\n x = F.relu(self.fc1(x))\n x = F.relu(self.fc2(x))\n x = self.fc3(x)\n return x" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "4. Define a Loss function and optimizer\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nLet\u2019s use a Classification Cross-Entropy loss and SGD with momentum.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "net = Net()\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "5. Zero the gradients while training the network\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis is when things start to get interesting. We simply have to loop\nover our data iterator, and feed the inputs to the network and optimize.\n\nNotice that for each entity of data, we zero out the gradients. This is\nto ensure that we aren\u2019t tracking any unnecessary information when we\ntrain our neural network.\n\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "for epoch in range(2): # loop over the dataset multiple times\n\n running_loss = 0.0\n for i, data in enumerate(trainloader, 0):\n # get the inputs; data is a list of [inputs, labels]\n inputs, labels = data\n\n # zero the parameter gradients\n optimizer.zero_grad()\n\n # forward + backward + optimize\n outputs = net(inputs)\n loss = criterion(outputs, labels)\n loss.backward()\n optimizer.step()\n\n # print statistics\n running_loss += loss.item()\n if i % 2000 == 1999: # print every 2000 mini-batches\n print('[%d, %5d] loss: %.3f' %\n (epoch + 1, i + 1, running_loss / 2000))\n running_loss = 0.0\n\nprint('Finished Training')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also use ``model.zero_grad()``. This is the same as using\n``optimizer.zero_grad()`` as long as all your model parameters are in\nthat optimizer. Use your best judgement to decide which one to use.\n\nCongratulations! You have successfully zeroed out gradients PyTorch.\n\nLearn More\n----------\n\nTake a look at these other recipes to continue your learning:\n\n- `Loading data in PyTorch `__\n- `Saving and loading models across devices in PyTorch `__\n\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.4" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/recipes/recipes/zeroing_out_gradients.py b/recipes/recipes/zeroing_out_gradients.py deleted file mode 100644 index 687120dcfcb..00000000000 --- a/recipes/recipes/zeroing_out_gradients.py +++ /dev/null @@ -1,193 +0,0 @@ -""" -Zeroing out gradients in PyTorch -================================ -It is beneficial to zero out gradients when building a neural network. -This is because by default, gradients are accumulated in buffers (i.e, -not overwritten) whenever ``.backward()`` is called. - -Introduction ------------- -When training your neural network, models are able to increase their -accuracy through gradient decent. In short, gradient descent is the -process of minimizing our loss (or error) by tweaking the weights and -biases in our model. - -``torch.Tensor`` is the central class of PyTorch. When you create a -tensor, if you set its attribute ``.requires_grad`` as ``True``, the -package tracks all operations on it. This happens on subsequent backward -passes. The gradient for this tensor will be accumulated into ``.grad`` -attribute. The accumulation (or sum) of all the gradients is calculated -when .backward() is called on the loss tensor. - -There are cases where it may be necessary to zero-out the gradients of a -tensor. For example: when you start your training loop, you should zero -out the gradients so that you can perform this tracking correctly. -In this recipe, we will learn how to zero out gradients using the -PyTorch library. We will demonstrate how to do this by training a neural -network on the ``CIFAR10`` dataset built into PyTorch. - -Setup ------ -Since we will be training data in this recipe, if you are in a runable -notebook, it is best to switch the runtime to GPU or TPU. -Before we begin, we need to install ``torch`` and ``torchvision`` if -they aren’t already available. - -:: - - pip install torchvision - - -""" - - -###################################################################### -# Steps -# ----- -# -# Steps 1 through 4 set up our data and neural network for training. The -# process of zeroing out the gradients happens in step 5. If you already -# have your data and neural network built, skip to 5. -# -# 1. Import all necessary libraries for loading our data -# 2. Load and normalize the dataset -# 3. Build the neural network -# 4. Define the loss function -# 5. Zero the gradients while training the network -# -# 1. Import necessary libraries for loading our data -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# For this recipe, we will just be using ``torch`` and ``torchvision`` to -# access the dataset. -# - -import torch - -import torch.nn as nn -import torch.nn.functional as F - -import torch.optim as optim - -import torchvision -import torchvision.transforms as transforms - - -###################################################################### -# 2. Load and normalize the dataset -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# PyTorch features various built-in datasets (see the Loading Data recipe -# for more information). -# - -transform = transforms.Compose( - [transforms.ToTensor(), - transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) - -trainset = torchvision.datasets.CIFAR10(root='./data', train=True, - download=True, transform=transform) -trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, - shuffle=True, num_workers=2) - -testset = torchvision.datasets.CIFAR10(root='./data', train=False, - download=True, transform=transform) -testloader = torch.utils.data.DataLoader(testset, batch_size=4, - shuffle=False, num_workers=2) - -classes = ('plane', 'car', 'bird', 'cat', - 'deer', 'dog', 'frog', 'horse', 'ship', 'truck') - - -###################################################################### -# 3. Build the neural network -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# We will use a convolutional neural network. To learn more see the -# Defining a Neural Network recipe. -# - -class Net(nn.Module): - def __init__(self): - super(Net, self).__init__() - self.conv1 = nn.Conv2d(3, 6, 5) - self.pool = nn.MaxPool2d(2, 2) - self.conv2 = nn.Conv2d(6, 16, 5) - self.fc1 = nn.Linear(16 * 5 * 5, 120) - self.fc2 = nn.Linear(120, 84) - self.fc3 = nn.Linear(84, 10) - - def forward(self, x): - x = self.pool(F.relu(self.conv1(x))) - x = self.pool(F.relu(self.conv2(x))) - x = x.view(-1, 16 * 5 * 5) - x = F.relu(self.fc1(x)) - x = F.relu(self.fc2(x)) - x = self.fc3(x) - return x - - -###################################################################### -# 4. Define a Loss function and optimizer -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Let’s use a Classification Cross-Entropy loss and SGD with momentum. -# - -net = Net() -criterion = nn.CrossEntropyLoss() -optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) - - -###################################################################### -# 5. Zero the gradients while training the network -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# This is when things start to get interesting. We simply have to loop -# over our data iterator, and feed the inputs to the network and optimize. -# -# Notice that for each entity of data, we zero out the gradients. This is -# to ensure that we aren’t tracking any unnecessary information when we -# train our neural network. -# - -for epoch in range(2): # loop over the dataset multiple times - - running_loss = 0.0 - for i, data in enumerate(trainloader, 0): - # get the inputs; data is a list of [inputs, labels] - inputs, labels = data - - # zero the parameter gradients - optimizer.zero_grad() - - # forward + backward + optimize - outputs = net(inputs) - loss = criterion(outputs, labels) - loss.backward() - optimizer.step() - - # print statistics - running_loss += loss.item() - if i % 2000 == 1999: # print every 2000 mini-batches - print('[%d, %5d] loss: %.3f' % - (epoch + 1, i + 1, running_loss / 2000)) - running_loss = 0.0 - -print('Finished Training') - - -###################################################################### -# You can also use ``model.zero_grad()``. This is the same as using -# ``optimizer.zero_grad()`` as long as all your model parameters are in -# that optimizer. Use your best judgement to decide which one to use. -# -# Congratulations! You have successfully zeroed out gradients PyTorch. -# -# Learn More -# ---------- -# -# Take a look at these other recipes to continue your learning: -# -# - `Loading data in PyTorch `__ -# - `Saving and loading models across devices in PyTorch `__ diff --git a/recipes/recipes/zeroing_out_gradients.rst b/recipes/recipes/zeroing_out_gradients.rst deleted file mode 100644 index ecfcf7f4a02..00000000000 --- a/recipes/recipes/zeroing_out_gradients.rst +++ /dev/null @@ -1,248 +0,0 @@ -.. note:: - :class: sphx-glr-download-link-note - - Click :ref:`here ` to download the full example code -.. rst-class:: sphx-glr-example-title - -.. _sphx_glr_recipes_recipes_zeroing_out_gradients.py: - - -Zeroing out gradients in PyTorch -================================ -It is beneficial to zero out gradients when building a neural network. -This is because by default, gradients are accumulated in buffers (i.e, -not overwritten) whenever ``.backward()`` is called. - -Introduction ------------- -When training your neural network, models are able to increase their -accuracy through gradient decent. In short, gradient descent is the -process of minimizing our loss (or error) by tweaking the weights and -biases in our model. - -``torch.Tensor`` is the central class of PyTorch. When you create a -tensor, if you set its attribute ``.requires_grad`` as ``True``, the -package tracks all operations on it. This happens on subsequent backward -passes. The gradient for this tensor will be accumulated into ``.grad`` -attribute. The accumulation (or sum) of all the gradients is calculated -when .backward() is called on the loss tensor. - -There are cases where it may be necessary to zero-out the gradients of a -tensor. For example: when you start your training loop, you should zero -out the gradients so that you can perform this tracking correctly. -In this recipe, we will learn how to zero out gradients using the -PyTorch library. We will demonstrate how to do this by training a neural -network on the ``CIFAR10`` dataset built into PyTorch. - -Setup ------ -Since we will be training data in this recipe, if you are in a runable -notebook, it is best to switch the runtime to GPU or TPU. -Before we begin, we need to install ``torch`` and ``torchvision`` if -they aren’t already available. - -:: - - pip install torchvision -Steps ------ - -Steps 1 through 4 set up our data and neural network for training. The -process of zeroing out the gradients happens in step 5. If you already -have your data and neural network built, skip to 5. - -1. Import all necessary libraries for loading our data -2. Load and normalize the dataset -3. Build the neural network -4. Define the loss function -5. Zero the gradients while training the network - -1. Import necessary libraries for loading our data -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For this recipe, we will just be using ``torch`` and ``torchvision`` to -access the dataset. - - - -.. code-block:: default - - - import torch - - import torch.nn as nn - import torch.nn.functional as F - - import torch.optim as optim - - import torchvision - import torchvision.transforms as transforms - - - -2. Load and normalize the dataset -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -PyTorch features various built-in datasets (see the Loading Data recipe -for more information). - - - -.. code-block:: default - - - transform = transforms.Compose( - [transforms.ToTensor(), - transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) - - trainset = torchvision.datasets.CIFAR10(root='./data', train=True, - download=True, transform=transform) - trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, - shuffle=True, num_workers=2) - - testset = torchvision.datasets.CIFAR10(root='./data', train=False, - download=True, transform=transform) - testloader = torch.utils.data.DataLoader(testset, batch_size=4, - shuffle=False, num_workers=2) - - classes = ('plane', 'car', 'bird', 'cat', - 'deer', 'dog', 'frog', 'horse', 'ship', 'truck') - - - -3. Build the neural network -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -We will use a convolutional neural network. To learn more see the -Defining a Neural Network recipe. - - - -.. code-block:: default - - - class Net(nn.Module): - def __init__(self): - super(Net, self).__init__() - self.conv1 = nn.Conv2d(3, 6, 5) - self.pool = nn.MaxPool2d(2, 2) - self.conv2 = nn.Conv2d(6, 16, 5) - self.fc1 = nn.Linear(16 * 5 * 5, 120) - self.fc2 = nn.Linear(120, 84) - self.fc3 = nn.Linear(84, 10) - - def forward(self, x): - x = self.pool(F.relu(self.conv1(x))) - x = self.pool(F.relu(self.conv2(x))) - x = x.view(-1, 16 * 5 * 5) - x = F.relu(self.fc1(x)) - x = F.relu(self.fc2(x)) - x = self.fc3(x) - return x - - - -4. Define a Loss function and optimizer -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Let’s use a Classification Cross-Entropy loss and SGD with momentum. - - - -.. code-block:: default - - - net = Net() - criterion = nn.CrossEntropyLoss() - optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) - - - -5. Zero the gradients while training the network -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This is when things start to get interesting. We simply have to loop -over our data iterator, and feed the inputs to the network and optimize. - -Notice that for each entity of data, we zero out the gradients. This is -to ensure that we aren’t tracking any unnecessary information when we -train our neural network. - - - -.. code-block:: default - - - for epoch in range(2): # loop over the dataset multiple times - - running_loss = 0.0 - for i, data in enumerate(trainloader, 0): - # get the inputs; data is a list of [inputs, labels] - inputs, labels = data - - # zero the parameter gradients - optimizer.zero_grad() - - # forward + backward + optimize - outputs = net(inputs) - loss = criterion(outputs, labels) - loss.backward() - optimizer.step() - - # print statistics - running_loss += loss.item() - if i % 2000 == 1999: # print every 2000 mini-batches - print('[%d, %5d] loss: %.3f' % - (epoch + 1, i + 1, running_loss / 2000)) - running_loss = 0.0 - - print('Finished Training') - - - -You can also use ``model.zero_grad()``. This is the same as using -``optimizer.zero_grad()`` as long as all your model parameters are in -that optimizer. Use your best judgement to decide which one to use. - -Congratulations! You have successfully zeroed out gradients PyTorch. - -Learn More ----------- - -Take a look at these other recipes to continue your learning: - -- `Loading data in PyTorch `__ -- `Saving and loading models across devices in PyTorch `__ - - -.. rst-class:: sphx-glr-timing - - **Total running time of the script:** ( 0 minutes 0.000 seconds) - - -.. _sphx_glr_download_recipes_recipes_zeroing_out_gradients.py: - - -.. only :: html - - .. container:: sphx-glr-footer - :class: sphx-glr-footer-example - - - - .. container:: sphx-glr-download - - :download:`Download Python source code: zeroing_out_gradients.py ` - - - - .. container:: sphx-glr-download - - :download:`Download Jupyter notebook: zeroing_out_gradients.ipynb ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/recipes/recipes_index.rst b/recipes/recipes_index.rst deleted file mode 100644 index 251db43297b..00000000000 --- a/recipes/recipes_index.rst +++ /dev/null @@ -1,197 +0,0 @@ -PyTorch Recipes ---------------------------------------------- -Recipes are bite-sized bite-sized, actionable examples of how to use specific PyTorch features, different from our full-length tutorials. - -.. raw:: html - -
- - -
- - - -
- -
- -
-
- -.. Add recipe cards below this line - -.. Basics - -.. customcarditem:: - :header: Loading data in PyTorch - :card_description: Learn how to use PyTorch packages to prepare and load common datasets for your model. - :image: ../_static/img/thumbnails/cropped/loading-data.PNG - :link: ../recipes/recipes/loading_data_recipe.html - :tags: Basics - - -.. customcarditem:: - :header: Defining a Neural Network - :card_description: Learn how to use PyTorch's torch.nn package to create and define a neural network the MNIST dataset. - :image: ../_static/img/thumbnails/cropped/defining-a-network.PNG - :link: ../recipes/recipes/defining_a_neural_network.html - :tags: Basics - -.. customcarditem:: - :header: What is a state_dict in PyTorch - :card_description: Learn how state_dict objects, Python dictionaries, are used in saving or loading models from PyTorch. - :image: ../_static/img/thumbnails/cropped/what-is-a-state-dict.PNG - :link: ../recipes/recipes/what_is_state_dict.html - :tags: Basics - -.. customcarditem:: - :header: Saving and loading models for inference in PyTorch - :card_description: Learn about the two approaches for saving and loading models for inference in PyTorch - via the state_dict and via the entire model. - :image: ../_static/img/thumbnails/cropped/saving-and-loading-models-for-inference.PNG - :link: ../recipes/recipes/saving_and_loading_models_for_inference.html - :tags: Basics - - -.. customcarditem:: - :header: Saving and loading a general checkpoint in PyTorch - :card_description: Saving and loading a general checkpoint model for inference or resuming training can be helpful for picking up where you last left off. In this recipe, explore how to save and load multiple checkpoints. - :image: ../_static/img/thumbnails/cropped/saving-and-loading-general-checkpoint.PNG - :link: ../recipes/recipes/saving_and_loading_a_general_checkpoint.html - :tags: Basics - -.. customcarditem:: - :header: Saving and loading multiple models in one file using PyTorch - :card_description: In this recipe, learn how saving and loading multiple models can be helpful for reusing models that you have previously trained. - :image: ../_static/img/thumbnails/cropped/saving-multiple-models.PNG - :link: ../recipes/recipes/saving_multiple_models_in_one_file.html - :tags: Basics - -.. customcarditem:: - :header: Warmstarting model using parameters from a different model in PyTorch - :card_description: Learn how warmstarting the training process by partially loading a model or loading a partial model can help your model converge much faster than training from scratch. - :image: ../_static/img/thumbnails/cropped/warmstarting-models.PNG - :link: ../recipes/recipes/warmstarting_model_using_parameters_from_a_different_model.html - :tags: Basics - -.. customcarditem:: - :header: Saving and loading models across devices in PyTorch - :card_description: Learn how saving and loading models across devices (CPUs and GPUs) is relatively straightforward using PyTorch. - :image: ../_static/img/thumbnails/cropped/saving-and-loading-models-across-devices.PNG - :link: ../recipes/recipes/save_load_across_devices.html - :tags: Basics - -.. customcarditem:: - :header: Zeroing out gradients in PyTorch - :card_description: Learn when you should zero out graidents and how doing so can help increase the accuracy of your model. - :image: ../_static/img/thumbnails/cropped/zeroing-out-gradients.PNG - :link: ../recipes/recipes/zeroing_out_gradients.html - :tags: Basics - -.. customcarditem:: - :header: PyTorch Profiler - :card_description: Learn how to use PyTorch's profiler to measure operators time and memory consumption - :image: ../_static/img/thumbnails/cropped/profiler.png - :link: ../recipes/recipes/profiler.html - :tags: Basics - -.. Customization - -.. customcarditem:: - :header: Custom Datasets, Transforms & Dataloaders - :card_description: Learn how to leverage the PyTorch dataset API to easily create a custom dataset and custom dataloader. - :image: ../_static/img/thumbnails/cropped/custom-datasets-transforms-and-dataloaders.png - :link: ../recipes/recipes/custom_dataset_transforms_loader.html - :tags: Data-Customization - -.. Interpretability - -.. customcarditem:: - :header: Model Interpretability using Captum - :card_description: Learn how to use Captum attribute the predictions of an image classifier to their corresponding image features and visualize the attribution results. - :image: ../_static/img/thumbnails/cropped/model-interpretability-using-captum.png - :link: ../recipes/recipes/Captum_Recipe.html - :tags: Interpretability,Captum - -.. customcarditem:: - :header: How to use TensorBoard with PyTorch - :card_description: Learn basic usage of TensorBoard with PyTorch, and how to visualize data in TensorBoard UI - :image: ../_static/img/thumbnails/tensorboard_scalars.png - :link: ../recipes/recipes/tensorboard_with_pytorch.html - :tags: Visualization,TensorBoard - -.. Quantization - -.. customcarditem:: - :header: Dynamic Quantization - :card_description: Apply dynamic quantization to a simple LSTM model. - :image: ../_static/img/thumbnails/cropped/using-dynamic-post-training-quantization.png - :link: ../recipes/recipes/dynamic_quantization.html - :tags: Quantization,Text,Model-Optimization - - -.. Production Development - -.. customcarditem:: - :header: TorchScript for Deployment - :card_description: Learn how to export your trained model in TorchScript format and how to load your TorchScript model in C++ and do inference. - :image: ../_static/img/thumbnails/cropped/torchscript_overview.png - :link: ../recipes/torchscript_inference.html - :tags: TorchScript - -.. customcarditem:: - :header: Deploying with Flask - :card_description: Learn how to use Flask, a lightweight web server, to quickly setup a web API from your trained PyTorch model. - :image: ../_static/img/thumbnails/cropped/using-flask-create-restful-api.png - :link: ../recipes/deployment_with_flask.html - :tags: Production,TorchScript - -.. customcarditem:: - :header: PyTorch Mobile Performance Recipes - :card_description: List of recipes for performance optimizations for using PyTorch on Mobile. - :image: ../_static/img/thumbnails/cropped/zeroing-out-gradients.PNG - :link: ../recipes/mobile_perf.html - :tags: Mobile,Model-Optimization - - -.. End of tutorial card section - -.. raw:: html - -
- - - -
- -
- -.. ----------------------------------------- -.. Page TOC -.. ----------------------------------------- -.. toctree:: - :hidden: - - /recipes/recipes/loading_data_recipe - /recipes/recipes/defining_a_neural_network - /recipes/recipes/what_is_state_dict - /recipes/recipes/saving_and_loading_models_for_inference - /recipes/recipes/saving_and_loading_a_general_checkpoint - /recipes/recipes/saving_multiple_models_in_one_file - /recipes/recipes/warmstarting_model_using_parameters_from_a_different_model - /recipes/recipes/save_load_across_devices - /recipes/recipes/zeroing_out_gradients - /recipes/recipes/profiler - /recipes/recipes/custom_dataset_transforms_loader - /recipes/recipes/Captum_Recipe - /recipes/recipes/tensorboard_with_pytorch - /recipes/recipes/dynamic_quantization - /recipes/torchscript_inference - /recipes/deployment_with_flask diff --git a/recipes/torchscript_inference.rst b/recipes/torchscript_inference.rst deleted file mode 100644 index 54068e70723..00000000000 --- a/recipes/torchscript_inference.rst +++ /dev/null @@ -1,197 +0,0 @@ -TorchScript for Deployment -========================== - -In this recipe, you will learn: - -- What TorchScript is -- How to export your trained model in TorchScript format -- How to load your TorchScript model in C++ and do inference - -Requirements ------------- - -- PyTorch 1.5 -- TorchVision 0.6.0 -- libtorch 1.5 -- C++ compiler - -The instructions for installing the three PyTorch components are -available at `pytorch.org`_. The C++ compiler will depend on your -platform. - -What is TorchScript? --------------------- - -**TorchScript** is an intermediate representation of a PyTorch model -(subclass of ``nn.Module``) that can then be run in a high-performance -environment like C++. It’s a high-performance subset of Python that is -meant to be consumed by the **PyTorch JIT Compiler,** which performs -run-time optimization on your model’s computation. TorchScript is the -recommended model format for doing scaled inference with PyTorch models. -For more information, see the PyTorch `Introduction to TorchScript -tutorial`_, the `Loading A TorchScript Model in C++ tutorial`_, and the -`full TorchScript documentation`_, all of which are available on -`pytorch.org`_. - -How to Export Your Model ------------------------- - -As an example, let’s take a pretrained vision model. All of the -pretrained models in TorchVision are compatible with TorchScript. - -Run the following Python 3 code, either in a script or from the REPL: - -.. code:: python3 - - import torch - import torch.nn.functional as F - import torchvision.models as models - - r18 = models.resnet18(pretrained=True) # We now have an instance of the pretrained model - r18_scripted = torch.jit.script(r18) # *** This is the TorchScript export - dummy_input = torch.rand(1, 3, 224, 224) # We should run a quick test - -Let’s do a sanity check on the equivalence of the two models: - -:: - - unscripted_output = r18(dummy_input) # Get the unscripted model's prediction... - scripted_output = r18_scripted(dummy_input) # ...and do the same for the scripted version - - unscripted_top5 = F.softmax(unscripted_output, dim=1).topk(5).indices - scripted_top5 = F.softmax(scripted_output, dim=1).topk(5).indices - - print('Python model top 5 results:\n {}'.format(unscripted_top5)) - print('TorchScript model top 5 results:\n {}'.format(scripted_top5)) - -You should see that both versions of the model give the same results: - -:: - - Python model top 5 results: - tensor([[463, 600, 731, 899, 898]]) - TorchScript model top 5 results: - tensor([[463, 600, 731, 899, 898]]) - -With that check confirmed, go ahead and save the model: - -:: - - r18_scripted.save('r18_scripted.pt') - -Loading TorchScript Models in C++ ---------------------------------- - -Create the following C++ file and name it ``ts-infer.cpp``: - -.. code:: cpp - - #include - #include - - - int main(int argc, const char* argv[]) { - if (argc != 2) { - std::cerr << "usage: ts-infer \n"; - return -1; - } - - std::cout << "Loading model...\n"; - - // deserialize ScriptModule - torch::jit::script::Module module; - try { - module = torch::jit::load(argv[1]); - } catch (const c10::Error& e) { - std::cerr << "Error loading model\n"; - std::cerr << e.msg_without_backtrace(); - return -1; - } - - std::cout << "Model loaded successfully\n"; - - torch::NoGradGuard no_grad; // ensures that autograd is off - module.eval(); // turn off dropout and other training-time layers/functions - - // create an input "image" - std::vector inputs; - inputs.push_back(torch::rand({1, 3, 224, 224})); - - // execute model and package output as tensor - at::Tensor output = module.forward(inputs).toTensor(); - - namespace F = torch::nn::functional; - at::Tensor output_sm = F::softmax(output, F::SoftmaxFuncOptions(1)); - std::tuple top5_tensor = output_sm.topk(5); - at::Tensor top5 = std::get<1>(top5_tensor); - - std::cout << top5[0] << "\n"; - - std::cout << "\nDONE\n"; - return 0; - } - -This program: - -- Loads the model you specify on the command line -- Creates a dummy “image” input tensor -- Performs inference on the input - -Also, notice that there is no dependency on TorchVision in this code. -The saved version of your TorchScript model has your learning weights -*and* your computation graph - nothing else is needed. - -Building and Running Your C++ Inference Engine ----------------------------------------------- - -Create the following ``CMakeLists.txt`` file: - -:: - - cmake_minimum_required(VERSION 3.0 FATAL_ERROR) - project(custom_ops) - - find_package(Torch REQUIRED) - - add_executable(ts-infer ts-infer.cpp) - target_link_libraries(ts-infer "${TORCH_LIBRARIES}") - set_property(TARGET ts-infer PROPERTY CXX_STANDARD 11) - -Make the program: - -:: - - cmake -DCMAKE_PREFIX_PATH= - make - -Now, we can run inference in C++, and verify that we get a result: - -:: - - $ ./ts-infer r18_scripted.pt - Loading model... - Model loaded successfully - 418 - 845 - 111 - 892 - 644 - [ CPULongType{5} ] - - DONE - -Important Resources -------------------- - -- `pytorch.org`_ for installation instructions, and more documentation - and tutorials. -- `Introduction to TorchScript tutorial`_ for a deeper initial - exposition of TorchScript -- `Full TorchScript documentation`_ for complete TorchScript language - and API reference - -.. _pytorch.org: https://pytorch.org/ -.. _Introduction to TorchScript tutorial: https://pytorch.org/tutorials/beginner/Intro_to_TorchScript_tutorial.html -.. _Full TorchScript documentation: https://pytorch.org/docs/stable/jit.html -.. _Loading A TorchScript Model in C++ tutorial: https://pytorch.org/tutorials/advanced/cpp_export.html -.. _full TorchScript documentation: https://pytorch.org/docs/stable/jit.html From 8e4e3796cdcd112db2906c5cd853ffdab0f7b060 Mon Sep 17 00:00:00 2001 From: IvanKobzarev Date: Thu, 25 Jun 2020 18:01:54 -0600 Subject: [PATCH 07/33] [android] android native app recipe --- .../android_native_app_with_custom_op.rst | 735 ++++++++++++++++++ recipes_source/recipes_index.rst | 6 + 2 files changed, 741 insertions(+) create mode 100644 recipes_source/android_native_app_with_custom_op.rst diff --git a/recipes_source/android_native_app_with_custom_op.rst b/recipes_source/android_native_app_with_custom_op.rst new file mode 100644 index 00000000000..ba488de9672 --- /dev/null +++ b/recipes_source/android_native_app_with_custom_op.rst @@ -0,0 +1,735 @@ +Making Native Android Application that uses PyTorch prebuilt libraries +====================================================================== + +**Author**: `Ivan Kobzarev `_ + +In this recipe, you will learn: + + - How to make an Android Application that uses LibTorch API from native code (C++). + + - How to use within this application TorchScript models with custom operators. + +The full setup of this app you can find in `PyTorch Android Demo Application Repository `_. + + +Setup +~~~~~ + +You will need a Python 3 environment with the following packages (and their dependencies) installed: + +- PyTorch 1.6 + +For Android development, you will need to install: + +- Android NDK + +:: + + wget https://dl.google.com/android/repository/android-ndk-r19c-linux-x86_64.zip + unzip android-ndk-r19c-linux-x86_64.zip + export ANDROID_NDK=$(pwd)/android-ndk-r19c + + +- Android SDK + +:: + + wget https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip + unzip sdk-tools-linux-3859397.zip -d android_sdk + export ANDROID_HOME=$(pwd)/android_sdk + + + +- Gradle 4.10.3 + +Gradle is the most widely used build system for android applications, and we will need it to build our application. Download it and add to the path to use ``gradle`` in the command line. + +.. code-block:: shell + + wget https://services.gradle.org/distributions/gradle-4.10.3-bin.zip + unzip gradle-4.10.3-bin.zip + export GRADLE_HOME=$(pwd)/gradle-4.10.3 + export PATH="${GRADLE_HOME}/bin/:${PATH}" + +- JDK + +Gradle requires JDK, you need to install it and set environment variable ``JAVA_HOME`` to point to it. +For example you can install OpenJDK, following `instructions `_. + +- OpenCV SDK for Android + +Our custom operator will be implemented using the OpenCV library. To use it for Android, we need to download OpenCV SDK for Android with prebuilt libraries. +Download from `OpenCV releases page `_. Unzip it and set the environment variable ``OPENCV_ANDROID_SDK`` to it. + + +Preparing TorchScript Model With Custom C++ Operator +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +TorchScript allows using custom C++ operators, to read about it with details you can read +`the dedicated tutorial `_. + +As a result, you can script the model that uses custom op, that uses OpenCV ``cv::warpPerspective`` function. + +.. code-block:: python + + import torch + import torch.utils.cpp_extension + + print(torch.version.__version__) + op_source = """ + #include + #include + + torch::Tensor warp_perspective(torch::Tensor image, torch::Tensor warp) { + cv::Mat image_mat(/*rows=*/image.size(0), + /*cols=*/image.size(1), + /*type=*/CV_32FC1, + /*data=*/image.data_ptr()); + cv::Mat warp_mat(/*rows=*/warp.size(0), + /*cols=*/warp.size(1), + /*type=*/CV_32FC1, + /*data=*/warp.data_ptr()); + + cv::Mat output_mat; + cv::warpPerspective(image_mat, output_mat, warp_mat, /*dsize=*/{64, 64}); + + torch::Tensor output = + torch::from_blob(output_mat.ptr(), /*sizes=*/{64, 64}); + return output.clone(); + } + + static auto registry = + torch::RegisterOperators("my_ops::warp_perspective", &warp_perspective); + """ + + torch.utils.cpp_extension.load_inline( + name="warp_perspective", + cpp_sources=op_source, + extra_ldflags=["-lopencv_core", "-lopencv_imgproc"], + is_python_module=False, + verbose=True, + ) + + print(torch.ops.my_ops.warp_perspective) + + + @torch.jit.script + def compute(x, y): + if bool(x[0][0] == 42): + z = 5 + else: + z = 10 + x = torch.ops.my_ops.warp_perspective(x, torch.eye(3)) + return x.matmul(y) + z + + + compute.save("compute.pt") + + +This snippet generates ``compute.pt`` file which is TorchScript model that uses custom op ``my_ops.warp_perspective``. + +You need to have installed OpenCV for development to run it. +For Linux systems that can be done using the next commands: +CentOS: + +.. code-block:: shell + + yum install opencv-devel + +Ubuntu: + +.. code-block:: shell + + apt-get install libopencv-dev + +Making Android Application +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +After we succeeded in having ``compute.pt``, we want to use this TorchScript model within Android application. Using general TorchScript models (without custom operators) on Android, using Java API, you can find `here `_. We can not use this approach for our case, as our model uses a custom operator(``my_ops.warp_perspective``), default TorchScript execution will fail to find it. + +Registration of ops is not exposed to PyTorch Java API, thus we need to build Android Application with native part (C++) and using LibTorch C++ API to implement and register the same custom operator for Android. As our operator uses the OpenCV library - we will use prebuilt OpenCV Android libraries and use the same functions from OpenCV. + +Let's start creating Android application in ``NativeApp`` folder. + +.. code-block:: shell + + mkdir NativeApp + cd NativeApp + +Android Application Build Setup +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Android Application build consists of the main gradle part and native build CMake part. +All the listings here are the full file listing, that if to recreate the whole structure, +you will be able to build and install the result Android Application without any code additions. + +Gradle Build Setup +------------------ +We will need to add gradle setup files: build.gradle, gradle.properties, settings.gradle. +More about Android Gradle build configurations you can find `here `_. + +``NativeApp/settings.gradle`` + +.. code-block:: gradle + + include ':app' + + +``NativeApp/gradle.properties`` + +.. code-block:: gradle + + android.useAndroidX=true + android.enableJetifier=true + + +``NativeApp/build.gradle`` + +.. code-block:: gradle + + buildscript { + repositories { + google() + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.5.0' + } + } + + allprojects { + repositories { + google() + jcenter() + } + } + + +In ``NativeApp/build.gradle`` we specify Android gradle plugin version `3.5.0`. This version is not recent. Still, we use it as PyTorch android gradle builds use this version. + +``NativeApp/settings.gradle`` shows that out project contains only one module - ``app``, which will be our Android Application. + +.. code-block:: shell + + mkdir app + cd app + + +``NativeApp/app/build.gradle`` + +.. code-block:: gradle + + apply plugin: 'com.android.application' + + repositories { + jcenter() + maven { + url "https://oss.sonatype.org/content/repositories/snapshots" + } + } + + android { + configurations { + extractForNativeBuild + } + compileSdkVersion 28 + buildToolsVersion "29.0.2" + defaultConfig { + applicationId "org.pytorch.nativeapp" + minSdkVersion 21 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + externalNativeBuild { + cmake { + arguments "-DANDROID_STL=c++_shared" + } + } + } + buildTypes { + release { + minifyEnabled false + } + } + externalNativeBuild { + cmake { + path "CMakeLists.txt" + } + } + sourceSets { + main { + jniLibs.srcDirs = ['src/main/jniLibs'] + } + } + } + + dependencies { + implementation 'com.android.support:appcompat-v7:28.0.0' + + implementation 'org.pytorch:pytorch_android:1.6.0-SNAPSHOT' + extractForNativeBuild 'org.pytorch:pytorch_android:1.6.0-SNAPSHOT' + } + + task extractAARForNativeBuild { + doLast { + configurations.extractForNativeBuild.files.each { + def file = it.absoluteFile + copy { + from zipTree(file) + into "$buildDir/$file.name" + include "headers/**" + include "jni/**" + } + } + } + } + + tasks.whenTaskAdded { task -> + if (task.name.contains('externalNativeBuild')) { + task.dependsOn(extractAARForNativeBuild) + } + } + +This gradle build script registers dependencies on pytorch_android snapshots, +that are published on nightly channels. + +As they are published to nexus sonatype repository - we need to register that repository: +``https://oss.sonatype.org/content/repositories/snapshots``. + +In our application we need to use LibTorch C++ API in our application native build part. For this, we need access to prebuilt binaries and headers. They are prepacked in PyTorch Android builds, which is published in Maven repositories. + +To use PyTorch Android prebuilt libraries from gradle dependencies (which is aar files) - +we should add registration for configuration ``extractForNativeBuild``, +add this configuration in dependencies and put its definition in the end. + +``extractForNativeBuild`` task will call ``extractAARForNativeBuild`` task that unpacks pytorch_android aar +to gradle build directory. + +Pytorch_android aar contains LibTorch headers in ``headers`` folder +and prebuilt libraries for different Android abis in ``jni`` folder: +``$ANDROID_ABI/libpytorch_jni.so``, ``$ANDROID_ABI/libfbjni.so``. +We will use them for our native build. + +The native build is registered in this ``build.gradle`` with lines: + +.. code-block:: gradle + + android { + ... + externalNativeBuild { + cmake { + path "CMakeLists.txt" + } + } + ... + defaultConfig { + externalNativeBuild { + cmake { + arguments "-DANDROID_STL=c++_shared" + } + } + } + +We will use ``CMake`` configuration for a native build. Here we also specify that we will dynamically link with STL, as we have several libraries. More about this, you can find `here `_. + + +Native Build CMake Setup +------------------------ + +The native build will be configured in ``NativeApp/app/CMakeLists.txt``: + +.. code-block:: cmake + + cmake_minimum_required(VERSION 3.4.1) + set(TARGET pytorch_nativeapp) + project(${TARGET} CXX) + set(CMAKE_CXX_STANDARD 14) + + set(build_DIR ${CMAKE_SOURCE_DIR}/build) + + set(pytorch_testapp_cpp_DIR ${CMAKE_CURRENT_LIST_DIR}/src/main/cpp) + file(GLOB pytorch_testapp_SOURCES + ${pytorch_testapp_cpp_DIR}/pytorch_nativeapp.cpp + ) + + add_library(${TARGET} SHARED + ${pytorch_testapp_SOURCES} + ) + + file(GLOB PYTORCH_INCLUDE_DIRS "${build_DIR}/pytorch_android*.aar/headers") + file(GLOB PYTORCH_LINK_DIRS "${build_DIR}/pytorch_android*.aar/jni/${ANDROID_ABI}") + + target_compile_options(${TARGET} PRIVATE + -fexceptions + ) + + set(BUILD_SUBDIR ${ANDROID_ABI}) + + find_library(PYTORCH_LIBRARY pytorch_jni + PATHS ${PYTORCH_LINK_DIRS} + NO_CMAKE_FIND_ROOT_PATH) + find_library(FBJNI_LIBRARY fbjni + PATHS ${PYTORCH_LINK_DIRS} + NO_CMAKE_FIND_ROOT_PATH) + + # OpenCV + if(NOT DEFINED ENV{OPENCV_ANDROID_SDK}) + message(FATAL_ERROR "Environment var OPENCV_ANDROID_SDK is not set") + endif() + + set(OPENCV_INCLUDE_DIR "$ENV{OPENCV_ANDROID_SDK}/sdk/native/jni/include") + + target_include_directories(${TARGET} PRIVATE + "${OPENCV_INCLUDE_DIR}" + ${PYTORCH_INCLUDE_DIRS}) + + set(OPENCV_LIB_DIR "$ENV{OPENCV_ANDROID_SDK}/sdk/native/libs/${ANDROID_ABI}") + + find_library(OPENCV_LIBRARY opencv_java4 + PATHS ${OPENCV_LIB_DIR} + NO_CMAKE_FIND_ROOT_PATH) + + target_link_libraries(${TARGET} + ${PYTORCH_LIBRARY} + ${FBJNI_LIBRARY} + ${OPENCV_LIBRARY} + log) + +Here we register only one source file ``pytorch_nativeapp.cpp``. + +On the previous step in ``NativeApp/app/build.gradle``, the task ``extractAARForNativeBuild`` extracts headers and native libraries to build directory. We set ``PYTORCH_INCLUDE_DIRS`` and ``PYTORCH_LINK_DIRS`` to them. + +After that, we find libraries ``libpytorch_jni.so`` and ``libfbjni.so`` and add them to the linking of our target. + +As we plan to use OpenCV functions to implement our custom operator ``my_ops::warp_perspective`` - we need to link to ``libopencv_java4.so``. It is packaged in OpenCV SDK for Android, that was downloaded on the Setup step. +In this configuration, we find it by environment variable ``OPENCV_ANDROID_SDK``. + +We also link with ``log`` library to be able to log our results to Android LogCat. + +As we link to OpenCV Android SDK's ``libopencv_java4.so``, we should copy it to ``NativeApp/app/src/main/jniLibs/${ANDROID_ABI}`` + +.. code-block:: shell + + cp -R $OPENCV_ANDROID_SDK/sdk/native/libs/* NativeApp/app/src/main/jniLibs/ + + +Adding the model file to the application +---------------------------------------- + +To package the TorschScript model ``compute.pt`` within our application we should copy it to assets folder: + +.. code-block:: shell + + mkdir -p NativeApp/app/src/main/assets + cp compute.pt NativeApp/app/src/main/assets + + +Android Application Manifest +---------------------------- + +Every Android application has a manifest. +Here we specify the application name, package, main activity. + +``NativeApp/app/src/main/AndroidManifest.xml``: + +.. code-block:: default + + + + + + + + + + + + + + + + +Sources +------- + +Java Code +--------- + +Now we are ready to implement our MainActivity in + +``NativeApp/app/src/main/java/org/pytorch/nativeapp/MainActivity.java``: + +.. code-block:: java + + package org.pytorch.nativeapp; + + import android.content.Context; + import android.os.Bundle; + import android.util.Log; + import androidx.appcompat.app.AppCompatActivity; + import java.io.File; + import java.io.FileOutputStream; + import java.io.IOException; + import java.io.InputStream; + import java.io.OutputStream; + + public class MainActivity extends AppCompatActivity { + + private static final String TAG = "PyTorchNativeApp"; + + public static String assetFilePath(Context context, String assetName) { + File file = new File(context.getFilesDir(), assetName); + if (file.exists() && file.length() > 0) { + return file.getAbsolutePath(); + } + + try (InputStream is = context.getAssets().open(assetName)) { + try (OutputStream os = new FileOutputStream(file)) { + byte[] buffer = new byte[4 * 1024]; + int read; + while ((read = is.read(buffer)) != -1) { + os.write(buffer, 0, read); + } + os.flush(); + } + return file.getAbsolutePath(); + } catch (IOException e) { + Log.e(TAG, "Error process asset " + assetName + " to file path"); + } + return null; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final String modelFileAbsoluteFilePath = + new File(assetFilePath(this, "compute.pt")).getAbsolutePath(); + NativeClient.loadAndForwardModel(modelFileAbsoluteFilePath); + } + } + + +In the previous step, when we copied our ``compute.pt`` to ``NativeApp/app/src/main/assets`` that file became an Android application asset, which will be packed in application. Android system provides only stream access to it. +To use this module from LibTorch, we need to materialize it as a file on the disk. ``assetFilePath`` function copies data from the asset input stream, writes it on the disk, and returns absolute file path for it. + +``OnCreate`` method of Activity is called just after Activity creation. In this method, we call ``assertFilePath`` and call ``NativeClient`` class that will dispatch it to native code through JNI call. + +``NativeClient`` is a helper class with an internal private class ``NativePeer``, which is responsible for working with the native part of our application. It has a static block that will load ``libpytorch_nativeapp.so``, that is build with ``CMakeLists.txt`` that we added on the previous step. The static block will be executed with the first reference of ``NativePeer`` class. It happens in ``NativeClient#loadAndForwardModel``. + +``NativeApp/app/src/main/java/org/pytorch/nativeapp/NativeClient.java``: + +.. code-block:: java + + package org.pytorch.nativeapp; + + public final class NativeClient { + + public static void loadAndForwardModel(final String modelPath) { + NativePeer.loadAndForwardModel(modelPath); + } + + private static class NativePeer { + static { + System.loadLibrary("pytorch_nativeapp"); + } + + private static native void loadAndForwardModel(final String modelPath); + } + } + +``NativePeer#loadAndForwardModel`` is declared as ``native``, it does not have definition in Java. Call to this method will be re-dispatched through JNI to C++ method in our ``libpytorch_nativeapp.so``, in ``NativeApp/app/src/main/cpp/pytorch_nativeapp.cpp``. + +Native code +----------- + +Now we are ready to write a native part of our application. + +``NativeApp/app/src/main/cpp/pytorch_nativeapp.cpp``: + +.. code-block:: cpp + + #include + #include + #include + #include + #include + #include + #define ALOGI(...) \ + __android_log_print(ANDROID_LOG_INFO, "PyTorchNativeApp", __VA_ARGS__) + #define ALOGE(...) \ + __android_log_print(ANDROID_LOG_ERROR, "PyTorchNativeApp", __VA_ARGS__) + + #include "jni.h" + + #include + #include + + namespace pytorch_nativeapp { + namespace { + torch::Tensor warp_perspective(torch::Tensor image, torch::Tensor warp) { + cv::Mat image_mat(/*rows=*/image.size(0), + /*cols=*/image.size(1), + /*type=*/CV_32FC1, + /*data=*/image.data_ptr()); + cv::Mat warp_mat(/*rows=*/warp.size(0), + /*cols=*/warp.size(1), + /*type=*/CV_32FC1, + /*data=*/warp.data_ptr()); + + cv::Mat output_mat; + cv::warpPerspective(image_mat, output_mat, warp_mat, /*dsize=*/{8, 8}); + + torch::Tensor output = + torch::from_blob(output_mat.ptr(), /*sizes=*/{8, 8}); + return output.clone(); + } + + static auto registry = + torch::RegisterOperators("my_ops::warp_perspective", &warp_perspective); + + template void log(const char *m, T t) { + std::ostringstream os; + os << t << std::endl; + ALOGI("%s %s", m, os.str().c_str()); + } + + struct JITCallGuard { + torch::autograd::AutoGradMode no_autograd_guard{false}; + torch::AutoNonVariableTypeMode non_var_guard{true}; + torch::jit::GraphOptimizerEnabledGuard no_optimizer_guard{false}; + }; + } // namespace + + static void loadAndForwardModel(JNIEnv *env, jclass, jstring jModelPath) { + const char *modelPath = env->GetStringUTFChars(jModelPath, 0); + assert(modelPath); + JITCallGuard guard; + torch::jit::Module module = torch::jit::load(modelPath); + module.eval(); + torch::Tensor x = torch::randn({4, 8}); + torch::Tensor y = torch::randn({8, 5}); + log("x:", x); + log("y:", y); + c10::IValue t_out = module.forward({x, y}); + log("result:", t_out); + env->ReleaseStringUTFChars(jModelPath, modelPath); + } + } // namespace pytorch_nativeapp + + JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *) { + JNIEnv *env; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + return JNI_ERR; + } + + jclass c = env->FindClass("org/pytorch/nativeapp/NativeClient$NativePeer"); + if (c == nullptr) { + return JNI_ERR; + } + + static const JNINativeMethod methods[] = { + {"loadAndForwardModel", "(Ljava/lang/String;)V", + (void *)pytorch_nativeapp::loadAndForwardModel}, + }; + int rc = env->RegisterNatives(c, methods, + sizeof(methods) / sizeof(JNINativeMethod)); + + if (rc != JNI_OK) { + return rc; + } + + return JNI_VERSION_1_6; + } + + +This listing is quite long, and a few things intermixed here, we will follow control flow to understand how this code works. +The first place where the control flow arrives is ``JNI_OnLoad``. +This function is called after loading the library. It is responsible for registering native method, which is called when ``NativePeer#loadAndForwardModel`` called, here it is ``pytorch_nativeapp::loadAndForwardModel`` function. + +``pytorch_nativeapp::loadAndForwardModel`` takes as an argument model path. +First, we extract its ``const char*`` value and loading the module with ``torch::jit::load``. + +To load TorchScript model for mobile, we need to set these guards, because mobile build doesn't support +features like autograd for smaller build size, placed in ``struct JITCallGuard`` in this example. +It may change in the future. You can track the latest changes keeping an eye on the +`source in PyTorch GitHub `_. + +Implementation of method ``warp_perspective`` and registration of it is entirely the same as +in `tutorial for desktop build `_. + +Building the app +---------------- + +To specify to gradle where is Android SDK and Android NDK, we need to fill ``NativeApp/local.properties``. + +.. code-block:: shell + + cd NativeApp + echo "sdk.dir=$ANDROID_HOME" >> NativeApp/local.properties + echo "ndk.dir=$ANDROID_NDK" >> NativeApp/local.properties + + +To build the result ``apk`` file we run: + +.. code-block:: shell + + cd NativeApp + gradle app:assembleDebug + +To install the app on the connected device: + +.. code-block:: shell + + cd NativeApp + gradle app::installDebug + +After that, you can run the app on the device by clicking on PyTorchNativeApp icon. +Or you can do it from the command line: + +.. code-block:: shell + + adb shell am start -n org.pytorch.nativeapp/.MainActivity + +If you check the android logcat: + +.. code-block:: shell + + adb logcat -v brief | grep PyTorchNativeApp + + +You should see logs with tag 'PyTorchNativeApp' that prints x, y, and the result of the model forward, which we print with ``log`` function in ``NativeApp/app/src/main/cpp/pytorch_nativeapp.cpp``. + +.. code-block:: + + I/PyTorchNativeApp(26968): x: -0.9484 -1.1757 -0.5832 0.9144 0.8867 1.0933 -0.4004 -0.3389 + I/PyTorchNativeApp(26968): -1.0343 1.5200 -0.7625 -1.5724 -1.2073 0.4613 0.2730 -0.6789 + I/PyTorchNativeApp(26968): -0.2247 -1.2790 1.0067 -0.9266 0.6034 -0.1941 0.7021 -1.5368 + I/PyTorchNativeApp(26968): -0.3803 -0.0188 0.2021 -0.7412 -0.2257 0.5044 0.6592 0.0826 + I/PyTorchNativeApp(26968): [ CPUFloatType{4,8} ] + I/PyTorchNativeApp(26968): y: -1.0084 1.8733 0.5435 0.1087 -1.1066 + I/PyTorchNativeApp(26968): -1.9926 1.1047 0.5311 -0.4944 1.9178 + I/PyTorchNativeApp(26968): -1.5451 0.8867 1.0473 -1.7571 0.3909 + I/PyTorchNativeApp(26968): 0.4039 0.5085 -0.2776 0.4080 0.9203 + I/PyTorchNativeApp(26968): 0.3655 1.4395 -1.4467 -0.9837 0.3335 + I/PyTorchNativeApp(26968): -0.0445 0.8039 -0.2512 -1.3122 0.6543 + I/PyTorchNativeApp(26968): -1.5819 0.0525 1.5680 -0.6442 -1.3090 + I/PyTorchNativeApp(26968): -1.6197 -0.0773 -0.5967 -0.1105 -0.3122 + I/PyTorchNativeApp(26968): [ CPUFloatType{8,5} ] + I/PyTorchNativeApp(26968): result: 16.0274 9.0330 6.0124 9.8644 11.0493 + I/PyTorchNativeApp(26968): 8.7633 6.9657 12.3469 10.3159 12.0683 + I/PyTorchNativeApp(26968): 12.4529 9.4559 11.7038 7.8396 6.9716 + I/PyTorchNativeApp(26968): 8.5279 9.1780 11.3849 8.4368 9.1480 + I/PyTorchNativeApp(26968): 10.0000 10.0000 10.0000 10.0000 10.0000 + I/PyTorchNativeApp(26968): 10.0000 10.0000 10.0000 10.0000 10.0000 + I/PyTorchNativeApp(26968): 10.0000 10.0000 10.0000 10.0000 10.0000 + I/PyTorchNativeApp(26968): 10.0000 10.0000 10.0000 10.0000 10.0000 + I/PyTorchNativeApp(26968): [ CPUFloatType{8,5} ] + + + +The full setup of this app you can find in `PyTorch Android Demo Application Repository `_. diff --git a/recipes_source/recipes_index.rst b/recipes_source/recipes_index.rst index 251db43297b..ffdc1aaaa78 100644 --- a/recipes_source/recipes_index.rst +++ b/recipes_source/recipes_index.rst @@ -160,6 +160,12 @@ Recipes are bite-sized bite-sized, actionable examples of how to use specific Py :link: ../recipes/mobile_perf.html :tags: Mobile,Model-Optimization +.. customcarditem:: + :header: Making Android Native Application That Uses PyTorch Android Prebuilt Libraries + :card_description: Learn how to make android application from the scratch that uses LibTorch C++ API and uses TorchScript model with custom C++ operator. + :image: ../_static/img/thumbnails/cropped/zeroing-out-gradients.PNG + :link: ../recipes/android_native_app_with_custom_op.html + :tags: Mobile .. End of tutorial card section From 9cc3ce81ea1cc00a74ab583d1effa07481cbcec2 Mon Sep 17 00:00:00 2001 From: IvanKobzarev Date: Fri, 26 Jun 2020 16:25:31 -0600 Subject: [PATCH 08/33] [mobile_perf][recipe] Add ChannelsLast recommendation --- recipes_source/mobile_perf.rst | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/recipes_source/mobile_perf.rst b/recipes_source/mobile_perf.rst index d0fbf3cb6fb..d138939448f 100644 --- a/recipes_source/mobile_perf.rst +++ b/recipes_source/mobile_perf.rst @@ -49,6 +49,7 @@ Code your model: self.dequant = torch.quantization.DeQuantStub() def forward(self, x): + x.contiguous(memory_format=torch.channels_last) x = self.quant(x) x = self.conv(x) x = self.bn(x) @@ -129,9 +130,24 @@ Next we call ``optimize_for_mobile`` and save model on the disk. torchscript_model_optimized = optimize_for_mobile(torchscript_model) torch.jit.save(torchscript_model_optimized, "model.pt") +4. Prefer Using Channels Last Tensor memory format +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -4. Android. Reusing tensors for forward. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Channels Last(NHWC) memory format was introduced in PyTorch 1.4.0. It is supported only for four-dimensional tensors. This memory format gives a better memory locality for most operators, especially convolution. Our measurements showed a 3x speedup of MobileNetV2 model compared with the default Channels First(NCHW) format. + +At the moment of writing this recipe, PyTorch Android java API does not support using inputs in Channels Last memory format. But it can be used on the TorchScript model level, by adding the conversion to it for model inputs. + +.. code-block:: python + + def forward(self, x): + x.contiguous(memory_format=torch.channels_last) + ... + + +This conversion is zero cost if your input is already in Channels Last memory format. After it, all operators will work preserving ChannelsLast memory format. + +5. Android. Reusing tensors for forward +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This recipe is Android only. Memory is a critical resource for android performance, especially on old devices. From 86b5c4a37ecd8a94421f1075bc4894a119cbee69 Mon Sep 17 00:00:00 2001 From: Shen Li Date: Sat, 11 Apr 2020 14:54:53 -0700 Subject: [PATCH 09/33] Adding distributed pipeline parallel tutorial --- ...ributed-Pipeline-Parallelism-Using-RPC.png | Bin 0 -> 35776 bytes index.rst | 8 + .../dist_pipeline_parallel_tutorial.rst | 370 ++++++++++++++++++ 3 files changed, 378 insertions(+) create mode 100644 _static/img/thumbnails/cropped/Distributed-Pipeline-Parallelism-Using-RPC.png create mode 100644 intermediate_source/dist_pipeline_parallel_tutorial.rst diff --git a/_static/img/thumbnails/cropped/Distributed-Pipeline-Parallelism-Using-RPC.png b/_static/img/thumbnails/cropped/Distributed-Pipeline-Parallelism-Using-RPC.png new file mode 100644 index 0000000000000000000000000000000000000000..426a14d98f5f7fbbf695626658ad1946fe4ef63e GIT binary patch literal 35776 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 literal 0 HcmV?d00001 diff --git a/index.rst b/index.rst index 81113c919ed..22921f61b35 100644 --- a/index.rst +++ b/index.rst @@ -332,6 +332,13 @@ Welcome to PyTorch Tutorials :link: intermediate/rpc_param_server_tutorial.html :tags: Parallel-and-Distributed-Training +.. customcarditem:: + :header: Distributed Pipeline Parallelism Using RPC + :card_description: Demonstrate how to implement distributed pipeline parallelism using RPC + :image: _static/img/thumbnails/cropped/Distributed-Pipeline-Parallelism-Using-RPC.png + :link: intermediate/dist_pipeline_parallel_tutorial.html + :tags: Parallel-and-Distributed-Training + .. End of tutorial card section .. raw:: html @@ -497,3 +504,4 @@ Additional Resources intermediate/rpc_tutorial beginner/aws_distributed_training_tutorial intermediate/rpc_param_server_tutorial + intermediate/dist_pipeline_parallel_tutorial diff --git a/intermediate_source/dist_pipeline_parallel_tutorial.rst b/intermediate_source/dist_pipeline_parallel_tutorial.rst new file mode 100644 index 00000000000..ef7df000508 --- /dev/null +++ b/intermediate_source/dist_pipeline_parallel_tutorial.rst @@ -0,0 +1,370 @@ +Distributed Pipeline Parallelism Using RPC +========================================== +**Author**: `Shen Li `_ + +Prerequisites: + +- `Single-Machine Model Parallel Best Practices `__ +- `Getting started with Distributed RPC Framework `__ +- RRef helper functions: + `RRef.rpc_sync() `__, + `RRef.rpc_async() `__, and + `RRef.remote() `__ + + + +This tutorial uses a Resnet50 model to demonstrate implementing distributed +pipeline parallelism with `torch.distributed.rpc `__ +APIs. This can be viewed as the distributed counterpart of the multi-GPU +pipeline parallelism discussed in +`Single-Machine Model Parallel Best Practices `_. + +.. note:: This tutorial requires PyTorch v1.6.0 or above. + +.. note:: Full source code of this tutorial can be found at + `pytorch/examples `__. + +Basics +------ + + +The previous tutorial, `Getting Started with Distributed RPC Framework `_ +shows how to use `torch.distributed.rpc `_ +to implement distributed model parallelism for an RNN model. That tutorial uses +one GPU to host the ``EmbeddingTable``, and the provided code works fine. +However, if a model lives on multiple GPUs, it would require some extra steps to +increase the amortized utilization of all GPUs. Pipeline parallelism is one type +of paradigm that can help in this case. + +In this tutorial, we use ``ResNet50`` as an example model which is also used by +the `Single-Machine Model Parallel Best Practices `_ +tutorial. Similarly, the ``ResNet50`` model is divided into two shards and +the input batch is partitioned into multiple splits and fed into the two model +shards in a pipelined fashion. The difference is that, instead of parallelizing +the execution using CUDA streams, this tutorial invokes asynchronous RPCs. So, +the solution presented in this tutorial also works across machine boundaries. +The remainder of this tutorial presents the implementation in four steps. + + + +Step 1: Partition ResNet50 Model +-------------------------------- + +This is the preparation step which implements ``ResNet50`` in two model shards. +The code below is borrowed from the +`ResNet implementation in torchvision `_. +The ``ResNetBase`` module contains the common building blocks and attributes for +the two ResNet shards. + + +.. code:: python + import threading + + import torch + import torch.nn as nn + + from torchvision.models.resnet import Bottleneck + + num_classes = 1000 + + + def conv1x1(in_planes, out_planes, stride=1): + return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False) + + + class ResNetBase(nn.Module): + def __init__(self, block, inplanes, num_classes=1000, + groups=1, width_per_group=64, norm_layer=None): + super(ResNetBase, self).__init__() + + self._lock = threading.Lock() + self._block = block + self._norm_layer = nn.BatchNorm2d + self.inplanes = inplanes + self.dilation = 1 + self.groups = groups + self.base_width = width_per_group + + def _make_layer(self, planes, blocks, stride=1): + norm_layer = self._norm_layer + downsample = None + previous_dilation = self.dilation + if stride != 1 or self.inplanes != planes * self._block.expansion: + downsample = nn.Sequential( + conv1x1(self.inplanes, planes * self._block.expansion, stride), + norm_layer(planes * self._block.expansion), + ) + + layers = [] + layers.append(self._block(self.inplanes, planes, stride, downsample, self.groups, + self.base_width, previous_dilation, norm_layer)) + self.inplanes = planes * self._block.expansion + for _ in range(1, blocks): + layers.append(self._block(self.inplanes, planes, groups=self.groups, + base_width=self.base_width, dilation=self.dilation, + norm_layer=norm_layer)) + + return nn.Sequential(*layers) + + def parameter_rrefs(self): + return [RRef(p) for p in self.parameters()] + + +Now, we are ready to define the two model shards. For the constructor, we +simply split all ResNet50 layers into two parts and move each part into the +provided device. The ``forward`` functions of both shards take an ``RRef`` of +the input data, fetch the data locally, and then move it to the expected device. +After applying all layers to the input, it moves the output to CPU and returns. +It is because the RPC API requires tensors to reside on CPU to avoid invalid +device errors when the numbers of devices in the caller and the callee do not +match. + + +.. code:: python + + class ResNetShard1(ResNetBase): + def __init__(self, device, *args, **kwargs): + super(ResNetShard1, self).__init__( + Bottleneck, 64, num_classes=num_classes, *args, **kwargs) + + self.device = device + self.seq = nn.Sequential( + nn.Conv2d(3, self.inplanes, kernel_size=7, stride=2, padding=3, bias=False), + self._norm_layer(self.inplanes), + nn.ReLU(inplace=True), + nn.MaxPool2d(kernel_size=3, stride=2, padding=1), + self._make_layer(64, 3), + self._make_layer(128, 4, stride=2) + ).to(self.device) + + for m in self.modules(): + if isinstance(m, nn.Conv2d): + nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') + elif isinstance(m, nn.BatchNorm2d): + nn.init.constant_(m.weight, 1) + nn.init.constant_(m.bias, 0) + + def forward(self, x_rref): + x = x_rref.to_here().to(self.device) + with self._lock: + out = self.seq(x) + return out.cpu() + + + class ResNetShard2(ResNetBase): + def __init__(self, device, *args, **kwargs): + super(ResNetShard2, self).__init__( + Bottleneck, 512, num_classes=num_classes, *args, **kwargs) + + self.device = device + self.seq = nn.Sequential( + self._make_layer(256, 6, stride=2), + self._make_layer(512, 3, stride=2), + nn.AdaptiveAvgPool2d((1, 1)), + ).to(self.device) + + self.fc = nn.Linear(512 * self._block.expansion, num_classes).to(self.device) + + def forward(self, x_rref): + x = x_rref.to_here().to(self.device) + with self._lock: + out = self.fc(torch.flatten(self.seq(x), 1)) + return out.cpu() + + +Step 2: Stitch ResNet50 Model Shards Into One Module +---------------------------------------------------- + + +Then, we create a ``DistResNet50`` module to assemble the two shards and +implement the pipeline parallel logic. In the constructor, we use two +``rpc.remote`` calls to put the two shards on two different RPC workers +respectively and hold on to the ``RRef`` to the two model parts so that they +can be referenced in the forward pass. The ``forward`` function +splits the input batch into multiple micro-batches, and feeds these +micro-batches to the two model parts in a pipelined fashion. It first uses an +``rpc.remote`` call to apply the first shard to a micro-batch and then forwards +the returned intermediate output ``RRef`` to the second model shard. After that, +it collects the ``Future`` of all micro-outputs, and waits for all of them after +the loop. Note that both ``remote()`` and ``rpc_async()`` return immediately and +run asynchronously. Therefore, the entire loop is non-blocking, and will launch +multiple RPCs concurrently. The execution order of one micro-batch on two model +parts are preserved by intermediate output ``y_rref``. The execution order +across micro-batches does not matter. In the end, the forward function +concatenates outputs of all micro-batches into one single output tensor and +returns. The ``parameter_rrefs`` function is a helper to +simplify distributed optimizer construction, which will be used later. + + + +.. code:: python + + class DistResNet50(nn.Module): + def __init__(self, num_split, workers, *args, **kwargs): + super(DistResNet50, self).__init__() + + self.num_split = num_split + + # Put the first part of the ResNet50 on workers[0] + self.p1_rref = rpc.remote( + workers[0], + ResNetShard1, + args = ("cuda:0",) + args, + kwargs = kwargs + ) + + # Put the second part of the ResNet50 on workers[1] + self.p2_rref = rpc.remote( + workers[1], + ResNetShard2, + args = ("cuda:1",) + args, + kwargs = kwargs + ) + + def forward(self, xs): + out_futures = [] + for x in iter(xs.split(self.split_size, dim=0)): + x_rref = RRef(x) + y_rref = self.p1_rref.remote().forward(x_rref) + z_fut = self.p2_rref.rpc_async().forward(y_rref) + out_futures.append(z_fut) + + return torch.cat(torch.futures.wait_all(out_futures)) + + def parameter_rrefs(self): + remote_params = [] + remote_params.extend(self.p1_rref.remote().parameter_rrefs().to_here()) + remote_params.extend(self.p2_rref.remote().parameter_rrefs().to_here()) + return remote_params + + +Step 3: Define The Training Loop +-------------------------------- + + +After defining the model, let us implement the training loop. We use a +dedicated "master" worker to prepare random inputs and labels, and control the +distributed backward pass and distributed optimizer step. It first creates an +instance of the ``DistResNet50`` module. It specifies the number of +micro-batches for each batch, and also provides the name of the two RPC workers +(i.e., "worker1", and "worker2"). Then it defines the loss function and creates +a ``DistributedOptimizer`` using the ``parameter_rrefs()`` helper to acquire a +list of parameter ``RRefs``. Then, the main training loop is very similar to +regular local training, except that it uses ``dist_autograd`` to launch +backward and provides the ``context_id`` for both backward and optimizer +``step()``. + + +.. code:: python + + import torch.distributed.autograd as dist_autograd + import torch.optim as optim + from torch.distributed.optim import DistributedOptimizer + + num_batches = 3 + batch_size = 120 + image_w = 128 + image_h = 128 + + + def run_master(num_split): + # put the two model parts on worker1 and worker2 respectively + model = DistResNet50(num_split, ["worker1", "worker2"]) + loss_fn = nn.MSELoss() + opt = DistributedOptimizer( + optim.SGD, + model.parameter_rrefs(), + lr=0.05, + ) + + one_hot_indices = torch.LongTensor(batch_size) \ + .random_(0, num_classes) \ + .view(batch_size, 1) + + for i in range(num_batches): + print(f"Processing batch {i}") + # generate random inputs and labels + inputs = torch.randn(batch_size, 3, image_w, image_h) + labels = torch.zeros(batch_size, num_classes) \ + .scatter_(1, one_hot_indices, 1) + + with dist_autograd.context() as context_id: + outputs = model(inputs) + dist_autograd.backward(context_id, [loss_fn(outputs, labels)]) + opt.step(context_id) + + +Step 4: Launch RPC Processes +---------------------------- + + +Finally, the code below shows the target function for all processes. The main +logic is defined in ``run_master``. The workers passively waiting for +commands from the master, and hence simply runs ``init_rpc`` and ``shutdown``, +where the ``shutdown`` by default will block until all RPC participants finish. + +.. code:: python + + import os + import time + + import torch.multiprocessing as mp + + + def run_worker(rank, world_size, num_split): + os.environ['MASTER_ADDR'] = 'localhost' + os.environ['MASTER_PORT'] = '29500' + options = rpc.ProcessGroupRpcBackendOptions(num_send_recv_threads=128) + + if rank == 0: + rpc.init_rpc( + "master", + rank=rank, + world_size=world_size, + rpc_backend_options=options + ) + run_master(num_split) + else: + rpc.init_rpc( + f"worker{rank}", + rank=rank, + world_size=world_size, + rpc_backend_options=options + ) + pass + + # block until all rpcs finish + rpc.shutdown() + + + if __name__=="__main__": + world_size = 3 + for num_split in [1, 2, 4, 8]: + tik = time.time() + mp.spawn(run_worker, args=(world_size, num_split), nprocs=world_size, join=True) + tok = time.time() + print(f"number of splits = {num_split}, execution time = {tok - tik}") + + +The output below shows the speedup attained by increasing the number of splits +in each batch. + +:: + + $ python main.py + Processing batch 0 + Processing batch 1 + Processing batch 2 + number of splits = 1, execution time = 16.45062756538391 + Processing batch 0 + Processing batch 1 + Processing batch 2 + number of splits = 2, execution time = 12.329529762268066 + Processing batch 0 + Processing batch 1 + Processing batch 2 + number of splits = 4, execution time = 10.164430618286133 + Processing batch 0 + Processing batch 1 + Processing batch 2 + number of splits = 8, execution time = 9.076049566268921 \ No newline at end of file From f0be561cf085d1d57676712b67f11eda0d28831a Mon Sep 17 00:00:00 2001 From: Shen Li Date: Tue, 30 Jun 2020 13:00:24 -0700 Subject: [PATCH 10/33] Add async execution tutorials --- _static/img/rpc-images/batch.png | Bin 0 -> 20174 bytes ...ocessing-Using-Asynchronous-Executions.png | Bin 0 -> 35776 bytes index.rst | 8 + intermediate_source/rpc_async_execution.rst | 522 ++++++++++++++++++ 4 files changed, 530 insertions(+) create mode 100644 _static/img/rpc-images/batch.png create mode 100644 _static/img/thumbnails/cropped/Implementing-Batch-RPC-Processing-Using-Asynchronous-Executions.png create mode 100644 intermediate_source/rpc_async_execution.rst diff --git a/_static/img/rpc-images/batch.png b/_static/img/rpc-images/batch.png new file mode 100644 index 0000000000000000000000000000000000000000..cde410d1bd1be3e82e57f8eeec3155867cdd0eb6 GIT binary patch literal 20174 zcmd74c{tVEA1}VEQ=R6KO5s#gXfn&#AVMT_Y*gl%%(EtqD3KvDWe!O@Q>YZloGCK4 zGlyi#d|#_N=X~#dzV|-A-*f-DJ>T>A+WTYgwLfdE_xm-ieM3<}dew5KDuuGZ_Rl4FWIpqjDSrH6dsIf{PyBQHQ~x@CUuJ#cj4g$-<{bHNL4riQ2_6)( zJFa1;Y-MQYcW-NAy`&ry7iTr^R0wNQ(I=Yb{$?{ki8Ilq6ix3uof6P3LTipw^KOwM-j6s)bv z9%k{r*f262EM3N5XkQ?)^L@N)YPRLA7R$^P7=1$5i)Lp0q*yQU;ldBmh5w&_9A$P~ zYR&(kJg)HC8ZIFr4aQq8sd zt{=O$I#K4{E(UQO`qqZ__OmkGI@j;qSt}UNU13Tyr5E}1wd7{Hxw$nn7sr~)rPQ9u zP&6t(8qUAM-o^LD3(@1!(jrsOZ`S@OqUk#yK5^oDu|Vp@ix-zKTQ>ZWRm!ODzGh;E zVN^xvRbey6zEROlTJx`)j{^N(~43oOi#{q|MF;}&>Z{H63DtXtLs)h^O zt0*ce?i&dyIguhRF8+OdJT+RMLWvYk81peZefqBNwnNb~Gj5L396>FX>XMRIRx=B$ z&?ln!m6G3{KYeeP{KkzNRgU*kC|E|~$R9(t0FJ1HgeEOc9J7Z-1up0iV`@Y6V^-(XKRjqe;bwysR ztlw^#2#={mm(iZ6d%I6@?%n(Nz26!N<)lFT4SlYIR`~(ckuIKPMt|Q`v5$=zM*I$6 zHLiMlC!H(w4i&af`s$F&);rdqt6uEO%&=|SN-GcP>EMyw)pg1eg)&+D%(o2_#j=6Gas@`<10e><#HVcK7{g{*?nt2 zB5liro9Wox&;?2S#T_>J5T4_<-4%7UiOMviN|xBTxFj2TQLOLun+>OW z?n<+qmXh*L&HB8273SKg@?lEO>jjj8XgwrwGDsyMPm(r{|j%s6+zFf+6X}Y@p8(CNeHRvvVwH*62o*X`q|07!J_U7#} zruC0QQVZs$l#1QI+?BQ~-Pc{;SrN>A%%bU8_1x@qU6N{Y(w?mpO080u&DdG(96Ju$ zI?tm=FO3g;ivRri{J_WbW0|ck$>-0X--tWC)iUdde?VZMYKL2!=X~Ds6)Wm8j4D^K z2#Slh4%_SNKVmy{e>>TaH$QD{(Dj%+?%3v@$;4+I>E%VCnKdZrx{n3YZLpb5YT`I} zc$5|_SRi}yWNp6tbaQal6xW_TiV@=O8`rOo7HoAo_9@eelshcDfql2XBE_e_}t5SltMZ9F2#%*;oP+arNeCUd#7)D zR+dn4gF~+NNOxt!y;I_exV>`zYN^!3BA=~h<)QpJ{i(T0qPF(_fq}7KzUcdG;g!<$ zoTceFbu1Q}{+e;~#*MpaX=y)y9j!VxHU7zp->m+zQrp(8Te+~5*NFc(jL)Zh_^{Wd zOPBbZzw3*Qf7JUvHm2H;sy)`9R?wF_lwZ?R9n+%1PNBR!WNAUg6UpN4Q^w!gU&)?2 z)nE{$9ak{ZdBCOp;!-x5kdP2zm(hgv8#XYvZD3+bst6Y`8|fa+1)Ii-QCG3(kO2A zKZfb{^RpAB=hJ)Y)OG8~-MG49Q(vfAYV`}}5x%o8Dbgu-@7|S_k*TKpNu=K0Dc$$b zb+Gz-XXzL=$GcCTv})qyYeFH1^Fm_(hP zJhy5~UAAGDaqov%ChqfB@@Kxw*$*_;RUZz_8mSO5NY{8~T9?FQ8~5muanbE921Uxs z$_$$~YnVP(2p7J2{ko!^-7|WMt&?|j^bTfWyA#rWECyWr(dwuk9{Jv@w#GF#Q?FjR zqVhvDQy?`zKfmu|dJK7w;*Yp7H1}x-Qfria%$$v=F)=Y?pR5W3owqMvzTB>-YCFG0 zQ*E`Rx*!%(yh6B=wY7C3^B<-=cG4I(ZtQ7)b@`^RZ|ZYT2ZnUb6j?brvQbQio1TS zTer^t(bunEF&GDj=cze;Nf)|DM@H^NMCgXtu(C$*NIXLud#bVb`}gnuK|!AcHTI~N zXmzn4GUM4vb8&VqIiDUd-QC9_dMJ0@KRxx9U&_noBHD6YUZZ)p=B8!1wW_WYgZuc$ z+vexg)6>)aE8G>icI=3*jc*?L{{5MKzZyFmn~I4VsT@I3yr}{Y
L2rz$ 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

V$u6yLi6z5jy3Vrf>My+{o@$g6zO13d7zIqSl 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∈;;l7HF%M_Nq; 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� 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 literal 0 HcmV?d00001 diff --git a/recipes_source/distributed_rpc_profiling.rst b/recipes_source/distributed_rpc_profiling.rst new file mode 100644 index 00000000000..d43c3a0e217 --- /dev/null +++ b/recipes_source/distributed_rpc_profiling.rst @@ -0,0 +1,314 @@ +Profiling PyTorch RPC-Based Workloads +====================================== + +In this recipe, you will learn: + +- An overview of the `Distributed RPC Framework`_ +- An overview of the `PyTorch Profiler`_ +- How to use the profiler to profile RPC-based workloads + +Requirements +------------ + +- PyTorch 1.6 + +The instructions for installing PyTorch are +available at `pytorch.org`_. + +What is the Distributed RPC Framework? +--------------------------------------- + +The **Distributed RPC Framework** provides mechanisms for multi-machine model +training through a set of primitives to allow for remote communication, and a +higher-level API to automatically differentiate models split across several machines. +For this recipe, it would be helpful to be familiar with the `Distributed RPC Framework`_ +as well as the `RPC Tutorials`_. + +What is the PyTorch Profiler? +--------------------------------------- +The profiler is a context manager based API that allows for on-demand profiling of +operators in a model's workload. The profiler can be used to analyze various aspects +of a model including execution time, operators invoked, and memory consumption. For a +detailed tutorial on using the profiler to profile a single-node model, please see the +`Profiler Recipe`_. + + + +How to use the Profiler for RPC-based workloads +----------------------------------------------- + +The profiler supports profiling of calls made of RPC and allows the user to have a +detailed view into the operations that take place on different nodes. To demonstrate an +example of this, let's first set up the RPC framework. The below code snippet will initialize +two RPC workers on the same host, named ``worker0`` and ``worker1`` respectively. The workers will +be spawned as subprocesses, and we set some environment variables required for proper +initialization. + +:: + + import torch + import torch.distributed.rpc as rpc + import torch.autograd.profiler as profiler + import torch.multiprocessing as mp + import os + import logging + import sys + + logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) + logger = logging.getLogger() + + def random_tensor(): + return torch.rand((3, 3), requires_grad=True) + + + def worker(rank, world_size): + os.environ["MASTER_ADDR"] = "localhost" + os.environ["MASTER_PORT"] = "29500" + worker_name = f"worker{rank}" + + # Initialize RPC framework. + rpc.init_rpc( + name=worker_name, + rank=rank, + world_size=world_size + ) + logger.debug(f"{worker_name} successfully initialized RPC.") + + pass # to be continued below + + logger.debug(f"Rank {rank} waiting for workers and shutting down RPC") + rpc.shutdown() + logger.debug(f"Rank {rank} shutdown RPC") + + + if __name__ == '__main__': + # Run 2 RPC workers. + world_size = 2 + mp.spawn(worker, args=(world_size,), nprocs=world_size) + +Running the above program should present you with the following output: + +:: + + DEBUG:root:worker1 successfully initialized RPC. + DEBUG:root:worker0 successfully initialized RPC. + DEBUG:root:Rank 0 waiting for workers and shutting down RPC + DEBUG:root:Rank 1 waiting for workers and shutting down RPC + DEBUG:root:Rank 1 shutdown RPC + DEBUG:root:Rank 0 shutdown RPC + +Now that we have a skeleton setup of our RPC framework, we can move on to +sending RPCs back and forth and using the profiler to obtain a view of what's +happening under the hood. Let's add to the above ``worker`` function: + +:: + + def worker(rank, world_size): + # Above code omitted... + if rank == 0: + dst_worker_rank = (rank + 1) % world_size + dst_worker_name = f"worker{dst_worker_rank}" + t1, t2 = random_tensor(), random_tensor() + # Send and wait RPC completion under profiling scope. + with profiler.profile() as prof: + fut1 = rpc.rpc_async(dst_worker_name, torch.add, args=(t1, t2)) + fut2 = rpc.rpc_async(dst_worker_name, torch.mul, args=(t1, t2)) + # RPCs must be awaited within profiling scope. + fut1.wait() + fut2.wait() + + print(prof.key_averages().table()) + +The aformented code creates 2 RPCs, specifying ``torch.add`` and ``torch.mul``, respectively, +to be run with two random input tensors on worker 1. Since we use the ``rpc_async`` API, +we are returned a ``torch.futures.Future`` object, which must be awaited for the result +of the computation. Note that this wait must take place within the scope created by +the profiling context manager in order for the RPC to be accurately profiled. Running +the code with this new worker function should result in the following output: + +:: + + # Some columns are omitted for brevity, exact output subject to randomness + ---------------------------------------------------------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- + Name Self CPU total % Self CPU total CPU total % CPU total CPU time avg Number of Calls Node ID + ---------------------------------------------------------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- + rpc_async#aten::add(worker0 -> worker1) 0.00% 0.000us 0 20.462ms 20.462ms 1 0 + rpc_async#aten::mul(worker0 -> worker1) 0.00% 0.000us 0 5.712ms 5.712ms 1 0 + rpc_async#aten::mul(worker0 -> worker1)#remote_op: mul 1.84% 206.864us 2.69% 302.162us 151.081us 2 1 + rpc_async#aten::add(worker0 -> worker1)#remote_op: add 1.41% 158.501us 1.57% 176.924us 176.924us 1 1 + rpc_async#aten::mul(worker0 -> worker1)#remote_op: output_nr 0.04% 4.980us 0.04% 4.980us 2.490us 2 1 + rpc_async#aten::mul(worker0 -> worker1)#remote_op: is_leaf 0.07% 7.806us 0.07% 7.806us 1.952us 4 1 + rpc_async#aten::add(worker0 -> worker1)#remote_op: empty 0.16% 18.423us 0.16% 18.423us 18.423us 1 1 + rpc_async#aten::mul(worker0 -> worker1)#remote_op: empty 0.14% 15.712us 0.14% 15.712us 15.712us 1 1 + ---------------------------------------------------------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- + Self CPU time total: 11.237ms + +Here we can see that the profiler has profiled our ``rpc_async`` calls made to ``worker1`` +from ``worker0``. In particular, the first 2 entries in the table show details (such as +the operator name, originating worker, and destination worker) about each RPC call made +and the ``CPU total`` column indicates the end-to-end latency of the RPC call. + +We also have visibility into the actual operators invoked remotely on worker 1 due RPC. +We can see operations that took place on ``worker1`` by checking the ``Node ID`` column. For +example, we can interpret the row with name ``rpc_async#aten::mul(worker0 -> worker1)#remote_op: mul`` +as a ``mul`` operation taking place on the remote node, as a result of the RPC sent to ``worker1`` +from ``worker0``, specifying ``worker1`` to run the builtin ``mul`` operator on the input tensors. +Note that names of remote operations are prefixed with the name of the RPC event that resulted +in them. For example, remote operations corresponding to the ``rpc.rpc_async(dst_worker_name, torch.add, args=(t1, t2))`` +call are prefixed with ``rpc_async#aten::mul(worker0 -> worker1)``. + +We can also use the profiler gain insight into user-defined functions that are executed over RPC. +For example, let's add the following to the above ``worker`` function: + +:: + + # Define somewhere outside of worker() func. + def udf_with_ops(): + import time + time.sleep(1) + t1, t2 = random_tensor(), random_tensor() + torch.add(t1, t2) + torch.mul(t1, t2) + + def worker(rank, world_size): + # Above code omitted + with profiler.profile() as p: + fut = rpc.rpc_async(dst_worker_name, udf_with_ops, args=()) + fut.wait() + print(p.key_averages().table()) + +The above code creates a user-defined function that sleeps for 1 second, and then executes various +operators. Similar to what we've done above, we send an RPC to the remote worker, specifying it to +run our user-defined function. Running this code should result in the following output: + +:: + + # Exact output subject to randomness + -------------------------------------------------------------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- + Name Self CPU total % Self CPU total CPU total % CPU total CPU time avg Number of Calls Node ID + -------------------------------------------------------------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- + rpc_async#udf_with_ops(worker0 -> worker1) 0.00% 0.000us 0 1.008s 1.008s 1 0 + rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: rand 12.58% 80.037us 47.09% 299.589us 149.795us 2 1 + rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: empty 15.40% 98.013us 15.40% 98.013us 24.503us 4 1 + rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: uniform_ 22.85% 145.358us 23.87% 151.870us 75.935us 2 1 + rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: is_complex 1.02% 6.512us 1.02% 6.512us 3.256us 2 1 + rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: add 25.80% 164.179us 28.43% 180.867us 180.867us 1 1 + rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: mul 20.48% 130.293us 31.43% 199.949us 99.975us 2 1 + rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: output_nr 0.71% 4.506us 0.71% 4.506us 2.253us 2 1 + rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: is_leaf 1.16% 7.367us 1.16% 7.367us 1.842us 4 1 + -------------------------------------------------------------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- + +Here we can see that the user-defined function has successfully been profiled with its name +``(rpc_async#udf_with_ops(worker0 -> worker1))``, and has the CPU total time we would roughly expect +(slightly greater than 1s given the ``sleep``). Similar to the above profiling output, we can see the +remote operators that have been executed on worker 1 as part of executing this RPC request. + +Lastly, we can visualize remote execution using the tracing functionality provided by the profiler. +Let's add the following code to the above ``worker`` function: + +:: + + def worker(rank, world_size): + # Above code omitted + # Will generated trace for above profiling output + trace_file = "/tmp/trace.json" + prof.export_chrome_trace(trace_file) + logger.debug(f"Wrote trace to {trace_file}") + +Now, we can load the trace file in Chrome (``chrome://tracing``). We should see output similar to +the following: + +.. image:: ../_static/img/rpc_trace_img.png + :scale: 25 % + +As we can see, we have traced our RPC requests and can also visualize traces of the remote operations, +in this case, given in the trace column for ``node_id: 1``. + +Putting it all together, we have the following code for this recipe: + +:: + + import torch + import torch.distributed.rpc as rpc + import torch.autograd.profiler as profiler + import torch.multiprocessing as mp + import os + import logging + import sys + + logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) + logger = logging.getLogger() + + def random_tensor(): + return torch.rand((3, 3), requires_grad=True) + + def udf_with_ops(): + import time + time.sleep(1) + t1, t2 = random_tensor(), random_tensor() + torch.add(t1, t2) + torch.mul(t1, t2) + + def worker(rank, world_size): + os.environ["MASTER_ADDR"] = "localhost" + os.environ["MASTER_PORT"] = "29500" + worker_name = f"worker{rank}" + + # Initialize RPC framework. + rpc.init_rpc( + name=worker_name, + rank=rank, + world_size=world_size + ) + logger.debug(f"{worker_name} successfully initialized RPC.") + + if rank == 0: + dst_worker_rank = (rank + 1) % world_size + dst_worker_name = f"worker{dst_worker_rank}" + t1, t2 = random_tensor(), random_tensor() + # Send and wait RPC completion under profiling scope. + with profiler.profile() as prof: + fut1 = rpc.rpc_async(dst_worker_name, torch.add, args=(t1, t2)) + fut2 = rpc.rpc_async(dst_worker_name, torch.mul, args=(t1, t2)) + # RPCs must be awaited within profiling scope. + fut1.wait() + fut2.wait() + print(prof.key_averages().table()) + + with profiler.profile() as p: + fut = rpc.rpc_async(dst_worker_name, udf_with_ops, args=()) + fut.wait() + + print(p.key_averages().table()) + + trace_file = "/tmp/trace.json" + prof.export_chrome_trace(trace_file) + logger.debug(f"Wrote trace to {trace_file}") + + + logger.debug(f"Rank {rank} waiting for workers and shutting down RPC") + rpc.shutdown() + logger.debug(f"Rank {rank} shutdown RPC") + + + + if __name__ == '__main__': + # Run 2 RPC workers. + world_size = 2 + mp.spawn(worker, args=(world_size,), nprocs=world_size) + + +Learn More +------------------- + +- `pytorch.org`_ for installation instructions, and more documentation + and tutorials. +- `Distributed RPC Framework`_ for RPC framework and API reference. +- `Full profiler documentation`_ for profiler documentation. + +.. _pytorch.org: https://pytorch.org/ +.. _Full profiler documentation: https://pytorch.org/docs/stable/autograd.html#profiler +.. _Pytorch Profiler: https://pytorch.org/docs/stable/autograd.html#profiler +.. _Distributed RPC Framework: https://pytorch.org/docs/stable/rpc.html +.. _RPC Tutorials: https://pytorch.org/tutorials/intermediate/rpc_tutorial.html +.. _Profiler Recipe: https://pytorch.org/tutorials/recipes/recipes/profiler.html diff --git a/recipes_source/recipes_index.rst b/recipes_source/recipes_index.rst index ffdc1aaaa78..e25cb199aa5 100644 --- a/recipes_source/recipes_index.rst +++ b/recipes_source/recipes_index.rst @@ -102,6 +102,13 @@ Recipes are bite-sized bite-sized, actionable examples of how to use specific Py :link: ../recipes/recipes/profiler.html :tags: Basics +.. customcarditem:: + :header: Distributed RPC Profiling + :card_description: Learn how to use PyTorch's profiler in conjunction with the Distributed RPC Framework. + :image: ../_static/img/thumbnails/cropped/profiler.png + :link: ../recipes/distributed_rpc_profiling.html + :tags: Basics + .. Customization .. customcarditem:: @@ -201,3 +208,4 @@ Recipes are bite-sized bite-sized, actionable examples of how to use specific Py /recipes/recipes/dynamic_quantization /recipes/torchscript_inference /recipes/deployment_with_flask + /recipes/distributed_rpc_profiling From 26c729c23c980bb5a79132f1ac0432abf91689dd Mon Sep 17 00:00:00 2001 From: hx89 <43588773+hx89@users.noreply.github.com> Date: Wed, 15 Jul 2020 23:16:52 -0700 Subject: [PATCH 22/33] Update numeric_suite_tutorial.py --- prototype_source/numeric_suite_tutorial.py | 51 ++++++++++++---------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/prototype_source/numeric_suite_tutorial.py b/prototype_source/numeric_suite_tutorial.py index 6bdcfb431de..f4a609a26b7 100644 --- a/prototype_source/numeric_suite_tutorial.py +++ b/prototype_source/numeric_suite_tutorial.py @@ -49,15 +49,14 @@ float_model.eval() float_model.fuse_model() float_model.qconfig = torch.quantization.default_qconfig -img_data = [(torch.rand(2, 3, 10, 10, dtype=torch.float), torch.randint(0, 1, (2,), dtype=torch.long)) - for _ in range(2)] +img_data = [(torch.rand(2, 3, 10, 10, dtype=torch.float), torch.randint(0, 1, (2,), dtype=torch.long)) for _ in range(2)] qmodel = quantize(float_model, default_eval_fn, img_data, inplace=False) ############################################################################## # 1. Compare the weights of float and quantized models # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # The first thing we usually want to compare are the weights of quantized model and float model. -# We can call ``compare_weights()`` from PyTorch Numeric Suite to get a ``dictionary wt_compare_dict`` with key corresponding to module names and each entry is a dictionary with two keys 'float' and 'quantized', containing the float and quantized weights. +# We can call ``compare_weights()`` from PyTorch Numeric Suite to get a dictionary ``wt_compare_dict`` with key corresponding to module names and each entry is a dictionary with two keys 'float' and 'quantized', containing the float and quantized weights. # ``compare_weights()`` takes in floating point and quantized state dict and returns a dict, with keys corresponding to the # floating point weights and values being a dictionary of floating point and quantized weights @@ -74,7 +73,7 @@ ############################################################################## # Once get ``wt_compare_dict``, users can process this dictionary in whatever way they want. Here as an example we compute the quantization error of the weights of float and quantized models as following. -# Compute the Signal-to-Quantization-Noise Ratio (SQNR) of the quantized tensor ``y```. The SQNR reflects the +# Compute the Signal-to-Quantization-Noise Ratio (SQNR) of the quantized tensor ``y``. The SQNR reflects the # relationship between the maximum nominal signal strength and the quantization error introduced in the # quantization. Higher SQNR corresponds to lower quantization error. @@ -115,8 +114,7 @@ def compute_error(x, y): # We call ``compare_model_outputs()`` from PyTorch Numeric Suite to get the activations in float model and quantized model at corresponding locations for the given input data. This API returns a dict with module names being keys. Each entry is itself a dict with two keys 'float' and 'quantized' containing the activations. data = img_data[0][0] -############################################################################## -# ``img_data`` in floating point and quantized model as well as input data, and returns a dict, with keys +# Take in floating point and quantized model as well as input data, and returns a dict, with keys # corresponding to the quantized module names and each entry being a dictionary with two keys 'float' and # 'quantized', containing the activations of floating point and quantized model at matching locations. act_compare_dict = ns.compare_model_outputs(float_model, qmodel, data) @@ -137,15 +135,14 @@ def compute_error(x, y): ############################################################################## # If we want to do the comparison for more than one input data, we can do the following. # Prepare the model by attaching the logger to both floating point module and quantized -# module if they are in the white_list. Default logger is OutputLogger, and default white_list -# is DEFAULT_NUMERIC_SUITE_COMPARE_MODEL_OUTPUT_WHITE_LIST +# module if they are in the ``white_list``. Default logger is ``OutputLogger``, and default white_list +# is ``DEFAULT_NUMERIC_SUITE_COMPARE_MODEL_OUTPUT_WHITE_LIST`` ns.prepare_model_outputs(float_model, qmodel) for data in img_data: float_model(data[0]) qmodel(data[0]) -############################################################################## # Find the matching activation between floating point and quantized modules, and return a dict with key # corresponding to quantized module names and each entry being a dictionary with two keys 'float' # and 'quantized', containing the matching floating point and quantized activations logged by the logger @@ -153,7 +150,7 @@ def compute_error(x, y): ############################################################################## -# The default logger used in above APIs is OutputLogger, which is used to log the outputs of the modules. We can inherit from base Logger class and create our own logger to perform different functionalities. For example we can make a new MyOutputLogger class as below. +# The default logger used in above APIs is ``OutputLogger``, which is used to log the outputs of the modules. We can inherit from base ``Logger`` class and create our own logger to perform different functionalities. For example we can make a new ``MyOutputLogger`` class as below. class MyOutputLogger(ns.Logger): r"""Customized logger class @@ -199,26 +196,24 @@ def forward(self, x): # # The Shadow module takes quantized module, float module and logger as input, and creates a forward path inside to make the float module to shadow quantized module sharing the same input tensor. # -# The logger can be customizable, default logger is ShadowLogger and it will save the outputs of the quantized module and float module that can be used to compute the module level quantization error. +# The logger can be customizable, default logger is ``ShadowLogger`` and it will save the outputs of the quantized module and float module that can be used to compute the module level quantization error. # -# Notice before each call of compare_model_outputs() and compare_model_stub() we need to have clean float and quantized model. This is because compare_model_outputs() and compare_model_stub() modify float and quantized model inplace, and it will cause unexpected results if call one right after another. +# Notice before each call of ``compare_model_outputs()`` and ``compare_model_stub()`` we need to have clean float and quantized model. This is because ``compare_model_outputs()`` and ``compare_model_stub()`` modify float and quantized model inplace, and it will cause unexpected results if call one right after another. float_model = torchvision.models.quantization.resnet18(pretrained=True, quantize=False) float_model.to('cpu') float_model.eval() float_model.fuse_model() float_model.qconfig = torch.quantization.default_qconfig -img_data = [(torch.rand(2, 3, 10, 10, dtype=torch.float), torch.randint(0, 1, (2,), dtype=torch.long)) - for _ in range(2)] +img_data = [(torch.rand(2, 3, 10, 10, dtype=torch.float), torch.randint(0, 1, (2,), dtype=torch.long)) for _ in range(2)] qmodel = quantize(float_model, default_eval_fn, img_data, inplace=False) ############################################################################## -# In the following example we call compare_model_stub() from PyTorch Numeric Suite to compare QuantizableBasicBlock module with its float point equivalent. This API returns a dict with key corresponding to module names and each entry being a dictionary with two keys 'float' and 'quantized', containing the output tensors of quantized and its matching float shadow module. +# In the following example we call ``compare_model_stub()`` from PyTorch Numeric Suite to compare ``QuantizableBasicBlock`` module with its float point equivalent. This API returns a dict with key corresponding to module names and each entry being a dictionary with two keys 'float' and 'quantized', containing the output tensors of quantized and its matching float shadow module. data = img_data[0][0] module_swap_list = [torchvision.models.quantization.resnet.QuantizableBasicBlock] -############################################################################## # Takes in floating point and quantized model as well as input data, and returns a dict with key # corresponding to module names and each entry being a dictionary with two keys 'float' and # 'quantized', containing the output tensors of quantized module and its matching floating point shadow module. @@ -247,7 +242,7 @@ def forward(self, x): ob_dict = ns.get_logger_dict(qmodel) ############################################################################## -# The default logger used in above APIs is ShadowLogger, which is used to log the outputs of the quantized module and its matching float shadow module. We can inherit from base Logger class and create our own logger to perform different functionalities. For example we can make a new MyShadowLogger class as below. +# The default logger used in above APIs is ``ShadowLogger``, which is used to log the outputs of the quantized module and its matching float shadow module. We can inherit from base ``Logger`` class and create our own logger to perform different functionalities. For example we can make a new ``MyShadowLogger`` class as below. class MyShadowLogger(ns.Logger): r"""Customized logger class @@ -279,13 +274,13 @@ def forward(self, x, y): # Numeric Suite for Dynamic Quantization # ------------------------------------- # -# Numeric Suite APIs are designed in such as way that they work for both dynamic quantized model and static quantized model. We will use a model with both LSTM and Linear modules to demonstrate the usage of Numeric Suite on dynamic quantized model. +# Numeric Suite APIs are designed in such as way that they work for both dynamic quantized model and static quantized model. We will use a model with both LSTM and Linear modules to demonstrate the usage of Numeric Suite on dynamic quantized model. This model is the same one used in the tutorial of dynamic quantization on LSTM word language model [1]. # ################################# # Setup # ^^^^^^ -# First we define the model as below. Notice that within this model only nn. LSTM and nn.Linear modules will be quantized dynamically and nn. Embedding will remain as floating point module after quantization. +# First we define the model as below. Notice that within this model only ``nn.LSTM`` and ``nn.Linear`` modules will be quantized dynamically and ``nn.Embedding`` will remain as floating point module after quantization. class LSTMModel(nn.Module): """Container module with an encoder, a recurrent module, and a decoder.""" @@ -319,7 +314,7 @@ def init_hidden(self, bsz): weight.new_zeros(self.nlayers, bsz, self.nhid)) ############################################################################## -# Then we create the ``float_model``` and quantize it into qmodel. +# Then we create the ``float_model`` and quantize it into qmodel. ntokens = 10 @@ -361,7 +356,7 @@ def init_hidden(self, bsz): # 2. Compare float point and quantized models at corresponding locations # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # -# Then we call ``compare_model_outputs()`` from PyTorch Numeric Suite to get the activations in float model and quantized model at corresponding locations for the given input data. This API returns a dict with module names being keys. Each entry is itself a dict with two keys 'float' and 'quantized' containing the activations. Notice that this sequence model has two inputs, and we can pass both inputs into compare_model_outputs() and compare_model_stub(). +# Then we call ``compare_model_outputs()`` from PyTorch Numeric Suite to get the activations in float model and quantized model at corresponding locations for the given input data. This API returns a dict with module names being keys. Each entry is itself a dict with two keys 'float' and 'quantized' containing the activations. Notice that this sequence model has two inputs, and we can pass both inputs into ``compare_model_outputs()`` and ``compare_model_stub()``. input_ = torch.randint(ntokens, (1, 1), dtype=torch.long) @@ -371,7 +366,7 @@ def init_hidden(self, bsz): print(act_compare_dict.keys()) ############################################################################## -# This dict can be used to compare and compute the quantization error of the activations of float and quantized models as following. The LSTM module in this model has two outputs, in this example we compute the error of the first output as below. +# This dict can be used to compare and compute the quantization error of the activations of float and quantized models as following. The LSTM module in this model has two outputs, in this example we compute the error of the first output. for key in act_compare_dict: @@ -382,7 +377,7 @@ def init_hidden(self, bsz): # 3. Compare a module in a quantized model with its float point equivalent, with the same input data # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # -# Next we call compare_model_stub() from PyTorch Numeric Suite to compare LSTM and Linear module with its float point equivalent. This API returns a dict with key corresponding to module names and each entry being a dictionary with two keys 'float' and 'quantized', containing the output tensors of quantized and its matching float shadow module. +# Next we call ``compare_model_stub()`` from PyTorch Numeric Suite to compare LSTM and Linear module with its float point equivalent. This API returns a dict with key corresponding to module names and each entry being a dictionary with two keys 'float' and 'quantized', containing the output tensors of quantized and its matching float shadow module. # # We reset the model first. @@ -414,3 +409,13 @@ def init_hidden(self, bsz): ############################################################################## # SQNR of 40 dB is high and this is a situation where we have very good numerical alignment between the floating point and quantized model. + +# Conclusion +# ---------- +# In this tutorial, we demonstrated how to use PyTorch Numeric Suite to measure and compare the statistics between quantized model and float model in eager mode with unified APIs for both static quantization and dynamic quantization. + +# Thanks for reading! As always, we welcome any feedback, so please create an issue `here `_ if you have any. + +# References +# ---------- +# [1] `DYNAMIC QUANTIZATION ON AN LSTM WORD LANGUAGE MODEL `_. From f13c38265b44769fb2b8663aea5a8c097eb5867b Mon Sep 17 00:00:00 2001 From: hx89 <43588773+hx89@users.noreply.github.com> Date: Wed, 15 Jul 2020 23:35:52 -0700 Subject: [PATCH 23/33] Update numeric_suite_tutorial.py --- prototype_source/numeric_suite_tutorial.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prototype_source/numeric_suite_tutorial.py b/prototype_source/numeric_suite_tutorial.py index f4a609a26b7..61b7c670fd8 100644 --- a/prototype_source/numeric_suite_tutorial.py +++ b/prototype_source/numeric_suite_tutorial.py @@ -409,13 +409,13 @@ def init_hidden(self, bsz): ############################################################################## # SQNR of 40 dB is high and this is a situation where we have very good numerical alignment between the floating point and quantized model. - +# # Conclusion # ---------- # In this tutorial, we demonstrated how to use PyTorch Numeric Suite to measure and compare the statistics between quantized model and float model in eager mode with unified APIs for both static quantization and dynamic quantization. - +# # Thanks for reading! As always, we welcome any feedback, so please create an issue `here `_ if you have any. - +# # References # ---------- # [1] `DYNAMIC QUANTIZATION ON AN LSTM WORD LANGUAGE MODEL `_. From 5aa9d48783748ab2ee87efbab998167d6771ec76 Mon Sep 17 00:00:00 2001 From: Jessica Lin Date: Thu, 16 Jul 2020 13:14:59 -0700 Subject: [PATCH 24/33] Push latest changes from master into release/1.6 (#1074) * Update feature classification labels * Update NVidia -> Nvidia * Bring back default filename_pattern so that by default we run all galleries. Signed-off-by: Edward Z. Yang * Add prototype_source directory * Add prototype directory * Add prototype * Remove extra "done" * Add REAME.txt * Update for prototype instructions * Update for prototype feature * refine torchvision_tutorial doc for windows * Update neural_style_tutorial.py (#1059) Updated the mistake in the Loading Images Section. * torch_script_custom_ops restructure (#1057) Signed-off-by: Edward Z. Yang * Port custom ops tutorial to new registration API, increase testability. Signed-off-by: Edward Z. Yang * Kill some other occurrences of RegisterOperators Signed-off-by: Edward Z. Yang * Update README.md * Make torch_script_custom_classes tutorial runnable I also fixed some warnings in the tutorial, and fixed some minor bitrot (e.g., torch::script::Module to torch::jit::Module) I also added some missing quotes around some bash expansions. Signed-off-by: Edward Z. Yang * Update torch_script_custom_classes to use TORCH_LIBRARY (#1062) Signed-off-by: Edward Z. Yang Co-authored-by: Edward Z. Yang Co-authored-by: Yang Gu Co-authored-by: Hritik Bhandari --- .gitignore | 4 + .jenkins/build.sh | 16 +- README.md | 4 +- .../dynamic_quantization_tutorial.py | 4 +- advanced_source/neural_style_tutorial.py | 2 +- .../static_quantization_tutorial.py | 2 +- .../torch_script_custom_classes.rst | 374 +++--------------- .../CMakeLists.txt | 15 + .../custom_class_project/CMakeLists.txt | 10 + .../custom_class_project/class.cpp | 132 +++++++ .../custom_class_project/custom_test.py | 48 +++ .../custom_class_project/export_attr.py | 21 + .../custom_class_project/save.py | 18 + .../torch_script_custom_classes/infer.cpp | 20 + .../torch_script_custom_classes/run.sh | 21 + .../torch_script_custom_classes/run2.sh | 13 + advanced_source/torch_script_custom_ops.rst | 340 +++++++--------- .../torch_script_custom_ops/CMakeLists.txt | 14 + .../torch_script_custom_ops/op.cpp | 36 ++ .../torch_script_custom_ops/smoke_test.py | 3 + .../torch_script_custom_ops/test.py | 34 ++ conf.py | 5 +- index.rst | 12 +- .../dynamic_quantization_bert_tutorial.rst | 8 +- intermediate_source/memory_format_tutorial.py | 28 +- intermediate_source/named_tensor_tutorial.py | 2 +- .../quantized_transfer_learning_tutorial.rst | 10 +- intermediate_source/rpc_tutorial.rst | 2 +- intermediate_source/torchvision_tutorial.rst | 5 + prototype_source/README.md | 9 + prototype_source/README.txt | 2 + .../recipes/dynamic_quantization.py | 64 +-- 32 files changed, 695 insertions(+), 583 deletions(-) create mode 100644 advanced_source/torch_script_custom_classes/CMakeLists.txt create mode 100644 advanced_source/torch_script_custom_classes/custom_class_project/CMakeLists.txt create mode 100644 advanced_source/torch_script_custom_classes/custom_class_project/class.cpp create mode 100644 advanced_source/torch_script_custom_classes/custom_class_project/custom_test.py create mode 100644 advanced_source/torch_script_custom_classes/custom_class_project/export_attr.py create mode 100644 advanced_source/torch_script_custom_classes/custom_class_project/save.py create mode 100644 advanced_source/torch_script_custom_classes/infer.cpp create mode 100755 advanced_source/torch_script_custom_classes/run.sh create mode 100755 advanced_source/torch_script_custom_classes/run2.sh create mode 100644 advanced_source/torch_script_custom_ops/CMakeLists.txt create mode 100644 advanced_source/torch_script_custom_ops/op.cpp create mode 100644 advanced_source/torch_script_custom_ops/smoke_test.py create mode 100644 advanced_source/torch_script_custom_ops/test.py create mode 100644 prototype_source/README.md create mode 100644 prototype_source/README.txt diff --git a/.gitignore b/.gitignore index c4a23cc1208..27c61631029 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ intermediate advanced pytorch_basics recipes +prototype #data things _data/ @@ -117,3 +118,6 @@ ENV/ .DS_Store cleanup.sh *.swp + +# PyTorch things +*.pt diff --git a/.jenkins/build.sh b/.jenkins/build.sh index d8f9fc28cfc..95ad25d52da 100755 --- a/.jenkins/build.sh +++ b/.jenkins/build.sh @@ -86,6 +86,16 @@ if [[ "${JOB_BASE_NAME}" == *worker_* ]]; then FILES_TO_RUN+=($(basename $filename .py)) fi count=$((count+1)) + done + for filename in $(find prototype_source/ -name '*.py' -not -path '*/data/*'); do + if [ $(($count % $NUM_WORKERS)) != $WORKER_ID ]; then + echo "Removing runnable code from "$filename + python $DIR/remove_runnable_code.py $filename $filename + else + echo "Keeping "$filename + FILES_TO_RUN+=($(basename $filename .py)) + fi + count=$((count+1)) done echo "FILES_TO_RUN: " ${FILES_TO_RUN[@]} @@ -94,13 +104,13 @@ if [[ "${JOB_BASE_NAME}" == *worker_* ]]; then # Step 4: If any of the generated files are not related the tutorial files we want to run, # then we remove them - for filename in $(find docs/beginner docs/intermediate docs/advanced docs/recipes -name '*.html'); do + for filename in $(find docs/beginner docs/intermediate docs/advanced docs/recipes docs/prototype -name '*.html'); do file_basename=$(basename $filename .html) if [[ ! " ${FILES_TO_RUN[@]} " =~ " ${file_basename} " ]]; then rm $filename fi done - for filename in $(find docs/beginner docs/intermediate docs/advanced docs/recipes -name '*.rst'); do + for filename in $(find docs/beginner docs/intermediate docs/advanced docs/recipes docs/prototype -name '*.rst'); do file_basename=$(basename $filename .rst) if [[ ! " ${FILES_TO_RUN[@]} " =~ " ${file_basename} " ]]; then rm $filename @@ -124,7 +134,7 @@ if [[ "${JOB_BASE_NAME}" == *worker_* ]]; then rm $filename fi done - for filename in $(find docs/.doctrees/beginner docs/.doctrees/intermediate docs/.doctrees/advanced docs/.doctrees/recipes -name '*.doctree'); do + for filename in $(find docs/.doctrees/beginner docs/.doctrees/intermediate docs/.doctrees/advanced docs/.doctrees/recipes docs/.doctrees/prototype -name '*.doctree'); do file_basename=$(basename $filename .doctree) if [[ ! " ${FILES_TO_RUN[@]} " =~ " ${file_basename} " ]]; then rm $filename diff --git a/README.md b/README.md index ca418738738..5a9edf740ad 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,8 @@ We use sphinx-gallery's [notebook styled examples](https://sphinx-gallery.github Here's how to create a new tutorial or recipe: 1. Create a notebook styled python file. If you want it executed while inserted into documentation, save the file with suffix `tutorial` so that file name is `your_tutorial.py`. 2. Put it in one of the beginner_source, intermediate_source, advanced_source based on the level. If it is a recipe, add to recipes_source. -2. For Tutorials, include it in the TOC tree at index.rst -3. For Tutorials, create a thumbnail in the [index.rst file](https://github.com/pytorch/tutorials/blob/master/index.rst) using a command like `.. customcarditem:: beginner/your_tutorial.html`. For Recipes, create a thumbnail in the [recipes_index.rst](https://github.com/pytorch/tutorials/blob/master/recipes_source/recipes_index.rst) +2. For Tutorials (except if it is a prototype feature), include it in the TOC tree at index.rst +3. For Tutorials (except if it is a prototype feature), create a thumbnail in the [index.rst file](https://github.com/pytorch/tutorials/blob/master/index.rst) using a command like `.. customcarditem:: beginner/your_tutorial.html`. For Recipes, create a thumbnail in the [recipes_index.rst](https://github.com/pytorch/tutorials/blob/master/recipes_source/recipes_index.rst) In case you prefer to write your tutorial in jupyter, you can use [this script](https://gist.github.com/chsasank/7218ca16f8d022e02a9c0deb94a310fe) to convert the notebook to python file. After conversion and addition to the project, please make sure the sections headings etc are in logical order. diff --git a/advanced_source/dynamic_quantization_tutorial.py b/advanced_source/dynamic_quantization_tutorial.py index 990fd688651..cabdd90d224 100644 --- a/advanced_source/dynamic_quantization_tutorial.py +++ b/advanced_source/dynamic_quantization_tutorial.py @@ -1,5 +1,5 @@ """ -(experimental) Dynamic Quantization on an LSTM Word Language Model +(beta) Dynamic Quantization on an LSTM Word Language Model ================================================================== **Author**: `James Reed `_ @@ -13,7 +13,7 @@ to int, which can result in smaller model size and faster inference with only a small hit to accuracy. -In this tutorial, we'll apply the easiest form of quantization - +In this tutorial, we'll apply the easiest form of quantization - `dynamic quantization `_ - to an LSTM-based next word-prediction model, closely following the `word language model `_ diff --git a/advanced_source/neural_style_tutorial.py b/advanced_source/neural_style_tutorial.py index 5b8e56bdc0c..b3421a11bee 100644 --- a/advanced_source/neural_style_tutorial.py +++ b/advanced_source/neural_style_tutorial.py @@ -83,7 +83,7 @@ # An important detail to note is that neural networks from the # torch library are trained with tensor values ranging from 0 to 1. If you # try to feed the networks with 0 to 255 tensor images, then the activated -# feature maps will be unable sense the intended content and style. +# feature maps will be unable to sense the intended content and style. # However, pre-trained networks from the Caffe library are trained with 0 # to 255 tensor images. # diff --git a/advanced_source/static_quantization_tutorial.py b/advanced_source/static_quantization_tutorial.py index 60f9112a8c1..72ea3cc703e 100644 --- a/advanced_source/static_quantization_tutorial.py +++ b/advanced_source/static_quantization_tutorial.py @@ -1,5 +1,5 @@ """ -(experimental) Static Quantization with Eager Mode in PyTorch +(beta) Static Quantization with Eager Mode in PyTorch ========================================================= **Author**: `Raghuraman Krishnamoorthi `_ diff --git a/advanced_source/torch_script_custom_classes.rst b/advanced_source/torch_script_custom_classes.rst index 031e6c3f696..be13759cdc6 100644 --- a/advanced_source/torch_script_custom_classes.rst +++ b/advanced_source/torch_script_custom_classes.rst @@ -2,7 +2,7 @@ Extending TorchScript with Custom C++ Classes =============================================== This tutorial is a follow-on to the -`custom operator `_ +:doc:`custom operator ` tutorial, and introduces the API we've built for binding C++ classes into TorchScript and Python simultaneously. The API is very similar to `pybind11 `_, and most of the concepts will transfer @@ -14,44 +14,10 @@ Implementing and Binding the Class in C++ For this tutorial, we are going to define a simple C++ class that maintains persistent state in a member variable. -.. code-block:: cpp - - // This header is all you need to do the C++ portions of this - // tutorial - #include - // This header is what defines the custom class registration - // behavior specifically. script.h already includes this, but - // we include it here so you know it exists in case you want - // to look at the API or implementation. - #include - - #include - #include - - template - struct MyStackClass : torch::CustomClassHolder { - std::vector stack_; - MyStackClass(std::vector init) : stack_(init.begin(), init.end()) {} - - void push(T x) { - stack_.push_back(x); - } - T pop() { - auto val = stack_.back(); - stack_.pop_back(); - return val; - } - - c10::intrusive_ptr clone() const { - return c10::make_intrusive(stack_); - } - - void merge(const c10::intrusive_ptr& c) { - for (auto& elem : c->stack_) { - push(elem); - } - } - }; +.. literalinclude:: ../advanced_source/torch_script_custom_classes/custom_class_project/class.cpp + :language: cpp + :start-after: BEGIN class + :end-before: END class There are several things to note: @@ -59,56 +25,25 @@ There are several things to note: with your custom class. - Notice that whenever we are working with instances of the custom class, we do it via instances of ``c10::intrusive_ptr<>``. Think of ``intrusive_ptr`` - as a smart pointer like ``std::shared_ptr``. The reason for using this smart pointer - is to ensure consistent lifetime management of the object instances between languages - (C++, Python and TorchScript). + as a smart pointer like ``std::shared_ptr``, but the reference count is stored + directly in the object, as opposed to a separate metadata block (as is done in + ``std::shared_ptr``. ``torch::Tensor`` internally uses the same pointer type; + and custom classes have to also use this pointer type so that we can + consistently manage different object types. - The second thing to notice is that the user-defined class must inherit from - ``torch::CustomClassHolder``. This ensures that everything is set up to handle - the lifetime management system previously mentioned. + ``torch::CustomClassHolder``. This ensures that the custom class has space to + store the reference count. Now let's take a look at how we will make this class visible to TorchScript, a process called *binding* the class: -.. code-block:: cpp - - // Notice a few things: - // - We pass the class to be registered as a template parameter to - // `torch::class_`. In this instance, we've passed the - // specialization of the MyStackClass class ``MyStackClass``. - // In general, you cannot register a non-specialized template - // class. For non-templated classes, you can just pass the - // class name directly as the template parameter. - // - The arguments passed to the constructor make up the "qualified name" - // of the class. In this case, the registered class will appear in - // Python and C++ as `torch.classes.my_classes.MyStackClass`. We call - // the first argument the "namespace" and the second argument the - // actual class name. - static auto testStack = - torch::class_>("my_classes", "MyStackClass") - // The following line registers the contructor of our MyStackClass - // class that takes a single `std::vector` argument, - // i.e. it exposes the C++ method `MyStackClass(std::vector init)`. - // Currently, we do not support registering overloaded - // constructors, so for now you can only `def()` one instance of - // `torch::init`. - .def(torch::init>()) - // The next line registers a stateless (i.e. no captures) C++ lambda - // function as a method. Note that a lambda function must take a - // `c10::intrusive_ptr` (or some const/ref version of that) - // as the first argument. Other arguments can be whatever you want. - .def("top", [](const c10::intrusive_ptr>& self) { - return self->stack_.back(); - }) - // The following four lines expose methods of the MyStackClass - // class as-is. `torch::class_` will automatically examine the - // argument and return types of the passed-in method pointers and - // expose these to Python and TorchScript accordingly. Finally, notice - // that we must take the *address* of the fully-qualified method name, - // i.e. use the unary `&` operator, due to C++ typing rules. - .def("push", &MyStackClass::push) - .def("pop", &MyStackClass::pop) - .def("clone", &MyStackClass::clone) - .def("merge", &MyStackClass::merge); +.. literalinclude:: ../advanced_source/torch_script_custom_classes/custom_class_project/class.cpp + :language: cpp + :start-after: BEGIN binding + :end-before: END binding + :append: + ; + } @@ -121,18 +56,8 @@ we've covered so far and place it in a file called ``class.cpp``. Then, write a simple ``CMakeLists.txt`` file and place it in the same directory. Here is what ``CMakeLists.txt`` should look like: -.. code-block:: cmake - - cmake_minimum_required(VERSION 3.1 FATAL_ERROR) - project(custom_class) - - find_package(Torch REQUIRED) - - # Define our library target - add_library(custom_class SHARED class.cpp) - set(CMAKE_CXX_STANDARD 14) - # Link against LibTorch - target_link_libraries(custom_class "${TORCH_LIBRARIES}") +.. literalinclude:: ../advanced_source/torch_script_custom_classes/custom_class_project/CMakeLists.txt + :language: cmake Also, create a ``build`` directory. Your file tree should look like this:: @@ -141,16 +66,14 @@ Also, create a ``build`` directory. Your file tree should look like this:: CMakeLists.txt build/ -Now, to build the project, go ahead and download the appropriate libtorch -binary from the `PyTorch website `_. Extract the -zip archive somewhere (within the project directory might be convenient) -and note the path you've extracted it to. Next, go ahead and invoke cmake and -then make to build the project: +We assume you've setup your environment in the same way as described in +the :doc:`previous tutorial `. +Go ahead and invoke cmake and then make to build the project: .. code-block:: shell $ cd build - $ cmake -DCMAKE_PREFIX_PATH=/path/to/libtorch .. + $ cmake -DCMAKE_PREFIX_PATH="$(python -c 'import torch.utils; print(torch.utils.cmake_prefix_path)')" .. -- The C compiler identification is GNU 7.3.1 -- The CXX compiler identification is GNU 7.3.1 -- Check for working C compiler: /opt/rh/devtoolset-7/root/usr/bin/cc @@ -201,52 +124,9 @@ Now that we have our class and its registration compiled into an ``.so`` file, we can load that `.so` into Python and try it out. Here's a script that demonstrates that: -.. code-block:: python +.. literalinclude:: ../advanced_source/torch_script_custom_classes/custom_class_project/custom_test.py + :language: python - import torch - - # `torch.classes.load_library()` allows you to pass the path to your .so file - # to load it in and make the custom C++ classes available to both Python and - # TorchScript - torch.classes.load_library("libcustom_class.so") - # You can query the loaded libraries like this: - print(torch.classes.loaded_libraries) - # prints {'/custom_class_project/build/libcustom_class.so'} - - # We can find and instantiate our custom C++ class in python by using the - # `torch.classes` namespace: - # - # This instantiation will invoke the MyStackClass(std::vector init) constructor - # we registered earlier - s = torch.classes.my_classes.MyStackClass(["foo", "bar"]) - - # We can call methods in Python - s.push("pushed") - assert s.pop() == "pushed" - - # Returning and passing instances of custom classes works as you'd expect - s2 = s.clone() - s.merge(s2) - for expected in ["bar", "foo", "bar", "foo"]: - assert s.pop() == expected - - # We can also use the class in TorchScript - # For now, we need to assign the class's type to a local in order to - # annotate the type on the TorchScript function. This may change - # in the future. - MyStackClass = torch.classes.my_classes.MyStackClass - - @torch.jit.script - def do_stacks(s : MyStackClass): # We can pass a custom class instance to TorchScript - s2 = torch.classes.my_classes.MyStackClass(["hi", "mom"]) # We can instantiate the class - s2.merge(s) # We can call a method on the class - return s2.clone(), s2.top() # We can also return instances of the class - # from TorchScript function/methods - - stack, top = do_stacks(torch.classes.my_classes.MyStackClass(["wow"])) - assert top == "wow" - for expected in ["wow", "mom", "hi"]: - assert stack.pop() == expected Saving, Loading, and Running TorchScript Code Using Custom Classes ------------------------------------------------------------------ @@ -255,24 +135,8 @@ We can also use custom-registered C++ classes in a C++ process using libtorch. As an example, let's define a simple ``nn.Module`` that instantiates and calls a method on our MyStackClass class: -.. code-block:: python - - import torch - - torch.classes.load_library('libcustom_class.so') - - class Foo(torch.nn.Module): - def __init__(self): - super().__init__() - - def forward(self, s : str) -> str: - stack = torch.classes.my_classes.MyStackClass(["hi", "mom"]) - return stack.pop() + s - - scripted_foo = torch.jit.script(Foo()) - print(scripted_foo.graph) - - scripted_foo.save('foo.pt') +.. literalinclude:: ../advanced_source/torch_script_custom_classes/custom_class_project/save.py + :language: python ``foo.pt`` in our filesystem now contains the serialized TorchScript program we've just defined. @@ -300,55 +164,20 @@ build the custom class into the binary. Let's populate ``infer.cpp`` with the following: -.. code-block:: cpp - - #include - - #include - #include - - int main(int argc, const char* argv[]) { - torch::script::Module module; - try { - // Deserialize the ScriptModule from a file using torch::jit::load(). - module = torch::jit::load("foo.pt"); - } - catch (const c10::Error& e) { - std::cerr << "error loading the model\n"; - return -1; - } - - std::vector inputs = {"foobarbaz"}; - auto output = module.forward(inputs).toString(); - std::cout << output->string() << std::endl; - } +.. literalinclude:: ../advanced_source/torch_script_custom_classes/infer.cpp + :language: cpp And similarly let's define our CMakeLists.txt file: -.. code-block:: cmake - - cmake_minimum_required(VERSION 3.1 FATAL_ERROR) - project(infer) - - find_package(Torch REQUIRED) - - add_subdirectory(custom_class_project) - - # Define our library target - add_executable(infer infer.cpp) - set(CMAKE_CXX_STANDARD 14) - # Link against LibTorch - target_link_libraries(infer "${TORCH_LIBRARIES}") - # This is where we link in our libcustom_class code, making our - # custom class available in our binary. - target_link_libraries(infer -Wl,--no-as-needed custom_class) +.. literalinclude:: ../advanced_source/torch_script_custom_classes/CMakeLists.txt + :language: cpp You know the drill: ``cd build``, ``cmake``, and ``make``: .. code-block:: shell $ cd build - $ cmake -DCMAKE_PREFIX_PATH=/path/to/libtorch .. + $ cmake -DCMAKE_PREFIX_PATH="$(python -c 'import torch.utils; print(torch.utils.cmake_prefix_path)')" .. -- The C compiler identification is GNU 7.3.1 -- The CXX compiler identification is GNU 7.3.1 -- Check for working C compiler: /opt/rh/devtoolset-7/root/usr/bin/cc @@ -412,7 +241,7 @@ or you want to instantiate a custom class attribute in C++. For creating an - In the event that you already have an ``intrusive_ptr`` pointing to your class, you can directly construct an IValue from it using the constructor ``IValue(intrusive_ptr)``. -For converting ``IValue``s back to custom classes: +For converting ``IValue`` back to custom classes: - ``IValue::toCustomClass()`` will return an ``intrusive_ptr`` pointing to the custom class that the ``IValue`` contains. Internally, this function is checking @@ -426,24 +255,8 @@ Defining Serialization/Deserialization Methods for Custom C++ Classes If you try to save a ``ScriptModule`` with a custom-bound C++ class as an attribute, you'll get the following error: -.. code-block:: python - - # export_attr.py - import torch - - torch.classes.load_library('libcustom_class.so') - - class Foo(torch.nn.Module): - def __init__(self): - super().__init__() - self.stack = torch.classes.my_classes.MyStackClass(["just", "testing"]) - - def forward(self, s : str) -> str: - return self.stack.pop() + s - - scripted_foo = torch.jit.script(Foo()) - - scripted_foo.save('foo.pt') +.. literalinclude:: ../advanced_source/torch_script_custom_classes/custom_class_project/export_attr.py + :language: python .. code-block:: shell @@ -461,55 +274,13 @@ the special ``def_pickle`` method on ``class_``. `read more `_ about how we use these methods. -Here is an example of how we can update the registration code for our -``MyStackClass`` class to include serialization methods: - -.. code-block:: cpp - - static auto testStack = - torch::class_>("my_classes", "MyStackClass") - .def(torch::init>()) - .def("top", [](const c10::intrusive_ptr>& self) { - return self->stack_.back(); - }) - .def("push", &MyStackClass::push) - .def("pop", &MyStackClass::pop) - .def("clone", &MyStackClass::clone) - .def("merge", &MyStackClass::merge) - // class_<>::def_pickle allows you to define the serialization - // and deserialization methods for your C++ class. - // Currently, we only support passing stateless lambda functions - // as arguments to def_pickle - .def_pickle( - // __getstate__ - // This function defines what data structure should be produced - // when we serialize an instance of this class. The function - // must take a single `self` argument, which is an intrusive_ptr - // to the instance of the object. The function can return - // any type that is supported as a return value of the TorchScript - // custom operator API. In this instance, we've chosen to return - // a std::vector as the salient data to preserve - // from the class. - [](const c10::intrusive_ptr>& self) - -> std::vector { - return self->stack_; - }, - // __setstate__ - // This function defines how to create a new instance of the C++ - // class when we are deserializing. The function must take a - // single argument of the same type as the return value of - // `__getstate__`. The function must return an intrusive_ptr - // to a new instance of the C++ class, initialized however - // you would like given the serialized state. - [](std::vector state) - -> c10::intrusive_ptr> { - // A convenient way to instantiate an object and get an - // intrusive_ptr to it is via `make_intrusive`. We use - // that here to allocate an instance of MyStackClass - // and call the single-argument std::vector - // constructor with the serialized state. - return c10::make_intrusive>(std::move(state)); - }); +Here is an example of the ``def_pickle`` call we can add to the registration of +``MyStackClass`` to include serialization methods: + +.. literalinclude:: ../advanced_source/torch_script_custom_classes/custom_class_project/class.cpp + :language: cpp + :start-after: BEGIN def_pickle + :end-before: END def_pickle .. note:: We take a different approach from pybind11 in the pickle API. Whereas pybind11 @@ -520,27 +291,6 @@ Here is an example of how we can update the registration code for our Once we have defined the (de)serialization behavior in this way, our script can now run successfully: -.. code-block:: python - - import torch - - torch.classes.load_library('libcustom_class.so') - - class Foo(torch.nn.Module): - def __init__(self): - super().__init__() - self.stack = torch.classes.my_classes.MyStackClass(["just", "testing"]) - - def forward(self, s : str) -> str: - return self.stack.pop() + s - - scripted_foo = torch.jit.script(Foo()) - - scripted_foo.save('foo.pt') - loaded = torch.jit.load('foo.pt') - - print(loaded.stack.pop()) - .. code-block:: shell $ python ../export_attr.py @@ -550,21 +300,21 @@ Defining Custom Operators that Take or Return Bound C++ Classes --------------------------------------------------------------- Once you've defined a custom C++ class, you can also use that class -as an argument or return from a custom operator (i.e. free functions). Here's an -example of how to do that: +as an argument or return from a custom operator (i.e. free functions). Suppose +you have the following free function: -.. code-block:: cpp +.. literalinclude:: ../advanced_source/torch_script_custom_classes/custom_class_project/class.cpp + :language: cpp + :start-after: BEGIN free_function + :end-before: END free_function - c10::intrusive_ptr> manipulate_instance(const c10::intrusive_ptr>& instance) { - instance->pop(); - return instance; - } +You can register it running the following code inside your ``TORCH_LIBRARY`` +block: - static auto instance_registry = torch::RegisterOperators().op( - torch::RegisterOperators::options() - .schema( - "foo::manipulate_instance(__torch__.torch.classes.my_classes.MyStackClass x) -> __torch__.torch.classes.my_classes.MyStackClass Y") - .catchAllKernel()); +.. literalinclude:: ../advanced_source/torch_script_custom_classes/custom_class_project/class.cpp + :language: cpp + :start-after: BEGIN def_free + :end-before: END def_free Refer to the `custom op tutorial `_ for more details on the registration API. @@ -584,12 +334,12 @@ Once this is done, you can use the op like the following example: .. note:: Registration of an operator that takes a C++ class as an argument requires that - the custom class has already been registered. This is fine if your op is - registered after your class in a single compilation unit, however, if your - class is registered in a separate compilation unit from the op you will need - to enforce that dependency. One way to do this is to wrap the class registration - in a `Meyer's singleton `_, which can be - called from the compilation unit that does the operator registration. + the custom class has already been registered. You can enforce this by + making sure the custom class registration and your free function definitions + are in the same ``TORCH_LIBRARY`` block, and that the custom class + registration comes first. In the future, we may relax this requirement, + so that these can be registered in any order. + Conclusion ---------- diff --git a/advanced_source/torch_script_custom_classes/CMakeLists.txt b/advanced_source/torch_script_custom_classes/CMakeLists.txt new file mode 100644 index 00000000000..6a1eb3e87fa --- /dev/null +++ b/advanced_source/torch_script_custom_classes/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.1 FATAL_ERROR) +project(infer) + +find_package(Torch REQUIRED) + +add_subdirectory(custom_class_project) + +# Define our library target +add_executable(infer infer.cpp) +set(CMAKE_CXX_STANDARD 14) +# Link against LibTorch +target_link_libraries(infer "${TORCH_LIBRARIES}") +# This is where we link in our libcustom_class code, making our +# custom class available in our binary. +target_link_libraries(infer -Wl,--no-as-needed custom_class) diff --git a/advanced_source/torch_script_custom_classes/custom_class_project/CMakeLists.txt b/advanced_source/torch_script_custom_classes/custom_class_project/CMakeLists.txt new file mode 100644 index 00000000000..bb3d41aa997 --- /dev/null +++ b/advanced_source/torch_script_custom_classes/custom_class_project/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.1 FATAL_ERROR) +project(custom_class) + +find_package(Torch REQUIRED) + +# Define our library target +add_library(custom_class SHARED class.cpp) +set(CMAKE_CXX_STANDARD 14) +# Link against LibTorch +target_link_libraries(custom_class "${TORCH_LIBRARIES}") diff --git a/advanced_source/torch_script_custom_classes/custom_class_project/class.cpp b/advanced_source/torch_script_custom_classes/custom_class_project/class.cpp new file mode 100644 index 00000000000..c5849cef102 --- /dev/null +++ b/advanced_source/torch_script_custom_classes/custom_class_project/class.cpp @@ -0,0 +1,132 @@ +// BEGIN class +// This header is all you need to do the C++ portions of this +// tutorial +#include +// This header is what defines the custom class registration +// behavior specifically. script.h already includes this, but +// we include it here so you know it exists in case you want +// to look at the API or implementation. +#include + +#include +#include + +template +struct MyStackClass : torch::CustomClassHolder { + std::vector stack_; + MyStackClass(std::vector init) : stack_(init.begin(), init.end()) {} + + void push(T x) { + stack_.push_back(x); + } + T pop() { + auto val = stack_.back(); + stack_.pop_back(); + return val; + } + + c10::intrusive_ptr clone() const { + return c10::make_intrusive(stack_); + } + + void merge(const c10::intrusive_ptr& c) { + for (auto& elem : c->stack_) { + push(elem); + } + } +}; +// END class + +// BEGIN free_function +c10::intrusive_ptr> manipulate_instance(const c10::intrusive_ptr>& instance) { + instance->pop(); + return instance; +} +// END free_function + +// BEGIN binding +// Notice a few things: +// - We pass the class to be registered as a template parameter to +// `torch::class_`. In this instance, we've passed the +// specialization of the MyStackClass class ``MyStackClass``. +// In general, you cannot register a non-specialized template +// class. For non-templated classes, you can just pass the +// class name directly as the template parameter. +// - The arguments passed to the constructor make up the "qualified name" +// of the class. In this case, the registered class will appear in +// Python and C++ as `torch.classes.my_classes.MyStackClass`. We call +// the first argument the "namespace" and the second argument the +// actual class name. +TORCH_LIBRARY(my_classes, m) { + m.class_>("MyStackClass") + // The following line registers the contructor of our MyStackClass + // class that takes a single `std::vector` argument, + // i.e. it exposes the C++ method `MyStackClass(std::vector init)`. + // Currently, we do not support registering overloaded + // constructors, so for now you can only `def()` one instance of + // `torch::init`. + .def(torch::init>()) + // The next line registers a stateless (i.e. no captures) C++ lambda + // function as a method. Note that a lambda function must take a + // `c10::intrusive_ptr` (or some const/ref version of that) + // as the first argument. Other arguments can be whatever you want. + .def("top", [](const c10::intrusive_ptr>& self) { + return self->stack_.back(); + }) + // The following four lines expose methods of the MyStackClass + // class as-is. `torch::class_` will automatically examine the + // argument and return types of the passed-in method pointers and + // expose these to Python and TorchScript accordingly. Finally, notice + // that we must take the *address* of the fully-qualified method name, + // i.e. use the unary `&` operator, due to C++ typing rules. + .def("push", &MyStackClass::push) + .def("pop", &MyStackClass::pop) + .def("clone", &MyStackClass::clone) + .def("merge", &MyStackClass::merge) +// END binding +#ifndef NO_PICKLE +// BEGIN def_pickle + // class_<>::def_pickle allows you to define the serialization + // and deserialization methods for your C++ class. + // Currently, we only support passing stateless lambda functions + // as arguments to def_pickle + .def_pickle( + // __getstate__ + // This function defines what data structure should be produced + // when we serialize an instance of this class. The function + // must take a single `self` argument, which is an intrusive_ptr + // to the instance of the object. The function can return + // any type that is supported as a return value of the TorchScript + // custom operator API. In this instance, we've chosen to return + // a std::vector as the salient data to preserve + // from the class. + [](const c10::intrusive_ptr>& self) + -> std::vector { + return self->stack_; + }, + // __setstate__ + // This function defines how to create a new instance of the C++ + // class when we are deserializing. The function must take a + // single argument of the same type as the return value of + // `__getstate__`. The function must return an intrusive_ptr + // to a new instance of the C++ class, initialized however + // you would like given the serialized state. + [](std::vector state) + -> c10::intrusive_ptr> { + // A convenient way to instantiate an object and get an + // intrusive_ptr to it is via `make_intrusive`. We use + // that here to allocate an instance of MyStackClass + // and call the single-argument std::vector + // constructor with the serialized state. + return c10::make_intrusive>(std::move(state)); + }); +// END def_pickle +#endif // NO_PICKLE + +// BEGIN def_free + m.def( + "foo::manipulate_instance(__torch__.torch.classes.my_classes.MyStackClass x) -> __torch__.torch.classes.my_classes.MyStackClass Y", + manipulate_instance + ); +// END def_free +} diff --git a/advanced_source/torch_script_custom_classes/custom_class_project/custom_test.py b/advanced_source/torch_script_custom_classes/custom_class_project/custom_test.py new file mode 100644 index 00000000000..ba8448e4545 --- /dev/null +++ b/advanced_source/torch_script_custom_classes/custom_class_project/custom_test.py @@ -0,0 +1,48 @@ +import torch + +# `torch.classes.load_library()` allows you to pass the path to your .so file +# to load it in and make the custom C++ classes available to both Python and +# TorchScript +torch.classes.load_library("build/libcustom_class.so") +# You can query the loaded libraries like this: +print(torch.classes.loaded_libraries) +# prints {'/custom_class_project/build/libcustom_class.so'} + +# We can find and instantiate our custom C++ class in python by using the +# `torch.classes` namespace: +# +# This instantiation will invoke the MyStackClass(std::vector init) +# constructor we registered earlier +s = torch.classes.my_classes.MyStackClass(["foo", "bar"]) + +# We can call methods in Python +s.push("pushed") +assert s.pop() == "pushed" + +# Returning and passing instances of custom classes works as you'd expect +s2 = s.clone() +s.merge(s2) +for expected in ["bar", "foo", "bar", "foo"]: + assert s.pop() == expected + +# We can also use the class in TorchScript +# For now, we need to assign the class's type to a local in order to +# annotate the type on the TorchScript function. This may change +# in the future. +MyStackClass = torch.classes.my_classes.MyStackClass + + +@torch.jit.script +def do_stacks(s: MyStackClass): # We can pass a custom class instance + # We can instantiate the class + s2 = torch.classes.my_classes.MyStackClass(["hi", "mom"]) + s2.merge(s) # We can call a method on the class + # We can also return instances of the class + # from TorchScript function/methods + return s2.clone(), s2.top() + + +stack, top = do_stacks(torch.classes.my_classes.MyStackClass(["wow"])) +assert top == "wow" +for expected in ["wow", "mom", "hi"]: + assert stack.pop() == expected diff --git a/advanced_source/torch_script_custom_classes/custom_class_project/export_attr.py b/advanced_source/torch_script_custom_classes/custom_class_project/export_attr.py new file mode 100644 index 00000000000..9999d5c8183 --- /dev/null +++ b/advanced_source/torch_script_custom_classes/custom_class_project/export_attr.py @@ -0,0 +1,21 @@ +# export_attr.py +import torch + +torch.classes.load_library('build/libcustom_class.so') + + +class Foo(torch.nn.Module): + def __init__(self): + super().__init__() + self.stack = torch.classes.my_classes.MyStackClass(["just", "testing"]) + + def forward(self, s: str) -> str: + return self.stack.pop() + s + + +scripted_foo = torch.jit.script(Foo()) + +scripted_foo.save('foo.pt') +loaded = torch.jit.load('foo.pt') + +print(loaded.stack.pop()) diff --git a/advanced_source/torch_script_custom_classes/custom_class_project/save.py b/advanced_source/torch_script_custom_classes/custom_class_project/save.py new file mode 100644 index 00000000000..8826f95da7c --- /dev/null +++ b/advanced_source/torch_script_custom_classes/custom_class_project/save.py @@ -0,0 +1,18 @@ +import torch + +torch.classes.load_library('build/libcustom_class.so') + + +class Foo(torch.nn.Module): + def __init__(self): + super().__init__() + + def forward(self, s: str) -> str: + stack = torch.classes.my_classes.MyStackClass(["hi", "mom"]) + return stack.pop() + s + + +scripted_foo = torch.jit.script(Foo()) +print(scripted_foo.graph) + +scripted_foo.save('foo.pt') diff --git a/advanced_source/torch_script_custom_classes/infer.cpp b/advanced_source/torch_script_custom_classes/infer.cpp new file mode 100644 index 00000000000..1ca5b002383 --- /dev/null +++ b/advanced_source/torch_script_custom_classes/infer.cpp @@ -0,0 +1,20 @@ +#include + +#include +#include + +int main(int argc, const char* argv[]) { + torch::jit::Module module; + try { + // Deserialize the ScriptModule from a file using torch::jit::load(). + module = torch::jit::load("foo.pt"); + } + catch (const c10::Error& e) { + std::cerr << "error loading the model\n"; + return -1; + } + + std::vector inputs = {"foobarbaz"}; + auto output = module.forward(inputs).toString(); + std::cout << output->string() << std::endl; +} diff --git a/advanced_source/torch_script_custom_classes/run.sh b/advanced_source/torch_script_custom_classes/run.sh new file mode 100755 index 00000000000..52c59581309 --- /dev/null +++ b/advanced_source/torch_script_custom_classes/run.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +set -ex + +rm -rf build +rm -rf custom_class_project/build + +pushd custom_class_project + mkdir build + (cd build && cmake CXXFLAGS="-DNO_PICKLE" -DCMAKE_PREFIX_PATH="$(python -c 'import torch.utils; print(torch.utils.cmake_prefix_path)')" ..) + (cd build && make) + python custom_test.py + python save.py + ! python export_attr.py +popd + +mkdir build +(cd build && cmake -DCMAKE_PREFIX_PATH="$(python -c 'import torch.utils; print(torch.utils.cmake_prefix_path)')" ..) +(cd build && make) +mv custom_class_project/foo.pt build/foo.pt +(cd build && ./infer) diff --git a/advanced_source/torch_script_custom_classes/run2.sh b/advanced_source/torch_script_custom_classes/run2.sh new file mode 100755 index 00000000000..d4ef0101a83 --- /dev/null +++ b/advanced_source/torch_script_custom_classes/run2.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -ex + +rm -rf build +rm -rf custom_class_project/build + +pushd custom_class_project + mkdir build + (cd build && cmake -DCMAKE_PREFIX_PATH="$(python -c 'import torch.utils; print(torch.utils.cmake_prefix_path)')" ..) + (cd build && make) + python export_attr.py +popd diff --git a/advanced_source/torch_script_custom_ops.rst b/advanced_source/torch_script_custom_ops.rst index 9127855878d..d5620387451 100644 --- a/advanced_source/torch_script_custom_ops.rst +++ b/advanced_source/torch_script_custom_ops.rst @@ -23,7 +23,7 @@ Python and in their serialized form directly in C++. The following paragraphs give an example of writing a TorchScript custom op to call into `OpenCV `_, a computer vision library written in C++. We will discuss how to work with tensors in C++, how to efficiently -convert them to third party tensor formats (in this case, OpenCV ``Mat``s), how +convert them to third party tensor formats (in this case, OpenCV ``Mat``), how to register your operator with the TorchScript runtime and finally how to compile the operator and use it in Python and C++. @@ -37,27 +37,10 @@ TorchScript as a custom operator. The first step is to write the implementation of our custom operator in C++. Let's call the file for this implementation ``op.cpp`` and make it look like this: -.. code-block:: cpp - - #include - #include - - torch::Tensor warp_perspective(torch::Tensor image, torch::Tensor warp) { - cv::Mat image_mat(/*rows=*/image.size(0), - /*cols=*/image.size(1), - /*type=*/CV_32FC1, - /*data=*/image.data()); - cv::Mat warp_mat(/*rows=*/warp.size(0), - /*cols=*/warp.size(1), - /*type=*/CV_32FC1, - /*data=*/warp.data()); - - cv::Mat output_mat; - cv::warpPerspective(image_mat, output_mat, warp_mat, /*dsize=*/{8, 8}); - - torch::Tensor output = torch::from_blob(output_mat.ptr(), /*sizes=*/{8, 8}); - return output.clone(); - } +.. literalinclude:: ../advanced_source/torch_script_custom_ops/op.cpp + :language: cpp + :start-after: BEGIN warp_perspective + :end-before: END warp_perspective The code for this operator is quite short. At the top of the file, we include the OpenCV header file, ``opencv2/opencv.hpp``, alongside the ``torch/script.h`` @@ -92,12 +75,10 @@ tensors to OpenCV matrices, as OpenCV's ``warpPerspective`` expects ``cv::Mat`` objects as inputs. Fortunately, there is a way to do this **without copying any** data. In the first few lines, -.. code-block:: cpp - - cv::Mat image_mat(/*rows=*/image.size(0), - /*cols=*/image.size(1), - /*type=*/CV_32FC1, - /*data=*/image.data()); +.. literalinclude:: ../advanced_source/torch_script_custom_ops/op.cpp + :language: cpp + :start-after: BEGIN image_mat + :end-before: END image_mat we are calling `this constructor `_ @@ -113,12 +94,10 @@ subsequent OpenCV routines with the library's native matrix type, even though we're actually storing the data in a PyTorch tensor. We repeat this procedure to convert the ``warp`` PyTorch tensor to the ``warp_mat`` OpenCV matrix: -.. code-block:: cpp - - cv::Mat warp_mat(/*rows=*/warp.size(0), - /*cols=*/warp.size(1), - /*type=*/CV_32FC1, - /*data=*/warp.data()); +.. literalinclude:: ../advanced_source/torch_script_custom_ops/op.cpp + :language: cpp + :start-after: BEGIN warp_mat + :end-before: END warp_mat Next, we are ready to call the OpenCV function we were so eager to use in TorchScript: ``warpPerspective``. For this, we pass the OpenCV function the @@ -126,10 +105,10 @@ TorchScript: ``warpPerspective``. For this, we pass the OpenCV function the called ``output_mat``. We also specify the size ``dsize`` we want the output matrix (image) to be. It is hardcoded to ``8 x 8`` for this example: -.. code-block:: cpp - - cv::Mat output_mat; - cv::warpPerspective(image_mat, output_mat, warp_mat, /*dsize=*/{8, 8}); +.. literalinclude:: ../advanced_source/torch_script_custom_ops/op.cpp + :language: cpp + :start-after: BEGIN output_mat + :end-before: END output_mat The final step in our custom operator implementation is to convert the ``output_mat`` back into a PyTorch tensor, so that we can further use it in @@ -139,12 +118,13 @@ other direction. In this case, PyTorch provides a ``torch::from_blob`` method. A we want to interpret as a PyTorch tensor. The call to ``torch::from_blob`` looks like this: -.. code-block:: cpp - - torch::from_blob(output_mat.ptr(), /*sizes=*/{8, 8}) +.. literalinclude:: ../advanced_source/torch_script_custom_ops/op.cpp + :language: cpp + :start-after: BEGIN output_tensor + :end-before: END output_tensor We use the ``.ptr()`` method on the OpenCV ``Mat`` class to get a raw -pointer to the underlying data (just like ``.data()`` for the PyTorch +pointer to the underlying data (just like ``.data_ptr()`` for the PyTorch tensor earlier). We also specify the output shape of the tensor, which we hardcoded as ``8 x 8``. The output of ``torch::from_blob`` is then a ``torch::Tensor``, pointing to the memory owned by the OpenCV matrix. @@ -165,40 +145,28 @@ Registering the Custom Operator with TorchScript Now that have implemented our custom operator in C++, we need to *register* it with the TorchScript runtime and compiler. This will allow the TorchScript compiler to resolve references to our custom operator in TorchScript code. -Registration is very simple. For our case, we need to write: - -.. code-block:: cpp - - static auto registry = - torch::RegisterOperators("my_ops::warp_perspective", &warp_perspective); - -somewhere in the global scope of our ``op.cpp`` file. This creates a global -variable ``registry``, which will register our operator with TorchScript in its -constructor (i.e. exactly once per program). We specify the name of the -operator, and a pointer to its implementation (the function we wrote earlier). -The name consists of two parts: a *namespace* (``my_ops``) and a name for the -particular operator we are registering (``warp_perspective``). The namespace and -operator name are separated by two colons (``::``). - -.. tip:: - - If you want to register more than one operator, you can chain calls to - ``.op()`` after the constructor: - - .. code-block:: cpp - - static auto registry = - torch::RegisterOperators("my_ops::warp_perspective", &warp_perspective) - .op("my_ops::another_op", &another_op) - .op("my_ops::and_another_op", &and_another_op); - -Behind the scenes, ``RegisterOperators`` will perform a number of fairly -complicated C++ template metaprogramming magic tricks to infer the argument and -return value types of the function pointer we pass it (``&warp_perspective``). -This information is used to form a *function schema* for our operator. A -function schema is a structured representation of an operator -- a kind of -"signature" or "prototype" -- used by the TorchScript compiler to verify -correctness in TorchScript programs. +If you have ever used the pybind11 library, our syntax for registration +resembles the pybind11 syntax very closely. To register a single function, +we write: + +.. literalinclude:: ../advanced_source/torch_script_custom_ops/op.cpp + :language: cpp + :start-after: BEGIN registry + :end-before: END registry + +somewhere at the top level of our ``op.cpp`` file. The ``TORCH_LIBRARY`` macro +creates a function that will be called when your program starts. The name +of your library (``my_ops``) is given as the first argument (it should not +be in quotes). The second argument (``m``) defines a variable of type +``torch::Library`` which is the main interface to register your operators. +The method ``Library::def`` actually creates an operator named ``warp_perspective``, +exposing it to both Python and TorchScript. You can define as many operators +as you like by making multiple calls to ``def``. + +Behinds the scenes, the ``def`` function is actually doing quite a bit of work: +it is using template metaprogramming to inspect the type signature of your +function and translate it into an operator schema which specifies the operators +type within TorchScript's type system. Building the Custom Operator ---------------------------- @@ -209,7 +177,16 @@ we can load into Python for research and experimentation, or into C++ for inference in a no-Python environment. There exist multiple ways to build our operator, using either pure CMake, or Python alternatives like ``setuptools``. For brevity, the paragraphs below only discuss the CMake approach. The appendix -of this tutorial dives into the Python based alternatives. +of this tutorial dives into other alternatives. + +Environment setup +***************** + +We need an installation of PyTorch and OpenCV. The easiest and most platform +independent way to get both is to via Conda:: + + conda install -c pytorch pytorch + conda install opencv Building with CMake ******************* @@ -223,42 +200,10 @@ a directory structure that looks like this:: op.cpp CMakeLists.txt -Also, make sure to grab the latest version of the LibTorch distribution, which -packages PyTorch's C++ libraries and CMake build files, from `pytorch.org -`_. Place the unzipped distribution -somewhere accessible in your file system. The following paragraphs will refer to -that location as ``/path/to/libtorch``. The contents of our ``CMakeLists.txt`` -file should then be the following: - -.. code-block:: cmake - - cmake_minimum_required(VERSION 3.1 FATAL_ERROR) - project(warp_perspective) - - find_package(Torch REQUIRED) - find_package(OpenCV REQUIRED) +The contents of our ``CMakeLists.txt`` file should then be the following: - # Define our library target - add_library(warp_perspective SHARED op.cpp) - # Enable C++11 - target_compile_features(warp_perspective PRIVATE cxx_range_for) - # Link against LibTorch - target_link_libraries(warp_perspective "${TORCH_LIBRARIES}") - # Link against OpenCV - target_link_libraries(warp_perspective opencv_core opencv_imgproc) - -.. warning:: - - This setup makes some assumptions about the build environment, particularly - what pertains to the installation of OpenCV. The above ``CMakeLists.txt`` file - was tested inside a Docker container running Ubuntu Xenial with - ``libopencv-dev`` installed via ``apt``. If it does not work for you and you - feel stuck, please use the ``Dockerfile`` in the `accompanying tutorial - repository `_ to - build an isolated, reproducible environment in which to play around with the - code from this tutorial. If you run into further troubles, please file an - issue in the tutorial repository or post a question in `our forum - `_. +.. literalinclude:: ../advanced_source/torch_script_custom_ops/CMakeLists.txt + :language: cpp To now build our operator, we can run the following commands from our ``warp_perspective`` folder: @@ -267,7 +212,7 @@ To now build our operator, we can run the following commands from our $ mkdir build $ cd build - $ cmake -DCMAKE_PREFIX_PATH=/path/to/libtorch .. + $ cmake -DCMAKE_PREFIX_PATH="$(python -c 'import torch.utils; print(torch.utils.cmake_prefix_path)')" .. -- The C compiler identification is GNU 5.4.0 -- The CXX compiler identification is GNU 5.4.0 -- Check for working C compiler: /usr/bin/cc @@ -302,24 +247,18 @@ To now build our operator, we can run the following commands from our [100%] Built target warp_perspective which will place a ``libwarp_perspective.so`` shared library file in the -``build`` folder. In the ``cmake`` command above, you should replace -``/path/to/libtorch`` with the path to your unzipped LibTorch distribution. +``build`` folder. In the ``cmake`` command above, we use the helper +variable ``torch.utils.cmake_prefix_path`` to conveniently tell us where +the cmake files for our PyTorch install are. We will explore how to use and call our operator in detail further below, but to get an early sensation of success, we can try running the following code in Python: -.. code-block:: python +.. literalinclude:: ../advanced_source/torch_script_custom_ops/smoke_test.py + :language: python - >>> import torch - >>> torch.ops.load_library("/path/to/libwarp_perspective.so") - >>> print(torch.ops.my_ops.warp_perspective) - -Here, ``/path/to/libwarp_perspective.so`` should be a relative or absolute path -to the ``libwarp_perspective.so`` shared library we just built. If all goes -well, this should print something like - -.. code-block:: python +If all goes well, this should print something like:: @@ -336,10 +275,9 @@ TorchScript code. You already saw how to import your operator into Python: ``torch.ops.load_library()``. This function takes the path to a shared library containing custom operators, and loads it into the current process. Loading the -shared library will also execute the constructor of the global -``RegisterOperators`` object we placed into our custom operator implementation -file. This will register our custom operator with the TorchScript compiler and -allow us to use that operator in TorchScript code. +shared library will also execute the ``TORCH_LIBRARY`` block. This will register +our custom operator with the TorchScript compiler and allow us to use that +operator in TorchScript code. You can refer to your loaded operator as ``torch.ops..``, where ```` is the namespace part of your operator name, and @@ -350,11 +288,16 @@ While this function can be used in scripted or traced TorchScript modules, we can also just use it in vanilla eager PyTorch and pass it regular PyTorch tensors: +.. literalinclude:: ../advanced_source/torch_script_custom_ops/test.py + :language: python + :prepend: import torch + :start-after: BEGIN preamble + :end-before: END preamble + +producing: + .. code-block:: python - >>> import torch - >>> torch.ops.load_library("libwarp_perspective.so") - >>> torch.ops.my_ops.warp_perspective(torch.randn(32, 32), torch.rand(3, 3)) tensor([[0.0000, 0.3218, 0.4611, ..., 0.4636, 0.4636, 0.4636], [0.3746, 0.0978, 0.5005, ..., 0.4636, 0.4636, 0.4636], [0.3245, 0.0169, 0.0000, ..., 0.4458, 0.4458, 0.4458], @@ -366,24 +309,26 @@ tensors: .. note:: - What happens behind the scenes is that the first time you access - ``torch.ops.namespace.function`` in Python, the TorchScript compiler (in C++ - land) will see if a function ``namespace::function`` has been registered, and - if so, return a Python handle to this function that we can subsequently use to - call into our C++ operator implementation from Python. This is one noteworthy - difference between TorchScript custom operators and C++ extensions: C++ - extensions are bound manually using pybind11, while TorchScript custom ops are - bound on the fly by PyTorch itself. Pybind11 gives you more flexibility with - regards to what types and classes you can bind into Python and is thus - recommended for purely eager code, but it is not supported for TorchScript - ops. + What happens behind the scenes is that the first time you access + ``torch.ops.namespace.function`` in Python, the TorchScript compiler (in C++ + land) will see if a function ``namespace::function`` has been registered, and + if so, return a Python handle to this function that we can subsequently use to + call into our C++ operator implementation from Python. This is one noteworthy + difference between TorchScript custom operators and C++ extensions: C++ + extensions are bound manually using pybind11, while TorchScript custom ops are + bound on the fly by PyTorch itself. Pybind11 gives you more flexibility with + regards to what types and classes you can bind into Python and is thus + recommended for purely eager code, but it is not supported for TorchScript + ops. From here on, you can use your custom operator in scripted or traced code just as you would other functions from the ``torch`` package. In fact, "standard library" functions like ``torch.matmul`` go through largely the same registration path as custom operators, which makes custom operators really first-class citizens when it comes to how and where they can be used in -TorchScript. +TorchScript. (One difference, however, is that standard library functions +have custom written Python argument parsing logic that differs from +``torch.ops`` argument parsing.) Using the Custom Operator with Tracing ************************************** @@ -391,10 +336,10 @@ Using the Custom Operator with Tracing Let's start by embedding our operator in a traced function. Recall that for tracing, we start with some vanilla Pytorch code: -.. code-block:: python - - def compute(x, y, z): - return x.matmul(y) + torch.relu(z) +.. literalinclude:: ../advanced_source/torch_script_custom_ops/test.py + :language: python + :start-after: BEGIN compute + :end-before: END compute and then call ``torch.jit.trace`` on it. We further pass ``torch.jit.trace`` some example inputs, which it will forward to our implementation to record the @@ -402,54 +347,54 @@ sequence of operations that occur as the inputs flow through it. The result of this is effectively a "frozen" version of the eager PyTorch program, which the TorchScript compiler can further analyze, optimize and serialize: -.. code-block:: python +.. literalinclude:: ../advanced_source/torch_script_custom_ops/test.py + :language: python + :start-after: BEGIN trace + :end-before: END trace - >>> inputs = [torch.randn(4, 8), torch.randn(8, 5), torch.randn(4, 5)] - >>> trace = torch.jit.trace(compute, inputs) - >>> print(trace.graph) - graph(%x : Float(4, 8) - %y : Float(8, 5) - %z : Float(4, 5)) { - %3 : Float(4, 5) = aten::matmul(%x, %y) - %4 : Float(4, 5) = aten::relu(%z) - %5 : int = prim::Constant[value=1]() - %6 : Float(4, 5) = aten::add(%3, %4, %5) - return (%6); - } +Producing:: + + graph(%x : Float(4:8, 8:1), + %y : Float(8:5, 5:1), + %z : Float(4:5, 5:1)): + %3 : Float(4:5, 5:1) = aten::matmul(%x, %y) # test.py:10:0 + %4 : Float(4:5, 5:1) = aten::relu(%z) # test.py:10:0 + %5 : int = prim::Constant[value=1]() # test.py:10:0 + %6 : Float(4:5, 5:1) = aten::add(%3, %4, %5) # test.py:10:0 + return (%6) Now, the exciting revelation is that we can simply drop our custom operator into our PyTorch trace as if it were ``torch.relu`` or any other ``torch`` function: -.. code-block:: python - - torch.ops.load_library("libwarp_perspective.so") - - def compute(x, y, z): - x = torch.ops.my_ops.warp_perspective(x, torch.eye(3)) - return x.matmul(y) + torch.relu(z) +.. literalinclude:: ../advanced_source/torch_script_custom_ops/test.py + :language: python + :start-after: BEGIN compute2 + :end-before: END compute2 and then trace it as before: -.. code-block:: python - - >>> inputs = [torch.randn(4, 8), torch.randn(8, 5), torch.randn(8, 5)] - >>> trace = torch.jit.trace(compute, inputs) - >>> print(trace.graph) - graph(%x.1 : Float(4, 8) - %y : Float(8, 5) - %z : Float(8, 5)) { - %3 : int = prim::Constant[value=3]() - %4 : int = prim::Constant[value=6]() - %5 : int = prim::Constant[value=0]() - %6 : int[] = prim::Constant[value=[0, -1]]() - %7 : Float(3, 3) = aten::eye(%3, %4, %5, %6) - %x : Float(8, 8) = my_ops::warp_perspective(%x.1, %7) - %11 : Float(8, 5) = aten::matmul(%x, %y) - %12 : Float(8, 5) = aten::relu(%z) - %13 : int = prim::Constant[value=1]() - %14 : Float(8, 5) = aten::add(%11, %12, %13) - return (%14); - } +.. literalinclude:: ../advanced_source/torch_script_custom_ops/test.py + :language: python + :start-after: BEGIN trace2 + :end-before: END trace2 + +Producing:: + + graph(%x.1 : Float(4:8, 8:1), + %y : Float(8:5, 5:1), + %z : Float(8:5, 5:1)): + %3 : int = prim::Constant[value=3]() # test.py:25:0 + %4 : int = prim::Constant[value=6]() # test.py:25:0 + %5 : int = prim::Constant[value=0]() # test.py:25:0 + %6 : Device = prim::Constant[value="cpu"]() # test.py:25:0 + %7 : bool = prim::Constant[value=0]() # test.py:25:0 + %8 : Float(3:3, 3:1) = aten::eye(%3, %4, %5, %6, %7) # test.py:25:0 + %x : Float(8:8, 8:1) = my_ops::warp_perspective(%x.1, %8) # test.py:25:0 + %10 : Float(8:5, 5:1) = aten::matmul(%x, %y) # test.py:26:0 + %11 : Float(8:5, 5:1) = aten::relu(%z) # test.py:26:0 + %12 : int = prim::Constant[value=1]() # test.py:26:0 + %13 : Float(8:5, 5:1) = aten::add(%10, %11, %12) # test.py:26:0 + return (%13) Integrating TorchScript custom ops into traced PyTorch code is as easy as this! @@ -539,7 +484,7 @@ function inside of our script code: When the TorchScript compiler sees the reference to ``torch.ops.my_ops.warp_perspective``, it will find the implementation we -registered via the ``RegisterOperators`` object in C++, and compile it into its +registered via the ``TORCH_LIBRARY`` function in C++, and compile it into its graph representation: .. code-block:: python @@ -660,11 +605,11 @@ Along with a small ``CMakeLists.txt`` file: At this point, we should be able to build the application: -.. code-block:: cpp +.. code-block:: $ mkdir build $ cd build - $ cmake -DCMAKE_PREFIX_PATH=/path/to/libtorch .. + $ cmake -DCMAKE_PREFIX_PATH="$(python -c 'import torch.utils; print(torch.utils.cmake_prefix_path)')" .. -- The C compiler identification is GNU 5.4.0 -- The CXX compiler identification is GNU 5.4.0 -- Check for working C compiler: /usr/bin/cc @@ -700,7 +645,7 @@ At this point, we should be able to build the application: And run it without passing a model just yet: -.. code-block:: cpp +.. code-block:: $ ./example_app usage: example_app @@ -727,7 +672,7 @@ The last line will serialize the script function into a file called "example.pt". If we then pass this serialized model to our C++ application, we can run it straight away: -.. code-block:: cpp +.. code-block:: $ ./example_app example.pt terminate called after throwing an instance of 'torch::jit::script::ErrorReport' @@ -775,7 +720,7 @@ library. ``-Wl,--no-as-needed`` prefix to the ``warp_perspective`` link line. This is required because we will not actually be calling any function from the ``warp_perspective`` shared library in our application code. We only need the - global ``RegisterOperators`` object's constructor to run. Inconveniently, this + ``TORCH_LIBRARY`` function to run. Inconveniently, this confuses the linker and makes it think it can just skip linking against the library altogether. On Linux, the ``-Wl,--no-as-needed`` flag forces the link to happen (NB: this flag is specific to Linux!). There are other workarounds @@ -807,7 +752,7 @@ library. In the top level ``example_app`` directory: $ mkdir build $ cd build - $ cmake -DCMAKE_PREFIX_PATH=/path/to/libtorch .. + $ cmake -DCMAKE_PREFIX_PATH="$(python -c 'import torch.utils; print(torch.utils.cmake_prefix_path)')" .. -- The C compiler identification is GNU 5.4.0 -- The CXX compiler identification is GNU 5.4.0 -- Check for working C compiler: /usr/bin/cc @@ -981,8 +926,9 @@ custom TorchScript operator as a string. For this, use return output.clone(); } - static auto registry = - torch::RegisterOperators("my_ops::warp_perspective", &warp_perspective); + TORCH_LIBRARY(my_ops, m) { + m.def("warp_perspective", &warp_perspective); + } """ torch.utils.cpp_extension.load_inline( diff --git a/advanced_source/torch_script_custom_ops/CMakeLists.txt b/advanced_source/torch_script_custom_ops/CMakeLists.txt new file mode 100644 index 00000000000..e116153b941 --- /dev/null +++ b/advanced_source/torch_script_custom_ops/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.1 FATAL_ERROR) +project(warp_perspective) + +find_package(Torch REQUIRED) +find_package(OpenCV REQUIRED) + +# Define our library target +add_library(warp_perspective SHARED op.cpp) +# Enable C++14 +target_compile_features(warp_perspective PRIVATE cxx_std_14) +# Link against LibTorch +target_link_libraries(warp_perspective "${TORCH_LIBRARIES}") +# Link against OpenCV +target_link_libraries(warp_perspective opencv_core opencv_imgproc) diff --git a/advanced_source/torch_script_custom_ops/op.cpp b/advanced_source/torch_script_custom_ops/op.cpp new file mode 100644 index 00000000000..ff2eb049c4c --- /dev/null +++ b/advanced_source/torch_script_custom_ops/op.cpp @@ -0,0 +1,36 @@ +#include +#include + +// BEGIN warp_perspective +torch::Tensor warp_perspective(torch::Tensor image, torch::Tensor warp) { + // BEGIN image_mat + cv::Mat image_mat(/*rows=*/image.size(0), + /*cols=*/image.size(1), + /*type=*/CV_32FC1, + /*data=*/image.data_ptr()); + // END image_mat + + // BEGIN warp_mat + cv::Mat warp_mat(/*rows=*/warp.size(0), + /*cols=*/warp.size(1), + /*type=*/CV_32FC1, + /*data=*/warp.data_ptr()); + // END warp_mat + + // BEGIN output_mat + cv::Mat output_mat; + cv::warpPerspective(image_mat, output_mat, warp_mat, /*dsize=*/{8, 8}); + // END output_mat + + // BEGIN output_tensor + torch::Tensor output = torch::from_blob(output_mat.ptr(), /*sizes=*/{8, 8}); + return output.clone(); + // END output_tensor +} +// END warp_perspective + +// BEGIN registry +TORCH_LIBRARY(my_ops, m) { + m.def("warp_perspective", warp_perspective); +} +// END registry diff --git a/advanced_source/torch_script_custom_ops/smoke_test.py b/advanced_source/torch_script_custom_ops/smoke_test.py new file mode 100644 index 00000000000..fa629ddcafb --- /dev/null +++ b/advanced_source/torch_script_custom_ops/smoke_test.py @@ -0,0 +1,3 @@ +import torch +torch.ops.load_library("build/libwarp_perspective.so") +print(torch.ops.my_ops.warp_perspective) diff --git a/advanced_source/torch_script_custom_ops/test.py b/advanced_source/torch_script_custom_ops/test.py new file mode 100644 index 00000000000..26f96ef4599 --- /dev/null +++ b/advanced_source/torch_script_custom_ops/test.py @@ -0,0 +1,34 @@ +import torch + + +print("BEGIN preamble") +torch.ops.load_library("build/libwarp_perspective.so") +print(torch.ops.my_ops.warp_perspective(torch.randn(32, 32), torch.rand(3, 3))) +print("END preamble") + + +# BEGIN compute +def compute(x, y, z): + return x.matmul(y) + torch.relu(z) +# END compute + + +print("BEGIN trace") +inputs = [torch.randn(4, 8), torch.randn(8, 5), torch.randn(4, 5)] +trace = torch.jit.trace(compute, inputs) +print(trace.graph) +print("END trace") + + +# BEGIN compute2 +def compute(x, y, z): + x = torch.ops.my_ops.warp_perspective(x, torch.eye(3)) + return x.matmul(y) + torch.relu(z) +# END compute2 + + +print("BEGIN trace2") +inputs = [torch.randn(4, 8), torch.randn(8, 5), torch.randn(8, 5)] +trace = torch.jit.trace(compute, inputs) +print(trace.graph) +print("END trace2") diff --git a/conf.py b/conf.py index 93cba840893..683abf00899 100644 --- a/conf.py +++ b/conf.py @@ -63,8 +63,9 @@ sphinx_gallery_conf = { 'examples_dirs': ['beginner_source', 'intermediate_source', - 'advanced_source', 'recipes_source'], - 'gallery_dirs': ['beginner', 'intermediate', 'advanced', 'recipes'], + 'advanced_source', 'recipes_source', 'prototype_source'], + 'gallery_dirs': ['beginner', 'intermediate', 'advanced', 'recipes', 'prototype'], + 'filename_pattern': 'tutorial.py', 'backreferences_dir': False } diff --git a/index.rst b/index.rst index 0e04e92bf35..7e1c4001285 100644 --- a/index.rst +++ b/index.rst @@ -203,14 +203,14 @@ Welcome to PyTorch Tutorials .. Frontend APIs .. customcarditem:: - :header: (experimental) Introduction to Named Tensors in PyTorch + :header: (prototype) Introduction to Named Tensors in PyTorch :card_description: Learn how to use PyTorch to train a Deep Q Learning (DQN) agent on the CartPole-v0 task from the OpenAI Gym. :image: _static/img/thumbnails/cropped/experimental-Introduction-to-Named-Tensors-in-PyTorch.png :link: intermediate/memory_format_tutorial.html :tags: Frontend-APIs,Named-Tensor,Best-Practice .. customcarditem:: - :header: (experimental) Channels Last Memory Format in PyTorch + :header: (beta) Channels Last Memory Format in PyTorch :card_description: Get an overview of Channels Last memory format and understand how it is used to order NCHW tensors in memory preserving dimensions. :image: _static/img/thumbnails/cropped/experimental-Channels-Last-Memory-Format-in-PyTorch.png :link: intermediate/memory_format_tutorial.html @@ -268,28 +268,28 @@ Welcome to PyTorch Tutorials :tags: Model-Optimization,Best-Practice .. customcarditem:: - :header: (experimental) Dynamic Quantization on an LSTM Word Language Model + :header: (beta) Dynamic Quantization on an LSTM Word Language Model :card_description: Apply dynamic quantization, the easiest form of quantization, to a LSTM-based next word prediction model. :image: _static/img/thumbnails/cropped/experimental-Dynamic-Quantization-on-an-LSTM-Word-Language-Model.png :link: advanced/dynamic_quantization_tutorial.html :tags: Text,Quantization,Model-Optimization .. customcarditem:: - :header: (experimental) Dynamic Quantization on BERT + :header: (beta) Dynamic Quantization on BERT :card_description: Apply the dynamic quantization on a BERT (Bidirectional Embedding Representations from Transformers) model. :image: _static/img/thumbnails/cropped/experimental-Dynamic-Quantization-on-BERT.png :link: intermediate/dynamic_quantization_bert_tutorial.html :tags: Text,Quantization,Model-Optimization .. customcarditem:: - :header: (experimental) Static Quantization with Eager Mode in PyTorch + :header: (beta) Static Quantization with Eager Mode in PyTorch :card_description: Learn techniques to impove a model's accuracy = post-training static quantization, per-channel quantization, and quantization-aware training. :image: _static/img/thumbnails/cropped/experimental-Static-Quantization-with-Eager-Mode-in-PyTorch.png :link: advanced/static_quantization_tutorial.html :tags: Image/Video,Quantization,Model-Optimization .. customcarditem:: - :header: (experimental) Quantized Transfer Learning for Computer Vision Tutorial + :header: (beta) Quantized Transfer Learning for Computer Vision Tutorial :card_description: Learn techniques to impove a model's accuracy - post-training static quantization, per-channel quantization, and quantization-aware training. :image: _static/img/thumbnails/cropped/experimental-Quantized-Transfer-Learning-for-Computer-Vision-Tutorial.png :link: advanced/static_quantization_tutorial.html diff --git a/intermediate_source/dynamic_quantization_bert_tutorial.rst b/intermediate_source/dynamic_quantization_bert_tutorial.rst index c3c800bbf89..6642f6768c8 100644 --- a/intermediate_source/dynamic_quantization_bert_tutorial.rst +++ b/intermediate_source/dynamic_quantization_bert_tutorial.rst @@ -1,10 +1,10 @@ -(experimental) Dynamic Quantization on BERT +(beta) Dynamic Quantization on BERT =========================================== .. tip:: - To get the most of this tutorial, we suggest using this + To get the most of this tutorial, we suggest using this `Colab Version `_. This will allow you to experiment with the information presented below. - + **Author**: `Jianyu Huang `_ **Reviewed by**: `Raghuraman Krishnamoorthi `_ @@ -71,7 +71,7 @@ built-in F1 score calculation helper function. pip install transformers -Because we will be using the experimental parts of the PyTorch, it is +Because we will be using the beta parts of the PyTorch, it is recommended to install the latest version of torch and torchvision. You can find the most recent instructions on local installation `here `_. For example, to install on diff --git a/intermediate_source/memory_format_tutorial.py b/intermediate_source/memory_format_tutorial.py index 2c3109de1e1..244e23ac204 100644 --- a/intermediate_source/memory_format_tutorial.py +++ b/intermediate_source/memory_format_tutorial.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ -(experimental) Channels Last Memory Format in PyTorch +(beta) Channels Last Memory Format in PyTorch ******************************************************* **Author**: `Vitaly Fedyunin `_ @@ -11,12 +11,12 @@ For example, classic (contiguous) storage of NCHW tensor (in our case it is two 2x2 images with 3 color channels) look like this: -.. figure:: /_static/img/classic_memory_format.png +.. figure:: /_static/img/classic_memory_format.png :alt: classic_memory_format Channels Last memory format orders data differently: -.. figure:: /_static/img/channels_last_memory_format.png +.. figure:: /_static/img/channels_last_memory_format.png :alt: channels_last_memory_format Pytorch supports memory formats (and provides back compatibility with existing models including eager, JIT, and TorchScript) by utilizing existing strides structure. @@ -34,7 +34,7 @@ # Memory Format API # ----------------------- # -# Here is how to convert tensors between contiguous and channels +# Here is how to convert tensors between contiguous and channels # last memory formats. ###################################################################### @@ -104,9 +104,9 @@ ###################################################################### # Performance Gains # ------------------------------------------------------------------------------------------- -# The most significant performance gains are observed on NVidia's hardware with +# The most significant performance gains are observed on Nvidia's hardware with # Tensor Cores support. We were able to archive over 22% perf gains while running ' -# AMP (Automated Mixed Precision) training scripts supplied by NVidia https://github.com/NVIDIA/apex. +# AMP (Automated Mixed Precision) training scripts supplied by Nvidia https://github.com/NVIDIA/apex. # # ``python main_amp.py -a resnet50 --b 200 --workers 16 --opt-level O2 ./data`` @@ -144,7 +144,7 @@ ###################################################################### # Passing ``--channels-last true`` allows running a model in Channels Last format with observed 22% perf gain. -# +# # ``python main_amp.py -a resnet50 --b 200 --workers 16 --opt-level O2 --channels-last true ./data`` # opt_level = O2 @@ -192,7 +192,7 @@ # Converting existing models # -------------------------- # -# Channels Last support not limited by existing models, as any model can be converted to Channels Last and propagate format through the graph as soon as input formatted correctly. +# Channels Last support not limited by existing models, as any model can be converted to Channels Last and propagate format through the graph as soon as input formatted correctly. # # Need to be done once, after model initialization (or load) @@ -203,12 +203,12 @@ output = model(input) ####################################################################### -# However, not all operators fully converted to support Channels Last (usually returning -# contiguous output instead). That means you need to verify the list of used operators -# against supported operators list https://github.com/pytorch/pytorch/wiki/Operators-with-Channels-Last-support, +# However, not all operators fully converted to support Channels Last (usually returning +# contiguous output instead). That means you need to verify the list of used operators +# against supported operators list https://github.com/pytorch/pytorch/wiki/Operators-with-Channels-Last-support, # or introduce memory format checks into eager execution mode and run your model. -# -# After running the code below, operators will raise an exception if the output of the +# +# After running the code below, operators will raise an exception if the output of the # operator doesn't match the memory format of the input. # # @@ -282,7 +282,7 @@ def attribute(m): ###################################################################### # If you found an operator that doesn't support Channels Last tensors -# and you want to contribute, feel free to use following developers +# and you want to contribute, feel free to use following developers # guide https://github.com/pytorch/pytorch/wiki/Writing-memory-format-aware-operators. # diff --git a/intermediate_source/named_tensor_tutorial.py b/intermediate_source/named_tensor_tutorial.py index 09946a50809..34941604083 100644 --- a/intermediate_source/named_tensor_tutorial.py +++ b/intermediate_source/named_tensor_tutorial.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ -(experimental) Introduction to Named Tensors in PyTorch +(prototype) Introduction to Named Tensors in PyTorch ******************************************************* **Author**: `Richard Zou `_ diff --git a/intermediate_source/quantized_transfer_learning_tutorial.rst b/intermediate_source/quantized_transfer_learning_tutorial.rst index 5d734922aed..a4be9eafe9a 100644 --- a/intermediate_source/quantized_transfer_learning_tutorial.rst +++ b/intermediate_source/quantized_transfer_learning_tutorial.rst @@ -1,10 +1,10 @@ -(experimental) Quantized Transfer Learning for Computer Vision Tutorial +(beta) Quantized Transfer Learning for Computer Vision Tutorial ======================================================================== .. tip:: - To get the most of this tutorial, we suggest using this - `Colab Version `_. - This will allow you to experiment with the information presented below. + To get the most of this tutorial, we suggest using this + `Colab Version `_. + This will allow you to experiment with the information presented below. **Author**: `Zafar Takhirov `_ @@ -62,7 +62,7 @@ such as installations and data loading/visualizations. Installing the Nightly Build ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Because you will be using the experimental parts of the PyTorch, it is +Because you will be using the beta parts of the PyTorch, it is recommended to install the latest version of ``torch`` and ``torchvision``. You can find the most recent instructions on local installation `here `_. diff --git a/intermediate_source/rpc_tutorial.rst b/intermediate_source/rpc_tutorial.rst index c4ee6536830..bcad775496b 100644 --- a/intermediate_source/rpc_tutorial.rst +++ b/intermediate_source/rpc_tutorial.rst @@ -10,7 +10,7 @@ Prerequisites: This tutorial uses two simple examples to demonstrate how to build distributed training with the `torch.distributed.rpc `__ -package which is first introduced as an experimental feature in PyTorch v1.4. +package which is first introduced as a prototype feature in PyTorch v1.4. Source code of the two examples can be found in `PyTorch examples `__. diff --git a/intermediate_source/torchvision_tutorial.rst b/intermediate_source/torchvision_tutorial.rst index 93fcfd3d247..703deeab11c 100644 --- a/intermediate_source/torchvision_tutorial.rst +++ b/intermediate_source/torchvision_tutorial.rst @@ -56,6 +56,11 @@ If your model returns the above methods, they will make it work for both training and evaluation, and will use the evaluation scripts from ``pycocotools``. +.. note :: + For Windows, please install ``pycocotools`` from `gautamchitnis `__ with command + + ``pip install git+https://github.com/gautamchitnis/cocoapi.git@cocodataset-master#subdirectory=PythonAPI`` + One note on the ``labels``. The model considers class ``0`` as background. If your dataset does not contain the background class, you should not have ``0`` in your ``labels``. For example, assuming you have just two classes, *cat* and *dog*, you can define ``1`` (not ``0``) to represent *cats* and ``2`` to represent *dogs*. So, for instance, if one of the images has booth classes, your ``labels`` tensor should look like ``[1,2]``. Additionally, if you want to use aspect ratio grouping during training diff --git a/prototype_source/README.md b/prototype_source/README.md new file mode 100644 index 00000000000..ecb860aea68 --- /dev/null +++ b/prototype_source/README.md @@ -0,0 +1,9 @@ +# Prototype Tutorials + +This directory contains tutorials demonstrating prototype features in PyTorch. + +**Prototype features** are not available as part of binary distributions like PyPI or Conda (except maybe behind run-time flags). To test these features we would, depending on the feature, recommend building from master or using the nightly wheelss that are made available on pytorch.org. + +These tutorials are intentionally left out of the pytorch.org/tutorials build and will not show up on the website. + +*Level of commitment:* We are committing to gathering high bandwidth feedback only on these features. Based on this feedback and potential further engagement between community members, we as a community will decide if we want to upgrade the level of commitment or to fail fast. diff --git a/prototype_source/README.txt b/prototype_source/README.txt new file mode 100644 index 00000000000..b04371db306 --- /dev/null +++ b/prototype_source/README.txt @@ -0,0 +1,2 @@ +Prototype Tutorials +------------------ diff --git a/recipes_source/recipes/dynamic_quantization.py b/recipes_source/recipes/dynamic_quantization.py index 78dc1f5408a..945ea5f70fd 100644 --- a/recipes_source/recipes/dynamic_quantization.py +++ b/recipes_source/recipes/dynamic_quantization.py @@ -127,13 +127,13 @@ # define a very, very simple LSTM for demonstration purposes # in this case, we are wrapping nn.LSTM, one layer, no pre or post processing -# inspired by +# inspired by # https://pytorch.org/tutorials/beginner/nlp/sequence_models_tutorial.html, by Robert Guthrie # and https://pytorch.org/tutorials/advanced/dynamic_quantization_tutorial.html class lstm_for_demonstration(nn.Module): """Elementary Long Short Term Memory style model which simply wraps nn.LSTM - Not to be used for anything other than demonstration. - """ + Not to be used for anything other than demonstration. + """ def __init__(self,in_dim,out_dim,depth): super(lstm_for_demonstration,self).__init__() self.lstm = nn.LSTM(in_dim,out_dim,depth) @@ -142,7 +142,7 @@ def forward(self,inputs,hidden): out,hidden = self.lstm(inputs,hidden) return out, hidden - + torch.manual_seed(29592) # set the seed for reproducibility #shape parameters @@ -154,32 +154,32 @@ def forward(self,inputs,hidden): # random data for input inputs = torch.randn(sequence_length,batch_size,model_dimension) # hidden is actually is a tuple of the initial hidden state and the initial cell state -hidden = (torch.randn(lstm_depth,batch_size,model_dimension), torch.randn(lstm_depth,batch_size,model_dimension)) +hidden = (torch.randn(lstm_depth,batch_size,model_dimension), torch.randn(lstm_depth,batch_size,model_dimension)) ###################################################################### # 2: Do the Quantization # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# +# # Now we get to the fun part. First we create an instance of the model # called float\_lstm then we are going to quantize it. We're going to use # the -# +# # :: -# +# # torch.quantization.quantize_dynamic() -# +# # function here (`see # documentation `__) # which takes the model, then a list of the submodules which we want to # have quantized if they appear, then the datatype we are targeting. This # function returns a quantized version of the original model as a new # module. -# +# # That's all it takes. -# +# - # here is our floating point instance + # here is our floating point instance float_lstm = lstm_for_demonstration(model_dimension, model_dimension,lstm_depth) # this is the call that does the work @@ -206,7 +206,7 @@ def forward(self,inputs,hidden): # (for example you can set model dimension to something like 80) this will # converge towards 4x smaller as the stored model size dominated more and # more by the parameter values. -# +# def print_size_of_model(model, label=""): torch.save(model.state_dict(), "temp.p") @@ -221,7 +221,7 @@ def print_size_of_model(model, label=""): print("{0:.2f} times smaller".format(f/q)) # note that this value is wrong in PyTorch 1.4 due to https://github.com/pytorch/pytorch/issues/31468 -# this will be fixed in 1.5 with https://github.com/pytorch/pytorch/pull/31540 +# this will be fixed in 1.5 with https://github.com/pytorch/pytorch/pull/31540 ###################################################################### @@ -229,15 +229,15 @@ def print_size_of_model(model, label=""): # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # The second benefit is that the quantized model will typically run # faster. This is due to a combinations of effects including at least: -# +# # 1. Less time spent moving parameter data in # 2. Faster INT8 operations -# +# # As you will see the quantized version of this super-simple network runs # faster. This will generally be true of more complex networks but as they # say "your milage may vary" depending on a number of factors including # the structure of the model and the hardware you are running on. -# +# # compare the performance print("Floating point FP32") @@ -255,10 +255,10 @@ def print_size_of_model(model, label=""): # trained one. However, I think it is worth quickly showing that the # quantized network does produce output tensors that are "in the same # ballpark" as the original one. -# +# # For a more detailed analysis please see the more advanced tutorials # referenced at the end of this recipe. -# +# # run the float model out1, hidden1 = float_lstm(inputs, hidden) @@ -270,7 +270,7 @@ def print_size_of_model(model, label=""): mag2 = torch.mean(abs(out2)).item() print('mean absolute value of output tensor values in the INT8 model is {0:.5f}'.format(mag2)) -# compare them +# compare them mag3 = torch.mean(abs(out1-out2)).item() print('mean absolute value of the difference between the output tensors is {0:.5f} or {1:.2f} percent'.format(mag3,mag3/mag1*100)) @@ -281,26 +281,26 @@ def print_size_of_model(model, label=""): # We've explained what dynamic quantization is, what benefits it brings, # and you have used the ``torch.quantization.quantize_dynamic()`` function # to quickly quantize a simple LSTM model. -# +# # This was a fast and high level treatment of this material; for more -# detail please continue learning with `(experimental) Dynamic Quantization on an LSTM Word Language Model Tutorial `_. -# -# +# detail please continue learning with `(beta) Dynamic Quantization on an LSTM Word Language Model Tutorial `_. +# +# # Additional Resources # ========= # Documentation # ~~~~~~~~~~~~~~ -# +# # `Quantization API Documentaion `_ -# +# # Tutorials # ~~~~~~~~~~~~~~ -# -# `(experimental) Dynamic Quantization on BERT `_ -# -# `(experimental) Dynamic Quantization on an LSTM Word Language Model `_ -# +# +# `(beta) Dynamic Quantization on BERT `_ +# +# `(beta) Dynamic Quantization on an LSTM Word Language Model `_ +# # Blogs # ~~~~~~~~~~~~~~ # ` Introduction to Quantization on PyTorch `_ -# +# From bfb3bb5872d4f0bd6fd293ae31dc12e3c8c1340a Mon Sep 17 00:00:00 2001 From: Pritam Damania <9958665+pritamdamania87@users.noreply.github.com> Date: Thu, 16 Jul 2020 14:49:28 -0700 Subject: [PATCH 25/33] Tutorial for DDP + RPC (#1071) * Update feature classification labels * Update NVidia -> Nvidia * Bring back default filename_pattern so that by default we run all galleries. Signed-off-by: Edward Z. Yang * Tutorial for DDP + RPC. Summary: Based on example from https://github.com/pytorch/examples/pull/800 * Add to main section Summary: Test Plan: Reviewers: Subscribers: Tasks: Tags: * Added separate code file and used literalinclude Summary: Test Plan: Reviewers: Subscribers: Tasks: Tags: Co-authored-by: Jessica Lin Co-authored-by: Edward Z. Yang Co-authored-by: pritam --- ...arallel-with-Distributed-RPC-Framework.png | Bin 0 -> 35776 bytes advanced_source/rpc_ddp_tutorial.rst | 159 +++++++++++++++ advanced_source/rpc_ddp_tutorial/main.py | 191 ++++++++++++++++++ conf.py | 2 +- index.rst | 7 + 5 files changed, 358 insertions(+), 1 deletion(-) create mode 100644 _static/img/thumbnails/cropped/Combining-Distributed-DataParallel-with-Distributed-RPC-Framework.png create mode 100644 advanced_source/rpc_ddp_tutorial.rst create mode 100644 advanced_source/rpc_ddp_tutorial/main.py diff --git a/_static/img/thumbnails/cropped/Combining-Distributed-DataParallel-with-Distributed-RPC-Framework.png b/_static/img/thumbnails/cropped/Combining-Distributed-DataParallel-with-Distributed-RPC-Framework.png new file mode 100644 index 0000000000000000000000000000000000000000..426a14d98f5f7fbbf695626658ad1946fe4ef63e GIT binary patch literal 35776 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 literal 0 HcmV?d00001 diff --git a/advanced_source/rpc_ddp_tutorial.rst b/advanced_source/rpc_ddp_tutorial.rst new file mode 100644 index 00000000000..fd0d66e5d19 --- /dev/null +++ b/advanced_source/rpc_ddp_tutorial.rst @@ -0,0 +1,159 @@ +Combining Distributed DataParallel with Distributed RPC Framework +================================================================= +**Author**: `Pritam Damania `_ + + +This tutorial uses a simple example to demonstrate how you can combine +`DistributedDataParallel `__ (DDP) +with the `Distributed RPC framework `__ +to combine distributed data parallelism with distributed model parallelism to +train a simple model. Source code of the example can be found `here `__. + +Previous tutorials, +`Getting Started With Distributed Data Parallel `__ +and `Getting Started with Distributed RPC Framework `__, +described how to perform distributed data parallel and distributed model +parallel training respectively. Although, there are several training paradigms +where you might want to combine these two techniques. For example: + +1) If we have a model with a sparse part (large embedding table) and a dense + part (FC layers), we might want to put the embedding table on a parameter + server and replicate the FC layer across multiple trainers using `DistributedDataParallel `__. + The `Distributed RPC framework `__ + can be used to perform embedding lookups on the parameter server. +2) Enable hybrid parallelism as described in the `PipeDream `__ paper. + We can use the `Distributed RPC framework `__ + to pipeline stages of the model across multiple workers and replicate each + stage (if needed) using `DistributedDataParallel `__. + +| +In this tutorial we will cover case 1 mentioned above. We have a total of 4 +workers in our setup as follows: + + +1) 1 Master, which is responsible for creating an embedding table + (nn.EmbeddingBag) on the parameter server. The master also drives the + training loop on the two trainers. +2) 1 Parameter Server, which basically holds the embedding table in memory and + responds to RPCs from the Master and Trainers. +3) 2 Trainers, which store an FC layer (nn.Linear) which is replicated amongst + themselves using `DistributedDataParallel `__. + The trainers are also responsible for executing the forward pass, backward + pass and optimizer step. + +| +The entire training process is executed as follows: + +1) The master creates an embedding table on the Parameter Server and holds an + `RRef `__ to it. +2) The master, then kicks off the training loop on the trainers and passes the + embedding table RRef to the trainers. +3) The trainers create a ``HybridModel`` which first performs an embedding lookup + using the embedding table RRef provided by the master and then executes the + FC layer which is wrapped inside DDP. +4) The trainer executes the forward pass of the model and uses the loss to + execute the backward pass using `Distributed Autograd `__. +5) As part of the backward pass, the gradients for the FC layer are computed + first and synced to all trainers via allreduce in DDP. +6) Next, Distributed Autograd propagates the gradients to the parameter server, + where the gradients for the embedding table are updated. +7) Finally, the `Distributed Optimizer `__ is used to update all the parameters. + + +.. attention:: + + You should always use `Distributed Autograd `__ + for the backward pass if you're combining DDP and RPC. + + +Now, let's go through each part in detail. Firstly, we need to setup all of our +workers before we can perform any training. We create 4 processes such that +ranks 0 and 1 are our trainers, rank 2 is the master and rank 3 is the +parameter server. + +We initialize the RPC framework on all 4 workers using the TCP init_method. +Once RPC initialization is done, the master creates an `EmbeddingBag `__ +on the Parameter Server using `rpc.remote `__. +The master then loops through each trainer and kicks of the training loop by +calling ``_run_trainer`` on each trainer using `rpc_async `__. +Finally, the master waits for all training to finish before exiting. + +The trainers first initialize a ``ProcessGroup`` for DDP with world_size=2 +(for two trainers) using `init_process_group `__. +Next, they initialize the RPC framework using the TCP init_method. Note that +the ports are different in RPC initialization and ProcessGroup initialization. +This is to avoid port conflicts between initialization of both frameworks. +Once the initialization is done, the trainers just wait for the ``_run_trainer`` +RPC from the master. + +The parameter server just initializes the RPC framework and waits for RPCs from +the trainers and master. + + +.. literalinclude:: ../advanced_source/rpc_ddp_tutorial/main.py + :language: py + :start-after: BEGIN run_worker + :end-before: END run_worker + +Before we discuss details of the Trainer, let's introduce the ``HybridModel`` that +the trainer uses. As described below, the ``HybridModel`` is initialized using an +RRef to the embedding table (emb_rref) on the parameter server and the ``device`` +to use for DDP. The initialization of the model wraps an +`nn.Linear `__ +layer inside DDP to replicate and synchronize this layer across all trainers. + +The forward method of the model is pretty straightforward. It performs an +embedding lookup on the parameter server using an +`RRef helper `__ +and passes its output onto the FC layer. + + +.. literalinclude:: ../advanced_source/rpc_ddp_tutorial/main.py + :language: py + :start-after: BEGIN hybrid_model + :end-before: END hybrid_model + +Next, let's look at the setup on the Trainer. The trainer first creates the +``HybridModel`` described above using an RRef to the embedding table on the +parameter server and its own rank. + +Now, we need to retrieve a list of RRefs to all the parameters that we would +like to optimize with `DistributedOptimizer `__. +To retrieve the parameters for the embedding table from the parameter server, +we define a simple helper function ``_retrieve_embedding_parameters``, which +basically walks through all the parameters for the embedding table and returns +a list of RRefs. The trainer calls this method on the parameter server via RPC +to receive a list of RRefs to the desired parameters. Since the +DistributedOptimizer always takes a list of RRefs to parameters that need to +be optimized, we need to create RRefs even for the local parameters for our +FC layers. This is done by walking ``model.parameters()``, creating an RRef for +each parameter and appending it to a list. Note that ``model.parameters()`` only +returns local parameters and doesn't include ``emb_rref``. + +Finally, we create our DistributedOptimizer using all the RRefs and define a +CrossEntropyLoss function. + +.. literalinclude:: ../advanced_source/rpc_ddp_tutorial/main.py + :language: py + :start-after: BEGIN setup_trainer + :end-before: END setup_trainer + +Now we're ready to introduce the main training loop that is run on each trainer. +``get_next_batch`` is just a helper function to generate random inputs and +targets for training. We run the training loop for multiple epochs and for each +batch: + +1) Setup a `Distributed Autograd Context `__ + for Distributed Autograd. +2) Run the forward pass of the model and retrieve its output. +3) Compute the loss based on our outputs and targets using the loss function. +4) Use Distributed Autograd to execute a distributed backward pass using the loss. +5) Finally, run a Distributed Optimizer step to optimize all the parameters. + +.. literalinclude:: ../advanced_source/rpc_ddp_tutorial/main.py + :language: py + :start-after: BEGIN run_trainer + :end-before: END run_trainer +.. code:: python + +Source code for the entire example can be found `here `__. diff --git a/advanced_source/rpc_ddp_tutorial/main.py b/advanced_source/rpc_ddp_tutorial/main.py new file mode 100644 index 00000000000..f83384d0a8d --- /dev/null +++ b/advanced_source/rpc_ddp_tutorial/main.py @@ -0,0 +1,191 @@ +import os +from functools import wraps + +import random +import torch +import torch.distributed as dist +import torch.distributed.autograd as dist_autograd +import torch.distributed.rpc as rpc +from torch.distributed.rpc import ProcessGroupRpcBackendOptions +import torch.multiprocessing as mp +import torch.optim as optim +from torch.distributed.optim import DistributedOptimizer +from torch.distributed.rpc import RRef +from torch.nn.parallel import DistributedDataParallel as DDP + +NUM_EMBEDDINGS = 100 +EMBEDDING_DIM = 16 + +# BEGIN hybrid_model +class HybridModel(torch.nn.Module): + r""" + The model consists of a sparse part and a dense part. The dense part is an + nn.Linear module that is replicated across all trainers using + DistributedDataParallel. The sparse part is an nn.EmbeddingBag that is + stored on the parameter server. + + The model holds a Remote Reference to the embedding table on the parameter + server. + """ + + def __init__(self, emb_rref, device): + super(HybridModel, self).__init__() + self.emb_rref = emb_rref + self.fc = DDP(torch.nn.Linear(16, 8).cuda(device), device_ids=[device]) + self.device = device + + def forward(self, indices, offsets): + emb_lookup = self.emb_rref.rpc_sync().forward(indices, offsets) + return self.fc(emb_lookup.cuda(self.device)) +# END hybrid_model + +# BEGIN setup_trainer +def _retrieve_embedding_parameters(emb_rref): + param_rrefs = [] + for param in emb_rref.local_value().parameters(): + param_rrefs.append(RRef(param)) + return param_rrefs + + +def _run_trainer(emb_rref, rank): + r""" + Each trainer runs a forward pass which involves an embedding lookup on the + parameter server and running nn.Linear locally. During the backward pass, + DDP is responsible for aggregating the gradients for the dense part + (nn.Linear) and distributed autograd ensures gradients updates are + propagated to the parameter server. + """ + + # Setup the model. + model = HybridModel(emb_rref, rank) + + # Retrieve all model parameters as rrefs for DistributedOptimizer. + + # Retrieve parameters for embedding table. + model_parameter_rrefs = rpc.rpc_sync( + "ps", _retrieve_embedding_parameters, args=(emb_rref,)) + + # model.parameters() only includes local parameters. + for param in model.parameters(): + model_parameter_rrefs.append(RRef(param)) + + # Setup distributed optimizer + opt = DistributedOptimizer( + optim.SGD, + model_parameter_rrefs, + lr=0.05, + ) + + criterion = torch.nn.CrossEntropyLoss() + # END setup_trainer + + # BEGIN run_trainer + def get_next_batch(rank): + for _ in range(10): + num_indices = random.randint(20, 50) + indices = torch.LongTensor(num_indices).random_(0, NUM_EMBEDDINGS) + + # Generate offsets. + offsets = [] + start = 0 + batch_size = 0 + while start < num_indices: + offsets.append(start) + start += random.randint(1, 10) + batch_size += 1 + + offsets_tensor = torch.LongTensor(offsets) + target = torch.LongTensor(batch_size).random_(8).cuda(rank) + yield indices, offsets_tensor, target + + # Train for 100 epochs + for epoch in range(100): + # create distributed autograd context + for indices, offsets, target in get_next_batch(rank): + with dist_autograd.context() as context_id: + output = model(indices, offsets) + loss = criterion(output, target) + + # Run distributed backward pass + dist_autograd.backward(context_id, [loss]) + + # Tun distributed optimizer + opt.step(context_id) + + # Not necessary to zero grads as each iteration creates a different + # distributed autograd context which hosts different grads + print("Training done for epoch {}".format(epoch)) + # END run_trainer + + +# BEGIN run_worker +def run_worker(rank, world_size): + r""" + A wrapper function that initializes RPC, calls the function, and shuts down + RPC. + """ + os.environ['MASTER_ADDR'] = 'localhost' + os.environ['MASTER_PORT'] = '29500' + + + rpc_backend_options = ProcessGroupRpcBackendOptions() + rpc_backend_options.init_method='tcp://localhost:29501' + + # Rank 2 is master, 3 is ps and 0 and 1 are trainers. + if rank == 2: + rpc.init_rpc( + "master", + rank=rank, + world_size=world_size, + rpc_backend_options=rpc_backend_options) + + # Build the embedding table on the ps. + emb_rref = rpc.remote( + "ps", + torch.nn.EmbeddingBag, + args=(NUM_EMBEDDINGS, EMBEDDING_DIM), + kwargs={"mode": "sum"}) + + # Run the training loop on trainers. + futs = [] + for trainer_rank in [0, 1]: + trainer_name = "trainer{}".format(trainer_rank) + fut = rpc.rpc_async( + trainer_name, _run_trainer, args=(emb_rref, rank)) + futs.append(fut) + + # Wait for all training to finish. + for fut in futs: + fut.wait() + elif rank <= 1: + # Initialize process group for Distributed DataParallel on trainers. + dist.init_process_group( + backend="gloo", rank=rank, world_size=2) + + # Initialize RPC. + trainer_name = "trainer{}".format(rank) + rpc.init_rpc( + trainer_name, + rank=rank, + world_size=world_size, + rpc_backend_options=rpc_backend_options) + + # Trainer just waits for RPCs from master. + else: + rpc.init_rpc( + "ps", + rank=rank, + world_size=world_size, + rpc_backend_options=rpc_backend_options) + # parameter server do nothing + pass + + # block until all rpcs finish + rpc.shutdown() + + +if __name__=="__main__": + # 2 trainers, 1 parameter server, 1 master. + world_size = 4 + mp.spawn(run_worker, args=(world_size, ), nprocs=world_size, join=True) +# END run_worker diff --git a/conf.py b/conf.py index 683abf00899..d53fce24ed5 100644 --- a/conf.py +++ b/conf.py @@ -250,4 +250,4 @@ def setup(app): app.add_directive('galleryitem', GalleryItemDirective) app.add_directive('customgalleryitem', CustomGalleryItemDirective) app.add_directive('customcarditem', CustomCardItemDirective) - app.add_directive('customcalloutitem', CustomCalloutItemDirective) + app.add_directive('customcalloutitem', CustomCalloutItemDirective) \ No newline at end of file diff --git a/index.rst b/index.rst index 7e1c4001285..26daf5392e3 100644 --- a/index.rst +++ b/index.rst @@ -359,6 +359,12 @@ Welcome to PyTorch Tutorials :image: _static/img/thumbnails/cropped/Implementing-Batch-RPC-Processing-Using-Asynchronous-Executions.png :link: intermediate/rpc_async_execution.html :tags: Parallel-and-Distributed-Training + +.. customcarditem:: + :header: Combining Distributed DataParallel with Distributed RPC Framework + :card_description: Walk through a through a simple example of how to combine distributed data parallelism with distributed model parallelism. + :image: _static/img/thumbnails/cropped/Combining-Distributed-DataParallel-with-Distributed-RPC-Framework.png + :link: advanced/rpc_ddp_tutorial.html .. End of tutorial card section @@ -529,3 +535,4 @@ Additional Resources intermediate/rpc_param_server_tutorial intermediate/dist_pipeline_parallel_tutorial intermediate/rpc_async_execution + advanced/rpc_ddp_tutorial From f9f30887d6f81699ed55fbc4a457e62f62afb799 Mon Sep 17 00:00:00 2001 From: Jessica Lin Date: Tue, 21 Jul 2020 11:51:57 -0700 Subject: [PATCH 26/33] Make RPC profiling recipe into prototype tutorial (#1078) --- .../distributed_rpc_profiling.rst | 70 +++++++++---------- recipes_source/recipes_index.rst | 7 -- 2 files changed, 35 insertions(+), 42 deletions(-) rename {recipes_source => prototype_source}/distributed_rpc_profiling.rst (88%) diff --git a/recipes_source/distributed_rpc_profiling.rst b/prototype_source/distributed_rpc_profiling.rst similarity index 88% rename from recipes_source/distributed_rpc_profiling.rst rename to prototype_source/distributed_rpc_profiling.rst index d43c3a0e217..6af0fd883e9 100644 --- a/recipes_source/distributed_rpc_profiling.rst +++ b/prototype_source/distributed_rpc_profiling.rst @@ -19,10 +19,10 @@ What is the Distributed RPC Framework? --------------------------------------- The **Distributed RPC Framework** provides mechanisms for multi-machine model -training through a set of primitives to allow for remote communication, and a +training through a set of primitives to allow for remote communication, and a higher-level API to automatically differentiate models split across several machines. For this recipe, it would be helpful to be familiar with the `Distributed RPC Framework`_ -as well as the `RPC Tutorials`_. +as well as the `RPC Tutorials`_. What is the PyTorch Profiler? --------------------------------------- @@ -97,7 +97,7 @@ Running the above program should present you with the following output: DEBUG:root:Rank 1 shutdown RPC DEBUG:root:Rank 0 shutdown RPC -Now that we have a skeleton setup of our RPC framework, we can move on to +Now that we have a skeleton setup of our RPC framework, we can move on to sending RPCs back and forth and using the profiler to obtain a view of what's happening under the hood. Let's add to the above ``worker`` function: @@ -108,7 +108,7 @@ happening under the hood. Let's add to the above ``worker`` function: if rank == 0: dst_worker_rank = (rank + 1) % world_size dst_worker_name = f"worker{dst_worker_rank}" - t1, t2 = random_tensor(), random_tensor() + t1, t2 = random_tensor(), random_tensor() # Send and wait RPC completion under profiling scope. with profiler.profile() as prof: fut1 = rpc.rpc_async(dst_worker_name, torch.add, args=(t1, t2)) @@ -119,37 +119,37 @@ happening under the hood. Let's add to the above ``worker`` function: print(prof.key_averages().table()) -The aformented code creates 2 RPCs, specifying ``torch.add`` and ``torch.mul``, respectively, -to be run with two random input tensors on worker 1. Since we use the ``rpc_async`` API, +The aformented code creates 2 RPCs, specifying ``torch.add`` and ``torch.mul``, respectively, +to be run with two random input tensors on worker 1. Since we use the ``rpc_async`` API, we are returned a ``torch.futures.Future`` object, which must be awaited for the result of the computation. Note that this wait must take place within the scope created by the profiling context manager in order for the RPC to be accurately profiled. Running the code with this new worker function should result in the following output: -:: +:: # Some columns are omitted for brevity, exact output subject to randomness - ---------------------------------------------------------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- - Name Self CPU total % Self CPU total CPU total % CPU total CPU time avg Number of Calls Node ID - ---------------------------------------------------------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- - rpc_async#aten::add(worker0 -> worker1) 0.00% 0.000us 0 20.462ms 20.462ms 1 0 - rpc_async#aten::mul(worker0 -> worker1) 0.00% 0.000us 0 5.712ms 5.712ms 1 0 - rpc_async#aten::mul(worker0 -> worker1)#remote_op: mul 1.84% 206.864us 2.69% 302.162us 151.081us 2 1 - rpc_async#aten::add(worker0 -> worker1)#remote_op: add 1.41% 158.501us 1.57% 176.924us 176.924us 1 1 - rpc_async#aten::mul(worker0 -> worker1)#remote_op: output_nr 0.04% 4.980us 0.04% 4.980us 2.490us 2 1 - rpc_async#aten::mul(worker0 -> worker1)#remote_op: is_leaf 0.07% 7.806us 0.07% 7.806us 1.952us 4 1 - rpc_async#aten::add(worker0 -> worker1)#remote_op: empty 0.16% 18.423us 0.16% 18.423us 18.423us 1 1 - rpc_async#aten::mul(worker0 -> worker1)#remote_op: empty 0.14% 15.712us 0.14% 15.712us 15.712us 1 1 - ---------------------------------------------------------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- + ---------------------------------------------------------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- + Name Self CPU total % Self CPU total CPU total % CPU total CPU time avg Number of Calls Node ID + ---------------------------------------------------------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- + rpc_async#aten::add(worker0 -> worker1) 0.00% 0.000us 0 20.462ms 20.462ms 1 0 + rpc_async#aten::mul(worker0 -> worker1) 0.00% 0.000us 0 5.712ms 5.712ms 1 0 + rpc_async#aten::mul(worker0 -> worker1)#remote_op: mul 1.84% 206.864us 2.69% 302.162us 151.081us 2 1 + rpc_async#aten::add(worker0 -> worker1)#remote_op: add 1.41% 158.501us 1.57% 176.924us 176.924us 1 1 + rpc_async#aten::mul(worker0 -> worker1)#remote_op: output_nr 0.04% 4.980us 0.04% 4.980us 2.490us 2 1 + rpc_async#aten::mul(worker0 -> worker1)#remote_op: is_leaf 0.07% 7.806us 0.07% 7.806us 1.952us 4 1 + rpc_async#aten::add(worker0 -> worker1)#remote_op: empty 0.16% 18.423us 0.16% 18.423us 18.423us 1 1 + rpc_async#aten::mul(worker0 -> worker1)#remote_op: empty 0.14% 15.712us 0.14% 15.712us 15.712us 1 1 + ---------------------------------------------------------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- Self CPU time total: 11.237ms Here we can see that the profiler has profiled our ``rpc_async`` calls made to ``worker1`` from ``worker0``. In particular, the first 2 entries in the table show details (such as the operator name, originating worker, and destination worker) about each RPC call made -and the ``CPU total`` column indicates the end-to-end latency of the RPC call. +and the ``CPU total`` column indicates the end-to-end latency of the RPC call. We also have visibility into the actual operators invoked remotely on worker 1 due RPC. -We can see operations that took place on ``worker1`` by checking the ``Node ID`` column. For +We can see operations that took place on ``worker1`` by checking the ``Node ID`` column. For example, we can interpret the row with name ``rpc_async#aten::mul(worker0 -> worker1)#remote_op: mul`` as a ``mul`` operation taking place on the remote node, as a result of the RPC sent to ``worker1`` from ``worker0``, specifying ``worker1`` to run the builtin ``mul`` operator on the input tensors. @@ -157,7 +157,7 @@ Note that names of remote operations are prefixed with the name of the RPC event in them. For example, remote operations corresponding to the ``rpc.rpc_async(dst_worker_name, torch.add, args=(t1, t2))`` call are prefixed with ``rpc_async#aten::mul(worker0 -> worker1)``. -We can also use the profiler gain insight into user-defined functions that are executed over RPC. +We can also use the profiler gain insight into user-defined functions that are executed over RPC. For example, let's add the following to the above ``worker`` function: :: @@ -184,19 +184,19 @@ run our user-defined function. Running this code should result in the following :: # Exact output subject to randomness - -------------------------------------------------------------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- - Name Self CPU total % Self CPU total CPU total % CPU total CPU time avg Number of Calls Node ID - -------------------------------------------------------------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- - rpc_async#udf_with_ops(worker0 -> worker1) 0.00% 0.000us 0 1.008s 1.008s 1 0 - rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: rand 12.58% 80.037us 47.09% 299.589us 149.795us 2 1 - rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: empty 15.40% 98.013us 15.40% 98.013us 24.503us 4 1 - rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: uniform_ 22.85% 145.358us 23.87% 151.870us 75.935us 2 1 - rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: is_complex 1.02% 6.512us 1.02% 6.512us 3.256us 2 1 - rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: add 25.80% 164.179us 28.43% 180.867us 180.867us 1 1 - rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: mul 20.48% 130.293us 31.43% 199.949us 99.975us 2 1 - rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: output_nr 0.71% 4.506us 0.71% 4.506us 2.253us 2 1 - rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: is_leaf 1.16% 7.367us 1.16% 7.367us 1.842us 4 1 - -------------------------------------------------------------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- + -------------------------------------------------------------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- + Name Self CPU total % Self CPU total CPU total % CPU total CPU time avg Number of Calls Node ID + -------------------------------------------------------------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- + rpc_async#udf_with_ops(worker0 -> worker1) 0.00% 0.000us 0 1.008s 1.008s 1 0 + rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: rand 12.58% 80.037us 47.09% 299.589us 149.795us 2 1 + rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: empty 15.40% 98.013us 15.40% 98.013us 24.503us 4 1 + rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: uniform_ 22.85% 145.358us 23.87% 151.870us 75.935us 2 1 + rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: is_complex 1.02% 6.512us 1.02% 6.512us 3.256us 2 1 + rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: add 25.80% 164.179us 28.43% 180.867us 180.867us 1 1 + rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: mul 20.48% 130.293us 31.43% 199.949us 99.975us 2 1 + rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: output_nr 0.71% 4.506us 0.71% 4.506us 2.253us 2 1 + rpc_async#udf_with_ops(worker0 -> worker1)#remote_op: is_leaf 1.16% 7.367us 1.16% 7.367us 1.842us 4 1 + -------------------------------------------------------------------- --------------- --------------- --------------- --------------- --------------- --------------- --------------- Here we can see that the user-defined function has successfully been profiled with its name ``(rpc_async#udf_with_ops(worker0 -> worker1))``, and has the CPU total time we would roughly expect diff --git a/recipes_source/recipes_index.rst b/recipes_source/recipes_index.rst index e25cb199aa5..92059e9afbe 100644 --- a/recipes_source/recipes_index.rst +++ b/recipes_source/recipes_index.rst @@ -102,13 +102,6 @@ Recipes are bite-sized bite-sized, actionable examples of how to use specific Py :link: ../recipes/recipes/profiler.html :tags: Basics -.. customcarditem:: - :header: Distributed RPC Profiling - :card_description: Learn how to use PyTorch's profiler in conjunction with the Distributed RPC Framework. - :image: ../_static/img/thumbnails/cropped/profiler.png - :link: ../recipes/distributed_rpc_profiling.html - :tags: Basics - .. Customization .. customcarditem:: From c8e79e1a43d53fbf6482cbe75085ea42fdc04407 Mon Sep 17 00:00:00 2001 From: Jessica Lin Date: Tue, 21 Jul 2020 12:00:01 -0700 Subject: [PATCH 27/33] Add RPC tutorial --- prototype_source/README.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/prototype_source/README.txt b/prototype_source/README.txt index b04371db306..38c3f251c48 100644 --- a/prototype_source/README.txt +++ b/prototype_source/README.txt @@ -1,2 +1,6 @@ Prototype Tutorials ------------------ + +1. distributed_rpc_profiling.rst + Profiling PyTorch RPC-Based Workloads + https://github.com/pytorch/tutorials/blob/release/1.6/prototype_source/distributed_rpc_profiling.rst From f2c549d6ff1870f69d0343eb7a5301d27b3b3c9e Mon Sep 17 00:00:00 2001 From: Jessica Lin Date: Tue, 21 Jul 2020 12:02:10 -0700 Subject: [PATCH 28/33] Update to include recipes --- prototype_source/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prototype_source/README.md b/prototype_source/README.md index ecb860aea68..d2b850043d7 100644 --- a/prototype_source/README.md +++ b/prototype_source/README.md @@ -1,9 +1,9 @@ -# Prototype Tutorials +# Prototype Tutorials and Recipes -This directory contains tutorials demonstrating prototype features in PyTorch. +This directory contains tutorials and recipes demonstrating prototype features in PyTorch. **Prototype features** are not available as part of binary distributions like PyPI or Conda (except maybe behind run-time flags). To test these features we would, depending on the feature, recommend building from master or using the nightly wheelss that are made available on pytorch.org. -These tutorials are intentionally left out of the pytorch.org/tutorials build and will not show up on the website. +These are intentionally left out of the pytorch.org/tutorials build and will not show up on the website. *Level of commitment:* We are committing to gathering high bandwidth feedback only on these features. Based on this feedback and potential further engagement between community members, we as a community will decide if we want to upgrade the level of commitment or to fail fast. From d9d152b33a80c4e5bf925a7b4663707dfc37b703 Mon Sep 17 00:00:00 2001 From: supriyar Date: Tue, 21 Jul 2020 13:55:04 -0700 Subject: [PATCH 29/33] Add Graph Mode Dynamic Quant tutorial (#1065) * Update feature classification labels * Update NVidia -> Nvidia * Bring back default filename_pattern so that by default we run all galleries. Signed-off-by: Edward Z. Yang * Add prototype_source directory * Add prototype directory * Add prototype * Remove extra "done" * Add REAME.txt * Update for prototype instructions * Update for prototype feature * refine torchvision_tutorial doc for windows * Update neural_style_tutorial.py (#1059) Updated the mistake in the Loading Images Section. * torch_script_custom_ops restructure (#1057) Signed-off-by: Edward Z. Yang * Port custom ops tutorial to new registration API, increase testability. Signed-off-by: Edward Z. Yang * Kill some other occurrences of RegisterOperators Signed-off-by: Edward Z. Yang * Update README.md * Make torch_script_custom_classes tutorial runnable I also fixed some warnings in the tutorial, and fixed some minor bitrot (e.g., torch::script::Module to torch::jit::Module) I also added some missing quotes around some bash expansions. Signed-off-by: Edward Z. Yang * Update torch_script_custom_classes to use TORCH_LIBRARY (#1062) Signed-off-by: Edward Z. Yang * Add Graph Mode Dynamic Quant tutorial Summary: Tutorial to demonstrate graph mode dynamic quant on BERT model. Currently not directly runnable as it requires to download glue dataset and fine-tuned model Co-authored-by: Jessica Lin Co-authored-by: Edward Z. Yang Co-authored-by: Yang Gu Co-authored-by: Hritik Bhandari --- prototype_source/README.txt | 3 +- .../graph_mode_dynamic_bert_tutorial.rst | 559 ++++++++++++++++++ 2 files changed, 560 insertions(+), 2 deletions(-) create mode 100644 prototype_source/graph_mode_dynamic_bert_tutorial.rst diff --git a/prototype_source/README.txt b/prototype_source/README.txt index 38c3f251c48..42753fe18f8 100644 --- a/prototype_source/README.txt +++ b/prototype_source/README.txt @@ -1,6 +1,5 @@ Prototype Tutorials ------------------ - 1. distributed_rpc_profiling.rst Profiling PyTorch RPC-Based Workloads - https://github.com/pytorch/tutorials/blob/release/1.6/prototype_source/distributed_rpc_profiling.rst + https://github.com/pytorch/tutorials/blob/release/1.6/prototype_source/distributed_rpc_profiling.rst \ No newline at end of file diff --git a/prototype_source/graph_mode_dynamic_bert_tutorial.rst b/prototype_source/graph_mode_dynamic_bert_tutorial.rst new file mode 100644 index 00000000000..2a296ccfa6b --- /dev/null +++ b/prototype_source/graph_mode_dynamic_bert_tutorial.rst @@ -0,0 +1,559 @@ +(prototype) Graph Mode Dynamic Quantization on BERT +============================================== + + +**Author**: `Supriya Rao `_ + +Introduction +------------ + +This tutorial introduces the steps to do post training Dynamic Quantization with Graph Mode Quantization. Dynamic quantization converts a float model to a quantized model with static int8 data types for the weights and dynamic quantization for the activations. The activations are quantized dynamically (per batch) to int8 while the weights are statically quantized to int8. Graph Mode Quantization flow operates on the model graph and requires minimal user intervention to quantize the model. To be able to use graph mode, the float model needs to be either traced or scripted first. + +Advantages of graph mode quantization are: + +- In graph mode, we can inspect the code that is executed in forward function (e.g. aten function calls) and quantization is achieved by module and graph manipulations. +- Simple quantization flow, minimal manual steps. +- Unlocks the possibility of doing higher level optimizations like automatic precision selection. + +For additional details on Graph Mode Quantization please refer to the `Graph Mode Static Quantization Tutorial `_. + +tl;dr The Graph Mode Dynamic `Quantization API `_: + +.. code:: python + + import torch + from torch.quantization import per_channel_dynamic_qconfig + from torch.quantization import quantize_dynamic_jit + + ts_model = torch.jit.script(float_model) # or torch.jit.trace(float_model, input) + + quantized = quantize_dynamic_jit(ts_model, {'': per_channel_dynamic_qconfig}) + +1. Quantizing BERT Model +------------------------ + +The installaion steps and details about the model are identical to the steps in the Eager Mode Tutorial. Please refer to the tutorial `here `_ for more details. + +1.1 Setup +^^^^^^^^^ +Once all the necesessary packages are downloaded and installed we setup the code. We first start with the necessary imports and setup for the model. + +.. code:: python + + from __future__ import absolute_import, division, print_function + + import logging + import numpy as np + import os + import random + import sys + import time + import torch + + from argparse import Namespace + from torch.utils.data import (DataLoader, RandomSampler, SequentialSampler, + TensorDataset) + from tqdm import tqdm + from transformers import (BertConfig, BertForSequenceClassification, BertTokenizer,) + from transformers import glue_compute_metrics as compute_metrics + from transformers import glue_output_modes as output_modes + from transformers import glue_processors as processors + from transformers import glue_convert_examples_to_features as convert_examples_to_features + from torch.quantization import per_channel_dynamic_qconfig + from torch.quantization import quantize_dynamic_jit + + global_rng = random.Random() + + def ids_tensor(shape, vocab_size, rng=None, name=None): + # Creates a random int32 tensor of the shape within the vocab size + if rng is None: + rng = global_rng + + total_dims = 1 + for dim in shape: + total_dims *= dim + + values = [] + for _ in range(total_dims): + values.append(rng.randint(0, vocab_size - 1)) + + return torch.tensor(data=values, dtype=torch.long, device='cpu').view(shape).contiguous() + + # Setup logging + logger = logging.getLogger(__name__) + logging.basicConfig(format = '%(asctime)s - %(levelname)s - %(name)s - %(message)s', + datefmt = '%m/%d/%Y %H:%M:%S', + level = logging.WARN) + + logging.getLogger("transformers.modeling_utils").setLevel( + logging.WARN) # Reduce logging + + print(torch.__version__) + + torch.set_num_threads(1) + print(torch.__config__.parallel_info()) + +1.2 Download GLUE dataset +^^^^^^^^^^^^^^^^^^^^^^^^^ +Before running MRPC tasks we download the GLUE data by running this script and unpack it to a directory glue_data. + +.. code:: shell + + python download_glue_data.py --data_dir='glue_data' --tasks='MRPC' + +1.3 Set global BERT configurations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +To run this experiment we first need a fine tuned BERT model. We provide the fined-tuned BERT model for MRPC task `here `_. To save time, you can download the model file (~400 MB) directly into your local folder $OUT_DIR. + + +.. code:: python + + configs = Namespace() + + # The output directory for the fine-tuned model, $OUT_DIR. + configs.output_dir = "./MRPC/" + + # The data directory for the MRPC task in the GLUE benchmark, $GLUE_DIR/$TASK_NAME. + configs.data_dir = "./glue_data/MRPC" + + # The model name or path for the pre-trained model. + configs.model_name_or_path = "bert-base-uncased" + # The maximum length of an input sequence + configs.max_seq_length = 128 + + # Prepare GLUE task. + configs.task_name = "MRPC".lower() + configs.processor = processors[configs.task_name]() + configs.output_mode = output_modes[configs.task_name] + configs.label_list = configs.processor.get_labels() + configs.model_type = "bert".lower() + configs.do_lower_case = True + + # Set the device, batch size, topology, and caching flags. + configs.device = "cpu" + configs.per_gpu_eval_batch_size = 8 + configs.n_gpu = 0 + configs.local_rank = -1 + configs.overwrite_cache = False + + # Set random seed for reproducibility. + def set_seed(seed): + random.seed(seed) + np.random.seed(seed) + torch.manual_seed(seed) + set_seed(42) + + tokenizer = BertTokenizer.from_pretrained( + configs.output_dir, do_lower_case=configs.do_lower_case) + + model = BertForSequenceClassification.from_pretrained(configs.output_dir, torchscript=True) + model.to(configs.device) + +1.4 Quantizing BERT model with Graph Mode Quantization +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +1.4.1 Script/Trace the model +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The input for graph mode quantization is a TorchScript model, so you'll need to either script or trace the model first. Currently, scripting the BERT model is not supported so we trace the model here. + +We first identify the inputs to be passed to the model. Here, we trace the model with the largest possible input size that will be passed during the evaluation. +We choose a batch size of 8 and sequence lenght of 128 based on the input sizes passed in during the evaluation step below. Using the max possible shape during inference while tracing is a limitation of the huggingface BERT model as mentioned `here `_. + +We trace the model using ``torch.jit.trace``. + +.. code:: python + + input_ids = ids_tensor([8, 128], 2) + token_type_ids = ids_tensor([8, 128], 2) + attention_mask = ids_tensor([8, 128], vocab_size=2) + dummy_input = (input_ids, attention_mask, token_type_ids) + traced_model = torch.jit.trace(model, dummy_input) + +1.4.2 Specify qconfig_dict +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: + + qconfig_dict = {'': per_channel_dynamic_qconfig} + +qconfig is a named tuple of the observers for activation and weight. For dynamic quantization we use a dummy activation observer to mimic the dynamic quantization process that happens in the operator during runtime. For the weight tensors we recommend using per-channel quantization which helps improve the final accuracy. +``qconfig_dict`` is a dictionary with names of sub modules as key and qconfig for that module as value, empty key means the qconfig will be applied to whole model unless it’s overwritten by more specific configurations, the qconfig for each module is either found in the dictionary or fallback to the qconfig of parent module. + +Right now qconfig_dict is the only way to configure how the model is quantized, and it is done in the granularity of module, that is, we only support one type of qconfig for each module, and the qconfig for sub module will override the qconfig for parent module. For example, if we have + +.. code:: + + qconfig = { + '' : qconfig_global, + 'sub' : qconfig_sub, + 'sub.fc1' : qconfig_fc, + 'sub.fc2': None + } + +Module ``sub.fc1`` will be configured with ``qconfig_fc``, and all other child modules in ``sub`` will be configured with ``qconfig_sub`` and ``sub.fc2`` will not be quantized. All other modules in the model will be quantized with qconfig_global + +.. code:: python + + qconfig_dict = {'': per_channel_dynamic_qconfig} + +1.4.3 Quantize the model (one-line API) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +We call the one line API (similar to eager mode) to perform quantization as follows. + +.. code:: python + + quantized_model = quantize_dynamic_jit(traced_model, qconfig_dict) + +2. Evaluation +------------- + +We reuse the tokenize and evaluation function from Huggingface. + +.. code:: python + + def evaluate(args, model, tokenizer, prefix=""): + # Loop to handle MNLI double evaluation (matched, mis-matched) + eval_task_names = ("mnli", "mnli-mm") if args.task_name == "mnli" else (args.task_name,) + eval_outputs_dirs = (args.output_dir, args.output_dir + '-MM') if args.task_name == "mnli" else (args.output_dir,) + + results = {} + for eval_task, eval_output_dir in zip(eval_task_names, eval_outputs_dirs): + eval_dataset = load_and_cache_examples(args, eval_task, tokenizer, evaluate=True) + + if not os.path.exists(eval_output_dir) and args.local_rank in [-1, 0]: + os.makedirs(eval_output_dir) + + args.eval_batch_size = args.per_gpu_eval_batch_size * max(1, args.n_gpu) + # Note that DistributedSampler samples randomly + eval_sampler = SequentialSampler(eval_dataset) if args.local_rank == -1 else DistributedSampler(eval_dataset) + eval_dataloader = DataLoader(eval_dataset, sampler=eval_sampler, batch_size=args.eval_batch_size) + + # multi-gpu eval + if args.n_gpu > 1: + model = torch.nn.DataParallel(model) + + # Eval! + logger.info("***** Running evaluation {} *****".format(prefix)) + logger.info(" Num examples = %d", len(eval_dataset)) + logger.info(" Batch size = %d", args.eval_batch_size) + nb_eval_steps = 0 + preds = None + out_label_ids = None + for batch in tqdm(eval_dataloader, desc="Evaluating"): + model.eval() + batch = tuple(t.to(args.device) for t in batch) + + with torch.no_grad(): + inputs = {'input_ids': batch[0], + 'attention_mask': batch[1]} + labels = batch[3] + if args.model_type != 'distilbert': + inputs['input'] = batch[2] if args.model_type in ['bert', 'xlnet'] else None # XLM, DistilBERT and RoBERTa don't use segment_ids + outputs = model(**inputs) + logits = outputs[0] + nb_eval_steps += 1 + if preds is None: + preds = logits.detach().cpu().numpy() + out_label_ids = labels.detach().cpu().numpy() + else: + preds = np.append(preds, logits.detach().cpu().numpy(), axis=0) + out_label_ids = np.append(out_label_ids, labels.detach().cpu().numpy(), axis=0) + + if args.output_mode == "classification": + preds = np.argmax(preds, axis=1) + elif args.output_mode == "regression": + preds = np.squeeze(preds) + result = compute_metrics(eval_task, preds, out_label_ids) + results.update(result) + + output_eval_file = os.path.join(eval_output_dir, prefix, "eval_results.txt") + with open(output_eval_file, "w") as writer: + logger.info("***** Eval results {} *****".format(prefix)) + for key in sorted(result.keys()): + logger.info(" %s = %s", key, str(result[key])) + writer.write("%s = %s\n" % (key, str(result[key]))) + + return results + + def load_and_cache_examples(args, task, tokenizer, evaluate=False): + if args.local_rank not in [-1, 0] and not evaluate: + torch.distributed.barrier() # Make sure only the first process in distributed training process the dataset, and the others will use the cache + + processor = processors[task]() + output_mode = output_modes[task] + # Load data features from cache or dataset file + cached_features_file = os.path.join(args.data_dir, 'cached_{}_{}_{}_{}'.format( + 'dev' if evaluate else 'train', + list(filter(None, args.model_name_or_path.split('/'))).pop(), + str(args.max_seq_length), + str(task))) + if os.path.exists(cached_features_file) and not args.overwrite_cache: + logger.info("Loading features from cached file %s", cached_features_file) + features = torch.load(cached_features_file) + else: + logger.info("Creating features from dataset file at %s", args.data_dir) + label_list = processor.get_labels() + if task in ['mnli', 'mnli-mm'] and args.model_type in ['roberta']: + # HACK(label indices are swapped in RoBERTa pretrained model) + label_list[1], label_list[2] = label_list[2], label_list[1] + examples = processor.get_dev_examples(args.data_dir) if evaluate else processor.get_train_examples(args.data_dir) + features = convert_examples_to_features(examples, + tokenizer, + label_list=label_list, + max_length=args.max_seq_length, + output_mode=output_mode,) + if args.local_rank in [-1, 0]: + logger.info("Saving features into cached file %s", cached_features_file) + torch.save(features, cached_features_file) + + if args.local_rank == 0 and not evaluate: + torch.distributed.barrier() # Make sure only the first process in distributed training process the dataset, and the others will use the cache + + # Convert to Tensors and build dataset + all_input_ids = torch.tensor([f.input_ids for f in features], dtype=torch.long) + all_attention_mask = torch.tensor([f.attention_mask for f in features], dtype=torch.long) + all_token_type_ids = torch.tensor([f.token_type_ids for f in features], dtype=torch.long) + if output_mode == "classification": + all_labels = torch.tensor([f.label for f in features], dtype=torch.long) + elif output_mode == "regression": + all_labels = torch.tensor([f.label for f in features], dtype=torch.float) + + dataset = TensorDataset(all_input_ids, all_attention_mask, all_token_type_ids, all_labels) + return dataset + + def time_model_evaluation(model, configs, tokenizer): + eval_start_time = time.time() + result = evaluate(configs, model, tokenizer, prefix="") + eval_end_time = time.time() + eval_duration_time = eval_end_time - eval_start_time + print(result) + print("Evaluate total time (seconds): {0:.1f}".format(eval_duration_time)) + + +2.1 Check Model Size +^^^^^^^^^^^^^^^^^^^^ + +We print the model size to account for wins from quantization + +.. code:: python + + def print_size_of_model(model): + if isinstance(model, torch.jit.RecursiveScriptModule): + torch.jit.save(model, "temp.p") + else: + torch.jit.save(torch.jit.script(model), "temp.p") + print('Size (MB):', os.path.getsize("temp.p")/1e6) + os.remove('temp.p') + + print("Size of model before quantization") + print_size_of_model(traced_model) + print("Size of model after quantization") + + print_size_of_model(quantized_model) + +.. code:: + + Size of model before quantization + Size (MB): 438.242141 + Size of model after quantization + Size (MB): 184.354759 + +2.2 Run the evaluation +^^^^^^^^^^^^^^^^^^^^^^ +We evaluate the FP32 and quantized model and compare the F1 score. Note that the performance numbers below are on a dev machine and they would likely improve on a production server. + +.. code:: python + + time_model_evaluation(traced_model, configs, tokenizer) + time_model_evaluation(quantized_model, configs, tokenizer) + +.. code:: + + FP32 model results - + 'f1': 0.901 + Time taken - 188.0s + + INT8 model results - + 'f1': 0.902 + Time taken - 157.4s + +3. Debugging the Quantized Model +-------------------------------- + +We can debug the quantized model by passing in the debug option. + +.. code:: + + quantized_model = quantize_dynamic_jit(traced_model, qconfig_dict, debug=True) + +If debug is set to True: + +- We can access the attributes of the quantized model the same way as in a torchscript model, e.g. model.fc1.weight (might be harder if you use a module list or sequential). +- The arithmetic operations all occur in floating point with the numerics being identical to the final quantized model, allowing for debugging. + +.. code:: python + + quantized_model_debug = quantize_dynamic_jit(traced_model, qconfig_dict, debug=True) + +Calling ``quantize_dynamic_jit`` is equivalent to calling ``prepare_dynamic_jit`` followed by ``convert_dynamic_jit``. Usage of the one-line API is recommended. But if you wish to debug or analyze the model after each step, the multi-line API comes into use. + +3.1. Evaluate the Debug Model +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: python + + # Evaluate the debug model + time_model_evaluation(quantized_model_debug, configs, tokenizer) + +.. code:: + + Size (MB): 438.406429 + + INT8 (debug=True) model results - + 'f1': 0.897 + +Note that the accuracy of the debug version is close to, but not exactly the same as the non-debug version as the debug version uses floating point ops to emulate quantized ops and the numerics match is approximate. +This is the case only for per-channel quantization (we are working on improving this). Per-tensor quantization (using default_dynamic_qconfig) has exact numerics match between debug and non-debug version. + +.. code:: python + + print(str(quantized_model_debug.graph)) + +Snippet of the graph printed - + +.. code:: + + %111 : Tensor = prim::GetAttr[name="bias"](%110) + %112 : Tensor = prim::GetAttr[name="weight"](%110) + %113 : Float(768:1) = prim::GetAttr[name="4_scale_0"](%110) + %114 : Int(768:1) = prim::GetAttr[name="4_zero_point_0"](%110) + %115 : int = prim::GetAttr[name="4_axis_0"](%110) + %116 : int = prim::GetAttr[name="4_scalar_type_0"](%110) + %4.quant.6 : Tensor = aten::quantize_per_channel(%112, %113, %114, %115, %116) + %4.dequant.6 : Tensor = aten::dequantize(%4.quant.6) + %1640 : bool = prim::Constant[value=1]() + %input.5.scale.1 : float, %input.5.zero_point.1 : int = aten::_choose_qparams_per_tensor(%input.5, %1640) + %input.5.quant.1 : Tensor = aten::quantize_per_tensor(%input.5, %input.5.scale.1, %input.5.zero_point.1, %74) + %input.5.dequant.1 : Float(8:98304, 128:768, 768:1) = aten::dequantize(%input.5.quant.1) + %119 : Tensor = aten::linear(%input.5.dequant.1, %4.dequant.6, %111) + +We can see that there is no ``quantized::linear_dynamic`` in the model, but the numerically equivalent pattern of ``aten::_choose_qparams_per_tensor`` - ``aten::quantize_per_tensor`` - ``aten::dequantize`` - ``aten::linear``. + +.. code:: python + + # Get the size of the debug model + print_size_of_model(quantized_model_debug) + +.. code:: + + Size (MB): 438.406429 + +Size of the debug model is the close to the floating point model because all the weights are in float and not yet quantized and frozen, this allows people to inspect the weight. +You may access the weight attributes directly in the torchscript model. Accessing the weight in the debug model is the same as accessing the weight in a TorchScript model: + +.. code:: python + + print(quantized_model.bert.encoder.layer._c.getattr('0').attention.self.query.weight) + +.. code:: + + tensor([[-0.0157, 0.0257, -0.0269, ..., 0.0158, 0.0764, 0.0548], + [-0.0325, 0.0345, -0.0423, ..., -0.0528, 0.1382, 0.0069], + [ 0.0106, 0.0335, 0.0113, ..., -0.0275, 0.0253, -0.0457], + ..., + [-0.0090, 0.0512, 0.0555, ..., 0.0277, 0.0543, -0.0539], + [-0.0195, 0.0943, 0.0619, ..., -0.1040, 0.0598, 0.0465], + [ 0.0009, -0.0949, 0.0097, ..., -0.0183, -0.0511, -0.0085]], + grad_fn=) + +Accessing the scale and zero_point for the corresponding weight can be done as follows - + +.. code:: python + + print(quantized_model.bert.encoder.layer._c.getattr('0').attention.self.query.getattr('4_scale_0')) + print(quantized_model.bert.encoder.layer._c.getattr('0').attention.self.query.getattr('4_zero_point_0')) + +Since we use per-channel quantization, we get per-channel scales tensor. + +.. code:: + + tensor([0.0009, 0.0011, 0.0010, 0.0011, 0.0034, 0.0013, 0.0010, 0.0010, 0.0013, + 0.0012, 0.0011, 0.0010, 0.0010, 0.0010, 0.0010, 0.0010, 0.0009, 0.0015, + 0.0016, 0.0036, 0.0012, 0.0009, 0.0010, 0.0014, 0.0008, 0.0008, 0.0008, + ..., + 0.0019, 0.0023, 0.0013, 0.0018, 0.0012, 0.0031, 0.0015, 0.0013, 0.0014, + 0.0022, 0.0011, 0.0024]) + +Zero-point tensor - + +.. code:: + + tensor([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + .., + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + dtype=torch.int32) + +4. Comparing Results with Eager Mode +------------------------------------ + +Following results show the F1 score and model size for Eager Mode Quantization of the same model by following the steps mentioned in the `tutorial `_. Results show that Eager and Graph Mode Quantization on the model produce identical results. + +.. code:: + + FP32 model results - + Size (MB): 438.016605 + 'f1': 0.901 + + INT8 model results - + Size (MB): 182.878029 + 'f1': 0.902 + +5. Benchmarking the Model +------------------------- + +We benchmark the model with dummy input and compare the Float model with Eager and Graph Mode Quantized Model on a production server machine. + +.. code:: python + + def benchmark(model): + model = torch.jit.load(model) + model.eval() + torch.set_num_threads(1) + input_ids = ids_tensor([8, 128], 2) + token_type_ids = ids_tensor([8, 128], 2) + attention_mask = ids_tensor([8, 128], vocab_size=2) + elapsed = 0 + for _i in range(50): + start = time.time() + output = model(input_ids, token_type_ids, attention_mask) + end = time.time() + elapsed = elapsed + (end - start) + print('Elapsed time: ', (elapsed / 50), ' s') + return + print("Running benchmark for Float model") + benchmark(args.jit_model_path_float) + print("Running benchmark for Eager Mode Quantized model") + benchmark(args.jit_model_path_eager) + print("Running benchmark for Graph Mode Quantized model") + benchmark(args.jit_model_path_graph) + +.. code:: + + Running benchmark for Float model + Elapsed time: 4.49 s + Running benchmark for Eager Mode Quantized model + Elapsed time: 2.67 s + Running benchmark for Graph Mode Quantized model + Elapsed time: 2.69 s + As we can see both graph mode and eager mode quantized model have a similar speed up over the floating point model. + +Conclusion +---------- + +In this tutorial, we demonstrated how to convert a well-known state-of-the-art NLP model like BERT into dynamic quantized model using graph mode with same performance as eager mode. +Dynamic quantization can reduce the size of the model while only having a limited implication on accuracy. + +Thanks for reading! As always, we welcome any feedback, so please create an issue `here `_ if you have any. From b53f2724dfbb9ade04d1dfef87736ddd2a6a2f50 Mon Sep 17 00:00:00 2001 From: Jessica Lin Date: Tue, 21 Jul 2020 16:56:28 -0700 Subject: [PATCH 30/33] Add mobile recipes images --- _static/img/thumbnails/cropped/android.png | Bin 0 -> 16276 bytes _static/img/thumbnails/cropped/ios.png | Bin 0 -> 17980 bytes _static/img/thumbnails/cropped/mobile.png | Bin 0 -> 24051 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 _static/img/thumbnails/cropped/android.png create mode 100644 _static/img/thumbnails/cropped/ios.png create mode 100644 _static/img/thumbnails/cropped/mobile.png diff --git a/_static/img/thumbnails/cropped/android.png b/_static/img/thumbnails/cropped/android.png new file mode 100644 index 0000000000000000000000000000000000000000..5c6079d909037136865dcec858996b82c7b10b83 GIT binary patch literal 16276 zcmeIZ`8U+>A3uyj5!%RDDmomuQl_T5;rWUWvbWi({Z&WwFunjzlE5W<)c z(q!MV3>n+~^8Nk~_qoq~K0i2fj(MHeT-Wn@UeD{XJg>J#2AV8PTud}HG%VU$PfciO z=*-UkE-(Q9!8?EaNkem!M*HbwNU+V?bWm6pDSY!_GlVekN%pGPvn$%tgj7dplU618 zIZv;FXK!s?relhy_uicr@x__7O4~uvOcE|=G;1UCKg2i*!J}L2)Tf?$Pv?W9<+rOu zA�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 NRyTNBsb=@`{{c#XliC0P literal 0 HcmV?d00001 diff --git a/_static/img/thumbnails/cropped/ios.png b/_static/img/thumbnails/cropped/ios.png new file mode 100644 index 0000000000000000000000000000000000000000..8c1d4a2b04d3b2616e6da169cdd9a6e982cff5f6 GIT binary patch literal 17980 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 KocGZ1{r>~yd6!)P literal 0 HcmV?d00001 diff --git a/_static/img/thumbnails/cropped/mobile.png b/_static/img/thumbnails/cropped/mobile.png new file mode 100644 index 0000000000000000000000000000000000000000..12dc917519ce0a2e8440785f3a98bf29d37898e1 GIT binary patch literal 24051 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 GLjMB>4rf~c literal 0 HcmV?d00001 From a855e46dfe6857569d0de0937590d7ab9d0ddb65 Mon Sep 17 00:00:00 2001 From: Jessica Lin Date: Tue, 21 Jul 2020 16:58:26 -0700 Subject: [PATCH 31/33] Update mobile recipe index --- recipes_source/recipes_index.rst | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/recipes_source/recipes_index.rst b/recipes_source/recipes_index.rst index 92059e9afbe..0e148538e3d 100644 --- a/recipes_source/recipes_index.rst +++ b/recipes_source/recipes_index.rst @@ -102,6 +102,13 @@ Recipes are bite-sized bite-sized, actionable examples of how to use specific Py :link: ../recipes/recipes/profiler.html :tags: Basics +.. customcarditem:: + :header: Distributed RPC Profiling + :card_description: Learn how to use PyTorch's profiler in conjunction with the Distributed RPC Framework. + :image: ../_static/img/thumbnails/cropped/profiler.png + :link: ../recipes/distributed_rpc_profiling.html + :tags: Basics + .. Customization .. customcarditem:: @@ -155,15 +162,15 @@ Recipes are bite-sized bite-sized, actionable examples of how to use specific Py .. customcarditem:: :header: PyTorch Mobile Performance Recipes - :card_description: List of recipes for performance optimizations for using PyTorch on Mobile. - :image: ../_static/img/thumbnails/cropped/zeroing-out-gradients.PNG + :card_description: List of recipes for performance optimizations for using PyTorch on Mobile (Android and iOS). + :image: ../_static/img/thumbnails/cropped/mobile.png :link: ../recipes/mobile_perf.html :tags: Mobile,Model-Optimization .. customcarditem:: :header: Making Android Native Application That Uses PyTorch Android Prebuilt Libraries - :card_description: Learn how to make android application from the scratch that uses LibTorch C++ API and uses TorchScript model with custom C++ operator. - :image: ../_static/img/thumbnails/cropped/zeroing-out-gradients.PNG + :card_description: Learn how to make Android application from the scratch that uses LibTorch C++ API and uses TorchScript model with custom C++ operator. + :image: ../_static/img/thumbnails/cropped/android.png :link: ../recipes/android_native_app_with_custom_op.html :tags: Mobile From f8b200dd2e9ea063cc9fd69b24f576a47336c169 Mon Sep 17 00:00:00 2001 From: Jessica Lin Date: Tue, 21 Jul 2020 17:00:35 -0700 Subject: [PATCH 32/33] Remove RPC Profiling recipe from index --- recipes_source/recipes_index.rst | 7 ------- 1 file changed, 7 deletions(-) diff --git a/recipes_source/recipes_index.rst b/recipes_source/recipes_index.rst index 0e148538e3d..e842c19bae5 100644 --- a/recipes_source/recipes_index.rst +++ b/recipes_source/recipes_index.rst @@ -102,13 +102,6 @@ Recipes are bite-sized bite-sized, actionable examples of how to use specific Py :link: ../recipes/recipes/profiler.html :tags: Basics -.. customcarditem:: - :header: Distributed RPC Profiling - :card_description: Learn how to use PyTorch's profiler in conjunction with the Distributed RPC Framework. - :image: ../_static/img/thumbnails/cropped/profiler.png - :link: ../recipes/distributed_rpc_profiling.html - :tags: Basics - .. Customization .. customcarditem:: From 6e261b2d556f250f6469ce996aaf88568243e761 Mon Sep 17 00:00:00 2001 From: Jessica Lin Date: Tue, 21 Jul 2020 22:06:52 -0700 Subject: [PATCH 33/33] 1.6 model freezing tutorial (#1077) * Update feature classification labels * Update NVidia -> Nvidia * Bring back default filename_pattern so that by default we run all galleries. Signed-off-by: Edward Z. Yang * Add prototype_source directory * Add prototype directory * Add prototype * Remove extra "done" * Add REAME.txt * Update for prototype instructions * Update for prototype feature * refine torchvision_tutorial doc for windows * Update neural_style_tutorial.py (#1059) Updated the mistake in the Loading Images Section. * torch_script_custom_ops restructure (#1057) Signed-off-by: Edward Z. Yang * Port custom ops tutorial to new registration API, increase testability. Signed-off-by: Edward Z. Yang * Kill some other occurrences of RegisterOperators Signed-off-by: Edward Z. Yang * Update README.md * Make torch_script_custom_classes tutorial runnable I also fixed some warnings in the tutorial, and fixed some minor bitrot (e.g., torch::script::Module to torch::jit::Module) I also added some missing quotes around some bash expansions. Signed-off-by: Edward Z. Yang * Update torch_script_custom_classes to use TORCH_LIBRARY (#1062) Signed-off-by: Edward Z. Yang * Add Model Freezing in TorchScript Co-authored-by: Edward Z. Yang Co-authored-by: Yang Gu Co-authored-by: Hritik Bhandari --- prototype_source/README.txt | 2 +- prototype_source/torchscript_freezing.py | 134 +++++++++++++++++++++++ 2 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 prototype_source/torchscript_freezing.py diff --git a/prototype_source/README.txt b/prototype_source/README.txt index 42753fe18f8..2da500a830c 100644 --- a/prototype_source/README.txt +++ b/prototype_source/README.txt @@ -2,4 +2,4 @@ Prototype Tutorials ------------------ 1. distributed_rpc_profiling.rst Profiling PyTorch RPC-Based Workloads - https://github.com/pytorch/tutorials/blob/release/1.6/prototype_source/distributed_rpc_profiling.rst \ No newline at end of file + https://github.com/pytorch/tutorials/blob/release/1.6/prototype_source/distributed_rpc_profiling.rst diff --git a/prototype_source/torchscript_freezing.py b/prototype_source/torchscript_freezing.py new file mode 100644 index 00000000000..0b6115c3dc7 --- /dev/null +++ b/prototype_source/torchscript_freezing.py @@ -0,0 +1,134 @@ +""" +Model Freezing in TorchScript +============================= + +In this tutorial, we introduce the syntax for *model freezing* in TorchScript. +Freezing is the process of inlining Pytorch module parameters and attributes +values into the TorchScript internal representation. Parameter and attribute +values are treated as final values and they cannot be modified in the resulting +Frozen module. + +Basic Syntax +------------ +Model freezing can be invoked using API below: + + ``torch.jit.freeze(mod : ScriptModule, names : str[]) -> SciptModule`` + +Note the input module can either be the result of scripting or tracing. +See https://pytorch.org/tutorials/beginner/Intro_to_TorchScript_tutorial.html + +Next, we demonstrate how freezing works using an example: +""" + +import torch, time + +class Net(torch.nn.Module): + def __init__(self): + super(Net, self).__init__() + self.conv1 = torch.nn.Conv2d(1, 32, 3, 1) + self.conv2 = torch.nn.Conv2d(32, 64, 3, 1) + self.dropout1 = torch.nn.Dropout2d(0.25) + self.dropout2 = torch.nn.Dropout2d(0.5) + self.fc1 = torch.nn.Linear(9216, 128) + self.fc2 = torch.nn.Linear(128, 10) + + def forward(self, x): + x = self.conv1(x) + x = torch.nn.functional.relu(x) + x = self.conv2(x) + x = torch.nn.functional.max_pool2d(x, 2) + x = self.dropout1(x) + x = torch.flatten(x, 1) + x = self.fc1(x) + x = torch.nn.functional.relu(x) + x = self.dropout2(x) + x = self.fc2(x) + output = torch.nn.functional.log_softmax(x, dim=1) + return output + + @torch.jit.export + def version(self): + return 1.0 + +net = torch.jit.script(Net()) +fnet = torch.jit.freeze(net) + +print(net.conv1.weight.size()) +print(net.conv1.bias) + +try: + print(fnet.conv1.bias) + # without exception handling, prints: + # RuntimeError: __torch__.z.___torch_mangle_3.Net does not have a field + # with name 'conv1' +except RuntimeError: + print("field 'conv1' is inlined. It does not exist in 'fnet'") + +try: + fnet.version() + # without exception handling, prints: + # RuntimeError: __torch__.z.___torch_mangle_3.Net does not have a field + # with name 'version' +except RuntimeError: + print("method 'version' is not deleted in fnet. Only 'forward' is preserved") + +fnet2 = torch.jit.freeze(net, ["version"]) + +print(fnet2.version()) + +B=1 +warmup = 1 +iter = 1000 +input = torch.rand(B, 1,28, 28) + +start = time.time() +for i in range(warmup): + net(input) +end = time.time() +print("Scripted - Warm up time: {0:7.4f}".format(end-start), flush=True) + +start = time.time() +for i in range(warmup): + fnet(input) +end = time.time() +print("Frozen - Warm up time: {0:7.4f}".format(end-start), flush=True) + +start = time.time() +for i in range(iter): + input = torch.rand(B, 1,28, 28) + net(input) +end = time.time() +print("Scripted - Inference: {0:5.2f}".format(end-start), flush=True) + +start = time.time() +for i in range(iter): + input = torch.rand(B, 1,28, 28) + fnet2(input) +end = time.time() +print("Frozen - Inference time: {0:5.2f}".format(end-start), flush =True) + +############################################################### +# On my machine, I measured the time: +# +# * Scripted - Warm up time: 0.0107 +# * Frozen - Warm up time: 0.0048 +# * Scripted - Inference: 1.35 +# * Frozen - Inference time: 1.17 + +############################################################### +# In our example, warm up time measures the first two runs. The frozen model +# is 50% faster than the scripted model. On some more complex models, we +# observed even higher speed up of warm up time. freezing achieves this speed up +# because it is doing some the work TorchScript has to do when the first couple +# runs are initiated. +# +# Inference time measures inference execution time after the model is warmed up. +# Although we observed significant variation in execution time, the +# frozen model is often about 15% faster than the scripted model. When input is larger, +# we observe a smaller speed up because the execution is dominated by tensor operations. + +############################################################### +# Conclusion +# ----------- +# In this tutorial, we learned about model freezing. Freezing is a useful technique to +# optimize models for inference and it also can significantly reduce TorchScript warmup time.

y^ zuO_sQb}g!ihW1MA1>`_}w)^hZ4+Voc{k2#)lT%Yn!atBjjYWzg3dNgNwig!TUIYFkq0XS#oqN+^FuA*Z@AiM?1Us!B@!0_F!N z?v;?esUK-G3xNC#cI1W30Kmv@hV;d7AJU7+6^WHi3dBYs$F83Jrxniai8G=vkHU{hW7)BS3` z7uQ>J5T^p}d>_gVgE&f!tvf14+!cHTN<}yF0FXCVi0fopYPVPyt_s>lH2OV$CZEoc z`+_$vqU!_HFI!cBO_zdlP6U+5(p>w+kJks4CY(IlDP=MiHI*m_pNanx4NhW0i&ZFDH0P@nzrFU%jcn7BA;!KJlDlTV3eySTV#{su=-Azq zoQ{J{Z}*qX5q!+#Ia)0}fY2xBgey!9%b+)ie^_GK(u>LqEg?+goVC(OsmxL<0;~h% z`tDH`k%v(eU~be>wU}+z9ZQga%_YC zOX}P+;W{%j^QF66-E=Rum%)}~WVNmr&mCm?rMdn*wusX)HN026y>&(p)lC(iA?H#M z@%vfQaHNLVU(ipQg9#{7P0@@1q9Ui1pz=Zz!%?_!f_uXc*-9aBW%p|$whsj$RONUr z>kg zmlVLYhL-e`nffK`-xe2(2wYP@cnws&g}Jn|t0J5nsKOr45xLn56&wbOYzt_6=b*;^ zVaRzsK!<9uPkyVGy$GsbaNMg5Jn^h7A|59)Q2=veLXsf=BWbLP_y+h{?c+1N7*7A^ zhHDdtiOKWk#s-G1Ta)2O5!?@BJU3TQ+yBeOm8GVK zqJA3D8k%wS4o7L55aj*X+1X9t>Os)=8zl#lAM4vF^Hq8 z!O66B<{%EENn>;hakm;{>8!0^wbiq9eD-v4%}LPOn<3PddUV z_kB@ z4AMV!>Xf77#vs7Sha`jK;J7kK$i6Qi4|$+*Wa*|?2{xaNIC$<-?#>WPw7PMUED`Z8 zMJs3|JSJB}D)0fG&V#({@SwbFa8F!L=Y6fO5Fa`D1waU?%aI8?tI_o(vp(&>B-4PKu~!{NxNkgS<#v$ z<>i5F;PP$dGE`(vHLUW+?!ymK);pjb2BApHGW$5dCuTqi)2ghZ60pBu<^oA-AY4V3 zE(FXZMBa(Oi=}HnR;O;(_|#y~w^IpAe5zPtE()CqOFc`(Hxcn2Va%fp5vAB=NHUTn zY9IjS916*P6PqZ!K}>59PQE*I`-Bou2+3abwY7la5PF4_rh^`jZQa*Wu9q>$ zvXaCCRAW`xsV&UKcDas2YVhfB77a%s9!<2mo>JFwj?L8z86b44DS5EX^Ee-JC?Cp_ol2R>z*LB__ZlDg~bb~ZCtvco}$0dhfx z!+p^Ad`>Z|ClyQ*$L<0Vpu(1VOpH2mwhF6}=#1O9AAy&k1QCKD={)$KL{W#=I5_CE z-{z-If-HiZ5329SVGa4i^qZzT5x!~jn6~hlE$D!@$O(@WLe~_MNQpR2vPVoTPl`Vc zHTuJqWg8$os&$=|mLDnRRl$xS2>iUB9u3iHSmaGUC1vR7BxFJkhx|G#gjY_vzh7HZ zZ)*eY(QyL)F}t}j!Is!VE;tcQj2;po$J>IDL$*@!nI3V&%7j?3N86#%fSfeLa*IKJ z#T4g627SHdUMek6S%KwHx0KZ_@|5_bB*MQoj@yEMyIv$mS02JEjoaC9Y4M6?=28_A zhp$z{4I+9!j&&p{$NRz!C8vW?Z>2$627P;Y&Ye3qaOHk9g|d1h`Cb4bV-xD^ zudg36pG{J;K2*l-G9&~yHN?ghPY?AfVz4v-FT-(exJ9m0C{HPf zX*)_M@gQvsMQ_meToS^8rbuJML9uRzVvvASpj^0x*rDW;5DgaMb;7%MC&=iL3@9}} zJpl=hoGY}H{i0bKbU+=aKrFL68BsuQKoW6w4xFnS8W`AzA`mB;st!B_R!BlSM45mF zGBkK%|2ibbfg`cIO1SR_1;qn;)hARcKT(Nf62VEp#0)`jG;(qZ$zz5M8|X#)T{18$ z;GMR5&bwoQiqv6;j*(L!XxxLohDq)74Xv}qlG^h& z6i*f-$6|#>4&k+Tb_TG*1*r(J-CG zoHpG&EiP!R=r>lPnZj66Q88=TazRRJ$;u^*MqR|3K7G0y7`QlA=?2PkqcBZ}9ML=Z zP;?7Y^heFj_u`ugE`fjoQ#IkqhL#o|oOxwqV>|oOYi@F=`s~5K1O+$2Sl!HPxYCzZ z!WZ|Ojh&smkh-es;_ZI`mXK%~P6fVhYh%UH=uCUlHD4|5k7#N#!3$W1+=b}e54uLa z+o9&>=CvC(=oLP84M{m*c6i<#{>Q?@ep;wUDa!r)j-jlS(<&I59mS|& z(cZwkrQ_heR{U5*Pg+nRxY`C3j!m3_vRS#gx3Iu$FakV#;w$X&+sqvrB9(etZUsPE{r#mVj{#XWa|f_9s=(abh&Tz=K(>C+UrK*lsp zjlGPGR(I#MwY0Y4h(dc;*OEZl#d$hw?v-4@ne;J!(A{iR|qHkwNr8O5h4 z_wox`wYs&tSB4$B93SDq3As=4A!v9B{>wsOl%vwp z#fw z_}YOdaB;J;vJS`BY~Q_mskXNEyRx!32(h$xbX<&%j%FEt@ZbSAc%CsI6Q-7tpP&Ef z9JJdV!oo~AC2{4}trg@*FpS&-O7q|ycm4e@0X(c^7TyS|<`^dJ=qLyY4zIR=rs)IN z9`*F8QlI*sjY1AzH{*#&+`;g0_hKPLfE5!};P@YaYrO>6L{eHhCMSofyE38}$9nbY zp3?(@We)_`V3Q3F4({2r=g!oXweKn_yyN3{*P8NUb>9gMT%r-S*EU0Yv3h}FolKIccr z9^N4)#*7?rF+}LI2RA?RYfhtPU&4XBKb9_y$ZZHU600Tzzs%#HFdSANz=UX zX{YZb3AJhzc!)tcxmHvpg@fpy5%NAJC%1yq4mX6<5A-dYj@OIPkw&kB5nW75;@qqG z>`#2$r{e1B>YII}piv(Ab2}|fJD2gukt0GLGeY_=Ri|)IC;xO2J16D#idJB17 zrV2UGA7At$~f zQ(E-n$170*UPc(*kBN@{28AO!J$)UH1_;^ptRVYT~F|nIdNhw5ci9`=NjAwJA;30&dn0u&JiOzP|OEDQdtb%eP-Kwyu=iK{aszVAk7(A;0WI)WH|hvU%qrnQcbO1 zq9`Fbc>_h#*?E$AdSql|F~z3$13O;NB~J%onv$Y7Z{ObN=`qG52>5LRTl|bwC3c>q zD2^to+(aoaZQ8kG$D0qaa#zuKy{rX&d!H&a&jn?-3nn|b*5Kgg_+Nh@{*mb!ijYJd*5?>Og3Qr&*{kJ%fW%W@gD7cjJr;qaZi;IvhUxHZpPy0d+(+T`T<-t!wmK z@La*`dQaQQaA5}gi3Yxc1(n;tef8?qV#{eTE%T?r?$TobJ(pjd z^=dR9tQN!}?%sU0geM*S+&p9)YN*QiGNR^>^2dHm9nu)&)Z;d$4+C*iKQ;&#)13W+YYVA#wj*S z%k%#J{Q?fw$<5;Z3$bMl`c14xMn?k%=VxtJEn9ZP%!~^x@^W9FDZZlMJdBJVSSmv% z`I7pq=yazeaH#hh3hXN!NU9e%|9Fu4!fD;nKNAo4m7As2*VUba0!Z;nP2G>7oYXYi zvVQ#~9GrPwTDoG<644)WiTCbpKn+#Y(z=D?Suz7O8ZG$Xxz}RFHVzI&5YW<{M-2DE zqJ3U52G16``y;ScAK!VLoP6&sR(c^~%dr;H($bq(F2VoUcJD3(9>LTewzfX_SdDAH zZvO2jPo7La%&0cT>G@@dthTqedqIShbBAk$L=5Xed3N9*c-EjaIBd5>RaG@u!c+XQTE9E zgrL@HJk?*u#Kd%#CuEST0vXL)5T5?x;aLx%RT6C-F>r3%u9HAOJE4OKt#D08PtTu#0SIrQ+P*0+rX$)yCYHcJG--N9 zrFgE{L)1mEpM9t+-1xcxZ1Th*M1bC4AN!!z^6~LS0tt{zx8DJyS#1vwaflb>gF-Ah z2clzbZy$L7ueGP=U%n2!{`IJWf`XslcX(7jNKD5>Mp8&=zwqT9bAio#B#dexm}>|E zZWFfq6NkSx3)}6XoG_9}QR6Z!4`~vcHFpt4<95p+Qt%H!9T*IAxoSa8-m=5i#qr3YWO4v3r7nO0FIf*U&rWo2jRe;uQ0?{W=2xtalmGK>{}XF=*s(iu$t}abuL}Y3@bBr0z108P3+5LY YZ~73hFbcnTI)&B)>Oj7rou)-Lc&u~R(Owu^vdwR2NV4z(ry2B4GD=JNk!qU zZeYP_7sh9r#ZbtDAAG_UiA(Gz6Q1<_OD+;+>G=~*zEZu#3PU>WIv-4D-){6C*cXst=D#k!;j7i3u!AhP{V)= zWaN#4(zMfeQ^1d2G$Vv^m<%z5v0Ep)*e`E6k|fmZ|3V6V%9dFFH<0|l!2j~`KREpF zH2hZ`{#OkCR~-M>8~#6NjSFI|Ezj?_6M|`8ninipY=Ma{ovmwGs~5zaOvE5pOI}KY z@~s^sP$mSts7EXpABu_1I(=nKr%j>=3!mO7er#x*j;~kza3eNESMo9D=Pm>Oo9hvN z=b|(onfMub<`}{U4l4@lll|(YrXXVTpc~Rbt7Lh6Nz_J%>31YUEPozl@qb>VqArNV zto$Ym#N0-rm3ad3mwk*O^wW)gy~0!%Lug$b8Rc}fKWX^<_)!Yp@5A!jJOrUAbu}ST z6?&{PkuOQ`e4_S@_`qrYEU>?EI-Wss;r#w}m+PqO7CJhASat`x1 z@Tbayc-Vtw$~TUy_n|w%WP_35E>ch?=L}oGg~t))L5s zs22%Q&i&9DY5FyXE+;RA)}ckhR5#4PA5C74NgJ>c-Qw|xWOxUTUlm1m^MDDydC3)B zre-9-()+;H*U!)D_}{O?(^K^?d15*bGEww(VCEVYd~c#u249o~ZFzC*%{b6|7+1Fg5wk)-R6mMA6@wo}(QHWjf#f7j2Y zg466!?-2RW_e13Y+`}FWSi!-i*8cvSzp*CQn%Gzk;mu? z*pw|(guP<;E_KR3ZgMdH?(cy6D*zGb_keaX=4{8!2s(fIcvTPMe47IMDW)DJ*`R9= zGh*%dMhbCalH7JxxL_)G;Y!ku>(JeID+|EY9mRq?>bZt24*zSue_c$M&r-3ZsXGe-)nav&lrUJdijq|_SV~>I$yX#-FuIB_h$i+`6!fjz zM+fKg=?8xN_<>Q|ea~_K@PH9x>%1J%o;&xF7@O{Vw0N3(o`-szsy-j(MJk@G89Mu zpu@kS9UVSh*c*UA4m@PxGpJizz=~kxQdIJG9V_vs^VvY**vf--6)uv-K1dxI-XscDZr{r=5(;D_I$;qc56T1 zW7sB5+Mbc*SD;r}MY_JV6Q~Cd{&6Sx^5qNO`r0scc#+~+4u|%ao{Rq^5OI-4u-rlz z%kS&P$iny9&?YfX;C>72C+a!hgG8|5NEvej!;bohn)YgZf?*U|^o_hb$bf%&aFnph zn{w{EhG3*(fO(^eUw~6V346_L-UF~&SWEgQ^C=(P<+&xtC3(tM5DIA%yW7q1nu)~7 zrDkW#`UK*E3l~sl0Y7|KA5C>PFb+HA4od&0K0*3+LvI3lcy^{}Vv=8lX{``Xy}qxZ z4ci=)f8|1gnbA^k%ph!UXAEWUC(adY?ou_V0)V95;p-K`M3pp}E8!*^{l* z#MUyPe{PcF+bXS-D%qT6j`cAVoG@yE|***CiCpLDFdqy@_BcqyS0#O8f{tp-F zpdn+VFh|8}PW7;>aT!%Bg3e$FpO;<_^kbbw^iB5f_qaD@8w-)`izx3}75a1)nm*}1 z5c>O;Ir(alyh(OG#Q8bSh$;-;Zt>4NUNHWVLUfsIf$hwqrFt>gb?si}lIKKBFxfP! zH8GG7VQ*cv3@a=tAOBphM52v*+r5)2S@-eAc`a=947B@2&9g7oV`NR-M}RX4Cqh&- zPxOM@Bxsz7`{Te+fpF2>@X6dMcp#MJ`PrUdH)XMT-p>OD9d-kS*3-j3hAR8#5DtCm z2#>?pX@|4WzcwsK3da6;gVZHto*99Cd=BXZ+)jz=Son1@zq`xYeP4G_GNc(78b4Pf zmR0wut~T-GX)9*m<<^MZt(T*IRaoG8CI1Q|fH_+zp0ba$oMe&vfg4w~B{xB0Uh)Ku zHDH%zC4~ETHsnpfrq$~L2wHS21}?ttv{o+<&-fXNO|Ro}8THYyiTmJ=3*AU!oWZ6I z;E5tt5&>fH0{Zu{-dY|_H70$KpYk(c(%d~a27#}($IQsFOBlBT_#*qUoLL+45_}l2 zN3`S^1_$50ATHh-px3^y>cvsxj?(==YB`jme}G*`8vKzUY&<>Q_sT?9{-w%wQ zQLnYDOCiihPX&K=;;l>Zd-Pwi&|-(&m!E4%QAQfPOIdZY9xTJCOXc98E>_xGc8EBW zJ}i_#d&1zBbJ`F2_sDZ?L}wiVqe!T`%5!ym^=1Bh8IDl0Hjaz_`>lQ)-oc4nK|vx^ zQRN_~ep!wQ3UG_f2tG0Pqa@0KxRZ~ZT{UYW=CG-kfP;D}u}O-%YWHx@MQCVODCpvQ zex2VqS$-yE@t(qFx91dm)dOhizx-{fGbkxx_MOhZiNe`qkAb%*OYJ`vt>%C0{rse- ztD5zeN5#DLO`6neC0ak-%TpmQei4W~XF$b(5&Ps9z1q@NR`xkQm;EXRC}|mHnf-5$ zy-95o4-17C7?f!I?*CdT2|??=BDI{?NVsX8i?b*{;;6f=6w|m!8~8osqG!+hDSU{P zFmzL1c2O#`yrL?4aS)~}$J_tlA^SM@j1CDW`ROL-G=}TTB~2E1a2G!%1K6l&Ln?1~ zR7rqUR{|tk^9g)C&RTGLfQTL7aQz6KOHEPdUY}Z0RNl;`w1QO(?Yd;u0@3>bVntHP zwK?pTpmBStW*EEF`6f!X;vbd5Vvouuqu``Y=J78?F9+ly9!&<)0hI~48@Cv4`t`wU zTZQ#Z&`m#8Y#Wot|Io_FFL+UI_-T=i(PT(c_@VXbAc*B-_~Q;Be>|(dwR$bv=(Uq# z@YBtr*YU3(+ezUUioU-7OU+Xz#WQlccv{tzuUjD2 z$wKwO<=(j0!&&RjvO}?Av%YXEJ&vn{mzA}6qN{*?Ch_ufmRU(R$jcwPI;po`I4!Hg|WTj#<{C2t8u`IkxpuU2PeMfg1O@Ft>5w45f(Jk)sn5aX~K1WZ~Tw9v@4efn`D%7?`%Jc_1<8M>q2Q-g8;fRCVzd7A8Nxa=+I4x5c7LuaG z+>Rs`JrC|E5EpZ%ZPWsKDJp-%6SW%aU!5tdY8eLQ0y^AF3 z@vbd6&S;t9(EJjH7dg*X@s4YJ{04c7l&?_2jfw}f`HIGO=B;6!B{Jd+jKDK#b#ETq zuaW(=s??4n>b@3lfw;^OyZ>44b!ZjGvXH|!bBy_o%+G~ zK?Sm^SEbav9KnuB&TQ+=G#3E~y1?527+R5qz_?N2vQ}IbD#&--jNRPFu<>O0JPamwdTmnH9PXNj6Kua--A}_QazHuc4ME_5E;9^_rL+SATG7TNFAf^; z@2)nZ3AQ6K=#;l>HcLrxYe;xMIf#*$OcgHWI5qzv+{xd6lfCptRfh9ByuahgebdC^ z-Q)S0z6W+89bwFQIYp#}+^@CNC7bW`>&h}^K{!NVmxA#d!R*^A89IrKncHkw7C(L_ zW`qTj=(+~&#K|1%ByYUum21a$pov(CJ+Lw{p_-tLi*NOKOXDVDV5_xR`p}s{#2bb) zmR_vPb&^${C)1(Y?fI*6(A|sJ>a`)z?OrJ4c&@nLF2~IN+1?1FLZ1wqKM|!J%L05Y zK^JoP)4&eo&}>htCFn=t6!Pk)W(hy!O+#d(J{y?ZqM zF#0n+(vI)b6UJ3}YO4ISYeLYla)B9D>7)7fQ#?2)+mi*SZk}bl>=cF9)#5N^$ z!bNOY(++y5GHTc~(O|WwXXK_#C)l9l>1OkX_ls53dm}tY>O>E^=3&F@yMm83EaCo( z0(95g=fy6U_a#@eyU<{@$?hMC?*=KOoB-XVG`*#_(Q z@V!(A*DgxP`xTpka==#~p~Lfa@zET{1dN*SFQ19OZ;)u9cO;@NxLezOKbHA8K&ayo z<9|&LI^)^2sCh-#(Ff^y9~}HzQZI!t=T*#}Ob4sg83%~Ej03SY)My@rmv};&4sAlOHQ%w2OD9T7Le1l#teADJN$Dv{K zYr{LFl{9IhKApI7XXX#mx=U;;EccjJgbXkWWU}NFKQ)`4-gKtMG0)9p3@+Rx0@LIV zn&Ly)$t$w?=%6>%NkiWFE{iqIA`&EqqxPMy%oC$?vusSalH)Ql&7SJD(fNlaq+WKs zO4KFg6?p%OY69a7FWwJ-P}jGoy$Jc|BHY2s7Jmh2fTAsSq{ER``EGpSOyLv7R0R4= z0kG5Mul%9tFRa%Y*gZH*!pXrXQzYR&_8R2sy5y!pab$1ix&?!{yN2A^N!?t}88&$` zXo?{MJ8A{hQbjEz`6F{R&(#r@)5^x4)oFVzXNM=y@%uo{e340G(+kiK^}?vB)Tur> z&J?v~G%R#p<-GKSRWYJ2$UVKN%z57R>0vz&e!hrX1Hk;v!i*1@L&eP!g}k`K6pebm z1m0QUSxY!=VIijXv}hWzO8mscoAK1c{J1Fj6M5`_9g&DxiCBQcy=jngR&@gSy+7x9 zr%_JghPfn*Ig0s!*je!yT`^?CIto8Lz>r>@0*@=tdwI%w@pJDA$}w|G6;@S>w=Ck2C&h+C&;Jn3^;>5~TtWfi7BuSVfby)$#QfQ&sVbM)ljDioQs9NsF*I zx{<(x_kL#uZeIuoZpGdh!Y3p`5Ld+`39Ln8Kg}{bhFi4HUt2Z*N5Xgx;Q8aTHLP7U zt`orv?bOAVCDZ*T_(u$JnA+EJWvxfZ@d}F9a}U>jwo%Dru~yC80)tTWYmMC&ckjrH zrUyykzb?%8#P>th8I+0oV`q^uXd=o}rC!Hzn>m+x9{*#j!lw z$&;lGaUEz>>C&*ac$^GV=YxAcXvx@(jC@8d3fSShNJm98uO9&LWgzX{$L?`WHm)U3Ys zQ`;ks9A4Ueh!mI;j*b}0LC*MY_>ezijoLX_K^;4)y-CoTO-VN#P-aMx7A;3_^27J9=2aNsm6?NdwAMXw!#_at_1JjZJ zLk=VZ5qAd~Y~Ek&t6Po3@uWkgS(G15*-&C*bBx0R@x|JnA?@I;DM_eM>n-fM=tuJ) ztt1rBJwLaZL^Q_iwb<+j$HJifTzjJLGrnjcS2%fFeDDzMh9$lV>m0_5StUTTi(EZ? zJBgQNp~<(WAZup6e%v%O>XC@Fw^aYat9JEAIm-)E2Sxz0&uP!3pt~H) zMcUN2GCKqAo#^v-S2M6#E&8HH;QdjvuvK;XIiN)sjUX?fb!b9dJ{+W2#oSSxfXbpT%+7o%s-($sfW zQQqH4fuNN^B#z%c{P~4jFb)sjH?Dh|54mD%_(BTp*bQAifZJM5S&R=)r_IYk?-L37 z)uL7_pY){aVOVQkBeWpI{mG=a4bKl_(n7bw!-9VaxjyzQ@-oV%Llh*npG=(y5zl)$ z7Q^>9CmMB+mj*q9SR=&|I6Q&WQ!)oM`D++ZaFGeN*<-;_I`T_dBCitTsn%{P(4m-Z z7>54rc@NlT#D5 z&jPV$v7IteWNMeh_}bPZZra zkphyqt?f^TBYYY`vzPvt>_0X$Gc@};?|d?b^!|OcsL!CNvfrLqe{LGC4hq40kwkd7 zdag99OxymB^??|Nn>sI6N^|pxyzD8%pHdbtE{Aif-#+;*n#EmB0|DecaO;Awp=&U0^GR6B<|m8ZsKT#f2U?1j zS9XAVi4(^Yt1Q@2e7XB=5*boXD0<-H$fj0acH+;98okfjjq~XF7f?bxv0?!#qWx*B_l$+9^>`8;@-J_G2@$)P zO#CNZiOvZ^kIjW2@9IDC5DA64LYqdDI6d(r3O2taH%F>Bb&W1j%W?c(SAeEQdI{0Q zx4N#_?y;K+e{3|pzWxf5+DR5XeO5Fr=aP3$9$m9keob8VZ%o;7Z);Ft>+VI*SRNcX zk+-rsU=25miri5RqjXJoYUL+^H7y^XP8bz~VJ6)9T9p<^z^qGL~^~c%vuO_|-wMew@Zz1xNGLtN2!wRpgF}JGANe>G}t- z*=zqYEu02$e;zk!RtVS`oJ#L?R&>p(CcLY;O^SQJL;ywX{d_tG&eR`eF$LBOkjAUU zKXD%t$YmZg--;l0MOyYSp98&^&!^HnD;hx6OASdOzJq7WiL+Z!D=z`C%WVNCSeC%< zYbGgMIl;UhyxeylPQ<^@fjY*V7glLso;WVK`Jw{vkZtxLsQbPU>&Wao5~u5U;NAEG z&Vo0)X;i3K%^VT|!cw^2>ATgs1%?Hy1bCcQI5lTSqYb!b^fhRsyynS*Q&K1a;LujL z5hJYIWtg{Q%h{4Wiuex4#78Ul!gh^h_b3h_x1NN`R8D_m3b8wH0R>G|uZFD(@6-Ab z`+B-y8y(8j`q!rRU05d~%b>6AFPtC%{EUE4%E`G9)Jk(p2jw&;9E%C5`E*we)djZU zHdow74uP}URrjeX^pb0Y+Do0-AC^XB?BOWzgp7(5!Q|#Oy!83NZnKhr4+h_X|1{uh zKGR#377tX2C-C!ncffG+5zOd-Wqc%%nHlXblpW6%2#oak`(yn0e1HHz;*iO!_IbU0 z6l=UHNvp;z34l0My1l6lcT0M0Y}9r*x|S?nx)2!J*o)vBTSKJS_#sTafQ6pBPt#vJ zDr>t}xKhI}+kkx?;461xf1`v17NOLtJ1%P>Lsuu#uDkvGN#|i(s&+Sa*y-(G+_^__ zZ~g1x1M%W-rwGU(SK7|Y0M{$peW&5N-ZIG*@3a4cj1@elI%C2Inco*oKG^8ye+Q;b zo@x52!=gvEWHSGi^xF>VU`T}vI@9{$F!Dk-V;a$J?tHdFk25fNyGg~)=&p|y_^2bo95ku0>!_|G*tnH+{owWMN zgO&pk6n3<+KpV2YP?M7Gt(5>x}Cou>j(hN$k^UK;X-wf13@CuabT zGxre8WBA(h+XR*{)OqyyATYS7IcG%w^ujWU!Y1Ca%ev%+o!_yX94*{VeA`=>FaQv8 zI^&sql^2&^@h-2EIpv^`@$P%Rdw*;94Y$dekM}my;!;6QMpg%bpx$pn;E>43uZ z7$t_@)$b&kA6j>NgGv;C(PYi)J2l?sK|`Uz`kgQwi`%Fy%zE`X-K8{&*-h&L5(A5e zmBsVo=ey$E6K>w9K>R=t+i6#G6ZFw1S|Qut7mXwaDeg1F54~0XPNCe1jK-DSPiG$a zNQ%b|0gQjJvX_j0{sp&r7@3K#uti7W4tMf-O|D((Gwdn=dg6d2E2gMAmHjeTtvwX@r9w+dFMe0n$OV6+H3+%H ze&DHhQMPQ3*#1FspPjyFP?Jaho2!TP;%u?>l$H1$Eom@~T637Lt5oGzQ2%u-+tbB! z^hyUX5#;0krRJmot?}-+5uZ}uh%AmiP3d%PIrm|I<_?eCG}?3-?5c8xE)J7YDjPL* zxdt^ojXuwLq4l2Ipfqek1(WBV(D{t{omI+%x8VR*&FrUFi6|l%ZNp1A1~CndDF&dw?V!lz;!VrP;dA!Q;1HQPmGN%L zE|=V*9_Kv{)VD#Og^`@-`J+R`M?aua1lOw#*hPy3yES>%Fq@;a>x97?i*qZ7+xlNs zx`;G2v~>Vf&EFfidoSAhba-wz`A`HNo}3Bx?N;SWK_iO?CHBmWr`{hy0=jeD8QTXb zYJFcuK0~R08x4(Dr)^>-5Zc>OtuX8DL^$VkEV1C)r*U&1{yOy_948t3%=pOjAkwEil&1GK zOJ1%>)kNK^SfC-tTwm>|ce-NXJlK3qSTJ5NGE*gm+uG*26(+X(Sze&8t0RdnV5*=a z4X4s0WQTe`szU9{!BE2cMe6y&s}Oa{p)%&N-f7e~(|%!IyK6;v(0y>*=8p8;PGxv< zG=Vq;^y6a3r_&#fsi^umY?@;?6gt0>MR?y`4|c_eJz21MgbpP(!hIkKm5xa(hEbs< zq=UP;y>>a}_Yh7(t-M%oX}R4vdm_Qgj|H|k#<5#O*<32Q-!U1YiBD+b2_&$y)PN|@ zdw#uzq60f8`hiXE`kB2qJ6k?5XVSApv$S*24iS&3vr*bkialrlg*JXfGz}|0zV(#A zmM2Q%!GT%QpZ6W&6oNL%xgUCNhj_WrV^=*<=rBx%;cMZ~p(6DkEX50CIR!9<+075_ zG6s+5E6=an{n&CaOlSfpOq)Mk7y7R_9mVB0(JbTTEg(_ZfOuD1+y~Oauiu1RnScd! z0S^VhiaD-;NDA#w044MebCf35!GP0U5q_Y^?J%4)mb)fF^npVS-kNz7A;%P_IaKjn4cD7594%la<^g6EyKJjTGfQ|n=)r^V^#@qT0q zrF{kFwAhhB_~p&r)&9=;8+_3dz_g!Iu(O6b4%Jn6#n4qzFr@E4BhIVD2S}>lwN}X%Yz&A>mJP1_h410!}Shk@j6@9Bbui>xX+lyn=vX$ByDgZ|5I@wsox>k6%jeoKdAbIqSA*GGQjxeqDn#eb|fHsen(>Q5x$J z%8RKQHI(KHiT!x4G&H&QlPYg|un(TwoXz1Af@Dd4{aCC_8NvEPZDRb^C=K(Ssw|%E zXik(%4R$`+&c&_owNG_qXRsqosnaV6w~N51J41@fj%N2q(_x9wFlaCt&H(*0vYM@N z2Ak$FaF!MX?thw~x`Va$(2i&GG?F0n$tXt@J$hyo(044|+xN)U+CtjelEs5Qh=GDf z*U3Xt7I$83Ud<=4#cw^szkU=-*miWtS6;aihySvzIj*clh)P|Z3VxMzaEDP!BKU+{ zPKU@Xl5u!INJ(Toa*LTY46ELq2hz|!!IPrC!hm(BB%Ew}OQ%GAG5+3zUBL4Npg!cz zn^>a;VZ8B8jgtU6=Op;us0Bc*s3~#8#I@$ZU_YI^ei=xsF0j<#mNXuX}{u0 zirlewn4`|G+Lnd}gaL`A_b2m#p3g0Hd<0b%7$ZSmk0mCb7hGAo)$^nXdm*S&#o^ch zncyZmUg=Z5i9$(&IR|yXm!qQ+wukdLVDboE5Nc1`ek`Yq*-fmUA7MI*xgp+ z3)nDS{bW#gGv<(S;kfxxkhw6jL)pykPK=R`la*xH!tP+CdKk{=Gd9$+Tg_j-#W(?0 zyGxcCHsxp8%UU!aP$zp*M@uc%|7YKi%nDv^v^VhwOM-7S^KUP+hxNrccdsc0PY*uJ z6AZEcC~{uFX-Cc_QmQnPfehP)U*w(NbmV|jUa;B0LWM7LYZ+|c*y{jZJqeC^P)=Io ztnGG&aIi@GYCy(NMTbaxuB4%%X~MF=vww43eBln~xnhvp9nXKWN?QeuQ~fOv_lZIv z@55B58fUl$UGO!(Poa+K#VxzLX;ea^r`y?vJF}uAC1Mi;=y=nNFLmC#kNn7ONeT}t zyqXXgY`}FW4iYH}Mu=Aiy-Qc(WUNkY(!Yc*5O!yO!p@A#Ht_^?NP1lO)ktDXEYR(M zdawR?MN9+JEF=&C0=Pi*EZpZ*C`bg$QPC=%d7n}%sJAaUef_* z!1i4d-%%k{3EW^)rLWI^tTE-ehRzF{YeC2A`#G-ww;UpUtgvKTk8L4$E9_kbqpSrB z);%s_K84Ze#ViS6r&j!QgbBO2IfCeHbbY&j?lGkO}+kx z_G0nj$faShxM?!E#}yp(iNgKe-VO2N1R?uR&mP*j+2c7gl#a%fwwwd|M*96?_Acjo5+tEVYLQNl?j{%?-_e) zr67xf+ zpB`vC|o)WY|`XR-&VK!9BN7u14PAPsgR91l2sg9-%P7;^y~czO(J-srI3G8Pt?A9z>5T+_bmlLf+p(y%3GT1Y0#fPR5mgcS{4Yw$7qkL@we+9qQY zeH%{FFUdP=>$~U%5>aF-?YM0b_xFoE?u%dGIx?=22MG&v^OXR8&gv8^f8%YgIX&Ex zJh)^jfaq9D8hs7!SoUsQHe9>;`(*a_)5KE^AZp*8hrHiHvBqtSxd zo&kpB&Hl$mY{hq)k}dDMQd^AC%$sos{jLp;f96P`POOFAdTs7G{H33~@Z+*c(9n-Z zC&jZXKwWpFi7yZ<4MulrA;%C{NU%P|_hw`~dKLqH!;N;ImV`<*+UI^lY7?tc&Bu#+ ztZ^s~Uf6&_$LPDq1D#MVZSyMuMsAe`qWr;55>+^d-$33k;|i>#^lz(;5Ngx-y>|vV zH*wXkn1B6WT3u(+1}z=X;m?!cN4$-T)Bsy3J2phVD-V=NIX&e1w%wKzqU-+>bFKK*7z?fwq$pY#fIXX!%b;~o3AQ1Uhn%}3u4h*`iBkqn%*SZz>m@CiSo zg~!R23Rd#jd!gaQluu_~&p7_m$M2xQ2f}<)=BRiN2!{bZ??`jYDfE8Wj4#rjbYKTqn)>Wao;Ju9n!gK|6{C{tx^W;J~~@%#~S_Izj33Ezg>hfw>mI9#=M-K{R>nSvXsNit1=?_yE))uf1NO^BqP*y^0ByXLW}JR7b# zSQ@tJPR?)(e6c06i(I`Z7Q8>{ZFXTlf~>>SM%HsRxoDf0aUukHk=n`nlR9*M-}IIL zu@+AN0dAV?(2K%p0*_~#a;I;ZEi&wdJZpfvF_?m-6pjVF0!Qd!5CDoNfoaHQL=q#A zXYbWAt!Z}ISO*4>*Ri)o4{z6w z@Y>woiS-{%3WBQ{I~ghB;swE9w-b%x@yWrg=x-wDU*X#z_7}IL;UtoS%hWdLt#NjZ zwxt4CcBuffM#91%^zB@Xi23;AZ&dB#MKNxe&dAPTF@LBnjyD=i|2A!%*X`&4_b2{Q zEvB?DH!?QC;T1!=ksv4~kIAOqL!7HiCx70rCzZ!O*;5kM&6C=WjaQ0hp){g1?fYHH z^M*~&o1lVWP|{;WKraHr(3RMmTXhXARzLIpodJ=?1KZ!kJv`qdhaGEjSEzNW=_Do&P&fV)3Ro-iYa)ss$5 zdoR`KDjbHCyNNvLE3xO!tBZQzBZa+Yp%o*6_(|SF;r3TF(W7nKzfnGiTdgfdG49vZeP97=Dv6td| zoF^L%Z1F-lA2d!H`rXRU*EPq{F=9nppKBX=%dPZA;$Cb}Kat^`s_ft4;*R!17jT$2{_54_gPTB|~lDRG=FcNfdnJOe+h{dY%6};m?dX&kQ4Lm{fXnaV@ z@an!v&i095ZGu{)k~_){drtq3d%idlSHJ~JekzPr%&`TIYwHhO znh$aNtB2!o?L__t(jR%Ub2r zPXcogw+fonrK4p_BZjK0)&KJXCQCY| zwAU(E>A_s$h+h5PtBhUoG1nv#JR$_$NtMPobJFUcwcO=4Mk&jwnH>QHNx7^PSp2#w zps71VA|{_|u$=FqUin25yXubEuq@;j3HhG%1#TF|5FvzAc+*4&f+D+q+)1$oz%L)| zNOz6OzUvRLv%YBIa;ElsvMyvEbxSd#whbGU%fT;>Bd(;-zs}?$h&4bK^wg$h?8Ss8GRRuiB&~*t3$f6&n@exQg8U;wQ%F*)U66j$o_?C+a4J-XJhb;$5g zjGC{yg{MkzlR;3WFRss^M>!Nz| z=^;eK9kNmH@R0Q!0D*@vPL`bEwgQ49I$GNd^Mrluvo~kCTC(6s<0iPLwhSG0S`3l% ziL}zapEP8+O-tbeLWAwuJH3BTa~1CMT6sw|>wr0TlqY)uV9F*RPK zxYiWqh6?Yp_&Ur7-sk?P9-1xsk|tiZxfL_*XCe}9@UWQBmZa(b|)eYTv*p0^9HY_G5-nni|e!ftivIgK5Q)jG+(P|%s)d*82fds|gWpah_ zZOH)({{R|p-9|f>d11Oi*b2qRKa76rl_(2fzW9cw#!7yU^P=fGPdEyW13nsv!ASiRq2rME3SN6MQV=ZBYfQ-~O8S?Q`c( zuJn5&D_$JC)EW~sbvuJgHk}UWDtax0&<(&p50&Tv#qTwgH+)9UD>xOTpV?uMismNC+~VsRR9B&e+c zy7gBKnTyq#UL>>GMS{)-@wU6Drgrg3C^F~syPsa>rgem0WyVnrv4jJO6>W!p3G-C- zr^)~0bi+KDKpPXBLjpV2Nl&S9-oV`7lE5PU2BT-xKJk!^QI$*n@|vOirSf%kJLdjx z60cEZW^Kq#&Z@ zuPv2?Rn-_#7~4|`vXWO!hK%F|`5fd{ewHiV76s%Co{YG>cJsG!YyiKZRm4UC7@d-P48Zvod zSnFf}b7F#b)bEpd=gy`MBYm9jS`q)fyMb-&`q8JdOU$XLz>exFvQR2Es5${XDI>dU zl8a~upVtfJZ!Fy%Nec7eoPC`N-Q(F5#er@{F+w4}avjq}50zG>3T@!;BR|{QS+2%E zTLND%s~usenpLL)S}ybB)D|s~q4Q&UWaIs(%n+0$@%EWAA-_c&ujHC(tQPR5N#pC?yT6ZxS?d7vyE;3L{ptBB>(0Ew z57_|F9=z4Cek`QX$sjCZL_R5@^4MHA;>`0QvsDw~miSat=eMfgsQ$i6T@&SYJwV5@ zF3pTAK#1<#GS-|uFsQbjOaFjnS9qs+H!6ZL)ZmW^VPSkgT`hof4Dfk1yU|={iG*?! zs%n1?zuCGNp=_z2q-X+Mp9~Y2$F*#(lJUwrCVvGYve|a?(Q*Q45V`-H)6!I&vP8l|M2|q z-4FX^$F6(FeeXK2>w2H>^K}|TnD}r&6~8vi9M<8KnqH65-Yy018QmRk(9-t%vRwJq z4Wp&StYOG0SZOaBJ=Zr z2HNQtJLUP9$*+*<<>3_Ny((I^9fqE#h6bx|WO$B`@g@-VW+9ntW2%Y_cS7lwOEz>q zM*NN{cY^J_>kfPhrCXj0K9!w!{*M0V6-b);Z3SiLiK1sib0M36B~x)|?@cvcwB>-`V0^UfGHncl0*cO&Pru9*F~`EJi1x zZJY%g@?t@?!ViU?oK9ygUWz?*nyErMMv8)74WbN5>gXlng`QI4o49{-Lfw|aC2k}; z<5!#z*N2qT)mOKVzsVl^ysk5Y5YByqG0W_(&c4L6ijN&PoOW?FPj|ZCeGXT>qux;K zWT6u${;^cRv;T_+ROw?&b*O>0rrbB#5Z@03*fb|83bdsOexAyVN48jz<)t`F-MBt~Q@(*>W|0KG5Zm7kXX!w3Ug=%s?#Y ztJF;cddxW~mjK`NPMDWf#FBbgr1u4P%qnK#*dM|ppXH(1T>SG8n-KJAd(P=Q`!K3#?7ASVUI%d3gtHK6rW$DxXPp0>H#vL1 zAi;??HMY1*U0$k@ITL#5ax}U4g9#8P#4>I z8{70ZhVhW(@Djk2jf59v6lHb|UcWivR$X`8EK>NwJU%z(Mp2gZ9=goYnDiAW${Iyh z%pBXvjpOz?rZYVfrk7)Zuk5Jb3D!Yos%+j6Hmt)+{h7G%am~1>76!_4A&f($NL6Rp z(aQ_iYxz9Wn&}ckbhI`Am+wsxUz5|+aUF|rh(#q1 zN&mvt*c_lx)13A4mmije@{oxxr@1t;uyuL+F#eL0+DfUyx8O8Hvbgms=84^?3WIN9xA0u`MELv7FByCnV4mE}R~7 z7$jFJmjI#trPpzFJc8b(gUlvhHl7hulLS@Nn0ELa(8EJ&vJa=^n0PR!*4V7*ntOw3 z;zAKJZh2Ixw+Y(Mnm*HN3}%9M?Zp|q&C~ZJzO>2i?=;-mi9Dy3$$$PTbc)ZmuVE>h zf>I-4>6eJz;Na5g3&tpqitbc?LUk8FfC>ouZ z#z*B^iyrsexRoX7+*Xq?(+dl*r2%7;*d0F2=E$xk(A~>?wUEs-_moNo7BjbA_}WV~ z*+fHG=Z6I2h1VOin+)$sekq2-w6J~&8#mGiW0@n(A? zF>a}RcEM%lhf~w?iz@%m#~~N=hCBLVvIvt-w8;K+_~P)y=W>S7>Ped=DSSv8r?5J% zs4w9Kjo}ZaR?!BNx|b>owb`&nA*M7Dx2&1jZ?0iOOEam$=Vl_|-;)tjFm5oq+4+N0 z?AIP?I~+4Y@KM|#qg3?gP^Cju_Lv5Lm@;_@9kUdd*Y8_Djb`SG>w5MEq|fVEe)!#Q zK6~B{HTe*}bi3A5D}0QrQXKCnv@}%ycN`&l9Ol0E`oTjEVS6XkhxgZk4+9^x$Akzo zBoAM#kyn@)<)(HWtr)c5V?eBy>Xij8uaxyAKOCtMFSXQk^_YxPHmXL;qTO62RaAxd z>}oOJ_FLa2(|A~u5`LIpLKa}+DnHLK9n5Fq#ayL-J)9~~z!5BhBGHJ}s~XQY}jx(fM+ z`vp8k+sD=l?wj|T+Se;)iLN-mDVJmqR9J3fNff$@Wq=DBb#>a<^~8=~sUiudUK(A8 zKNaNX*%>~n{b!x>s_8(x(35)?c0V}d(}x}B(P1Fcoq!+aW)b~NRpwG zBE~FY#^}a8Dk!{NDJuY28&U`#&>Iyq0M`ZuWbvi&I2Tqce!P41OuK`Fcg^iW?QKU{&Xd0;yjw4lFYzY6{|NgA z8*gBviCUfIDcbLI>r&C>&L-{m+i;P!*eHhxdx~*BoTjqo1m!x{N-%qw6Iq7-3$*sO3b?Mfq&;S<@1|E{nD;W@tq$LDW*_nghK$u& zdY`;Kn0!%C*?J}650y0n=?M<$;UM(=y^|MgWP_PesRm&q9q`OhS>sTDrx-NUDI@5k zrC67ibil3Clad#<;O6cm8On*tvZ5O5%i-T#;0TWxLvXMw>wkIcDjJ`#x_&~o#q{Uw zNcz`zn&@bty``SC=Cnq%lp|$+^x_!z9FMMrncU@4y~ww|du`tO@&xR{ic?apDDlxD2k8EdrRR?Ah{{re1MbC)Z<8`6iO9(b{3`n`4T5q3=f4WZ0W6 zWj`(7!{f;ZfckrI+)&}*Z<2x$u#+QBHf?A#+f9AT+?(A@(&Krg?|lv1AUC?Unj;>x zi||sY+6Uz)*axFH${%XNjt-&`Yp*On^_ggy-T|1|a;1AeCmq+1)1GG2W$Sb9Nzu~X z1s3G-S}3?n$(7KdCrvLBO0-L+Zgi#Yy9|;wUPMdoWga)9UeN$tPWV=amk&n(F35o= z1s%?B#l$lhfLO+JN41sK0HD~dP|SW%-OqiCY)p?uGG17Ul&dtHKC zb^A}f*4o&1 zc69N<{PWYjGhJ>ZU~*RV*12@hAz0@AXe@rk5!Q6c-+22)#T*4KvbMv|opaV4LK)0r zs54mwEN&}NrkHQ(iqOu=q6NO*HrUA-elhxEC`!Widt55iY)=CE@^F2q<8(da`xD9n zCljVv?vy{%x8VBaWn)2YxPlB{MsWRVvTT=LMoD5GFRS0qzFFDa1<7-4M7QTp=bxz? zK6UE0ZnF73dt@`SitqLvzdNBxRP-)WggjPcb#^t5IqKJA{*`Y(IM4FIwg%s!Iu2{f*emUseV2n;H7a@FB)*6X*R=<9Oj<|n=Ki@iZoq8x7gA6TJ`5@FzKm8Q|y4LtL z;h&A|pvTck;ZgGag7L{$?U@$Tfyg`Fu8@buYt^+YtP7ATdZ=au( zQqXu--9*T}7tnYMDs$*z{t)nMNA zGhP}K<{s0$q}3cW=cnXUfO8LIt^{avRQ}s4{?4WZZ;@E3j&ho5q@+j(@GrXEz=3vfk47Zj@3rfri+Z$kN{1k}7{ML?8r|B)R|c*vhuuLpT{Ibu*49rf z1&fK(Mv{PFvmOIZ@tUW<#d0r2C@{Dj(BJw zNm`gF8#6+9s#B^$eQ&SbsqRlF6-F}NHy+fdoIbzvxd%cjdG?q(brN`Gj;M8m>*3=E zm9Kts1uNq;2UA!}CXOrW9`uXS1OqIvg+sG!Z|nn4uvqR1M>-OfsS@+fCYkl|0d>w- zlOoAy_Y0^Unli4}h%ZS?7P2TLkf#!s{idWmRqhTyZoT+8dhtx{o~&r8tz2xm+e2(Q ze!2N3t|ygiYR7$@)$d$b>g3;7vEa9^ZP+n06)zr;%UiW+d+IWyZo`CKroKb10geQw zFuu(PNOsb_oQdZli*)!I#vv#HEoD`Fk?2SCy}1 zEaOT39Dj9r4rv`6$-M2@#;BM73>+{Lvvs+$)j-YQB;@sxwC(Yf3nX&r(HMnU;;a%M zZ(#ME6+k6tw_;&~!q#|eQqeh){wOERx$q+&*VZNPlCgM$cF%99+v!V%XZVL_@(s0h zLi)bq+VVk}xL@}LT3EeL^*dL23V&_AuX&>TyreovZ2mdq_iK-5jkV0zPV!**YBw40 zqDDr936IRR6)3ge!$C>=yyP4Ch`W;Fzq$1PeU(k|vc6sYL`&w-ec<5$qPornyDC;{ zRNWmOCXt;UJ6QVehX5fQ>dKvefd1QROoCjwDeAyx?36Yum?%;XccO#l&Aih^?1rn< z+x+=Bxc(8(h!vSh^N0!CJ!>DPm3T34s2F(8{z5oQ1DV+G1Ci-eWsTTW(p`%;#_ivMQ_s`MgClz^nq^#rWAIH4R zTu8J*<>c{iWP`)x>KczQX6ew_1|j z*Www8E+Y7LSM(*+HN;~ zo`= zUl{`T3iXhy*#Ew8Xjf$qON4ba;01BcUZ^ZX)OoUbqJOFOScMYVCs3#fZK z{L@`7+#A-4gu^p!%gR|FPqy3DYJgt}I&A&w29$I*jvS!@AKIx~sUZ_wM ziJg-9>aRoAfEE0qSD@5<^<9&@d_&#&aU8qPood*Iwr+=$V8HO!7%Qq%g6;0j^v}Fo zB9aM?Sp35+*>AlO|B`qM6KA0}xNt30tBBpsl{az-3psd{xHXf+^RS?pp{U;3#W;&9 z&o_A$sgv9Lynnv_IfUbXAC;tO`}k0t@rvrT2YA~n!}a8LC&AwHO!mvJLq>Pg175te z>5j!lRCZAZDYFJ#kCs|eNi#e!N+^?_mZy>V#)Xo0C||wlcuY*~nflLe*&Bk+po|TP zO4>4V<6B7h40|l--8chM6!F4INv<_UjB2cWyATus^%>H;j@0*Y=kru})2z4(+w5e$ zaqBLV=~@1GHrVsKKGj`{e*I2um!_k0{y_43MyFZL^CM$qhp*0WcPSe`v6p)NkwV7x z+lp?ah3g@EK6jD2lK*&7p@8%qPLR5xoZjD6UV-i>r`)b`zRrLpiwoxYt();@v9du8 z7K%eF$Z0$`-`El@@IiL;{GV^l@i)uozi#9&Ja1U{RB)wBx|}xs<55F-C!f7=O1tXh zA zIA5op^h%P^G2$zmZ2Af5v#MB*eIw@iL;T~DuS2$;u_3MW(+^dG=s946Q;^|$eB*i} zz)nddhqK&PX@mGTpX0UKaXL|qcHa-H4xsm0@PCeR>W$cExQm|iI#nDnJJgl|EPc%neSxk!Nh!!0 zOBOJh&59UUwq{r*;#$f963a1ZW~CBL84V|ZUDqfe0Hh^sUL3*Dod$O>^qtocFSe7d z_GRNQomPp%&TLR&xKc4O>sf*3LQO~UREC$8u$bm%^V*P6dU?HwP8$1H*WN&kdm98DJb{qBxEtw10ct3Y0vXUD?RS)H#=h>G2RF(>2gt!iws0 zgK<%!SbkB!slMtk!q7-S_tj_tcXe_8VA&g;CS6ykiEFji=~g-SX{}I+Et9OWnX2h~ z5_K}+hIqdl21qPoh4F9n*#X0J<+b^0c-t7q8t3RxMW&&K zf8?7X15Rm|OI{f((Rp5a$W31BoJV5gpRT4X#7to2l@Iu-t^+$SNhWLkz!0V=UtG#M z%waj-^I8kdYx+qi>vjEKW^01ONHhz!-ZNLdbJ>w$J8wO6TS;xlP*2{kN!u%M)>8pV zdHvBQ(m)&`Z965GZLGWwdg#m*qN^o|kFAM+JK7=jfWgo&6WwbC4}ItHX)I*7gGQ#* zpw>xncX*Zbf=L4X9Mnt<=i+RbO-Fxdgh}B@2KVHZ9({VAGog=*B_0 zoovEyELmmMMjJZJLAf)EKcUmcW#-F{(yp@0G6AZ{zYx`kS*u^wH%i}!fJUF}aAv7e z6v5Pr2t```M9hU6^!=ywVmcFyzF7uSVE)dqP+{d7*kiiF7y7IVkz1y&-9jsBq66^~ zhQGnG6kp9zZ&`R3`y%zU<|S8HhCaR2Sw_TZUUvblKKl&-C2#M56$i1;7Y?$mu92vb z7UR~WV&j#J7G$v$f?$M_>^)XGNHanA4)v=UIPSMA$_Gp-2s|s-G|-Efk6E#9vRsrC z=iLC!%uRlJ3$f~&BC)9^Qbj>wrQJu638y^|%;+RD(u?Wy{%yBQco zmJFfcV|J(YOuw=;bIHUh;e?Ojqlf5!)4aWhBNl60%R5ouWNrlbqBFT_j7~`v&_+(6 zL}tZRa`w5QzAu83zQ3VrSPb*e8UPaa->O4z|ZYBR{s;^OS~a8 zSRHOVAj+yCnH!9<0Df7w(?=Y$A_-X6izrfMhcZR%HmGBk+sC$!dKDtyrF$>^6ofHa93~_k_bAz`5uHjI11bIG}{*WXM6BC*8E%!Q>M??&CsCOiQAcDEN*adAf znV{2B(DiRwtr3OqKSrXE-dQ8}@^3`6iq=%d%nJ{1#V-MKeJ9A3av@V2|Bggx15a+) zR44aFQE(vW@JL$t&Ieq)gePB_+WT7P9YW+m%Q+RY<&;yy2UM`7ZARf*R{Er5%))?n zjoF~ewdxuR!+YK8hVjNS2wuSmy4C>qYB{i84LmgHzub(O?62V~pE+MJ?}9dMO*8O7 zhk)fx?9BGGq37$Se}mIxvLPbt#hEjAI@EO!hO@7b9A2f7=BE<8Ccd}@OxBlv9x#PH zli4O)4OiMlnuZ;Ks4kpm^Q?7#(}X%X3OLg@srj3*>$xbgY-YDH=QjMr*o}^Q$}?(x z_bqOMeKoWrI)(C4iEMS8Op9Vfi`M1uvddvd{4pLuRZkNF4i}_BIn_0Bs+)&>OuK|1 ze3L?5950l+K(1dzFa~?=Sbx zVr09ZMCNmeq1PGRUTAAH>6+cV+0{hN*@VIE5n2e5#(`IM{mqwGri*?8so9Ms9jcjn zE}Y@jFKL457LHeS6IbK1b1kZBsKWKth`Bv2CEaWnm5f*Cl(ieeo3MYs>lpd+;^O`D zCBA?(TC`clzz%2ro9ltHP|gAU>d^R8a2`;Zexo_;^?2y9;oF~Q5vBbC5Iyrr=gzKt zxKmNHORxRz4z?ZiY>`v}?5em6yz8{snoJ-T(Y54-U$i*yG=8pJc)=^AL`EC4T?zR8 z{J2t@AtdU~ENi5gv1nrvltmYvu*bA;5++%e4=iUO_ReVDwxJ5P+|jT&{mq3>HFroK{!uru>m-_&79JkNA*-k4D;X7xL`jWTbQE(I50_7SCKAhDb7szxs@1i-a% z3Hwfhn^8yJteeCS8SyX&Iff7PLY~jml3t{Fsyowyux=w;hgnyRV&|Xag{yvdf*FSe zPo8B(fX^x~eh*-qyp9car)WqP#ACymTR@LM1*%ECK#qC6bhzGtZf&iB*tWaP)#{^h#a#pAUyc-zEhpA;?rrTEf4+8cF|&rhXem6n?MF)+*`d4zQdG+8jI?v5g5{Z2VA zJpVGqGw*AcRr)tt-Xg5w5$>h6N+jRJ4FPO>_~s2f{6k5TT&D4geU&Me2&^hOfW6yrXj zQC5dpurP>mkCnBqqOE5zxjacW=-T&3A?8=bBx-{|E-%h$y@1UxcA~yQNcGErGm>$< z5bDxcxiFI9uJktjk*gq|@qKC*vd5Rc9Hr6Nf2eKNul!n**z9=2xKB&wb@hDdjzz4f zq^uS>C|WvihhP@Y8ldM28xWM)+HxsEVsx_bio0($UFSa=i4A%27#~+GQEL@le%(%L z*>$uT_{C=qQ@KdZwypGl5kDuvT=zLf`lFn)Rb@`LCM1LszF2P$FnaI5-BgY#cD zjf&y(IQD4(z2@Cs(C2fpbg5@lci|bnx#OHAA$V1h9di}tzIBST%il`c`ni29DloFr zFP80pRNZ?0@#d1`tl}xQTMM0Mw3SKr{?$pHDa^l~sjISGLI* zR=+{KVbwpYo^jMCqq@qXc1c`a2jw3VmnI~osgRIz7J(^hU?7!OvW$N1c~be2mE%}U zHYwLtaJ3^0xAY(`%;WOgOKX#aj%s9_SNd@yBpbIjT0Usi30`_Lwb`h4bX>%{ZpE62Zs{AQK_|@|?x1u(m)ss{t!k`)YE4H|9NF?EGym zz}K3J-hFS#8R}CmDwQD<*le4eaab}jz}^?eg59zfePW!7em|V12+4(S#(&=mxO{> zHxd6@@KYvdgLcx(&Bx1GC*SA=4M(Nj%T&-!M&anbJVQ$3BPPf4#E5>&^@s+2F*Lu+ zKt5&5>5xkUdZyyzS1Law@ng*2P`>S*zyX2UI^5;_Ga1jD-qcYjkQ0pP1P^1Y-)ArD zK{J^wd>&cf+Fr=)Ak7Kio?e<+#K{O`w6|=*vpS{~iTR(L1nS=M<=1jGL zjL#`2=5Y6T<7ZFS=D}d!8@I^|{ai(OKZu)8}#ssLH#^4Mt zzUhiC!#Kx79_3AF>iCX77cI#6(+%TM&3CVt^TiYRX4Cj)kvX$WCb=l{c(Ink9lH^S ze{h3D9OGyrupBWUW81U{}lPdG3r-eHTxYK1DIRBLIKMd(yYtKX; z^K~)C$NiglAnRSpE>#VL026g(kj>xj@B-&gom-j3EM?9deU}&2Sr?@y8K?U(fu?*0 zW$p_g41N$e){ zt)=EWr~oW#)`Xd*tZ(ur=(T~lPoBBYsy<{*Byf|wj7|8!S=A|VOfDH9=!Nn4MUuqJ znOqvR4a_`}=mpGIlx&rWO>=Ji)d9im-+g4DU|noY*7`Aym^tO|5|!z_blXd&#(AM; z0Tp9gs+9BSz}9tIBbrvO=M3_$Rf1m|+;OVG;5#k_bXnK)FuQ!_Vf*t2V<2=bL1r@X z(oAO_3v9l-Vq(hly;G?+`*~C6`|nkCd6&6)z9q_ig!NqPNYQC*V){${{lUR#-59@8PzTkVh^{e|9pReUU8{74b$Q zim@MrKyD1oLx9cwFnv&nrIeYERS{n|WQT@yN^z>tXEC#hU?{n&}b~UB$2jn0~)vy{vMH!JMsU zd1+X+9A@lpG!t=d3zF8|k)Cx248By4|3guEpq*>$&vLDUS-xAGcahYR2_=lrdIs+C z$(Z6odz%)+?0%JPsgR4e5smc8A1b@4cB{qqQ0%&Yl3%2f_n8UHJn2y`o<+}CJury^ znO!(eZCT!GpEKj?=eQQeW;mj?sgm)hHw+z3xZKaOa z6)8Zrq8a<0B+BRit~9X$t=IZgo4xUs4CHxht=gBm#!g#w?G3_VCjzGO6Jc9f6=l-}>OJ3srEVS|YU2q_WI_Fs=9j`!;Jp6$FbslMtWcC@HNYYoG@i{?!sDok6;vmi1(`U(x|$!Yg570P zL*h~JKQOzP8W(gvN#Giz+D0LsdlH{CS32YX-6&=WhuCZ|`wMz*CwD9=@B)RejDZY# zEV6CyOWWT(x%}xLP^Tzkq(&bMavc(7y?`#92^_yQ5w(x`8k)P+PKJ_Wj~2C=rS*VG zm_k!S`P0U$d-}<@292s;Tc5dWWQk!YtD1;}1_CpwImv9ZGh&;~Zm|!=KMQNf;w9iC zc}EmUT*)tg2XB^+4Hs+IyY*UDqDDuwI@>cY!mY!a?!85^KEAg7vTiJ=v1~4EeL!Ev z#|b_DI;n?RbWKR_VomEky*V(@tZzalc7ARN%&p3}s%NeS&CQeA(r3;CuM~rQ`tmvB zQx6JP9pQ7%^{q+zd^QoSbXo#ouyHFPxst;zz5LXYNU!ZXW+2KEit3R95j!4>{QzVj zPAPA|;QfSM!`LoxY8zbhF?4k1wM`AJf6FfvM?4MvGXrtSGEHjML!rw9bua^lRNDV8 zM*6G}@%ZGqB{km)oa%=tH@sZm137u!Mc|cA9pebPY-;I*f01g(z_hBJ#hMCmJnvV| zF)`NB83e> z8nm40GlZT0SlHA5A!x6WGJa3hli31-*FTO+LbIH@se8~BEYMWRa9~U9@hc8EDFJ(1dO@XMADM>yB?ZQ32fCix*@aReQ|nD} zis6{}Q^T1^qi`AT9CzHQBVZ4JY`#>q5t1pM24Ytgi}VuJ^=CJd;6hc3!EoEt4&)cE zs5*V!PkjZf2(OxylK7Ft0)Q&NixH>aB;bTfK|r*0#$4=aV0mdeFE;*6v*kku>by!~ z-mB?Mi#M4EHFry~%vi@gGn#32ij~-M$(7{UF>s-I&9VGWF%Ig*e*kCotc3sj1Su~C zc3M2%d2>onaVcj-Ip!H0FY{5ey<=VLKz{x`pUbiA9f#ho&P&|a4>QJX?FDod=)#_# zhnkZum_wJ@vooOh)|hBsVSN$(9}{SD=!+J|p-b3U0YPbOb=Ni~4ge;=JLH1_FKU7j zl;l)h1W};!5mU=)TEot`+Odg|@0;>qN6-)dX&?^~ll60@pW`8C2kv{+r;GXBi_DUe zh6@7Pj3u42Vk_K8za0Ux7$38*_1;^A`34ku_LK*>;D^Fx);vw<{RKF*vZRq3BjPtx zi@i`0g5vDGXi1SoD$*Kfy=YO+Xd39MaQZ>~+{;Nz0}-fpOuP{O5#;eEtjySpe}Exv z^OLF>tz!{AGKpV8qp!5yQP8ZonGI8L#$JLvU1A>}PL5wQSES%QQ!}FaUb4z>syW0$|{%N)-3i2_vUM#7Mkat|NP;lPiYblW+}Ju-Q+yJ zk`9{f>PF!VKf*L<8jQ26>eoKK`PiV5@IAD6D|C!A{+|L1aC=pvq8z&JPXs=H;oc^9*;lz(2B z6k4>HgAx3o*i~q)*F45tbd{`h(uDQdQhtPWBdNiSe%6Qc$IS*LljN7hGVclTDdV;S zu^kpKKQduZ*23(y0f;tz>3h6=_D59RVvd1833~8GR!&#%`rfWY&4UU#-h#dC1FYge zQkrsjzJ@+*2T+{l6J4c`;t5=HP}G_)cfvx(LFL@Lzfz&BUD`2ocY|Dvn3JfL2T<|E z`m!fLdDoJ%Y4cAM0yci(_wHZBO@8!on0mYQW)~B_;Ff8uHj+TkuRAV^u_?b+d*_wD zascfPzl=Kmlilt^=$`wZx1+2Ne$0sp4G~!VdtNO}k86H&TOULoQ2jzs2V>h8#y0F@ z&I(N**gPA|Pgz0y>RH{-{MYGx+$LxW+`}656-oUiw);~tBb#-UzpRWqLlW-Zc-rE! zyz{miLy1wkW>9Vp-3`1V8rOm|YQPIy<}G4$8{6))&I)=pK|gK?O(P$WCoveN8Npe=T4cg=-I>^njq%CtQHx|m!N#Tx(YDVYy4ij0;Uy(KWDuz)iYr7=#18cAmT7RU@>Fb_6|_ z$FKNxW(eXgG3nDP#R~yzBu24&$#v&hTpw4~q&y8Ecl~PF$JyT|r>3#F3sMdcg2*-k zyddTITZvGBWqmrbLo>kN>|cG+Or?iaA&e>i09v#DD9d}8*s6mfFG*JpXeR7jXATI|cV zLCEa#B1LqTYArH1U~AZ1gz(YAZlZ(mTgb5D-FmuTv|!^uxnsj2nbS|To3*;lA3Z_0 zlzqUYtyEGZeVK%WWWV;vl8hd6ieD!%XN~UczOyFE!t5FTTtE3zuxA%J7#-TSKgh0GIcY zo+@eLyl9v<6ly}Zi}~B=6(i}>V}#qvEHVn02+%Q{( zNf{M8m;eunWkcb|ufvl{2xTWDS4$%vJ?jz9rvA<`kf@ANXWr$QtCbk|PNNNu$9U(% zW}2vmY3qS*il67MYm5B;iL&6ewNp!$1ky=qkt$Xh@gp>`z^Jk-YRMA-WL*_pK{0iO zBE^R08$8m`WWJcdQn!IKFQN6wZ3`p9;8a;M(yHK~yt zgVoOSfPS6=`{B8_V2`wfUmkB)#gyb(U-&WSWQVr>-HTxI*GP*Rp1uIJT=uW)l_WJh zcwz!@c;-}vlssFHW_rM{{0aMbhI?Sx3}MP9vaO9z$U5(gvY%1apfVv4jVtgb)HAN1 zW7L#?fy78Ygh#fS{~DG}TH*h!gZo+8iC3FF8f?C7M0fR-?+irf2X{RHH#{<@N6m2?B<2;*^zjPLUrVHW?3mw$nDQ+#z+aksJ&)** z<4a2$5m2Ajleha-f~tB@T=!9c{b4OdG{(tp;<;WK(D|JP+;shv1Ihz;NHWIVdBjwN zfv5UuGXr|W*{Xc^NE*veaQxekJM|B#qgM1 zY8X%*llu7aIM=|+YkMdkThv-JeDHI7={MyhF8A`dvmQJNK4A)VT&#!eUS8vKTMw6q zoS(z&%st~|elNc@lJupKQR?WZ1=N;i4u>Qn?Pjd40&B&=gd4+-k4j=9LYbK~(nS!r zn(1qtCQE?jlesg2Gyjs6aRgrW5O$UV|LNxMIZvwGWOLtZFKI&|Eal?j%8&AIeGTt- z7V`gVu|M30EJPP-WTFhqsV}=2w^S~Aa zz4=B*z;SxEJ9{`sYDRocqo*IFykJ=txF1bhHmB6kq4}X_t2o(8GTG{Iz4=lawZ48z zk|esCkjM{&GM}~ETy?3e3}t$rSEHlPcYZFhwtOwMV1xeLid&*;g9t=|9O4wZK%j4E z*jUW4al!l;KANCc3cH#-ZZrRL?*u-!7$te8ANjqC*J+R_9DMKSMcXiv&{$bi@u(ja zYyjPsN)%_BQXZw;?%za~Z?J3=d;DSd?vK9E@!-0)6B9)DVe%d@z-47?p=60Ltb5bf z7>C+%ZYkfBk!Ca{H(a=~O;CM$NW`;6DfvBP(_nX1Hr9K;gsugnsz;=@mEND{w|dIK zHd&a8DmJP4KC|`N_xMU_5Mp;|v^|7d|1!fnn(SHk{4O8GU_Ld*vPEC*sP=at#y$qJ zc3#-m+C3h+j*?})wdm+>niN#aglfruv+3;Z@YQ5!Ekf@iwt0I%w;VL%vtB>TO7_n1 zI{!axcN2TG>EANvcZkg-4Xpk*EUVQj?^E=~lo#7gvTyRO64C|XtjO_ z^qU5a@dYx)E@Lw}jDoD4-SV1p{aE-IN~UEVezU^&At-mFWjK}HxQ&#RJ`33%3%Eh{ zZ|}p(J?2nTM0i#0uu58+@nPr9fW073+}vjJg&pILs()?_y^?MC}Z^ zKjLfQJXh!1>nU3XkS|#U($11v?+!~=V)hxy?$ci*jcZivi2}?-eKJCwWlzlHM={|^ zorCC^B)!~WUw`k8I}@j8Zq!ZP7Y1~TcijkL4-KMZhMwQ}=UUoX^RtLfXr0Jbx8NLO zZBlCJcf-PNSg(|fjLP7>f~;OEc(K2xhh)A@q8BnZ^>>+>$$0KXb}NE3KwhzjZgcAe z3Aa79g* zc0nU^f7MlOkt|bFRXf2m3V2%0Z)xT~)+Mn$DDwUCyh@md>?t!j{mb_&tAWK%Vv_VN zUq)lhXZqWA=ZR*dettvv`hzR5h23Jmu}EnHDbXV3@VWY9ZBGg!qtb@!I?Yvr(h zHn5e~SQ(OhWjfN#3fX2_nEXlu*vP*Dbwl*?yxX3gN1+N<2YzkGC;hs6D$VEaweNV8 zMn=Yb{ji&??FcFh@nmue-IXEDzIZR)=wlE~!v+Q8#llGigM z-(5pHo}(-CD>Ru{FBW1Gr6^=pU&WI9e85S}*GAOPRiTwUh?H1q<=8vhF5f1x?%n@x z%!100Ms^uR!)0X@H?l~`G+^MFU0|^&Xv%&z(E(c|TQXF&3omMW>$bMhuw6FcP>?l$ zX}SZXQ34|}ev5u1yU%=$>am@V(a}n~Zi;hhQ#%P$k;h=}EgJ(kD-<-U*I5@ZS-UeK z@N`JxWAav%l|)5BfikJdZE>a_;Vp#3teo-!2KLawiA8O|8L7!jn06V7=npgq{I}H> zmeY^n{zdgMn~aJq`a0<@M*Ay!Bn8IgOk-(INUVLwg=UB#$Hl+JoA_y6txcB0HBu`! zO;omGg!_0O*CvVN=C*LyJtYn!)Wdz(QtV7yN}h<=*2VEzJl+-e2sj)P_ZU-T*d(!c z$LD<9&@G>IJpE(hRkSnr_9$8(ylW-z);4$e{*Q#NAWg#dc*^V)5t5d|No7XrE*mb^ zQe{NrA)T9>Oe;Xw?<+uTtn`IC%4@Uzb4p7;JzU>@#CG+qq~##1MDuM!R?p5;GUf-@ z`7N1G$2g4wM~vgZ=7CrbgFgoxN!bi8bW zoo8VV4Wz^m{x3~nuZMH(JKG!^xP*`o*<+e?=U~@QIZ6(|8kOE{K`Fpx=khEm(UV55 zXG6tIWh|Cz!-gE=y0Qaan!rsEaEFS$|Fq{#Yc}HVe61`Tcp%_LlfpI2^ZOM+t}6}f zh9=+Y*4oE10SHo$BMH)V`$%D|#{-0nO-y@FK%O05kD1g3}7$O87M@8N$83gef7!3_oOG&o_ESn{CTq!9aphiRs2 zvjvt^T;4K*@2i?E!IcRzRFHPy7}bs%DFfp&4rXUR7P8iprR*jsFirWqj@x4R5bVi5w++At_vwy;sy!D3h}c`9C68$iDyp literal 0 HcmV?d00001 diff --git a/index.rst b/index.rst index dd0113a285c..8df662113d8 100644 --- a/index.rst +++ b/index.rst @@ -346,6 +346,13 @@ Welcome to PyTorch Tutorials :link: intermediate/dist_pipeline_parallel_tutorial.html :tags: Parallel-and-Distributed-Training +.. customcarditem:: + :header: Implementing Batch RPC Processing Using Asynchronous Executions + :card_description: Learn how to use rpc.functions.async_execution to implement batch RPC + :image: _static/img/thumbnails/cropped/Implementing-Batch-RPC-Processing-Using-Asynchronous-Executions.png + :link: intermediate/rpc_async_execution.html + :tags: Parallel-and-Distributed-Training + .. End of tutorial card section .. raw:: html @@ -513,3 +520,4 @@ Additional Resources beginner/aws_distributed_training_tutorial intermediate/rpc_param_server_tutorial intermediate/dist_pipeline_parallel_tutorial + intermediate/rpc_async_execution diff --git a/intermediate_source/rpc_async_execution.rst b/intermediate_source/rpc_async_execution.rst new file mode 100644 index 00000000000..e3e42f9135e --- /dev/null +++ b/intermediate_source/rpc_async_execution.rst @@ -0,0 +1,522 @@ +Implementing Batch RPC Processing Using Asynchronous Executions +=============================================================== +**Author**: `Shen Li `_ + + +Prerequisites: + +- `Getting started with Distributed RPC Framework `__ +- `Implementing a Parameter Server using Distributed RPC Framework `__ +- `RPC Asynchronous Execution Decorator `__ + +This tutorial demonstrates how to build batch-processing RPC applications with +the `@rpc.functions.async_execution `__ +decorator, which helps to speed up training by reducing the number of blocked +RPC threads and consolidating CUDA operations on the callee. This shares the +same idea as `Batch Inference with TorchServer `__. + +.. note:: This tutorial requires PyTorch v1.6.0 or above. + +Basics +------ + +Previous tutorials have shown the steps to build distributed training +applications using `torch.distributed.rpc `__, +but they didn't elaborate on what happens on the callee side when processing an +RPC request. As of PyTorch v1.5, each RPC request will block one thread on the +callee to execute the function in that request until that function returns. +This works for many use cases, but there is one caveat. If the user function +blocks on IO, e.g., with nested RPC invocation, or signaling, e.g., waiting for +a different RPC request to unblock, the RPC thread on the callee will have to +idle waiting until the IO finishes or the signaling event occurs. As a result, +RPC callees are likely to use more threads than necessary. The cause of this +problem is that RPC treats user functions as black boxes, and knows very little +about what happens in the function. To allow user functions to yield and free +RPC threads, more hints need to be provided to the RPC system. + +Since v1.6.0, PyTorch addresses this problem by introducing two new concepts: + +* A `torch.futures.Future `__ type + that encapsulates an asynchronous execution, which also supports installing + callback functions. +* An `@rpc.functions.async_execution `__ + decorator that allows applications to tell the callee that the target function + will return a future and can pause and yield multiple times during execution. + +With these two tools, the application code can break a user function into +multiple smaller functions, chain them together as callbacks on ``Future`` +objects, and return the ``Future`` that contains the final result. On the callee +side, when getting the ``Future`` object, it installs subsequent RPC response +preparation and communication as callbacks as well, which will be triggered +when the final result is ready. In this way, the callee no longer needs to block +one thread and wait until the final return value is ready. Please refer to the +API doc of +`@rpc.functions.async_execution `__ +for simple examples. + +Besides reducing the number of idle threads on the callee, these tools also help +to make batch RPC processing easier and faster. The following two sections of +this tutorial demonstrate how to build distributed batch-updating parameter +server and batch-processing reinforcement learning applications using the +`@rpc.functions.async_execution `__ +decorator. + +Batch-Updating Parameter Server +------------------------------- + +Consider a synchronized parameter server training application with one parameter +server (PS) and multiple trainers. In this application, the PS holds the +parameters and waits for all trainers to report gradients. In every iteration, +it waits until receiving gradients from all trainers and then updates all +parameters in one shot. The code below shows the implementation of the PS class. +The ``update_and_fetch_model`` method is decorated using +``@rpc.functions.async_execution`` and will be called by trainers. Each +invocation returns a ``Future`` object that will be populated with the updated +model. Invocations launched by most trainers just accumulate gradients to the +``.grad`` field, return immediately, and yield the RPC thread on the PS. The +last arriving trainer will trigger the optimizer step and consume all previously +reported gradients. Then it sets the ``future_model`` with the updated model, +which in turn notifies all previous requests from other trainers through the +``Future`` object and sends out the updated model to all trainers. + +.. code:: python + + import threading + import torchvision + import torch + import torch.distributed.rpc as rpc + from torch import optim + + num_classes, batch_update_size = 30, 5 + + class BatchUpdateParameterServer(object): + def __init__(self, batch_update_size=batch_update_size): + self.model = torchvision.models.resnet50(num_classes=num_classes) + self.lock = threading.Lock() + self.future_model = torch.futures.Future() + self.batch_update_size = batch_update_size + self.curr_update_size = 0 + self.optimizer = optim.SGD(self.model.parameters(), lr=0.001, momentum=0.9) + for p in self.model.parameters(): + p.grad = torch.zeros_like(p) + + def get_model(self): + return self.model + + @staticmethod + @rpc.functions.async_execution + def update_and_fetch_model(ps_rref, grads): + # Using the RRef to retrieve the local PS instance + self = ps_rref.local_value() + with self.lock: + self.curr_update_size += 1 + # accumulate gradients into .grad field + for p, g in zip(self.model.parameters(), grads): + p.grad += g + + # Save the current future_model and return it to make sure the + # returned Future object holds the correct model even if another + # thread modifies future_model before this thread returns. + fut = self.future_model + + if self.curr_update_size >= self.batch_update_size: + # update the model + for p in self.model.parameters(): + p.grad /= self.batch_update_size + self.curr_update_size = 0 + self.optimizer.step() + self.optimizer.zero_grad() + # by settiing the result on the Future object, all previous + # requests expecting this updated model will be notified and + # the their responses will be sent accordingly. + fut.set_result(self.model) + self.future_model = torch.futures.Future() + + return fut + +For the trainers, they are all initialized using the same set of +parameters from the PS. In every iteration, each trainer first runs the forward +and the backward passes to generate gradients locally. Then, each trainer +reports its gradients to the PS using RPC, and fetches back the updated +parameters through the return value of the same RPC request. In the trainer's +implementation, whether the target function is marked with +``@rpc.functions.async_execution`` or not makes no difference. The +trainer simply calls ``update_and_fetch_model`` using ``rpc_sync`` which will +block on the trainer until the updated model is returned. + +.. code:: python + + batch_size, image_w, image_h = 20, 64, 64 + + class Trainer(object): + def __init__(self, ps_rref): + self.ps_rref, self.loss_fn = ps_rref, torch.nn.MSELoss() + self.one_hot_indices = torch.LongTensor(batch_size) \ + .random_(0, num_classes) \ + .view(batch_size, 1) + + def get_next_batch(self): + for _ in range(6): + inputs = torch.randn(batch_size, 3, image_w, image_h) + labels = torch.zeros(batch_size, num_classes) \ + .scatter_(1, self.one_hot_indices, 1) + yield inputs.cuda(), labels.cuda() + + def train(self): + name = rpc.get_worker_info().name + # get initial model parameters + m = self.ps_rref.rpc_sync().get_model().cuda() + # start training + for inputs, labels in self.get_next_batch(): + self.loss_fn(m(inputs), labels).backward() + m = rpc.rpc_sync( + self.ps_rref.owner(), + BatchUpdateParameterServer.update_and_fetch_model, + args=(self.ps_rref, [p.grad for p in m.cpu().parameters()]), + ).cuda() + +We skip the code that launches multiple processes in this tutorial and please +refer to the `examples `__ +repo for the full implementation. Note that, it is possible to implement batch +processing without the +`@rpc.functions.async_execution `__ +decorator. However, that would require either blocking more RPC threads on +the PS or use another round of RPC to fetch updated models, where the latter +would add both more code complexity and more communication overhead. + +This section uses a simple parameter sever training example to show how to +implement batch RPC applications using the +`@rpc.functions.async_execution `__ +decorator. In the next section, we re-implement the reinforcement learning +example in the previous +`Getting started with Distributed RPC Framework `__ +tutorial using batch processing, and demonstrate its impact on the training +speed. + +Batch-Processing CartPole Solver +-------------------------------- + +This section uses CartPole-v1 from `OpenAI Gym `__ as +an example to show the performance impact of batch processing RPC. Please note +that the goal is to demonstrate the usage of +`@rpc.functions.async_execution `__ +instead of building the best CartPole solver or solving most different RL +problems, we use very simple policies and reward calculation strategies and +focus on the multi-observer single-agent batch RPC implementation. We use a +similar ``Policy`` model as the previous tutorial which is shown below. Compared +to the previous tutorial, the difference is that its constructor takes an +additional ``batch`` argument which controls the ``dim`` parameter for +``F.softmax`` because with batching, the ``x`` argument in the ``forward`` +function contains states from multiple observers and hence the dimension needs +to change properly. Everything else stays intact. + +.. code:: python + + import argparse + import torch.nn as nn + import torch.nn.functional as F + + parser = argparse.ArgumentParser(description='PyTorch RPC Batch RL example') + parser.add_argument('--gamma', type=float, default=1.0, metavar='G', + help='discount factor (default: 1.0)') + parser.add_argument('--seed', type=int, default=543, metavar='N', + help='random seed (default: 543)') + parser.add_argument('--num-episode', type=int, default=10, metavar='E', + help='number of episodes (default: 10)') + args = parser.parse_args() + + torch.manual_seed(args.seed) + + class Policy(nn.Module): + def __init__(self, batch=True): + super(Policy, self).__init__() + self.affine1 = nn.Linear(4, 128) + self.dropout = nn.Dropout(p=0.6) + self.affine2 = nn.Linear(128, 2) + self.dim = 2 if batch else 1 + + def forward(self, x): + x = self.affine1(x) + x = self.dropout(x) + x = F.relu(x) + action_scores = self.affine2(x) + return F.softmax(action_scores, dim=self.dim) + + +The constructor of the ``Observer`` adjusts accordingly as well. It also takes a +``batch`` argument, which governs which ``Agent`` function it uses to select +actions. In batch mode, it calls ``select_action_batch`` function on ``Agent`` +which will be presented shortly, and this function will be decorated with +`@rpc.functions.async_execution `__. + + +.. code:: python + + import gym + import torch.distributed.rpc as rpc + + class Observer: + def __init__(self, batch=True): + self.id = rpc.get_worker_info().id - 1 + self.env = gym.make('CartPole-v1') + self.env.seed(args.seed) + self.select_action = Agent.select_action_batch if batch else Agent.select_action + +Compared to the previous tutorial +`Getting started with Distributed RPC Framework `__, +observers behave a little differently. Instead of exiting when the environment +is stopped, it always runs ``n_steps`` iterations in every episode. When the +environment returns, the observer simply resets the environment and start over +again. With this design, the agent will receive a fixed number of states from +every observer and hence can pack them into a fixed-size tensor. In every +step, the ``Observer`` uses RPC to send its state to the ``Agent`` and fetches +the action through the return value. At the end of every episode, it returns the +rewards of all steps to ``Agent``. Note that this ``run_episode`` function will +be called by the ``Agent`` using RPC. So the ``rpc_sync`` call in this function +will be a nested RPC invocation. We could mark this function as ``@rpc.functions.async_execution`` +too to avoid blocking one thread on the ``Observer``. However, as the bottleneck +is the ``Agent`` instead of the ``Observer``, it should be OK to block one +thread on the ``Observer`` process. + + +.. code:: python + + import torch + + class Observer: + ... + + def run_episode(self, agent_rref, n_steps): + state, ep_reward = self.env.reset(), NUM_STEPS + rewards = torch.zeros(n_steps) + start_step = 0 + for step in range(n_steps): + state = torch.from_numpy(state).float().unsqueeze(0) + # send the state to the agent to get an action + action = rpc.rpc_sync( + agent_rref.owner(), + self.select_action, + args=(agent_rref, self.id, state) + ) + + # apply the action to the environment, and get the reward + state, reward, done, _ = self.env.step(action) + rewards[step] = reward + + if done or step + 1 >= n_steps: + curr_rewards = rewards[start_step:(step + 1)] + R = 0 + for i in range(curr_rewards.numel() -1, -1, -1): + R = curr_rewards[i] + args.gamma * R + curr_rewards[i] = R + state = self.env.reset() + if start_step == 0: + ep_reward = min(ep_reward, step - start_step + 1) + start_step = step + 1 + + return [rewards, ep_reward] + +The constructor of the ``Agent`` also takes a ``batch`` argument, which controls +how action probs are batched. In batch mode, the ``saved_log_probs`` contains a +list of tensors, where each tensor contains action robs from all observers in +one step. Without batching, the ``saved_log_probs`` is a dictionary where the +key is the observer id and the value is a list of action probs for that +observer. + +.. code:: python + + import threading + from torch.distributed.rpc import RRef + + class Agent: + def __init__(self, world_size, batch=True): + self.ob_rrefs = [] + self.agent_rref = RRef(self) + self.rewards = {} + self.policy = Policy(batch).cuda() + self.optimizer = optim.Adam(self.policy.parameters(), lr=1e-2) + self.running_reward = 0 + + for ob_rank in range(1, world_size): + ob_info = rpc.get_worker_info(OBSERVER_NAME.format(ob_rank)) + self.ob_rrefs.append(rpc.remote(ob_info, Observer, args=(batch,))) + self.rewards[ob_info.id] = [] + + self.states = torch.zeros(len(self.ob_rrefs), 1, 4) + self.batch = batch + self.saved_log_probs = [] if batch else {k:[] for k in range(len(self.ob_rrefs))} + self.future_actions = torch.futures.Future() + self.lock = threading.Lock() + self.pending_states = len(self.ob_rrefs) + +The non-batching ``select_acion`` simply runs the state throw the policy, saves +the action prob, and returns the action to the observer right away. + +.. code:: python + + from torch.distributions import Categorical + + class Agent: + ... + + @staticmethod + def select_action(agent_rref, ob_id, state): + self = agent_rref.local_value() + probs = self.policy(state.cuda()) + m = Categorical(probs) + action = m.sample() + self.saved_log_probs[ob_id].append(m.log_prob(action)) + return action.item() + +With batching, the state is stored in a 2D tensor ``self.states``, using the +observer id as the row id. Then, it chains a ``Future`` by installing a callback +function to the batch-generated ``self.future_actions`` ``Future`` object, which +will be populated with the specific row indexed using the id of that observer. +The last arriving observer runs all batched states through the policy in one +shot and set ``self.future_actions`` accordingly. When this occurs, all the +callback functions installed on ``self.future_actions`` will be triggered and +their return values will be used to populate the chained ``Future`` object, +which in turn notifies the ``Agent`` to prepare and communicate responses for +all previous RPC requests from other observers. + +.. code:: python + + class Agent: + ... + + @staticmethod + @rpc.functions.async_execution + def select_action_batch(agent_rref, ob_id, state): + self = agent_rref.local_value() + self.states[ob_id].copy_(state) + future_action = self.future_actions.then( + lambda future_actions: future_actions.wait()[ob_id].item() + ) + + with self.lock: + self.pending_states -= 1 + if self.pending_states == 0: + self.pending_states = len(self.ob_rrefs) + probs = self.policy(self.states.cuda()) + m = Categorical(probs) + actions = m.sample() + self.saved_log_probs.append(m.log_prob(actions).t()[0]) + future_actions = self.future_actions + self.future_actions = torch.futures.Future() + future_actions.set_result(actions.cpu()) + return future_action + +Now let's define how different RPC functions are stitched together. The ``Agent`` +controls the execution of every episode. It first uses ``rpc_async`` to kick off +the episode on all observers and block on the returned futures which will be +populated with observer rewards. Note that the code below uses the RRef helper +``ob_rref.rpc_async()`` to launch the ``run_episode`` function on the owner +of the ``ob_rref`` RRef with the provided arguments. +It then converts the saved action probs and returned observer rewards into +expected data format, and launch the training step. Finally, it resets all +states and returns the reward of the current episode. This function is the entry +point to run one episode. + +.. code:: python + + class Agent: + ... + + def run_episode(self, n_steps=0): + futs = [] + for ob_rref in self.ob_rrefs: + # make async RPC to kick off an episode on all observers + futs.append(ob_rref.rpc_async().run_episode(self.agent_rref, n_steps)) + + # wait until all obervers have finished this episode + rets = torch.futures.wait_all(futs) + rewards = torch.stack([ret[0] for ret in rets]).cuda().t() + ep_rewards = sum([ret[1] for ret in rets]) / len(rets) + + # stack saved probs into one tensor + if self.batch: + probs = torch.stack(self.saved_log_probs) + else: + probs = [torch.stack(self.saved_log_probs[i]) for i in range(len(rets))] + probs = torch.stack(probs) + + policy_loss = -probs * rewards / len(rets) + policy_loss.sum().backward() + self.optimizer.step() + self.optimizer.zero_grad() + + # reset variables + self.saved_log_probs = [] if self.batch else {k:[] for k in range(len(self.ob_rrefs))} + self.states = torch.zeros(len(self.ob_rrefs), 1, 4) + + # calculate running rewards + self.running_reward = 0.5 * ep_rewards + 0.5 * self.running_reward + return ep_rewards, self.running_reward + +The rest of the code is normal processes launching and logging which are +similar to other RPC tutorials. In this tutorial, all observers passively +waiting for commands from the agent. Please refer to the +`examples `__ +repo for the full implementation. + +.. code:: python + + def run_worker(rank, world_size, n_episode, batch, print_log=True): + os.environ['MASTER_ADDR'] = 'localhost' + os.environ['MASTER_PORT'] = '29500' + if rank == 0: + # rank0 is the agent + rpc.init_rpc(AGENT_NAME, rank=rank, world_size=world_size) + + agent = Agent(world_size, batch) + for i_episode in range(n_episode): + last_reward, running_reward = agent.run_episode(n_steps=NUM_STEPS) + + if print_log: + print('Episode {}\tLast reward: {:.2f}\tAverage reward: {:.2f}'.format( + i_episode, last_reward, running_reward)) + else: + # other ranks are the observer + rpc.init_rpc(OBSERVER_NAME.format(rank), rank=rank, world_size=world_size) + # observers passively waiting for instructions from agents + rpc.shutdown() + + + def main(): + for world_size in range(2, 12): + delays = [] + for batch in [True, False]: + tik = time.time() + mp.spawn( + run_worker, + args=(world_size, args.num_episode, batch), + nprocs=world_size, + join=True + ) + tok = time.time() + delays.append(tok - tik) + + print(f"{world_size}, {delays[0]}, {delays[1]}") + + + if __name__ == '__main__': + main() + +Batch RPC helps to consolidate the action inference into less CUDA operations, +and hence reduces the amortized overhead. The above ``main`` function runs the +same code on both batch and no-batch modes using different numbers of observers, +ranging from 1 to 10. The figure below plots the execution time of different +world sizes using default argument values. The results confirmed our expectation +that batch processing helped to speed up training. + + +.. figure:: /_static/img/rpc-images/batch.png + :alt: + +Learn More +---------- + +- `Batch-Updating Parameter Server Source Code `__ +- `Batch-Processing CartPole Solver `__ +- `Distributed Autograd `__ +- `Distributed Pipeline Parallelism `__ \ No newline at end of file From 51abc6a003f93e0053ec2d2e78195d796a95aaec Mon Sep 17 00:00:00 2001 From: Shen Li Date: Fri, 3 Jul 2020 18:40:05 -0700 Subject: [PATCH 11/33] Fix code block in pipeline tutorial --- intermediate_source/dist_pipeline_parallel_tutorial.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/intermediate_source/dist_pipeline_parallel_tutorial.rst b/intermediate_source/dist_pipeline_parallel_tutorial.rst index ef7df000508..346944705ed 100644 --- a/intermediate_source/dist_pipeline_parallel_tutorial.rst +++ b/intermediate_source/dist_pipeline_parallel_tutorial.rst @@ -58,6 +58,7 @@ the two ResNet shards. .. code:: python + import threading import torch @@ -367,4 +368,4 @@ in each batch. Processing batch 0 Processing batch 1 Processing batch 2 - number of splits = 8, execution time = 9.076049566268921 \ No newline at end of file + number of splits = 8, execution time = 9.076049566268921 From f8465c3ecf8e9e0ae325199817cedca2c3b5cc7e Mon Sep 17 00:00:00 2001 From: mrshenli Date: Thu, 9 Jul 2020 14:01:41 -0400 Subject: [PATCH 12/33] Adding an Overview Page for PyTorch Distributed (#1056) * Adding an Overview Page for PyTorch Distributed * Let existing PT Distributed tutorials link to the overview page * Add a link to AMP * Address Comments * Remove unnecessary dist.barrier() --- .../cropped/PyTorch-Distributed-Overview.png | Bin 0 -> 35776 bytes beginner_source/dist_overview.rst | 197 ++++++++++++++++++ index.rst | 22 +- intermediate_source/ddp_tutorial.rst | 13 +- .../dist_pipeline_parallel_tutorial.rst | 1 + intermediate_source/dist_tuto.rst | 4 + intermediate_source/rpc_async_execution.rst | 5 +- .../rpc_param_server_tutorial.rst | 15 +- intermediate_source/rpc_tutorial.rst | 5 + 9 files changed, 245 insertions(+), 17 deletions(-) create mode 100644 _static/img/thumbnails/cropped/PyTorch-Distributed-Overview.png create mode 100644 beginner_source/dist_overview.rst diff --git a/_static/img/thumbnails/cropped/PyTorch-Distributed-Overview.png b/_static/img/thumbnails/cropped/PyTorch-Distributed-Overview.png new file mode 100644 index 0000000000000000000000000000000000000000..426a14d98f5f7fbbf695626658ad1946fe4ef63e GIT binary patch literal 35776 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 literal 0 HcmV?d00001 diff --git a/beginner_source/dist_overview.rst b/beginner_source/dist_overview.rst new file mode 100644 index 00000000000..bc9f7fe6bf0 --- /dev/null +++ b/beginner_source/dist_overview.rst @@ -0,0 +1,197 @@ +PyTorch Distributed Overview +============================ +**Author**: `Shen Li `_ + + +This is the overview page for the ``torch.distributed`` package. As there are +more and more documents, examples and tutorials added at different locations, +it becomes unclear which document or tutorial to consult for a specific problem +or what is the best order to read these contents. The goal of this page is to +address this problem by categorizing documents into different topics and briefly +describe each of them. If this is your first time building distributed training +applications using PyTorch, it is recommended to use this document to navigate +to the technology that can best serve your use case. + + +Introduction +------------ + +As of PyTorch v1.6.0, features in ``torch.distributed`` can be categorized into +three main components: + +* `Distributed Data-Parallel Training `__ + (DDP) is a widely adopted single-program multiple-data training paradigm. With + DDP, the model is replicated on every process, and every model replica will be + fed with a different set of input data samples. DDP takes care of gradient + communications to keep model replicas synchronized and overlaps it with the + gradient computations to speed up training. +* `RPC-Based Distributed Training `__ + (RPC) is developed to support general training structures that cannot fit into + data-parallel training, such as distributed pipeline parallelism, parameter + server paradigm, and combination of DDP with other training paradigms. It + helps manage remote object lifetime and extend autograd engine to beyond + machine boundaries. +* `Collective Communication `__ + (c10d) library support sending tensors across processes within a group. It + offers both collective communication APIs (e.g., + `all_reduce `__ + and `all_gather `__) + and P2P communication APIs (e.g., + `send `__ + and `isend `__). + DDP and RPC (`ProcessGroup Backend `__) + are built on c10d as of v1.6.0, where the former uses collective communications + and the latter uses P2P communications. Usually, developers do not need to + directly use this raw communication API, as DDP and RPC features above can serve + many distributed training scenarios. However, there are use cases where this API + is still helpful. One example would be distributed parameter averaging, where + applications would like to compute the average values of all model parameters + after the backward pass instead of using DDP to communicate gradients. This can + decouple communications from computations and allow finer-grain control over + what to communicate, but on the other hand, it also gives up the performance + optimizations offered by DDP. The + `Writing Distributed Applications with PyTorch `__ + shows examples of using c10d communication APIs. + + +Most of the existing documents are written for either DDP or RPC, the remainder +of this page will elaborate materials for these two components. + + +Data Parallel Training +---------------------- + +PyTorch provides several options for data-parallel training. For applications +that gradually grow from simple to complex and from prototype to production, the +common development trajectory would be: + +1. Use single-device training, if the data and model can fit in one GPU, and the + training speed is not a concern. +2. Use single-machine multi-GPU + `DataParallel `__, + if there are multiple GPUs on the server, and you would like to speed up + training with the minimum code change. +3. Use single-machine multi-GPU + `DistributedDataParallel `__, + if you would like to further speed up training and are willing to write a + little more code to set it up. +4. Use multi-machine `DistributedDataParallel `__ + and the `launching script `__, + if the application needs to scale across machine boundaries. +5. Use `torchelastic `__ to launch distributed + training, if errors (e.g., OOM) are expected or if the resources can join and + leave dynamically during the training. + + +.. note:: Data-parallel training also works with `Automatic Mixed Precision (AMP) `__. + + +``torch.nn.DataParallel`` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The `DataParallel `__ +package enables single-machine multi-GPU parallelism with the lowest coding +hurdle. It only requires a one-line change to the application code. The tutorial +`Optional: Data Parallelism `__ +shows an example. The caveat is that, although ``DataParallel`` is very easy to +use, it usually does not offer the best performance. This is because the +implementation of ``DataParallel`` replicates the model in every forward pass, +and its single-process multi-thread parallelism naturally suffers from GIL +contentions. To get better performance, please consider using +`DistributedDataParallel `__. + + +``torch.nn.parallel.DistributedDataParallel`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Compared to `DataParallel `__, +`DistributedDataParallel `__ +requires one more step to set up, i.e., calling +`init_process_group `__. +DDP uses multi-process parallelism, and hence there is no GIL contention across +model replicas. Moreover, the model is broadcast at DDP construction time instead +of in every forward pass, which also helps to speed up training. DDP is shipped +with several performance optimization technologies. For a more in-depth +explanation, please refer to this +`DDP paper `__ (VLDB'20). + + +DDP materials are listed below: + +1. `DDP notes `__ + offer a starter example and some brief descriptions of its design and + implementation. If this is your first time using DDP, please start from this + document. +2. `Getting Started with Distributed Data Parallel <../intermediate/ddp_tutorial.html>`__ + explains some common problems with DDP training, including unbalanced + workload, checkpointing, and multi-device models. Note that, DDP can be + easily combined with single-machine multi-device model parallelism which is + described in the + `Single-Machine Model Parallel Best Practices <../intermediate/model_parallel_tutorial.html>`__ + tutorial. +3. The `Launching and configuring distributed data parallel applications `__ + document shows how to use the DDP launching script. +4. `PyTorch Distributed Trainer with Amazon AWS `__ + demonstrates how to use DDP on AWS. + +TorchElastic +~~~~~~~~~~~~ + +With the growth of the application complexity and scale, failure recovery +becomes an imperative requirement. Sometimes, it is inevitable to hit errors +like OOM when using DDP, but DDP itself cannot recover from those errors nor +does basic ``try-except`` block work. This is because DDP requires all processes +to operate in a closely synchronized manner and all ``AllReduce`` communications +launched in different processes must match. If one of the processes in the group +throws an OOM exception, it is likely to lead to desynchronization (mismatched +``AllReduce`` operations) which would then cause a crash or hang. If you expect +failures to occur during training or if resources might leave and join +dynamically, please launch distributed data-parallel training using +`torchelastic `__. + + +General Distributed Training +---------------------------- + +Many training paradigms do not fit into data parallelism, e.g., +parameter server paradigm, distributed pipeline parallelism, reinforcement +learning applications with multiple observers or agents, etc. The +`torch.distributed.rpc `__ aims at +supporting general distributed training scenarios. + +The `torch.distributed.rpc `__ package +has four main pillars: + +* `RPC `__ supports running + a given function on a remote worker. +* `RRef `__ helps to manage the + lifetime of a remote object. The reference counting protocol is presented in the + `RRef notes `__. +* `Distributed Autograd `__ + extends the autograd engine beyond machine boundaries. Please refer to + `Distributed Autograd Design `__ + for more details. +* `Distributed Optimizer `__ + that automatically reaches out to all participating workers to update + parameters using gradients computed by the distributed autograd engine. + +RPC Tutorials are listed below: + +1. The `Getting Started with Distributed RPC Framework <../intermediate/rpc_tutorial.html>`__ + tutorial first uses a simple Reinforcement Learning (RL) example to + demonstrate RPC and RRef. Then, it applies a basic distributed model + parallelism to an RNN example to show how to use distributed autograd and + distributed optimizer. +2. The `Implementing a Parameter Server Using Distributed RPC Framework <../intermediate/rpc_param_server_tutorial.html>`__ + tutorial borrows the spirit of + `HogWild! training `__ + and applies it to an asynchronous parameter server (PS) training application. +3. The `Distributed Pipeline Parallelism Using RPC <../intermediate/dist_pipeline_parallel_tutorial.html>`__ + tutorial extends the single-machine pipeline parallel example (presented in + `Single-Machine Model Parallel Best Practices <../intermediate/model_parallel_tutorial.html>`__) + to a distributed environment and shows how to implement it using RPC. +4. The `Implementing Batch RPC Processing Using Asynchronous Executions <../intermediate/rpc_async_execution.html>`__ + tutorial demonstrates how to implement RPC batch processing using the + `@rpc.functions.async_execution `__ + decorator, which can help speed up inference and training. It uses similar + RL and PS examples employed in the above tutorials 1 and 2. diff --git a/index.rst b/index.rst index 8df662113d8..0e04e92bf35 100644 --- a/index.rst +++ b/index.rst @@ -297,6 +297,13 @@ Welcome to PyTorch Tutorials .. Parallel-and-Distributed-Training +.. customcarditem:: + :header: PyTorch Distributed Overview + :card_description: Briefly go over all concepts and features in the distributed package. Use this document to find the distributed training technology that can best serve your application. + :image: _static/img/thumbnails/cropped/PyTorch-Distributed-Overview.png + :link: beginner/dist_overview.html + :tags: Parallel-and-Distributed-Training + .. customcarditem:: :header: Single-Machine Model Parallel Best Practices :card_description: Learn how to implement model parallel, a distributed training technique which splits a single model onto different GPUs, rather than replicating the entire model on each GPU @@ -311,6 +318,13 @@ Welcome to PyTorch Tutorials :link: intermediate/ddp_tutorial.html :tags: Parallel-and-Distributed-Training +.. customcarditem:: + :header: (advanced) PyTorch 1.0 Distributed Trainer with Amazon AWS + :card_description: Set up the distributed package of PyTorch, use the different communication strategies, and go over some the internals of the package. + :image: _static/img/thumbnails/cropped/advanced-PyTorch-1point0-Distributed-Trainer-with-Amazon-AWS.png + :link: beginner/aws_distributed_training_tutorial.html + :tags: Parallel-and-Distributed-Training + .. customcarditem:: :header: Writing Distributed Applications with PyTorch :card_description: Set up the distributed package of PyTorch, use the different communication strategies, and go over some the internals of the package. @@ -325,13 +339,6 @@ Welcome to PyTorch Tutorials :link: intermediate/rpc_tutorial.html :tags: Parallel-and-Distributed-Training -.. customcarditem:: - :header: (advanced) PyTorch 1.0 Distributed Trainer with Amazon AWS - :card_description: Set up the distributed package of PyTorch, use the different communication strategies, and go over some the internals of the package. - :image: _static/img/thumbnails/cropped/advanced-PyTorch-1point0-Distributed-Trainer-with-Amazon-AWS.png - :link: beginner/aws_distributed_training_tutorial.html - :tags: Parallel-and-Distributed-Training - .. customcarditem:: :header: Implementing a Parameter Server Using Distributed RPC Framework :card_description: Walk through a through a simple example of implementing a parameter server using PyTorch’s Distributed RPC framework. @@ -513,6 +520,7 @@ Additional Resources :hidden: :caption: Parallel and Distributed Training + beginner/dist_overview intermediate/model_parallel_tutorial intermediate/ddp_tutorial intermediate/dist_tuto diff --git a/intermediate_source/ddp_tutorial.rst b/intermediate_source/ddp_tutorial.rst index 4935b4c5652..68409e4daeb 100644 --- a/intermediate_source/ddp_tutorial.rst +++ b/intermediate_source/ddp_tutorial.rst @@ -2,6 +2,13 @@ Getting Started with Distributed Data Parallel ================================================= **Author**: `Shen Li `_ +Prerequisites: + +- `PyTorch Distributed Overview <../beginner/dist_overview.html>`__ +- `DistributedDataParallel API documents `__ +- `DistributedDataParallel notes `__ + + `DistributedDataParallel `__ (DDP) implements data parallelism at the module level which can run across multiple machines. Applications using DDP should spawn multiple processes and @@ -202,9 +209,9 @@ and elasticity support, please refer to `TorchElastic `__ - `Single-Machine Model Parallel Best Practices `__ - `Getting started with Distributed RPC Framework `__ - RRef helper functions: diff --git a/intermediate_source/dist_tuto.rst b/intermediate_source/dist_tuto.rst index 76538a81c90..1838abe8f72 100644 --- a/intermediate_source/dist_tuto.rst +++ b/intermediate_source/dist_tuto.rst @@ -2,6 +2,10 @@ Writing Distributed Applications with PyTorch ============================================= **Author**: `Séb Arnold `_ +Prerequisites: + +- `PyTorch Distributed Overview <../beginner/dist_overview.html>`__ + In this short tutorial, we will be going over the distributed package of PyTorch. We'll see how to set up the distributed setting, use the different communication strategies, and go over some the internals of diff --git a/intermediate_source/rpc_async_execution.rst b/intermediate_source/rpc_async_execution.rst index e3e42f9135e..08ba5028d5b 100644 --- a/intermediate_source/rpc_async_execution.rst +++ b/intermediate_source/rpc_async_execution.rst @@ -5,8 +5,9 @@ Implementing Batch RPC Processing Using Asynchronous Executions Prerequisites: -- `Getting started with Distributed RPC Framework `__ -- `Implementing a Parameter Server using Distributed RPC Framework `__ +- `PyTorch Distributed Overview <../beginner/dist_overview.html>`__ +- `Getting started with Distributed RPC Framework `__ +- `Implementing a Parameter Server using Distributed RPC Framework `__ - `RPC Asynchronous Execution Decorator `__ This tutorial demonstrates how to build batch-processing RPC applications with diff --git a/intermediate_source/rpc_param_server_tutorial.rst b/intermediate_source/rpc_param_server_tutorial.rst index cea2be7d647..0516cf60031 100644 --- a/intermediate_source/rpc_param_server_tutorial.rst +++ b/intermediate_source/rpc_param_server_tutorial.rst @@ -4,6 +4,11 @@ Implementing a Parameter Server Using Distributed RPC Framework **Author**\ : `Rohan Varma `_ +Prerequisites: + +- `PyTorch Distributed Overview <../beginner/dist_overview.html>`__ +- `RPC API documents `__ + This tutorial walks through a simple example of implementing a parameter server using PyTorch's `Distributed RPC framework `_. The parameter server framework is a paradigm in which a set of servers store parameters, such as large embedding tables, and several trainers query the parameter servers in order to retrieve the most up to date parameters. These trainers can run a training loop locally and occasionally synchronize with the parameter server to get the latest parameters. For more reading on the parameter server approach, check out `this paper `_. Using the Distributed RPC Framework, we'll build an example where multiple trainers use RPC to communicate with the same parameter server and use `RRef `_ to access states on the remote parameter server instance. Each trainer will launch its dedicated backward pass in a distributed fashion through stitching of the autograd graph across multiple nodes using distributed autograd. @@ -78,7 +83,7 @@ Next, let's define some helper functions that will be useful for the rest of our # On the local node, call a method with first arg as the value held by the # RRef. Other args are passed in as arguments to the function called. - # Useful for calling instance methods. method could be any matching function, including + # Useful for calling instance methods. method could be any matching function, including # class methods. def call_method(method, rref, *args, **kwargs): return method(rref.local_value(), *args, **kwargs) @@ -119,7 +124,7 @@ Next, we'll define our forward pass. Note that regardless of the device of the m # Tensors must be moved in and out of GPU memory due to this. out = out.to("cpu") return out -Next, we'll define a few miscellaneous functions useful for training and verification purposes. The first, ``get_dist_gradients``\ , will take in a Distributed Autograd context ID and call into the ``dist_autograd.get_gradients`` API in order to retrieve gradients computed by distributed autograd. More information can be found in the `distributed autograd documentation `_. Note that we also iterate through the resulting dictionary and convert each tensor to a CPU tensor, as the framework currently only supports sending tensors over RPC. Next, ``get_param_rrefs`` will iterate through our model parameters and wrap them as a (local) `RRef `_. This method will be invoked over RPC by trainer nodes and will return a list of the parameters to be optimized. This is required as input to the `Distributed Optimizer `_\ , which requires all parameters it must optimize as a list of ``RRef``\ s. +Next, we'll define a few miscellaneous functions useful for training and verification purposes. The first, ``get_dist_gradients``\ , will take in a Distributed Autograd context ID and call into the ``dist_autograd.get_gradients`` API in order to retrieve gradients computed by distributed autograd. More information can be found in the `distributed autograd documentation `_. Note that we also iterate through the resulting dictionary and convert each tensor to a CPU tensor, as the framework currently only supports sending tensors over RPC. Next, ``get_param_rrefs`` will iterate through our model parameters and wrap them as a (local) `RRef `_. This method will be invoked over RPC by trainer nodes and will return a list of the parameters to be optimized. This is required as input to the `Distributed Optimizer `_\ , which requires all parameters it must optimize as a list of ``RRef``\ s. .. code-block:: python @@ -224,7 +229,7 @@ Below, we initialize our ``TrainerNet`` and build a ``DistributedOptimizer``. No # Build DistributedOptimizer. param_rrefs = net.get_global_param_rrefs() opt = DistributedOptimizer(optim.SGD, param_rrefs, lr=0.03) -Next, we define our main training loop. We loop through iterables given by PyTorch's `DataLoader `_. Before writing our typical forward/backward/optimizer loop, we first wrap the logic within a `Distributed Autograd context `_. Note that this is needed to record RPCs invoked in the model's forward pass, so that an appropriate graph can be constructed which includes all participating distributed workers in the backward pass. The distributed autograd context returns a ``context_id`` which serves as an identifier for accumulating and optimizing gradients corresponding to a particular iteration. +Next, we define our main training loop. We loop through iterables given by PyTorch's `DataLoader `_. Before writing our typical forward/backward/optimizer loop, we first wrap the logic within a `Distributed Autograd context `_. Note that this is needed to record RPCs invoked in the model's forward pass, so that an appropriate graph can be constructed which includes all participating distributed workers in the backward pass. The distributed autograd context returns a ``context_id`` which serves as an identifier for accumulating and optimizing gradients corresponding to a particular iteration. As opposed to calling the typical ``loss.backward()`` which would kick off the backward pass on this local worker, we call ``dist_autograd.backward()`` and pass in our context_id as well as ``loss``\ , which is the root at which we want the backward pass to begin. In addition, we pass this ``context_id`` into our optimizer call, which is required to be able to look up the corresponding gradients computed by this particular backwards pass across all nodes. @@ -259,7 +264,7 @@ The following simply computes the accuracy of our model after we're done trainin model.eval() correct_sum = 0 # Use GPU to evaluate if possible - device = torch.device("cuda:0" if model.num_gpus > 0 + device = torch.device("cuda:0" if model.num_gpus > 0 and torch.cuda.is_available() else "cpu") with torch.no_grad(): for i, (data, target) in enumerate(test_loader): @@ -330,7 +335,7 @@ We've now completed our trainer and parameter server specific code, and all that assert args.num_gpus <= 3, f"Only 0-2 GPUs currently supported (got {args.num_gpus})." os.environ['MASTER_ADDR'] = args.master_addr os.environ["MASTER_PORT"] = args.master_port -Now, we'll create a process corresponding to either a parameter server or trainer depending on our command line arguments. We'll create a ``ParameterServer`` if our passed in rank is 0, and a ``TrainerNet`` otherwise. Note that we're using ``torch.multiprocessing`` to launch a subprocess corresponding to the function that we want to execute, and waiting on this process's completion from the main thread with ``p.join()``. In the case of initializing our trainers, we also use PyTorch's `dataloaders `_ in order to specify train and test data loaders on the MNIST dataset. +Now, we'll create a process corresponding to either a parameter server or trainer depending on our command line arguments. We'll create a ``ParameterServer`` if our passed in rank is 0, and a ``TrainerNet`` otherwise. Note that we're using ``torch.multiprocessing`` to launch a subprocess corresponding to the function that we want to execute, and waiting on this process's completion from the main thread with ``p.join()``. In the case of initializing our trainers, we also use PyTorch's `dataloaders `_ in order to specify train and test data loaders on the MNIST dataset. .. code-block:: python diff --git a/intermediate_source/rpc_tutorial.rst b/intermediate_source/rpc_tutorial.rst index 6d149e80837..c4ee6536830 100644 --- a/intermediate_source/rpc_tutorial.rst +++ b/intermediate_source/rpc_tutorial.rst @@ -3,6 +3,11 @@ Getting Started with Distributed RPC Framework **Author**: `Shen Li `_ +Prerequisites: + +- `PyTorch Distributed Overview <../beginner/dist_overview.html>`__ +- `RPC API documents `__ + This tutorial uses two simple examples to demonstrate how to build distributed training with the `torch.distributed.rpc `__ package which is first introduced as an experimental feature in PyTorch v1.4. From d766eeb0a3154547d07440079d5294893f6ddcb6 Mon Sep 17 00:00:00 2001 From: Tao Xu Date: Mon, 13 Jul 2020 12:30:59 -0700 Subject: [PATCH 13/33] [Mobile Perf Recipe] Add the benchmarking part for iOS (#1055) * [Mobile Perf Recipe] Add the benchmarking part for iOS * [Mobile Perf Recipe] Add the benchmarking part for iOS Co-authored-by: Jessica Lin --- recipes_source/mobile_perf.rst | 65 +++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 9 deletions(-) diff --git a/recipes_source/mobile_perf.rst b/recipes_source/mobile_perf.rst index 297cea7477a..e4d432b4297 100644 --- a/recipes_source/mobile_perf.rst +++ b/recipes_source/mobile_perf.rst @@ -23,7 +23,7 @@ We will start with preparing to optimize your model to help decrease execution t Setup -####### +^^^^^^^ First we need to installed pytorch using conda or pip with version at least 1.5.0. @@ -69,7 +69,7 @@ Code your model: 1. Fuse operators using ``torch.quantization.fuse_modules`` -############################################################# +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not be confused that fuse_modules is in the quantization package. It works for all ``torcn.nn.Module``. @@ -90,7 +90,7 @@ This script will fuse Convolution, Batch Normalization and Relu in previously de 2. Quantize your model -############################################################# +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You can find more about PyTorch quantization in `the dedicated tutorial `_. @@ -115,7 +115,7 @@ This code does quantization, using stub for model calibration function, you can 3. Use torch.utils.mobile_optimizer -############################################################# +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Torch mobile_optimizer package does several optimizations with the scripted model, which will help to conv2d and linear operations. @@ -136,7 +136,7 @@ Next we call ``optimize_for_mobile`` and save model on the disk. torch.jit.save(torchscript_model_optimized, "model.pt") 4. Prefer Using Channels Last Tensor memory format -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Channels Last(NHWC) memory format was introduced in PyTorch 1.4.0. It is supported only for four-dimensional tensors. This memory format gives a better memory locality for most operators, especially convolution. Our measurements showed a 3x speedup of MobileNetV2 model compared with the default Channels First(NCHW) format. @@ -151,10 +151,11 @@ At the moment of writing this recipe, PyTorch Android java API does not support This conversion is zero cost if your input is already in Channels Last memory format. After it, all operators will work preserving ChannelsLast memory format. -5. Android. Reusing tensors for forward -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +5. Android - Reusing tensors for forward +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This part of the recipe is Android only. -This recipe is Android only. Memory is a critical resource for android performance, especially on old devices. Tensors can need a significant amount of memory. For example, standard computer vision tensor contains 1*3*224*224 elements, @@ -208,7 +209,9 @@ this approach can give more stable measurements rather than testing inside the a Android - Benchmarking Setup -############################# +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This part of the recipe is Android only. For this you first need to build benchmark binary: @@ -240,3 +243,47 @@ Now we are ready to benchmark your model: Running warmup runs. Main runs. Main run finished. Microseconds per iter: 121318. Iters per second: 8.24281 + + +iOS - Benchmarking Setup +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +For iOS, we'll be using our `TestApp `_ as the benchmarking tool. + +To begin with, let's apply the ``optimize_for_mobile`` method to our python script located at `TestApp/benchmark/trace_mode.py `_. Simply modify the code as below. + +:: + + import torch + import torchvision + from torch.utils.mobile_optimizer import optimize_for_mobile + + model = torchvision.models.mobilenet_v2(pretrained=True) + model.eval() + example = torch.rand(1, 3, 224, 224) + traced_script_module = torch.jit.trace(model, example) + torchscript_model_optimized = optimize_for_mobile(traced_script_module) + torch.jit.save(torchscript_model_optimized, "model.pt") + +Now let's run ``python trace_model.py``. If everything works well, we should be able to generate our optimized model in the benchmark directory. + +Next, we're going to build the PyTorch libraries from source. + +:: + + BUILD_PYTORCH_MOBILE=1 IOS_ARCH=arm64 ./scripts/build_ios.sh + +Now that we have the optimized model and PyTorch ready, it's time to generate our XCode project and do benchmarking. To do that, we'll be using a ruby script - `setup.rb` which does the heavy lifting jobs of setting up the XCode project. + +:: + + ruby setup.rb + +Now open the `TestApp.xcodeproj` and plug in your iPhone, you're ready to go. Below is an example result from iPhoneX + +:: + + TestApp[2121:722447] Main runs + TestApp[2121:722447] Main run finished. Milliseconds per iter: 28.767 + TestApp[2121:722447] Iters per second: : 34.762 + TestApp[2121:722447] Done. From 55ec916dcba05fcdc70cd436e28471615352d8c8 Mon Sep 17 00:00:00 2001 From: hx89 <43588773+hx89@users.noreply.github.com> Date: Tue, 14 Jul 2020 10:24:55 -0700 Subject: [PATCH 14/33] Add files via upload --- _static/compare_output.png | Bin 0 -> 27076 bytes _static/compare_stub.png | Bin 0 -> 22859 bytes _static/shadow.png | Bin 0 -> 15597 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 _static/compare_output.png create mode 100644 _static/compare_stub.png create mode 100644 _static/shadow.png diff --git a/_static/compare_output.png b/_static/compare_output.png new file mode 100644 index 0000000000000000000000000000000000000000..4ece4d114833f0a4d975fe79b58737f5494e9c02 GIT binary patch literal 27076 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 Date: Tue, 14 Jul 2020 10:25:57 -0700 Subject: [PATCH 15/33] Create numeric_suite_tutorial.py --- prototype_source/numeric_suite_tutorial.py | 433 +++++++++++++++++++++ 1 file changed, 433 insertions(+) create mode 100644 prototype_source/numeric_suite_tutorial.py diff --git a/prototype_source/numeric_suite_tutorial.py b/prototype_source/numeric_suite_tutorial.py new file mode 100644 index 00000000000..c1da4a14929 --- /dev/null +++ b/prototype_source/numeric_suite_tutorial.py @@ -0,0 +1,433 @@ +# -*- coding: utf-8 -*- +""" +PyTorch Numeric Suite Tutorial +============================== + +Introduction +------------ + +Quantization is good when it works, but it’s difficult to know what's wrong when it doesn't satisfy the accuracy we expect. Debugging the accuracy issue of quantization is not easy and time consuming. + +One important step of debugging is to measure the statistics of the float model and its corresponding quantized model to know where are they differ most. We built a suite of numeric tools called PyTorch Numeric Suite in PyTorch quantization to enable the measurement of the statistics between quantized module and float module to support quantization debugging efforts. Even for the quantized model with good accuracy, PyTorch Numeric Suite can still be used as the profiling tool to better understand the quantization error within the model and provide the guidance for further optimization. + +PyTorch Numeric Suite crrently supports models quantized through both static quantization and dynamic quantization with unified APIs. + +In this tutorial we will first use ResNet18 as an example to show how to use PyTorch Numeric Suite to measure the statistics between static quantized model and float model in eager mode. Then we will use LSTM based sequence model as an example to show the usage of PyTorch Numeric Suite for dynamic quantized model. + +Numeric Suite for Static Quantization +------------------------------------- + +We’ll start by doing the necessary imports: +""" +############################################################################## + +from __future__ import print_function, division, absolute_import +import numpy as np +import torch +import torch.nn as nn +import torchvision +from torchvision import datasets +import torchvision.transforms as transforms +import os +import torch.quantization +import torch.quantization._numeric_suite as ns +from torch.quantization import ( + default_eval_fn, + default_qconfig, + quantize, +) + +############################################################################## +# Then we load the pretrained float ResNet18 model, and quantize it into qmodel. We cannot compare two arbitrary models, only a float model and the quantized model derived from it can be compared. +############################################################################## + +float_model = torchvision.models.quantization.resnet18(pretrained=True, quantize=False) +float_model.to('cpu') +float_model.eval() +float_model.fuse_model() +float_model.qconfig = torch.quantization.default_qconfig +img_data = [(torch.rand(2, 3, 10, 10, dtype=torch.float), torch.randint(0, 1, (2,), dtype=torch.long)) + for _ in range(2)] +qmodel = quantize(float_model, default_eval_fn, img_data, inplace=False) + +############################################################################## +# 1. Compare the weights of float and quantized models +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +# The first thing we usually want to compare are the weights of quantized model and float model. We can call compare_weights() from PyTorch Numeric Suite to get a dictionary wt_compare_dict with key corresponding to module names and each entry is a dictionary with two keys 'float' and 'quantized', containing the float and quantized weights. +############################################################################## + +# Takes in floating point and quantized state dict and returns a dict, with keys corresponding to the +# floating point weights and values being a dictionary of floating point and quantized weights +wt_compare_dict = ns.compare_weights(float_model.state_dict(), qmodel.state_dict()) + +print('keys of wt_compare_dict:') +print(wt_compare_dict.keys()) + +print("\nkeys of wt_compare_dict entry for conv1's weight:") +print(wt_compare_dict['conv1.weight'].keys()) +print(wt_compare_dict['conv1.weight']['float'].shape) +print(wt_compare_dict['conv1.weight']['quantized'].shape) + +############################################################################## +# Once get wt_compare_dict, users can process this dictionary in whatever way they want. Here as an example we compute the quantization error of the weights of float and quantized models as following. +############################################################################## + +# Compute the Signal-to-Quantization-Noise Ratio (SQNR) of the quantized tensor y. The SQNR reflects the +# relationship between the maximum nominal signal strength and the quantization error introduced in the +# quantization. Higher SQNR corresponds to lower quantization error. +def compute_error(x, y): + Ps = torch.norm(x) + Pn = torch.norm(x-y) + return 20*torch.log10(Ps/Pn) + +for key in wt_compare_dict: + print(key, compute_error(wt_compare_dict[key]['float'], wt_compare_dict[key]['quantized'].dequantize())) + +############################################################################## +# As another example wt_compare_dict can also be used to plot the histogram of the weights of floating point and quantized models. +############################################################################## + +import matplotlib.pyplot as plt + +f = wt_compare_dict['conv1.weight']['float'].flatten() +plt.hist(f, bins = 100) +plt.title("Floating point model weights of conv1") +plt.show() + +q = wt_compare_dict['conv1.weight']['quantized'].flatten().dequantize() +plt.hist(q, bins = 100) +plt.title("Quantized model weights of conv1") +plt.show() + +############################################################################## +""" +2. Compare float point and quantized models at corresponding locations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The second tool allows for comparison of weights and activations between float and quantized models at corresponding locations for the same input as shown in the figure below. Red arrows indicate the locations of the comparison. + +.. figure:: /_static/img/compare_output.png + +We call compare_model_outputs() from PyTorch Numeric Suite to get the activations in float model and quantized model at corresponding locations for the given input data. This API returns a dict with module names being keys. Each entry is itself a dict with two keys 'float' and 'quantized' containing the activations. +""" +############################################################################## + +data = img_data[0][0] + +# Takes in floating point and quantized model as well as input data, and returns a dict, with keys +# corresponding to the quantized module names and each entry being a dictionary with two keys 'float' and +# 'quantized', containing the activations of floating point and quantized model at matching locations. +act_compare_dict = ns.compare_model_outputs(float_model, qmodel, data) + +print('keys of act_compare_dict:') +print(act_compare_dict.keys()) + +print("\nkeys of act_compare_dict entry for conv1's output:") +print(act_compare_dict['conv1.stats'].keys()) +print(act_compare_dict['conv1.stats']['float'].shape) +print(act_compare_dict['conv1.stats']['quantized'].shape) + +############################################################################## +# This dict can be used to compare and compute the quantization error of the activations of float and quantized models as following. +############################################################################## + +for key in act_compare_dict: + print(key, compute_error(act_compare_dict[key]['float'], act_compare_dict[key]['quantized'].dequantize())) + +############################################################################## +If we want to do the comparison for more than one input data, we can do the following. +############################################################################## + +# Prepare the model by attaching the logger to both floating point module and quantized +# module if they are in the white_list. Default logger is OutputLogger, and default white_list +# is DEFAULT_NUMERIC_SUITE_COMPARE_MODEL_OUTPUT_WHITE_LIST +ns.prepare_model_outputs(float_model, qmodel) + +for data in img_data: + float_model(data[0]) + qmodel(data[0]) + +# Find the matching activation between floating point and quantized modules, and return a dict with key +# corresponding to quantized module names and each entry being a dictionary with two keys 'float' +# and 'quantized', containing the matching floating point and quantized activations logged by the logger +act_compare_dict = ns.get_matching_activations(float_model, qmodel) + +############################################################################## +# The default logger used in above APIs is OutputLogger, which is used to log the outputs of the modules. We can inherit from base Logger class and create our own logger to perform different functionalities. For example we can make a new MyOutputLogger class as below. +############################################################################## + +class MyOutputLogger(ns.Logger): + r"""Customized logger class + """ + + def __init__(self): + super(MyOutputLogger, self).__init__() + + def forward(self, x): + # Custom functionalities + # ... + return x + +############################################################################## +# And then we can pass this logger into above APIs such as: +############################################################################## + +data = img_data[0][0] +act_compare_dict = ns.compare_model_outputs(float_model, qmodel, data, Logger=MyOutputLogger) + +############################################################################## +# or: +############################################################################## + +ns.prepare_model_outputs(float_model, qmodel, MyOutputLogger) +for data in img_data: + float_model(data[0]) + qmodel(data[0]) +act_compare_dict = ns.get_matching_activations(float_model, qmodel) + +############################################################################## +""" +3. Compare a module in a quantized model with its float point equivalent, with the same input data +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The third tool allows for comparing a quantized module in a model with its float point counterpart, feeding both of them the same input and comparing their outputs as shown below. + +.. figure:: /_static/img/compare_stub.png + +In practice we call prepare_model_with_stubs() to swap the quantized module that we want to compare with the Shadow module, which is illustrated as below: + +.. figure:: /_static/img/shadow.png + +The Shadow module takes quantized module, float module and logger as input, and creates a forward path inside to make the float module to shadow quantized module sharing the same input tensor. + +The logger can be customizable, default logger is ShadowLogger and it will save the outputs of the quantized module and float module that can be used to compute the module level quantization error. + +Notice before each call of compare_model_outputs() and compare_model_stub() we need to have clean float and quantized model. This is because compare_model_outputs() and compare_model_stub() modify float and quantized model inplace, and it will cause unexpected results if call one right after another. +""" +############################################################################## + +float_model = torchvision.models.quantization.resnet18(pretrained=True, quantize=False) +float_model.to('cpu') +float_model.eval() +float_model.fuse_model() +float_model.qconfig = torch.quantization.default_qconfig +img_data = [(torch.rand(2, 3, 10, 10, dtype=torch.float), torch.randint(0, 1, (2,), dtype=torch.long)) + for _ in range(2)] +qmodel = quantize(float_model, default_eval_fn, img_data, inplace=False) + +############################################################################## +# In the following example we call compare_model_stub() from PyTorch Numeric Suite to compare QuantizableBasicBlock module with its float point equivalent. This API returns a dict with key corresponding to module names and each entry being a dictionary with two keys 'float' and 'quantized', containing the output tensors of quantized and its matching float shadow module. +############################################################################## + +data = img_data[0][0] +module_swap_list = [torchvision.models.quantization.resnet.QuantizableBasicBlock] + +# Takes in floating point and quantized model as well as input data, and returns a dict with key +# corresponding to module names and each entry being a dictionary with two keys 'float' and +# 'quantized', containing the output tensors of quantized module and its matching floating point shadow module. +ob_dict = ns.compare_model_stub(float_model, qmodel, module_swap_list, data) + +print('keys of ob_dict:') +print(ob_dict.keys()) + +print("\nkeys of ob_dict entry for layer1.0's output:") +print(ob_dict['layer1.0.stats'].keys()) +print(ob_dict['layer1.0.stats']['float'].shape) +print(ob_dict['layer1.0.stats']['quantized'].shape) + +############################################################################## +# This dict can be then used to compare and compute the module level quantization error. +############################################################################## + +for key in ob_dict: + print(key, compute_error(ob_dict[key]['float'], ob_dict[key]['quantized'].dequantize())) + +############################################################################## +# If we want to do the comparison for more than one input data, we can do the following. +############################################################################## + +ns.prepare_model_with_stubs(float_model, qmodel, module_swap_list, ns.ShadowLogger) +for data in img_data: + qmodel(data[0]) +ob_dict = ns.get_logger_dict(qmodel) + +############################################################################## +# The default logger used in above APIs is ShadowLogger, which is used to log the outputs of the quantized module and its matching float shadow module. We can inherit from base Logger class and create our own logger to perform different functionalities. For example we can make a new MyShadowLogger class as below. +############################################################################## + +class MyShadowLogger(ns.Logger): + r"""Customized logger class + """ + + def __init__(self): + super(MyShadowLogger, self).__init__() + + def forward(self, x, y): + # Custom functionalities + # ... + return x + +############################################################################## +# And then we can pass this logger into above APIs such as: +############################################################################## + +data = img_data[0][0] +ob_dict = ns.compare_model_stub(float_model, qmodel, module_swap_list, data, Logger=MyShadowLogger) + +############################################################################## +# or: +############################################################################## + +ns.prepare_model_with_stubs(float_model, qmodel, module_swap_list, MyShadowLogger) +for data in img_data: + qmodel(data[0]) +ob_dict = ns.get_logger_dict(qmodel) + +############################################################################## +"""" +Numeric Suite for Dynamic Quantization +====================================== + +Numeric Suite APIs are designed in such as way that they work for both dynamic quantized model and static quantized model. We will use a model with both LSTM and Linear modules to demonstrate the usage of Numeric Suite on dynamic quantized model. + +First we define the model as below. Notice that within this model only nn.LSTM and nn.Linear modules will be quantized dynamically and nn.Embedding will remain as floating point module after quantization. +"""" +############################################################################## + +class LSTMModel(nn.Module): + """Container module with an encoder, a recurrent module, and a decoder.""" + + def __init__(self, ntoken, ninp, nhid, nlayers, dropout=0.5): + super(LSTMModel, self).__init__() + self.encoder = nn.Embedding(ntoken, ninp) + self.rnn = nn.LSTM(ninp, nhid, nlayers, dropout=dropout) + self.decoder = nn.Linear(nhid, ntoken) + + self.init_weights() + + self.nhid = nhid + self.nlayers = nlayers + + def init_weights(self): + initrange = 0.1 + self.encoder.weight.data.uniform_(-initrange, initrange) + self.decoder.bias.data.zero_() + self.decoder.weight.data.uniform_(-initrange, initrange) + + def forward(self, input, hidden): + emb = self.encoder(input) + output, hidden = self.rnn(emb, hidden) + decoded = self.decoder(output) + return decoded, hidden + + def init_hidden(self, bsz): + weight = next(self.parameters()) + return (weight.new_zeros(self.nlayers, bsz, self.nhid), + weight.new_zeros(self.nlayers, bsz, self.nhid)) + +############################################################################## +# Then we create the float_model and quantize it into qmodel. +############################################################################## + +ntokens = 10 + +float_model = LSTMModel( + ntoken = ntokens, + ninp = 512, + nhid = 256, + nlayers = 5, +) + +float_model.eval() + +qmodel = torch.quantization.quantize_dynamic( + float_model, {nn.LSTM, nn.Linear}, dtype=torch.qint8 +) + +############################################################################## +"""" +1. Compare the weights of float and quantized models +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +We first call compare_weights() from PyTorch Numeric Suite to get a dictionary wt_compare_dict with key corresponding to module names and each entry is a dictionary with two keys 'float' and 'quantized', containing the float and quantized weights. +"""" +############################################################################## + +wt_compare_dict = ns.compare_weights(float_model.state_dict(), qmodel.state_dict()) + +############################################################################## +# Once we get wt_compare_dict, it can be used to compare and compute the quantization error of the weights of float and quantized models as following. +############################################################################## + +for key in wt_compare_dict: + if wt_compare_dict[key]['quantized'].is_quantized: + print(key, compute_error(wt_compare_dict[key]['float'], wt_compare_dict[key]['quantized'].dequantize())) + else: + print(key, compute_error(wt_compare_dict[key]['float'], wt_compare_dict[key]['quantized'])) + +############################################################################## +"""" +The Inf value in encoder.weight entry above is because encoder module is not quantized and the weights are the same in both floating point and quantized models. + +2. Compare float point and quantized models at corresponding locations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Then we call compare_model_outputs() from PyTorch Numeric Suite to get the activations in float model and quantized model at corresponding locations for the given input data. This API returns a dict with module names being keys. Each entry is itself a dict with two keys 'float' and 'quantized' containing the activations. Notice that this sequence model has two inputs, and we can pass both inputs into compare_model_outputs() and compare_model_stub(). +"""" +############################################################################## + +input_ = torch.randint(ntokens, (1, 1), dtype=torch.long) +hidden = float_model.init_hidden(1) + +act_compare_dict = ns.compare_model_outputs(float_model, qmodel, input_, hidden) +print(act_compare_dict.keys()) + +############################################################################## +# This dict can be used to compare and compute the quantization error of the activations of float and quantized models as following. The LSTM module in this model has two outputs, in this example we compute the error of the first output as below. +############################################################################## + +for key in act_compare_dict: + print(key, compute_error(act_compare_dict[key]['float'][0], act_compare_dict[key]['quantized'][0])) + +############################################################################## +"""" +3. Compare a module in a quantized model with its float point equivalent, with the same input data +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Next we call compare_model_stub() from PyTorch Numeric Suite to compare LSTM and Linear module with its float point equivalent. This API returns a dict with key corresponding to module names and each entry being a dictionary with two keys 'float' and 'quantized', containing the output tensors of quantized and its matching float shadow module. + +We reset the model first. +"""" +############################################################################## + +float_model = LSTMModel( + ntoken = ntokens, + ninp = 512, + nhid = 256, + nlayers = 5, +) +float_model.eval() + +qmodel = torch.quantization.quantize_dynamic( + float_model, {nn.LSTM, nn.Linear}, dtype=torch.qint8 +) + +############################################################################## +# Next we call compare_model_stub() from PyTorch Numeric Suite to compare LSTM and Linear module with its float point equivalent. This API returns a dict with key corresponding to module names and each entry being a dictionary with two keys 'float' and 'quantized', containing the output tensors of quantized and its matching float shadow module. +############################################################################## + +module_swap_list = [nn.Linear, nn.LSTM] +ob_dict = ns.compare_model_stub(float_model, qmodel, module_swap_list, input_, hidden) +print(ob_dict.keys()) + +############################################################################## +# This dict can be then used to compare and compute the module level quantization error. +############################################################################## + +for key in ob_dict: + print(key, compute_error(ob_dict[key]['float'], ob_dict[key]['quantized'])) + +############################################################################## +# SQNR of 40 dB is high and this is a situation where we have very good numerical alignment between the floating point and quantized model. +############################################################################## From d81933fc971397c30a6ca935db9ef6653e8f4a30 Mon Sep 17 00:00:00 2001 From: Jessica Lin Date: Tue, 14 Jul 2020 18:27:54 -0700 Subject: [PATCH 16/33] jlin27_numeric_suite_tutorial Made some syntax edits because original headings were not rendering properly and breaking the build: - Removed the lines of pound sign (#) delimiters under text because when placed under text, it renders them all as headers - Add lines of pound delimiters above certain blocks of text to force them to show up as plain text between the code rather than comments with the code - Added code syntax (e.g.``compare_weights``) Suggestions: - Link to code or documentation (for example in the beginning when referencing new code or new concepts) - Add a conclusion section with links to references or learn more at the end - Examples: https://pytorch.org/tutorials/intermediate/dynamic_quantization_bert_tutorial.html#conclusion Fixes: - Currently the tutorial references images in `/_static/img/` but they are placed in `/_static/`. Make sure these match up. --- prototype_source/numeric_suite_tutorial.py | 175 ++++++++++----------- 1 file changed, 79 insertions(+), 96 deletions(-) diff --git a/prototype_source/numeric_suite_tutorial.py b/prototype_source/numeric_suite_tutorial.py index c1da4a14929..6bdcfb431de 100644 --- a/prototype_source/numeric_suite_tutorial.py +++ b/prototype_source/numeric_suite_tutorial.py @@ -10,15 +10,18 @@ One important step of debugging is to measure the statistics of the float model and its corresponding quantized model to know where are they differ most. We built a suite of numeric tools called PyTorch Numeric Suite in PyTorch quantization to enable the measurement of the statistics between quantized module and float module to support quantization debugging efforts. Even for the quantized model with good accuracy, PyTorch Numeric Suite can still be used as the profiling tool to better understand the quantization error within the model and provide the guidance for further optimization. -PyTorch Numeric Suite crrently supports models quantized through both static quantization and dynamic quantization with unified APIs. +PyTorch Numeric Suite currently supports models quantized through both static quantization and dynamic quantization with unified APIs. In this tutorial we will first use ResNet18 as an example to show how to use PyTorch Numeric Suite to measure the statistics between static quantized model and float model in eager mode. Then we will use LSTM based sequence model as an example to show the usage of PyTorch Numeric Suite for dynamic quantized model. Numeric Suite for Static Quantization ------------------------------------- +Setup +^^^^^^ We’ll start by doing the necessary imports: """ + ############################################################################## from __future__ import print_function, division, absolute_import @@ -39,7 +42,7 @@ ############################################################################## # Then we load the pretrained float ResNet18 model, and quantize it into qmodel. We cannot compare two arbitrary models, only a float model and the quantized model derived from it can be compared. -############################################################################## + float_model = torchvision.models.quantization.resnet18(pretrained=True, quantize=False) float_model.to('cpu') @@ -53,12 +56,11 @@ ############################################################################## # 1. Compare the weights of float and quantized models # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -# The first thing we usually want to compare are the weights of quantized model and float model. We can call compare_weights() from PyTorch Numeric Suite to get a dictionary wt_compare_dict with key corresponding to module names and each entry is a dictionary with two keys 'float' and 'quantized', containing the float and quantized weights. -############################################################################## - -# Takes in floating point and quantized state dict and returns a dict, with keys corresponding to the +# The first thing we usually want to compare are the weights of quantized model and float model. +# We can call ``compare_weights()`` from PyTorch Numeric Suite to get a ``dictionary wt_compare_dict`` with key corresponding to module names and each entry is a dictionary with two keys 'float' and 'quantized', containing the float and quantized weights. +# ``compare_weights()`` takes in floating point and quantized state dict and returns a dict, with keys corresponding to the # floating point weights and values being a dictionary of floating point and quantized weights + wt_compare_dict = ns.compare_weights(float_model.state_dict(), qmodel.state_dict()) print('keys of wt_compare_dict:') @@ -69,13 +71,13 @@ print(wt_compare_dict['conv1.weight']['float'].shape) print(wt_compare_dict['conv1.weight']['quantized'].shape) -############################################################################## -# Once get wt_compare_dict, users can process this dictionary in whatever way they want. Here as an example we compute the quantization error of the weights of float and quantized models as following. -############################################################################## -# Compute the Signal-to-Quantization-Noise Ratio (SQNR) of the quantized tensor y. The SQNR reflects the +############################################################################## +# Once get ``wt_compare_dict``, users can process this dictionary in whatever way they want. Here as an example we compute the quantization error of the weights of float and quantized models as following. +# Compute the Signal-to-Quantization-Noise Ratio (SQNR) of the quantized tensor ``y```. The SQNR reflects the # relationship between the maximum nominal signal strength and the quantization error introduced in the # quantization. Higher SQNR corresponds to lower quantization error. + def compute_error(x, y): Ps = torch.norm(x) Pn = torch.norm(x-y) @@ -85,8 +87,7 @@ def compute_error(x, y): print(key, compute_error(wt_compare_dict[key]['float'], wt_compare_dict[key]['quantized'].dequantize())) ############################################################################## -# As another example wt_compare_dict can also be used to plot the histogram of the weights of floating point and quantized models. -############################################################################## +# As another example ``wt_compare_dict`` can also be used to plot the histogram of the weights of floating point and quantized models. import matplotlib.pyplot as plt @@ -100,22 +101,22 @@ def compute_error(x, y): plt.title("Quantized model weights of conv1") plt.show() -############################################################################## -""" -2. Compare float point and quantized models at corresponding locations -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The second tool allows for comparison of weights and activations between float and quantized models at corresponding locations for the same input as shown in the figure below. Red arrows indicate the locations of the comparison. -.. figure:: /_static/img/compare_output.png - -We call compare_model_outputs() from PyTorch Numeric Suite to get the activations in float model and quantized model at corresponding locations for the given input data. This API returns a dict with module names being keys. Each entry is itself a dict with two keys 'float' and 'quantized' containing the activations. -""" ############################################################################## - +# +# 2. Compare float point and quantized models at corresponding locations +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# The second tool allows for comparison of weights and activations between float and quantized models at corresponding locations for the same input as shown in the figure below. Red arrows indicate the locations of the comparison. +# +# .. figure:: /_static/img/compare_output.png +# +# We call ``compare_model_outputs()`` from PyTorch Numeric Suite to get the activations in float model and quantized model at corresponding locations for the given input data. This API returns a dict with module names being keys. Each entry is itself a dict with two keys 'float' and 'quantized' containing the activations. data = img_data[0][0] -# Takes in floating point and quantized model as well as input data, and returns a dict, with keys +############################################################################## +# ``img_data`` in floating point and quantized model as well as input data, and returns a dict, with keys # corresponding to the quantized module names and each entry being a dictionary with two keys 'float' and # 'quantized', containing the activations of floating point and quantized model at matching locations. act_compare_dict = ns.compare_model_outputs(float_model, qmodel, data) @@ -130,15 +131,11 @@ def compute_error(x, y): ############################################################################## # This dict can be used to compare and compute the quantization error of the activations of float and quantized models as following. -############################################################################## - for key in act_compare_dict: print(key, compute_error(act_compare_dict[key]['float'], act_compare_dict[key]['quantized'].dequantize())) ############################################################################## -If we want to do the comparison for more than one input data, we can do the following. -############################################################################## - +# If we want to do the comparison for more than one input data, we can do the following. # Prepare the model by attaching the logger to both floating point module and quantized # module if they are in the white_list. Default logger is OutputLogger, and default white_list # is DEFAULT_NUMERIC_SUITE_COMPARE_MODEL_OUTPUT_WHITE_LIST @@ -148,14 +145,15 @@ def compute_error(x, y): float_model(data[0]) qmodel(data[0]) +############################################################################## # Find the matching activation between floating point and quantized modules, and return a dict with key # corresponding to quantized module names and each entry being a dictionary with two keys 'float' # and 'quantized', containing the matching floating point and quantized activations logged by the logger act_compare_dict = ns.get_matching_activations(float_model, qmodel) + ############################################################################## # The default logger used in above APIs is OutputLogger, which is used to log the outputs of the modules. We can inherit from base Logger class and create our own logger to perform different functionalities. For example we can make a new MyOutputLogger class as below. -############################################################################## class MyOutputLogger(ns.Logger): r"""Customized logger class @@ -171,14 +169,12 @@ def forward(self, x): ############################################################################## # And then we can pass this logger into above APIs such as: -############################################################################## data = img_data[0][0] act_compare_dict = ns.compare_model_outputs(float_model, qmodel, data, Logger=MyOutputLogger) ############################################################################## # or: -############################################################################## ns.prepare_model_outputs(float_model, qmodel, MyOutputLogger) for data in img_data: @@ -186,26 +182,26 @@ def forward(self, x): qmodel(data[0]) act_compare_dict = ns.get_matching_activations(float_model, qmodel) -############################################################################## -""" -3. Compare a module in a quantized model with its float point equivalent, with the same input data -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The third tool allows for comparing a quantized module in a model with its float point counterpart, feeding both of them the same input and comparing their outputs as shown below. - -.. figure:: /_static/img/compare_stub.png -In practice we call prepare_model_with_stubs() to swap the quantized module that we want to compare with the Shadow module, which is illustrated as below: -.. figure:: /_static/img/shadow.png - -The Shadow module takes quantized module, float module and logger as input, and creates a forward path inside to make the float module to shadow quantized module sharing the same input tensor. - -The logger can be customizable, default logger is ShadowLogger and it will save the outputs of the quantized module and float module that can be used to compute the module level quantization error. - -Notice before each call of compare_model_outputs() and compare_model_stub() we need to have clean float and quantized model. This is because compare_model_outputs() and compare_model_stub() modify float and quantized model inplace, and it will cause unexpected results if call one right after another. -""" ############################################################################## +# +# 3. Compare a module in a quantized model with its float point equivalent, with the same input data +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# The third tool allows for comparing a quantized module in a model with its float point counterpart, feeding both of them the same input and comparing their outputs as shown below. +# +# .. figure:: /_static/img/compare_stub.png +# +# In practice we call prepare_model_with_stubs() to swap the quantized module that we want to compare with the Shadow module, which is illustrated as below: +# +# .. figure:: /_static/img/shadow.png +# +# The Shadow module takes quantized module, float module and logger as input, and creates a forward path inside to make the float module to shadow quantized module sharing the same input tensor. +# +# The logger can be customizable, default logger is ShadowLogger and it will save the outputs of the quantized module and float module that can be used to compute the module level quantization error. +# +# Notice before each call of compare_model_outputs() and compare_model_stub() we need to have clean float and quantized model. This is because compare_model_outputs() and compare_model_stub() modify float and quantized model inplace, and it will cause unexpected results if call one right after another. float_model = torchvision.models.quantization.resnet18(pretrained=True, quantize=False) float_model.to('cpu') @@ -218,11 +214,11 @@ def forward(self, x): ############################################################################## # In the following example we call compare_model_stub() from PyTorch Numeric Suite to compare QuantizableBasicBlock module with its float point equivalent. This API returns a dict with key corresponding to module names and each entry being a dictionary with two keys 'float' and 'quantized', containing the output tensors of quantized and its matching float shadow module. -############################################################################## data = img_data[0][0] module_swap_list = [torchvision.models.quantization.resnet.QuantizableBasicBlock] +############################################################################## # Takes in floating point and quantized model as well as input data, and returns a dict with key # corresponding to module names and each entry being a dictionary with two keys 'float' and # 'quantized', containing the output tensors of quantized module and its matching floating point shadow module. @@ -238,14 +234,12 @@ def forward(self, x): ############################################################################## # This dict can be then used to compare and compute the module level quantization error. -############################################################################## for key in ob_dict: print(key, compute_error(ob_dict[key]['float'], ob_dict[key]['quantized'].dequantize())) ############################################################################## # If we want to do the comparison for more than one input data, we can do the following. -############################################################################## ns.prepare_model_with_stubs(float_model, qmodel, module_swap_list, ns.ShadowLogger) for data in img_data: @@ -254,7 +248,6 @@ def forward(self, x): ############################################################################## # The default logger used in above APIs is ShadowLogger, which is used to log the outputs of the quantized module and its matching float shadow module. We can inherit from base Logger class and create our own logger to perform different functionalities. For example we can make a new MyShadowLogger class as below. -############################################################################## class MyShadowLogger(ns.Logger): r"""Customized logger class @@ -270,30 +263,29 @@ def forward(self, x, y): ############################################################################## # And then we can pass this logger into above APIs such as: -############################################################################## data = img_data[0][0] ob_dict = ns.compare_model_stub(float_model, qmodel, module_swap_list, data, Logger=MyShadowLogger) ############################################################################## # or: -############################################################################## ns.prepare_model_with_stubs(float_model, qmodel, module_swap_list, MyShadowLogger) for data in img_data: qmodel(data[0]) ob_dict = ns.get_logger_dict(qmodel) -############################################################################## -"""" -Numeric Suite for Dynamic Quantization -====================================== - -Numeric Suite APIs are designed in such as way that they work for both dynamic quantized model and static quantized model. We will use a model with both LSTM and Linear modules to demonstrate the usage of Numeric Suite on dynamic quantized model. +############################################################################### +# Numeric Suite for Dynamic Quantization +# ------------------------------------- +# +# Numeric Suite APIs are designed in such as way that they work for both dynamic quantized model and static quantized model. We will use a model with both LSTM and Linear modules to demonstrate the usage of Numeric Suite on dynamic quantized model. +# -First we define the model as below. Notice that within this model only nn.LSTM and nn.Linear modules will be quantized dynamically and nn.Embedding will remain as floating point module after quantization. -"""" -############################################################################## +################################# +# Setup +# ^^^^^^ +# First we define the model as below. Notice that within this model only nn. LSTM and nn.Linear modules will be quantized dynamically and nn. Embedding will remain as floating point module after quantization. class LSTMModel(nn.Module): """Container module with an encoder, a recurrent module, and a decoder.""" @@ -327,8 +319,7 @@ def init_hidden(self, bsz): weight.new_zeros(self.nlayers, bsz, self.nhid)) ############################################################################## -# Then we create the float_model and quantize it into qmodel. -############################################################################## +# Then we create the ``float_model``` and quantize it into qmodel. ntokens = 10 @@ -346,19 +337,16 @@ def init_hidden(self, bsz): ) ############################################################################## -"""" -1. Compare the weights of float and quantized models -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -We first call compare_weights() from PyTorch Numeric Suite to get a dictionary wt_compare_dict with key corresponding to module names and each entry is a dictionary with two keys 'float' and 'quantized', containing the float and quantized weights. -"""" -############################################################################## +# +# 1. Compare the weights of float and quantized models +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# We first call ``compare_weights()`` from PyTorch Numeric Suite to get a dictionary ``wt_compare_dict`` with key corresponding to module names and each entry is a dictionary with two keys 'float' and 'quantized', containing the float and quantized weights. wt_compare_dict = ns.compare_weights(float_model.state_dict(), qmodel.state_dict()) ############################################################################## -# Once we get wt_compare_dict, it can be used to compare and compute the quantization error of the weights of float and quantized models as following. -############################################################################## +# Once we get ``wt_compare_dict``, it can be used to compare and compute the quantization error of the weights of float and quantized models as following. for key in wt_compare_dict: if wt_compare_dict[key]['quantized'].is_quantized: @@ -367,15 +355,14 @@ def init_hidden(self, bsz): print(key, compute_error(wt_compare_dict[key]['float'], wt_compare_dict[key]['quantized'])) ############################################################################## -"""" -The Inf value in encoder.weight entry above is because encoder module is not quantized and the weights are the same in both floating point and quantized models. +# +# The Inf value in ``encoder.weight`` entry above is because encoder module is not quantized and the weights are the same in both floating point and quantized models. +# +# 2. Compare float point and quantized models at corresponding locations +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# Then we call ``compare_model_outputs()`` from PyTorch Numeric Suite to get the activations in float model and quantized model at corresponding locations for the given input data. This API returns a dict with module names being keys. Each entry is itself a dict with two keys 'float' and 'quantized' containing the activations. Notice that this sequence model has two inputs, and we can pass both inputs into compare_model_outputs() and compare_model_stub(). -2. Compare float point and quantized models at corresponding locations -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Then we call compare_model_outputs() from PyTorch Numeric Suite to get the activations in float model and quantized model at corresponding locations for the given input data. This API returns a dict with module names being keys. Each entry is itself a dict with two keys 'float' and 'quantized' containing the activations. Notice that this sequence model has two inputs, and we can pass both inputs into compare_model_outputs() and compare_model_stub(). -"""" -############################################################################## input_ = torch.randint(ntokens, (1, 1), dtype=torch.long) hidden = float_model.init_hidden(1) @@ -385,21 +372,20 @@ def init_hidden(self, bsz): ############################################################################## # This dict can be used to compare and compute the quantization error of the activations of float and quantized models as following. The LSTM module in this model has two outputs, in this example we compute the error of the first output as below. -############################################################################## + for key in act_compare_dict: print(key, compute_error(act_compare_dict[key]['float'][0], act_compare_dict[key]['quantized'][0])) ############################################################################## -"""" -3. Compare a module in a quantized model with its float point equivalent, with the same input data -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Next we call compare_model_stub() from PyTorch Numeric Suite to compare LSTM and Linear module with its float point equivalent. This API returns a dict with key corresponding to module names and each entry being a dictionary with two keys 'float' and 'quantized', containing the output tensors of quantized and its matching float shadow module. +# +# 3. Compare a module in a quantized model with its float point equivalent, with the same input data +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# Next we call compare_model_stub() from PyTorch Numeric Suite to compare LSTM and Linear module with its float point equivalent. This API returns a dict with key corresponding to module names and each entry being a dictionary with two keys 'float' and 'quantized', containing the output tensors of quantized and its matching float shadow module. +# +# We reset the model first. -We reset the model first. -"""" -############################################################################## float_model = LSTMModel( ntoken = ntokens, @@ -414,8 +400,7 @@ def init_hidden(self, bsz): ) ############################################################################## -# Next we call compare_model_stub() from PyTorch Numeric Suite to compare LSTM and Linear module with its float point equivalent. This API returns a dict with key corresponding to module names and each entry being a dictionary with two keys 'float' and 'quantized', containing the output tensors of quantized and its matching float shadow module. -############################################################################## +# Next we call ``compare_model_stub()`` from PyTorch Numeric Suite to compare LSTM and Linear module with its float point equivalent. This API returns a dict with key corresponding to module names and each entry being a dictionary with two keys 'float' and 'quantized', containing the output tensors of quantized and its matching float shadow module. module_swap_list = [nn.Linear, nn.LSTM] ob_dict = ns.compare_model_stub(float_model, qmodel, module_swap_list, input_, hidden) @@ -423,11 +408,9 @@ def init_hidden(self, bsz): ############################################################################## # This dict can be then used to compare and compute the module level quantization error. -############################################################################## for key in ob_dict: print(key, compute_error(ob_dict[key]['float'], ob_dict[key]['quantized'])) ############################################################################## # SQNR of 40 dB is high and this is a situation where we have very good numerical alignment between the floating point and quantized model. -############################################################################## From 2e9a502df4aa5ac9b33cb23341c061fb5829ca4c Mon Sep 17 00:00:00 2001 From: hx89 <43588773+hx89@users.noreply.github.com> Date: Tue, 14 Jul 2020 23:12:25 -0700 Subject: [PATCH 17/33] Delete compare_output.png --- _static/compare_output.png | Bin 27076 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 _static/compare_output.png diff --git a/_static/compare_output.png b/_static/compare_output.png deleted file mode 100644 index 4ece4d114833f0a4d975fe79b58737f5494e9c02..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27076 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 Date: Tue, 14 Jul 2020 23:12:39 -0700 Subject: [PATCH 18/33] Delete compare_stub.png --- _static/compare_stub.png | Bin 22859 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 _static/compare_stub.png diff --git a/_static/compare_stub.png b/_static/compare_stub.png deleted file mode 100644 index 8140a99b182c16e71410b1460ab7cb6bdf1e262c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22859 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 Date: Tue, 14 Jul 2020 23:12:51 -0700 Subject: [PATCH 19/33] Delete shadow.png --- _static/shadow.png | Bin 15597 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 _static/shadow.png diff --git a/_static/shadow.png b/_static/shadow.png deleted file mode 100644 index e09d0b87f01d494918d2799dc1e72e9b8f6c61bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15597 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 Date: Tue, 14 Jul 2020 23:14:11 -0700 Subject: [PATCH 20/33] Add files via upload --- _static/img/compare_output.png | Bin 0 -> 27076 bytes _static/img/compare_stub.png | Bin 0 -> 22859 bytes _static/img/shadow.png | Bin 0 -> 15597 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 _static/img/compare_output.png create mode 100644 _static/img/compare_stub.png create mode 100644 _static/img/shadow.png diff --git a/_static/img/compare_output.png b/_static/img/compare_output.png new file mode 100644 index 0000000000000000000000000000000000000000..4ece4d114833f0a4d975fe79b58737f5494e9c02 GIT binary patch literal 27076 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 Date: Wed, 15 Jul 2020 11:54:51 -0700 Subject: [PATCH 21/33] RPC profiling recipe (#1068) * Initial commit * Update * Complete most of recipe * Add image * Link image * Remove extra file * update * Update * update --- _static/img/rpc_trace_img.png | Bin 0 -> 314576 bytes recipes_source/distributed_rpc_profiling.rst | 314 +++++++++++++++++++ recipes_source/recipes_index.rst | 8 + 3 files changed, 322 insertions(+) create mode 100644 _static/img/rpc_trace_img.png create mode 100644 recipes_source/distributed_rpc_profiling.rst diff --git a/_static/img/rpc_trace_img.png b/_static/img/rpc_trace_img.png new file mode 100644 index 0000000000000000000000000000000000000000..4faaf97ad4769fe4ca1682ad89ef75b9413668a3 GIT binary patch literal 314576 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