From 7f76cfda03c52fee0d785063a90eb0a1fe945f36 Mon Sep 17 00:00:00 2001 From: Richard Zou Date: Mon, 6 Jan 2025 00:14:51 -0500 Subject: [PATCH 001/347] Clarify Python custom operators do not work without Python (#3212) See https://github.com/pytorch/pytorch/issues/143786 --- advanced_source/custom_ops_landing_page.rst | 1 + advanced_source/python_custom_ops.py | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/advanced_source/custom_ops_landing_page.rst b/advanced_source/custom_ops_landing_page.rst index 0f21ee2c4cf..1867fc29acb 100644 --- a/advanced_source/custom_ops_landing_page.rst +++ b/advanced_source/custom_ops_landing_page.rst @@ -23,6 +23,7 @@ You may wish to author a custom operator from Python (as opposed to C++) if: respect to ``torch.compile`` and ``torch.export``. - you have some Python bindings to C++/CUDA kernels and want those to compose with PyTorch subsystems (like ``torch.compile`` or ``torch.autograd``) +- you are using Python (and not a C++-only environment like AOTInductor). Integrating custom C++ and/or CUDA code with PyTorch ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/advanced_source/python_custom_ops.py b/advanced_source/python_custom_ops.py index ec7ed7782be..5ace0b40897 100644 --- a/advanced_source/python_custom_ops.py +++ b/advanced_source/python_custom_ops.py @@ -30,6 +30,12 @@ into the function). - Adding training support to an arbitrary Python function +Use :func:`torch.library.custom_op` to create Python custom operators. +Use the C++ ``TORCH_LIBRARY`` APIs to create C++ custom operators (these +work in Python-less environments). +See the `Custom Operators Landing Page `_ +for more details. + Please note that if your operation can be expressed as a composition of existing PyTorch operators, then there is usually no need to use the custom operator API -- everything (for example ``torch.compile``, training support) should From 20c3111ed705976071acdb1f1da65fbecf63334f Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Mon, 6 Jan 2025 10:20:38 -0800 Subject: [PATCH 002/347] Add new terms to en-wordlist.txt (#3215) functionalizes functionalization --- en-wordlist.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/en-wordlist.txt b/en-wordlist.txt index 50a577bedd4..7c2ed6c398c 100644 --- a/en-wordlist.txt +++ b/en-wordlist.txt @@ -392,6 +392,8 @@ FlexAttention fp frontend functionalized +functionalizes +functionalization functorch fuser geomean From 46943d65235d6d21602f0f34d829101f8e6946ac Mon Sep 17 00:00:00 2001 From: Zesheng Zong Date: Tue, 7 Jan 2025 05:23:49 +0800 Subject: [PATCH 003/347] Fix invalid page links in tutorial (#3205) Co-authored-by: Alanna Burke Co-authored-by: Svetlana Karslioglu --- intermediate_source/inductor_debug_cpu.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/intermediate_source/inductor_debug_cpu.py b/intermediate_source/inductor_debug_cpu.py index f44d5bd76b1..3545afeac05 100644 --- a/intermediate_source/inductor_debug_cpu.py +++ b/intermediate_source/inductor_debug_cpu.py @@ -19,8 +19,8 @@ # # Meanwhile, you may also find related tutorials about ``torch.compile`` # around `basic usage `_, -# comprehensive `troubleshooting `_ -# and GPU-specific knowledge like `GPU performance profiling `_. +# comprehensive `troubleshooting `_ +# and GPU-specific knowledge like `GPU performance profiling `_. # # We will start debugging with a motivating example that triggers compilation issues and accuracy problems # by demonstrating the process of debugging to pinpoint the problems. From b83178aa549a518f0bd15b9234534fc851eb19eb Mon Sep 17 00:00:00 2001 From: "G.O.D" <32255912+gameofdimension@users.noreply.github.com> Date: Wed, 8 Jan 2025 01:00:47 +0800 Subject: [PATCH 004/347] fix typo (#3149) Co-authored-by: Svetlana Karslioglu --- recipes_source/distributed_device_mesh.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recipes_source/distributed_device_mesh.rst b/recipes_source/distributed_device_mesh.rst index ee437f06f96..17ce3c4859e 100644 --- a/recipes_source/distributed_device_mesh.rst +++ b/recipes_source/distributed_device_mesh.rst @@ -164,7 +164,7 @@ DeviceMesh allows users to slice child mesh from the parent mesh and re-use the # Users can access the underlying process group thru `get_group` API. replicate_group = hsdp_mesh["replicate"].get_group() - shard_group = hsdp_mesh["Shard"].get_group() + shard_group = hsdp_mesh["shard"].get_group() tp_group = tp_mesh.get_group() From 090215790d4cfd1e473266c0bdbd29c7b46c5292 Mon Sep 17 00:00:00 2001 From: Napuh <55241721+Napuh@users.noreply.github.com> Date: Tue, 7 Jan 2025 18:02:33 +0100 Subject: [PATCH 005/347] Updated broken link on ONNX beginner tutorial (#3206) Co-authored-by: Alanna Burke Co-authored-by: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> Co-authored-by: Svetlana Karslioglu --- beginner_source/onnx/README.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beginner_source/onnx/README.txt b/beginner_source/onnx/README.txt index 61485869c99..6a598f9b9f5 100644 --- a/beginner_source/onnx/README.txt +++ b/beginner_source/onnx/README.txt @@ -3,7 +3,7 @@ ONNX 1. intro_onnx.py Introduction to ONNX - https://pytorch.org/tutorials/onnx/intro_onnx.html + https://pytorch.org/tutorials/beginner/onnx/intro_onnx.html 2. export_simple_model_to_onnx_tutorial.py Exporting a PyTorch model to ONNX From dea46739cd4183c98f446adb1e6157279937fe95 Mon Sep 17 00:00:00 2001 From: Yuanhao Ji Date: Wed, 8 Jan 2025 02:46:19 +0800 Subject: [PATCH 006/347] Fix dead link for compiler troubleshooting (#3201) * Fix dead link of compiler troubleshooting --------- Co-authored-by: Svetlana Karslioglu --- intermediate_source/inductor_debug_cpu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intermediate_source/inductor_debug_cpu.py b/intermediate_source/inductor_debug_cpu.py index 3545afeac05..4b6d62c0b0d 100644 --- a/intermediate_source/inductor_debug_cpu.py +++ b/intermediate_source/inductor_debug_cpu.py @@ -343,7 +343,7 @@ def forward2(self, arg0_1): return (neg,) ###################################################################### -# For more usage details about Minifier, please refer to `Troubleshooting `_. +# For more usage details about Minifier, please refer to `Troubleshooting `_. ###################################################################### From 355f2816aac101220ee732baf0d5aa43a10af61e Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Tue, 7 Jan 2025 10:47:09 -0800 Subject: [PATCH 007/347] Redirect tracing_based_selective_build.rst to the new ET tutorial (#3148) --- .../tracing_based_selective_build.rst | 199 +----------------- 1 file changed, 4 insertions(+), 195 deletions(-) diff --git a/prototype_source/tracing_based_selective_build.rst b/prototype_source/tracing_based_selective_build.rst index 811ca1cf897..a1b56072051 100644 --- a/prototype_source/tracing_based_selective_build.rst +++ b/prototype_source/tracing_based_selective_build.rst @@ -1,201 +1,10 @@ (prototype) Tracing-based Selective Build Mobile Interpreter in Android and iOS =============================================================================== +This tutorial has been replaced with a newer tutorial on this topic: https://pytorch.org/executorch/stable/kernel-library-selective-build.html -*Author*: Chen Lai , Dhruv Matani +Redirecting in 3 seconds... -.. warning:: - Tracing-based selective build a prototype feature to minimize library size. Since the traced result relies on the model input and traced environment, if the tracer runs in a different environment than mobile interpreter, the operator list might be different from the actual used operator list and missing operators error might raise. +.. raw:: html -Introduction ------------- - - -This tutorial introduces a new way to custom build mobile interpreter to further optimize mobile interpreter size. It restricts the set of operators included in the compiled binary to only the set of operators actually needed by target models. It is a technique to reduce the binary size of PyTorch for mobile deployments. Tracing Based Selective Build runs a model with specific representative inputs, and records which operators were called. The build then includes just those operators. - - -Following are the processes to use tracing-based selective approach to build a custom mobile interpreter. - -1. *Prepare model with bundled input* - -.. code:: python - - import numpy as np - import torch - import torch.jit - import torch.utils - import torch.utils.bundled_inputs - from PIL import Image - from torchvision import transforms - - # Step 1. Get the model - model = torch.hub.load('pytorch/vision:v0.7.0', 'deeplabv3_resnet50', pretrained=True) - model.eval() - - scripted_module = torch.jit.script(model) - # Export full jit version model (not compatible lite interpreter), leave it here for comparison - scripted_module.save("deeplabv3_scripted.pt") - # Export lite interpreter version model (compatible with lite interpreter) - # path = "" - - scripted_module._save_for_lite_interpreter(f"${path}/deeplabv3_scripted.ptl") - - model_file = f"${path}/deeplabv3_scripted.ptl" - - # Step 2. Prepare inputs for the model - input_image_1 = Image.open(f"${path}/dog.jpg") - preprocess = transforms.Compose([ - transforms.ToTensor(), - transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), - ]) - - input_tensor_1 = preprocess(input_image_1) - input_batch_1 = input_tensor_1.unsqueeze(0) # create a mini-batch as expected by the model - - scripted_module = torch.jit.load(model_file) - scripted_module.forward(input_batch_1) # optional, to validate the model can run with the input_batch_1 - - input_image_2 = Image.open(f"${path}/deeplab.jpg") - input_tensor_2 = preprocess(input_image_2) - input_batch_2 = input_tensor_2.unsqueeze(0) # create a mini-batch as expected by the model - - scripted_module = torch.jit.load(model_file) - scripted_module.forward(input_batch_2) # optional, to validate the model can run with the input_batch_2 - - # Step 3. Bundle the model with the prepared input from step2. Can bundle as many input as possible. - bundled_model_input = [ - (torch.utils.bundled_inputs.bundle_large_tensor(input_batch_1), ), - (torch.utils.bundled_inputs.bundle_large_tensor(input_batch_2), )] - bundled_model = torch.utils.bundled_inputs.bundle_inputs(scripted_module, bundled_model_input) - bundled_model._save_for_lite_interpreter(f"${path}/deeplabv3_scripted_with_bundled_input.ptl") - -2. Build tracer - -.. code:: shell - - MACOSX_DEPLOYMENT_TARGET=10.9 CC=clang CXX=clang++ MAX_JOBS=16 TRACING_BASED=1 python setup.py develop - -3. Run tracer with the model with bundled input - -.. code:: shell - - ./build/bin/model_tracer --model_input_path ${path}/deeplabv3_scripted_with_bundled_input.ptl --build_yaml_path ${path}/deeplabv3_scripted.yaml - - - -Android -------- - -Get the Image Segmentation demo app in Android: https://github.com/pytorch/android-demo-app/tree/master/ImageSegmentation - -1. **Tracing-based build libtorch lite for android**: Build libtorch for android for all 4 android abis (``armeabi-v7a``, ``arm64-v8a``, ``x86``, ``x86_64``) by running - -.. code-block:: bash - - SELECTED_OP_LIST=${path}/deeplabv3_scripted.yaml TRACING_BASED=1 ./scripts/build_pytorch_android.sh - -if it will be tested on Pixel 4 emulator with ``x86``, use cmd ``BUILD_LITE_INTERPRETER=1 ./scripts/build_pytorch_android.sh x86`` to specify abi to save build time. - -.. code-block:: bash - - SELECTED_OP_LIST=${path}/deeplabv3_scripted.yaml TRACING_BASED=1 ./scripts/build_pytorch_android.sh x86 - - -After the build finish, it will show the library path: - -.. code-block:: bash - - BUILD SUCCESSFUL in 55s - 134 actionable tasks: 22 executed, 112 up-to-date - + find /Users/chenlai/pytorch/android -type f -name '*aar' - + xargs ls -lah - -rw-r--r-- 1 chenlai staff 13M Feb 11 11:48 /Users/chenlai/pytorch/android/pytorch_android/build/outputs/aar/pytorch_android-release.aar - -rw-r--r-- 1 chenlai staff 36K Feb 9 16:45 /Users/chenlai/pytorch/android/pytorch_android_torchvision/build/outputs/aar/pytorch_android_torchvision-release.aar - -2. **Use the PyTorch Android libraries built from source in the ImageSegmentation app**: Create a folder `libs` in the path, the path from repository root will be `ImageSegmentation/app/libs`. Copy `pytorch_android-release` to the path ``ImageSegmentation/app/libs/pytorch_android-release.aar``. Copy `pytorch_android_torchvision` (downloaded from `Pytorch Android Torchvision Nightly `_) to the path ``ImageSegmentation/app/libs/pytorch_android_torchvision.aar``. Update the `dependencies` part of ``ImageSegmentation/app/build.gradle`` to - -.. code:: gradle - - dependencies { - implementation 'androidx.appcompat:appcompat:1.2.0' - implementation 'androidx.constraintlayout:constraintlayout:2.0.2' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test.ext:junit:1.1.2' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' - - - implementation(name:'pytorch_android-release', ext:'aar') - implementation(name:'pytorch_android_torchvision', ext:'aar') - - implementation 'com.android.support:appcompat-v7:28.0.0' - implementation 'com.facebook.fbjni:fbjni-java-only:0.0.3' - } - -Update `all projects` part in ``ImageSegmentation/build.gradle`` to - - -.. code:: gradle - - allprojects { - repositories { - google() - jcenter() - flatDir { - dirs 'libs' - } - } - } - - -3. **Test app**: Build and run the `ImageSegmentation` app in Android Studio - - -iOS ---- - -Get ImageSegmentation demo app in iOS: https://github.com/pytorch/ios-demo-app/tree/master/ImageSegmentation - - -1. **Build libtorch lite for iOS**: - -.. code-block:: bash - - SELECTED_OP_LIST=${path}/deeplabv3_scripted.yaml TRACING_BASED=1 IOS_PLATFORM=SIMULATOR ./scripts/build_ios.sh - - -2. **Remove Cocoapods from the project** (this step is only needed if you ran `pod install`): - - -.. code-block:: bash - - pod deintegrate - - -3. **Link ImageSegmentation demo app with the custom built library**: - -Open your project in XCode, go to your project Target’s **Build Phases - Link Binaries With Libraries**, click the **+** sign and add all the library files located in `build_ios/install/lib`. Navigate to the project **Build Settings**, set the value **Header Search Paths** to `build_ios/install/include` and **Library Search Paths** to `build_ios/install/lib`. -In the build settings, search for **other linker flags**. Add a custom linker flag below `-all_load`. -Finally, disable bitcode for your target by selecting the Build Settings, searching for Enable Bitcode, and set the value to **No**. - - -4. **Build and test the app in Xcode.** - - - -Conclusion ----------- - -In this tutorial, we demonstrated a new way to custom build PyTorch's efficient mobile interpreter - tracing-based selective build, in an Android and iOS app. - -We walked through an Image Segmentation example to show how to bundle inputs to a model, generated operator list by tracing the model with bundled input, and build a custom torch library from source with the operator list from tracing result. - -The custom build is still under development, and we will continue improving its size in the future. Note, however, that the APIs are subject to change in future versions. - -Thanks for reading! As always, we welcome any feedback, so please create an issue here `. - -Learn More - - -- To learn more about PyTorch Mobile, please refer to PyTorch Mobile Home Page - -* To learn more about Image Segmentation, please refer to the Image Segmentation DeepLabV3 on Android Recipe _ + From 33a52a5191dd5c1f0ed2f5fba60f2ff2d5813f2d Mon Sep 17 00:00:00 2001 From: zzb <43086393+zzb610@users.noreply.github.com> Date: Thu, 9 Jan 2025 00:37:41 +0800 Subject: [PATCH 008/347] Fix: add event sync in fsdp tutorial (#3166) Signed-off-by: zzb610 Co-authored-by: Svetlana Karslioglu --- intermediate_source/FSDP_tutorial.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/intermediate_source/FSDP_tutorial.rst b/intermediate_source/FSDP_tutorial.rst index 9b9845667fb..ce104889860 100644 --- a/intermediate_source/FSDP_tutorial.rst +++ b/intermediate_source/FSDP_tutorial.rst @@ -251,6 +251,7 @@ We add the following code snippets to a python script “FSDP_mnist.py”. init_end_event.record() if rank == 0: + init_end_event.synchronize() print(f"CUDA event elapsed time: {init_start_event.elapsed_time(init_end_event) / 1000}sec") print(f"{model}") From 82f449abde6514df1554f2be647715dcef28d2a3 Mon Sep 17 00:00:00 2001 From: Pian Pawakapan Date: Thu, 9 Jan 2025 22:55:46 +0700 Subject: [PATCH 009/347] [export] update dynamic shapes section (#3214) * Update torch_export_tutorial.py --------- Co-authored-by: Svetlana Karslioglu --- intermediate_source/torch_export_tutorial.py | 473 +++++++++++-------- 1 file changed, 283 insertions(+), 190 deletions(-) diff --git a/intermediate_source/torch_export_tutorial.py b/intermediate_source/torch_export_tutorial.py index dc5e226f86f..9acacf53629 100644 --- a/intermediate_source/torch_export_tutorial.py +++ b/intermediate_source/torch_export_tutorial.py @@ -3,7 +3,7 @@ """ torch.export Tutorial =================================================== -**Author:** William Wen, Zhengxu Chen, Angela Yi +**Author:** William Wen, Zhengxu Chen, Angela Yi, Pian Pawakapan """ ###################################################################### @@ -11,7 +11,7 @@ # .. warning:: # # ``torch.export`` and its related features are in prototype status and are subject to backwards compatibility -# breaking changes. This tutorial provides a snapshot of ``torch.export`` usage as of PyTorch 2.3. +# breaking changes. This tutorial provides a snapshot of ``torch.export`` usage as of PyTorch 2.5. # # :func:`torch.export` is the PyTorch 2.X way to export PyTorch models into # standardized model representations, intended @@ -304,237 +304,330 @@ def false_fn(x): # Constraints/Dynamic Shapes # -------------------------- # -# Ops can have different specializations/behaviors for different tensor shapes, so by default, -# ``torch.export`` requires inputs to ``ExportedProgram`` to have the same shape as the respective -# example inputs given to the initial ``torch.export.export()`` call. -# If we try to run the ``ExportedProgram`` in the example below with a tensor -# with a different shape, we get an error: +# This section covers dynamic behavior and representation of exported programs. Dynamic behavior is +# subjective to the particular model being exported, so for the most part of this tutorial, we'll focus +# on this particular toy model (with the resulting tensor shapes annotated): -class MyModule2(torch.nn.Module): +class DynamicModel(torch.nn.Module): def __init__(self): super().__init__() - self.lin = torch.nn.Linear(100, 10) + self.l = torch.nn.Linear(5, 3) + + def forward( + self, + w: torch.Tensor, # [6, 5] + x: torch.Tensor, # [4] + y: torch.Tensor, # [8, 4] + z: torch.Tensor, # [32] + ): + x0 = x + y # [8, 4] + x1 = self.l(w) # [6, 3] + x2 = x0.flatten() # [32] + x3 = x2 + z # [32] + return x1, x3 + +###################################################################### +# By default, ``torch.export`` produces a static program. One consequence of this is that at runtime, +# the program won't work on inputs with different shapes, even if they're valid in eager mode. + +w = torch.randn(6, 5) +x = torch.randn(4) +y = torch.randn(8, 4) +z = torch.randn(32) +model = DynamicModel() +ep = export(model, (w, x, y, z)) +model(w, x, torch.randn(3, 4), torch.randn(12)) +ep.module()(w, x, torch.randn(3, 4), torch.randn(12)) + +###################################################################### +# Basic concepts: symbols and guards +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# To enable dynamism, ``export()`` provides a ``dynamic_shapes`` argument. The easiest way to work with +# dynamic shapes is using ``Dim.AUTO`` and looking at the program that's returned. Dynamic behavior is specified +# at a input dimension-level; for each input we can specify a tuple of values: + +from torch.export.dynamic_shapes import Dim + +dynamic_shapes = { + "w": (Dim.AUTO, Dim.AUTO), + "x": (Dim.AUTO,), + "y": (Dim.AUTO, Dim.AUTO), + "z": (Dim.AUTO,), +} +ep = export(model, (w, x, y, z), dynamic_shapes=dynamic_shapes) - def forward(self, x, y): - return torch.nn.functional.relu(self.lin(x + y), inplace=True) +###################################################################### +# Before we look at the program that's produced, let's understand what specifying ``dynamic_shapes`` entails, +# and how that interacts with export. For every input dimension where a ``Dim`` object is specified, a symbol is +# `allocated `_, +# taking on a range of ``[2, inf]`` (why not ``[0, inf]`` or ``[1, inf]``? we'll explain later in the +# 0/1 specialization section). +# +# Export then runs model tracing, looking at each operation that's performed by the model. Each individual operation can emit +# what's called "guards"; basically boolean condition that are required to be true for the program to be valid. +# When guards involve symbols allocated for input dimensions, the program contains restrictions on what input shapes are valid; +# i.e. the program's dynamic behavior. The symbolic shapes subsystem is the part responsible for taking in all the emitted guards +# and producing a final program representation that adheres to all of these guards. Before we see this "final representation" in +# an ``ExportedProgram``, let's look at the guards emitted by the toy model we're tracing. +# +# Here, each forward input tensor is annotated with the symbol allocated at the start of tracing: -mod2 = MyModule2() -exported_mod2 = export(mod2, (torch.randn(8, 100), torch.randn(8, 100))) +class DynamicModel(torch.nn.Module): + def __init__(self): + super().__init__() + self.l = torch.nn.Linear(5, 3) -try: - exported_mod2.module()(torch.randn(10, 100), torch.randn(10, 100)) -except Exception: - tb.print_exc() + def forward( + self, + w: torch.Tensor, # [s0, s1] + x: torch.Tensor, # [s2] + y: torch.Tensor, # [s3, s4] + z: torch.Tensor, # [s5] + ): + x0 = x + y # guard: s2 == s4 + x1 = self.l(w) # guard: s1 == 5 + x2 = x0.flatten() # no guard added here + x3 = x2 + z # guard: s3 * s4 == s5 + return x1, x3 ###################################################################### -# We can relax this constraint using the ``dynamic_shapes`` argument of -# ``torch.export.export()``, which allows us to specify, using ``torch.export.Dim`` -# (`documentation `__), -# which dimensions of the input tensors are dynamic. +# Let's understand each of the operations and the emitted guards: # -# For each tensor argument of the input callable, we can specify a mapping from the dimension -# to a ``torch.export.Dim``. -# A ``torch.export.Dim`` is essentially a named symbolic integer with optional -# minimum and maximum bounds. +# - ``x0 = x + y``: This is an element-wise add with broadcasting, since ``x`` is a 1-d tensor and ``y`` a 2-d tensor. ``x`` is broadcasted along the last dimension of ``y``, emitting the guard ``s2 == s4``. +# - ``x1 = self.l(w)``: Calling ``nn.Linear()`` performs a matrix multiplication with model parameters. In export, parameters, buffers, and constants are considered program state, which is considered static, and so this is a matmul between a dynamic input (``w: [s0, s1]``), and a statically-shaped tensor. This emits the guard ``s1 == 5``. +# - ``x2 = x0.flatten()``: This call actually doesn't emit any guards! (at least none relevant to input shapes) +# - ``x3 = x2 + z``: ``x2`` has shape ``[s3*s4]`` after flattening, and this element-wise add emits ``s3 * s4 == s5``. # -# Then, the format of ``torch.export.export()``'s ``dynamic_shapes`` argument is a mapping -# from the input callable's tensor argument names, to dimension --> dim mappings as described above. -# If there is no ``torch.export.Dim`` given to a tensor argument's dimension, then that dimension is -# assumed to be static. +# Writing all of these guards down and summarizing is almost like a mathematical proof, which is what the symbolic shapes +# subsystem tries to do! In summary, we can conclude that the program must have the following input shapes to be valid: # -# The first argument of ``torch.export.Dim`` is the name for the symbolic integer, used for debugging. -# Then we can specify an optional minimum and maximum bound (inclusive). Below, we show a usage example. +# - ``w: [s0, 5]`` +# - ``x: [s2]`` +# - ``y: [s3, s2]`` +# - ``z: [s2*s3]`` # -# In the example below, our input -# ``inp1`` has an unconstrained first dimension, but the size of the second -# dimension must be in the interval [4, 18]. - -from torch.export import Dim - -inp1 = torch.randn(10, 10, 2) +# And when we do finally print out the exported program to see our result, those shapes are what we see annotated on the +# corresponding inputs: -class DynamicShapesExample1(torch.nn.Module): - def forward(self, x): - x = x[:, 2:] - return torch.relu(x) +print(ep) -inp1_dim0 = Dim("inp1_dim0") -inp1_dim1 = Dim("inp1_dim1", min=4, max=18) -dynamic_shapes1 = { - "x": {0: inp1_dim0, 1: inp1_dim1}, -} +###################################################################### +# Another feature to notice is the range_constraints field above, which contains a valid range for each symbol. This isn't +# so interesting currently, since this export call doesn't emit any guards related to symbol bounds and each base symbol has +# a generic bound, but this will come up later. +# +# So far, because we've been exporting this toy model, this experience has not been representative of how hard +# it typically is to debug dynamic shapes guards & issues. In most cases it isn't obvious what guards are being emitted, +# and which operations and parts of user code are responsible. For this toy model we pinpoint the exact lines, and the guards +# are rather intuitive. +# +# In more complicated cases, a helpful first step is always to enable verbose logging. This can be done either with the environment +# variable ``TORCH_LOGS="+dynamic"``, or interactively with ``torch._logging.set_logs(dynamic=10)``: -exported_dynamic_shapes_example1 = export(DynamicShapesExample1(), (inp1,), dynamic_shapes=dynamic_shapes1) +torch._logging.set_logs(dynamic=10) +ep = export(model, (w, x, y, z), dynamic_shapes=dynamic_shapes) -print(exported_dynamic_shapes_example1.module()(torch.randn(5, 5, 2))) +###################################################################### +# This spits out quite a handful, even with this simple toy model. The log lines here have been cut short at front and end +# to ignore unnecessary info, but looking through the logs we can see the lines relevant to what we described above; +# e.g. the allocation of symbols: -try: - exported_dynamic_shapes_example1.module()(torch.randn(8, 1, 2)) -except Exception: - tb.print_exc() +""" +create_symbol s0 = 6 for L['w'].size()[0] [2, int_oo] (_dynamo/variables/builder.py:2841 in ) +create_symbol s1 = 5 for L['w'].size()[1] [2, int_oo] (_dynamo/variables/builder.py:2841 in ) +runtime_assert True == True [statically known] +create_symbol s2 = 4 for L['x'].size()[0] [2, int_oo] (_dynamo/variables/builder.py:2841 in ) +create_symbol s3 = 8 for L['y'].size()[0] [2, int_oo] (_dynamo/variables/builder.py:2841 in ) +create_symbol s4 = 4 for L['y'].size()[1] [2, int_oo] (_dynamo/variables/builder.py:2841 in ) +create_symbol s5 = 32 for L['z'].size()[0] [2, int_oo] (_dynamo/variables/builder.py:2841 in ) +""" -try: - exported_dynamic_shapes_example1.module()(torch.randn(8, 20, 2)) -except Exception: - tb.print_exc() +###################################################################### +# The lines with `create_symbol` show when a new symbol has been allocated, and the logs also identify the tensor variable names +# and dimensions they've been allocated for. In other lines we can also see the guards emitted: -try: - exported_dynamic_shapes_example1.module()(torch.randn(8, 8, 3)) -except Exception: - tb.print_exc() +""" +runtime_assert Eq(s2, s4) [guard added] x0 = x + y # output shape: [8, 4] # dynamic_shapes_tutorial.py:16 in forward (_subclasses/fake_impls.py:845 in infer_size), for more info run with TORCHDYNAMO_EXTENDED_DEBUG_GUARD_ADDED="Eq(s2, s4)" +runtime_assert Eq(s1, 5) [guard added] x1 = self.l(w) # [6, 3] # dynamic_shapes_tutorial.py:17 in forward (_meta_registrations.py:2127 in meta_mm), for more info run with TORCHDYNAMO_EXTENDED_DEBUG_GUARD_ADDED="Eq(s1, 5)" +runtime_assert Eq(s2*s3, s5) [guard added] x3 = x2 + z # [32] # dynamic_shapes_tutorial.py:19 in forward (_subclasses/fake_impls.py:845 in infer_size), for more info run with TORCHDYNAMO_EXTENDED_DEBUG_GUARD_ADDED="Eq(s2*s3, s5)" +""" ###################################################################### -# Note that if our example inputs to ``torch.export`` do not satisfy the constraints -# given by ``dynamic_shapes``, then we get an error. +# Next to the ``[guard added]`` messages, we also see the responsible user lines of code - luckily here the model is simple enough. +# In many real-world cases it's not so straightforward: high-level torch operations can have complicated fake-kernel implementations +# or operator decompositions that complicate where and what guards are emitted. In such cases the best way to dig deeper and investigate +# is to follow the logs' suggestion, and re-run with environment variable ``TORCHDYNAMO_EXTENDED_DEBUG_GUARD_ADDED="..."``, to further +# attribute the guard of interest. +# +# ``Dim.AUTO`` is just one of the available options for interacting with ``dynamic_shapes``; as of writing this 2 other options are available: +# ``Dim.DYNAMIC``, and ``Dim.STATIC``. ``Dim.STATIC`` simply marks a dimension static, while ``Dim.DYNAMIC`` is similar to ``Dim.AUTO`` in all +# ways except one: it raises an error when specializing to a constant; this is designed to maintain dynamism. See for example what happens when a +# static guard is emitted on a dynamically-marked dimension: -inp1_dim1_bad = Dim("inp1_dim1_bad", min=11, max=18) -dynamic_shapes1_bad = { - "x": {0: inp1_dim0, 1: inp1_dim1_bad}, -} - -try: - export(DynamicShapesExample1(), (inp1,), dynamic_shapes=dynamic_shapes1_bad) -except Exception: - tb.print_exc() +dynamic_shapes["w"] = (Dim.AUTO, Dim.DYNAMIC) +export(model, (w, x, y, z), dynamic_shapes=dynamic_shapes) ###################################################################### -# We can enforce that equalities between dimensions of different tensors -# by using the same ``torch.export.Dim`` object, for example, in matrix multiplication: - -inp2 = torch.randn(4, 8) -inp3 = torch.randn(8, 2) +# Static guards also aren't always inherent to the model; they can also come from user specifications. In fact, a common pitfall leading to shape +# specializations is when the user specifies conflicting markers for equivalent dimensions; one dynamic and another static. The same error type is +# raised when this is the case for ``x.shape[0]`` and ``y.shape[1]``: -class DynamicShapesExample2(torch.nn.Module): - def forward(self, x, y): - return x @ y +dynamic_shapes["w"] = (Dim.AUTO, Dim.AUTO) +dynamic_shapes["x"] = (Dim.STATIC,) +dynamic_shapes["y"] = (Dim.AUTO, Dim.DYNAMIC) +export(model, (w, x, y, z), dynamic_shapes=dynamic_shapes) -inp2_dim0 = Dim("inp2_dim0") -inner_dim = Dim("inner_dim") -inp3_dim1 = Dim("inp3_dim1") +###################################################################### +# Here you might ask why export "specializes", i.e. why we resolve this static/dynamic conflict by going with the static route. The answer is because +# of the symbolic shapes system described above, of symbols and guards. When ``x.shape[0]`` is marked static, we don't allocate a symbol, and compile +# treating this shape as a concrete integer 4. A symbol is allocated for ``y.shape[1]``, and so we finally emit the guard ``s3 == 4``, leading to +# specialization. +# +# One feature of export is that during tracing, statements like asserts, ``torch._check()``, and ``if/else`` conditions will also emit guards. +# See what happens when we augment the existing model with such statements: -dynamic_shapes2 = { - "x": {0: inp2_dim0, 1: inner_dim}, - "y": {0: inner_dim, 1: inp3_dim1}, +class DynamicModel(torch.nn.Module): + def __init__(self): + super().__init__() + self.l = torch.nn.Linear(5, 3) + + def forward(self, w, x, y, z): + assert w.shape[0] <= 512 + torch._check(x.shape[0] >= 16) + if w.shape[0] == x.shape[0] + 2: + x0 = x + y + x1 = self.l(w) + x2 = x0.flatten() + x3 = x2 + z + return x1, x3 + else: + return w + +dynamic_shapes = { + "w": (Dim.AUTO, Dim.AUTO), + "x": (Dim.AUTO,), + "y": (Dim.AUTO, Dim.AUTO), + "z": (Dim.AUTO,), } - -exported_dynamic_shapes_example2 = export(DynamicShapesExample2(), (inp2, inp3), dynamic_shapes=dynamic_shapes2) - -print(exported_dynamic_shapes_example2.module()(torch.randn(2, 16), torch.randn(16, 4))) - -try: - exported_dynamic_shapes_example2.module()(torch.randn(4, 8), torch.randn(4, 2)) -except Exception: - tb.print_exc() +ep = export(DynamicModel(), (w, x, y, z), dynamic_shapes=dynamic_shapes) +print(ep) ###################################################################### -# We can also describe one dimension in terms of other. There are some -# restrictions to how detailed we can specify one dimension in terms of another, -# but generally, those in the form of ``A * Dim + B`` should work. - -class DerivedDimExample1(torch.nn.Module): - def forward(self, x, y): - return x + y[1:] - -foo = DerivedDimExample1() - -x, y = torch.randn(5), torch.randn(6) -dimx = torch.export.Dim("dimx", min=3, max=6) -dimy = dimx + 1 -derived_dynamic_shapes1 = ({0: dimx}, {0: dimy}) - -derived_dim_example1 = export(foo, (x, y), dynamic_shapes=derived_dynamic_shapes1) - -print(derived_dim_example1.module()(torch.randn(4), torch.randn(5))) - -try: - derived_dim_example1.module()(torch.randn(4), torch.randn(6)) -except Exception: - tb.print_exc() - - -class DerivedDimExample2(torch.nn.Module): - def forward(self, z, y): - return z[1:] + y[1::3] - -foo = DerivedDimExample2() - -z, y = torch.randn(4), torch.randn(10) -dx = torch.export.Dim("dx", min=3, max=6) -dz = dx + 1 -dy = dx * 3 + 1 -derived_dynamic_shapes2 = ({0: dz}, {0: dy}) - -derived_dim_example2 = export(foo, (z, y), dynamic_shapes=derived_dynamic_shapes2) -print(derived_dim_example2.module()(torch.randn(7), torch.randn(19))) +# Each of these statements emits an additional guard, and the exported program shows the changes; ``s0`` is eliminated in favor of ``s2 + 2``, +# and ``s2`` now contains lower and upper bounds, reflected in ``range_constraints``. +# +# For the if/else condition, you might ask why the True branch was taken, and why it wasn't the ``w.shape[0] != x.shape[0] + 2`` guard that +# got emitted from tracing. The answer is that export is guided by the sample inputs provided by tracing, and specializes on the branches taken. +# If different sample input shapes were provided that fail the ``if`` condition, export would trace and emit guards corresponding to the ``else`` branch. +# Additionally, you might ask why we traced only the ``if`` branch, and if it's possible to maintain control-flow in your program and keep both branches +# alive. For that, refer to rewriting your model code following the ``Control Flow Ops`` section above. ###################################################################### -# We can actually use ``torch.export`` to guide us as to which ``dynamic_shapes`` constraints -# are necessary. We can do this by relaxing all constraints (recall that if we -# do not provide constraints for a dimension, the default behavior is to constrain -# to the exact shape value of the example input) and letting ``torch.export`` -# error out. - -inp4 = torch.randn(8, 16) -inp5 = torch.randn(16, 32) - -class DynamicShapesExample3(torch.nn.Module): - def forward(self, x, y): - if x.shape[0] <= 16: - return x @ y[:, :16] - return y - -dynamic_shapes3 = { - "x": {i: Dim(f"inp4_dim{i}") for i in range(inp4.dim())}, - "y": {i: Dim(f"inp5_dim{i}") for i in range(inp5.dim())}, -} +# 0/1 specialization +# ^^^^^^^^^^^^^^^^^^ +# +# Since we're talking about guards and specializations, it's a good time to talk about the 0/1 specialization issue we brought up earlier. +# The bottom line is that export will specialize on sample input dimensions with value 0 or 1, because these shapes have trace-time properties that +# don't generalize to other shapes. For example, size 1 tensors can broadcast while other sizes fail; and size 0 ... . This just means that you should +# specify 0/1 sample inputs when you'd like your program to hardcode them, and non-0/1 sample inputs when dynamic behavior is desirable. See what happens +# at runtime when we export this linear layer: -try: - export(DynamicShapesExample3(), (inp4, inp5), dynamic_shapes=dynamic_shapes3) -except Exception: - tb.print_exc() +ep = export( + torch.nn.Linear(4, 3), + (torch.randn(1, 4),), + dynamic_shapes={ + "input": (Dim.AUTO, Dim.STATIC), + }, +) +ep.module()(torch.randn(2, 4)) ###################################################################### -# We can see that the error message gives us suggested fixes to our -# dynamic shape constraints. Let us follow those suggestions (exact -# suggestions may differ slightly): - -def suggested_fixes(): - inp4_dim1 = Dim('shared_dim') - # suggested fixes below - inp4_dim0 = Dim('inp4_dim0', max=16) - inp5_dim1 = Dim('inp5_dim1', min=17) - inp5_dim0 = inp4_dim1 - # end of suggested fixes - return { - "x": {0: inp4_dim0, 1: inp4_dim1}, - "y": {0: inp5_dim0, 1: inp5_dim1}, - } +# Named Dims +# ^^^^^^^^^^ +# +# So far we've only been talking about 3 ways to specify dynamic shapes: ``Dim.AUTO``, ``Dim.DYNAMIC``, and ``Dim.STATIC``. The attraction of these is the +# low-friction user experience; all the guards emitted during model tracing are adhered to, and dynamic behavior like min/max ranges, relations, and static/dynamic +# dimensions are automatically figured out underneath export. The dynamic shapes subsystem essentially acts as a "discovery" process, summarizing these guards +# and presenting what export believes is the overall dynamic behavior of the program. The drawback of this design appears once the user has stronger expectations or +# beliefs about the dynamic behavior of these models - maybe there is a strong desire on dynamism and specializations on particular dimensions are to be avoided at +# all costs, or maybe we just want to catch changes in dynamic behavior with changes to the original model code, or possibly underlying decompositions or meta-kernels. +# These changes won't be detected and the ``export()`` call will most likely succeed, unless tests are in place that check the resulting ``ExportedProgram`` representation. +# +# For such cases, our stance is to recommend the "traditional" way of specifying dynamic shapes, which longer-term users of export might be familiar with: named ``Dims``: -dynamic_shapes3_fixed = suggested_fixes() -exported_dynamic_shapes_example3 = export(DynamicShapesExample3(), (inp4, inp5), dynamic_shapes=dynamic_shapes3_fixed) -print(exported_dynamic_shapes_example3.module()(torch.randn(4, 32), torch.randn(32, 64))) +dx = Dim("dx", min=4, max=256) +dh = Dim("dh", max=512) +dynamic_shapes = { + "x": (dx, None), + "y": (2 * dx, dh), +} ###################################################################### -# Note that in the example above, because we constrained the value of ``x.shape[0]`` in -# ``dynamic_shapes_example3``, the exported program is sound even though there is a -# raw ``if`` statement. +# This style of dynamic shapes allows the user to specify what symbols are allocated for input dimensions, min/max bounds on those symbols, and places restrictions on the +# dynamic behavior of the ``ExportedProgram`` produced; ``ConstraintViolation`` errors will be raised if model tracing emits guards that conflict with the relations or static/dynamic +# specifications given. For example, in the above specification, the following is asserted: # -# If you want to see why ``torch.export`` generated these constraints, you can -# re-run the script with the environment variable ``TORCH_LOGS=dynamic,dynamo``, -# or use ``torch._logging.set_logs``. - -import logging -torch._logging.set_logs(dynamic=logging.INFO, dynamo=logging.INFO) -exported_dynamic_shapes_example3 = export(DynamicShapesExample3(), (inp4, inp5), dynamic_shapes=dynamic_shapes3_fixed) +# - ``x.shape[0]`` is to have range ``[4, 256]``, and related to ``y.shape[0]`` by ``y.shape[0] == 2 * x.shape[0]``. +# - ``x.shape[1]`` is static. +# - ``y.shape[1]`` has range ``[2, 512]``, and is unrelated to any other dimension. +# +# In this design, we allow relations between dimensions to be specified with univariate linear expressions: ``A * dim + B`` can be specified for any dimension. This allows users +# to specify more complex constraints like integer divisibility for dynamic dimensions: -# reset to previous values -torch._logging.set_logs(dynamic=logging.WARNING, dynamo=logging.WARNING) +dx = Dim("dx", min=4, max=512) +dynamic_shapes = { + "x": (4 * dx, None) # x.shape[0] has range [16, 2048], and is divisible by 4. +} ###################################################################### -# We can view an ``ExportedProgram``'s symbolic shape ranges using the -# ``range_constraints`` field. +# Constraint violations, suggested fixes +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# One common issue with this specification style (before ``Dim.AUTO`` was introduced), is that the specification would often be mismatched with what was produced by model tracing. +# That would lead to ``ConstraintViolation`` errors and export suggested fixes - see for example with this model & specification, where the model inherently requires equality between +# dimensions 0 of ``x`` and ``y``, and requires dimension 1 to be static. -print(exported_dynamic_shapes_example3.range_constraints) +class Foo(torch.nn.Module): + def forward(self, x, y): + w = x + y + return w + torch.ones(4) + +dx, dy, d1 = torch.export.dims("dx", "dy", "d1") +ep = export( + Foo(), + (torch.randn(6, 4), torch.randn(6, 4)), + dynamic_shapes={ + "x": (dx, d1), + "y": (dy, d1), + }, +) + +###################################################################### +# The expectation with suggested fixes is that the user can interactively copy-paste the changes into their dynamic shapes specification, and successfully export afterwards. +# +# Lastly, there's couple nice-to-knows about the options for specification: +# +# - ``None`` is a good option for static behavior: +# - ``dynamic_shapes=None`` (default) exports with the entire model being static. +# - specifying ``None`` at an input-level exports with all tensor dimensions static, and is also required for non-tensor inputs. +# - specifying ``None`` at a dimension-level specializes that dimension, though this is deprecated in favor of ``Dim.STATIC``. +# - specifying per-dimension integer values also produces static behavior, and will additionally check that the provided sample input matches the specification. +# +# These options are combined in the inputs & dynamic shapes spec below: + +inputs = ( + torch.randn(4, 4), + torch.randn(3, 3), + 16, + False, +) +dynamic_shapes = { + "tensor_0": (Dim.AUTO, None), + "tensor_1": None, + "int_val": None, + "bool_val": None, +} ###################################################################### # Custom Ops From e45efc2de5bd40e65d6af81600d6d24ec3715eda Mon Sep 17 00:00:00 2001 From: asideofcode-dev <133222359+asideofcode-dev@users.noreply.github.com> Date: Fri, 10 Jan 2025 20:28:02 +0000 Subject: [PATCH 010/347] Revert prefactor m in autograd documentation (#3221) The prefactor m was incorrectly added to the vector-Jacobian product formula in the autograd tutorial. This change was based on the mistaken assumption that m-scaling was necessary to account for multiple terms in the summation. However, the chain rule and vector-Jacobian product formula already correctly aggregate contributions from all intermediate variables without requiring explicit scaling by m. --- beginner_source/blitz/autograd_tutorial.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beginner_source/blitz/autograd_tutorial.py b/beginner_source/blitz/autograd_tutorial.py index b31a30409aa..b736b429eee 100644 --- a/beginner_source/blitz/autograd_tutorial.py +++ b/beginner_source/blitz/autograd_tutorial.py @@ -191,7 +191,7 @@ # .. math:: # # -# J^{T}\cdot \vec{v} = m \cdot \left(\begin{array}{ccc} +# J^{T}\cdot \vec{v} = \left(\begin{array}{ccc} # \frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{1}}\\ # \vdots & \ddots & \vdots\\ # \frac{\partial y_{1}}{\partial x_{n}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}} @@ -199,7 +199,7 @@ # \frac{\partial l}{\partial y_{1}}\\ # \vdots\\ # \frac{\partial l}{\partial y_{m}} -# \end{array}\right) = m \cdot \left(\begin{array}{c} +# \end{array}\right) = \left(\begin{array}{c} # \frac{\partial l}{\partial x_{1}}\\ # \vdots\\ # \frac{\partial l}{\partial x_{n}} From 01eeee67407d938c246dbd027a135408881bcff3 Mon Sep 17 00:00:00 2001 From: Guspan Tanadi <36249910+guspan-tanadi@users.noreply.github.com> Date: Mon, 13 Jan 2025 23:29:42 +0700 Subject: [PATCH 011/347] fix reference path links (#3229) --- advanced_source/pendulum.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/advanced_source/pendulum.py b/advanced_source/pendulum.py index 38524cfff40..fae3635de1c 100644 --- a/advanced_source/pendulum.py +++ b/advanced_source/pendulum.py @@ -33,9 +33,9 @@ In the process, we will touch three crucial components of TorchRL: -* `environments `__ -* `transforms `__ -* `models (policy and value function) `__ +* `environments `__ +* `transforms `__ +* `models (policy and value function) `__ """ @@ -384,7 +384,7 @@ def _reset(self, tensordict): # convenient shortcuts to the content of the output and input spec containers. # # TorchRL offers multiple :class:`~torchrl.data.TensorSpec` -# `subclasses `_ to +# `subclasses `_ to # encode the environment's input and output characteristics. # # Specs shape From e6fc18924dbea7d640b346c4e9631fa6eefe4e35 Mon Sep 17 00:00:00 2001 From: venkatram-dev <45727389+venkatram-dev@users.noreply.github.com> Date: Mon, 13 Jan 2025 12:39:20 -0800 Subject: [PATCH 012/347] make_distribute_tutorial_work_in_google_colab (#3022) * make_distribute_tutorial work in google_colab --------- Co-authored-by: Svetlana Karslioglu --- intermediate_source/dist_tuto.rst | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/intermediate_source/dist_tuto.rst b/intermediate_source/dist_tuto.rst index 25eb372e5ef..9a004aa67b4 100644 --- a/intermediate_source/dist_tuto.rst +++ b/intermediate_source/dist_tuto.rst @@ -47,6 +47,7 @@ the following template. """run.py:""" #!/usr/bin/env python import os + import sys import torch import torch.distributed as dist import torch.multiprocessing as mp @@ -66,8 +67,12 @@ the following template. if __name__ == "__main__": world_size = 2 processes = [] - mp.set_start_method("spawn") - for rank in range(world_size): + if "google.colab" in sys.modules: + print("Running in Google Colab") + mp.get_context("spawn") + else: + mp.set_start_method("spawn") + for rank in range(size): p = mp.Process(target=init_process, args=(rank, world_size, run)) p.start() processes.append(p) @@ -156,7 +161,8 @@ we should not modify the sent tensor nor access the received tensor before ``req In other words, - writing to ``tensor`` after ``dist.isend()`` will result in undefined behaviour. -- reading from ``tensor`` after ``dist.irecv()`` will result in undefined behaviour. +- reading from ``tensor`` after ``dist.irecv()`` will result in undefined + behaviour, until ``req.wait()`` has been executed. However, after ``req.wait()`` has been executed we are guaranteed that the communication took place, From 5a6b21795917de73c3444b5335eb365d75f2b9b7 Mon Sep 17 00:00:00 2001 From: Vincent Moens Date: Mon, 13 Jan 2025 20:48:52 +0000 Subject: [PATCH 013/347] Use DETERMINISTIC sampling in PPO (#3230) Co-authored-by: Svetlana Karslioglu --- intermediate_source/reinforcement_ppo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intermediate_source/reinforcement_ppo.py b/intermediate_source/reinforcement_ppo.py index 30216ff880c..ec2dc0a488d 100644 --- a/intermediate_source/reinforcement_ppo.py +++ b/intermediate_source/reinforcement_ppo.py @@ -639,7 +639,7 @@ # number of steps (1000, which is our ``env`` horizon). # The ``rollout`` method of the ``env`` can take a policy as argument: # it will then execute this policy at each step. - with set_exploration_type(ExplorationType.MEAN), torch.no_grad(): + with set_exploration_type(ExplorationType.DETERMINISTIC), torch.no_grad(): # execute a rollout with the trained policy eval_rollout = env.rollout(1000, policy_module) logs["eval reward"].append(eval_rollout["next", "reward"].mean().item()) From f7d06b68cbaa2e8aa59278064d40921b939d8a7a Mon Sep 17 00:00:00 2001 From: "Nichols A. Romero" <165712832+naromero77amd@users.noreply.github.com> Date: Mon, 13 Jan 2025 17:22:29 -0600 Subject: [PATCH 014/347] [ROCm] Update C++ Custom Operators tutorial with note about AMD GPU. (#3198) * Update C++ Custom Operators tutorial with note about AMD GPU. * Put in a NOTE call out box and change to AMD ROCm. --------- Co-authored-by: Svetlana Karslioglu --- advanced_source/cpp_custom_ops.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/advanced_source/cpp_custom_ops.rst b/advanced_source/cpp_custom_ops.rst index 2b7aa2a1a9c..9dc06daa6f4 100644 --- a/advanced_source/cpp_custom_ops.rst +++ b/advanced_source/cpp_custom_ops.rst @@ -19,6 +19,10 @@ Custom C++ and CUDA Operators * PyTorch 2.4 or later * Basic understanding of C++ and CUDA programming +.. note:: + + This tutorial will also work on AMD ROCm with no additional modifications. + PyTorch offers a large library of operators that work on Tensors (e.g. torch.add, torch.sum, etc). However, you may wish to bring a new custom operator to PyTorch. This tutorial demonstrates the blessed path to authoring a custom operator written in C++/CUDA. From 48a5f0ab955f443deeb067ab0eb309e2b948212d Mon Sep 17 00:00:00 2001 From: Ankith Gunapal Date: Tue, 21 Jan 2025 10:22:52 -0800 Subject: [PATCH 015/347] Add a recipe for showcasing torch.export flow for 4 models, with unique challenges and solutions (#3180) * Added a recipe for showcasing torch.export flow for 4 models. --------- Co-authored-by: Svetlana Karslioglu Co-authored-by: Angela Yi --- recipes_source/recipes_index.rst | 8 + .../torch_export_challenges_solutions.rst | 331 ++++++++++++++++++ 2 files changed, 339 insertions(+) create mode 100644 recipes_source/torch_export_challenges_solutions.rst diff --git a/recipes_source/recipes_index.rst b/recipes_source/recipes_index.rst index 7d6a067b7f3..b841d9ee759 100644 --- a/recipes_source/recipes_index.rst +++ b/recipes_source/recipes_index.rst @@ -157,6 +157,13 @@ Recipes are bite-sized, actionable examples of how to use specific PyTorch featu :link: ../recipes/torch_export_aoti_python.html :tags: Basics +.. customcarditem:: + :header: Demonstration of torch.export flow, common challenges and the solutions to address them + :card_description: Learn how to export models for popular usecases + :image: ../_static/img/thumbnails/cropped/generic-pytorch-logo.png + :link: ../recipes/torch_export_challenges_solutions.html + :tags: Compiler,TorchCompile + .. Interpretability .. customcarditem:: @@ -472,3 +479,4 @@ Recipes are bite-sized, actionable examples of how to use specific PyTorch featu /recipes/distributed_optim_torchscript /recipes/mobile_interpreter /recipes/distributed_comm_debug_mode + /recipes/torch_export_challenges_solutions diff --git a/recipes_source/torch_export_challenges_solutions.rst b/recipes_source/torch_export_challenges_solutions.rst new file mode 100644 index 00000000000..1f8b1ae45a4 --- /dev/null +++ b/recipes_source/torch_export_challenges_solutions.rst @@ -0,0 +1,331 @@ +Demonstration of torch.export flow, common challenges and the solutions to address them +======================================================================================= +**Authors:** `Ankith Gunapal `__, `Jordi Ramon `__, `Marcos Carranza `__ + +In the `Introduction to torch.export Tutorial `__ , we learned how to use `torch.export `__. +This tutorial expands on the previous one and explores the process of exporting popular models with code, as well as addresses common challenges that may arise with ``torch.export``. + +In this tutorial, you will learn how to export models for these use cases: + +* Video classifier (`MViT `__) +* Automatic Speech Recognition (`OpenAI Whisper-Tiny `__) +* Image Captioning (`BLIP `__) +* Promptable Image Segmentation (`SAM2 `__) + +Each of the four models were chosen to demonstrate unique features of `torch.export`, as well as some practical considerations +and issues faced in the implementation. + +Prerequisites +------------- + +* PyTorch 2.4 or later +* Basic understanding of ``torch.export`` and PyTorch Eager inference. + + +Key requirement for ``torch.export``: No graph break +---------------------------------------------------- + +`torch.compile `__ speeds up PyTorch code by using JIT to compile PyTorch code into optimized kernels. It optimizes the given model +using ``TorchDynamo`` and creates an optimized graph , which is then lowered into the hardware using the backend specified in the API. +When TorchDynamo encounters unsupported Python features, it breaks the computation graph, lets the default Python interpreter +handle the unsupported code, and then resumes capturing the graph. This break in the computation graph is called a `graph break `__. + +One of the key differences between ``torch.export`` and ``torch.compile`` is that ``torch.export`` doesn’t support graph breaks +which means that the entire model or part of the model that you are exporting needs to be a single graph. This is because handling graph breaks +involves interpreting the unsupported operation with default Python evaluation, which is incompatible with what ``torch.export`` is +designed for. You can read details about the differences between the various PyTorch frameworks in this `link `__ + +You can identify graph breaks in your program by using the following command: + +.. code:: sh + + TORCH_LOGS="graph_breaks" python .py + +You will need to modify your program to get rid of graph breaks. Once resolved, you are ready to export the model. +PyTorch runs `nightly benchmarks `__ for `torch.compile` on popular HuggingFace and TIMM models. +Most of these models have no graph breaks. + +The models in this recipe have no graph breaks, but fail with `torch.export`. + +Video Classification +-------------------- + +MViT is a class of models based on `MultiScale Vision Transformers `__. This model has been trained for video classification using the `Kinetics-400 Dataset `__. +This model with a relevant dataset can be used for action recognition in the context of gaming. + + +The code below exports MViT by tracing with ``batch_size=2`` and then checks if the ExportedProgram can run with ``batch_size=4``. + +.. code:: python + + import numpy as np + import torch + from torchvision.models.video import MViT_V1_B_Weights, mvit_v1_b + import traceback as tb + + model = mvit_v1_b(weights=MViT_V1_B_Weights.DEFAULT) + + # Create a batch of 2 videos, each with 16 frames of shape 224x224x3. + input_frames = torch.randn(2,16, 224, 224, 3) + # Transpose to get [1, 3, num_clips, height, width]. + input_frames = np.transpose(input_frames, (0, 4, 1, 2, 3)) + + # Export the model. + exported_program = torch.export.export( + model, + (input_frames,), + ) + + # Create a batch of 4 videos, each with 16 frames of shape 224x224x3. + input_frames = torch.randn(4,16, 224, 224, 3) + input_frames = np.transpose(input_frames, (0, 4, 1, 2, 3)) + try: + exported_program.module()(input_frames) + except Exception: + tb.print_exc() + + +Error: Static batch size +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: sh + + raise RuntimeError( + RuntimeError: Expected input at *args[0].shape[0] to be equal to 2, but got 4 + + +By default, the exporting flow will trace the program assuming that all input shapes are static, so if you run the program with +input shapes that are different than the ones you used while tracing, you will run into an error. + +Solution +~~~~~~~~ + +To address the error, we specify the first dimension of the input (``batch_size``) to be dynamic , specifying the expected range of ``batch_size``. +In the corrected example shown below, we specify that the expected ``batch_size`` can range from 1 to 16. +One detail to notice that ``min=2`` is not a bug and is explained in `The 0/1 Specialization Problem `__. A detailed description of dynamic shapes +for ``torch.export`` can be found in the export tutorial. The code shown below demonstrates how to export mViT with dynamic batch sizes: + +.. code:: python + + import numpy as np + import torch + from torchvision.models.video import MViT_V1_B_Weights, mvit_v1_b + import traceback as tb + + + model = mvit_v1_b(weights=MViT_V1_B_Weights.DEFAULT) + + # Create a batch of 2 videos, each with 16 frames of shape 224x224x3. + input_frames = torch.randn(2,16, 224, 224, 3) + + # Transpose to get [1, 3, num_clips, height, width]. + input_frames = np.transpose(input_frames, (0, 4, 1, 2, 3)) + + # Export the model. + batch_dim = torch.export.Dim("batch", min=2, max=16) + exported_program = torch.export.export( + model, + (input_frames,), + # Specify the first dimension of the input x as dynamic + dynamic_shapes={"x": {0: batch_dim}}, + ) + + # Create a batch of 4 videos, each with 16 frames of shape 224x224x3. + input_frames = torch.randn(4,16, 224, 224, 3) + input_frames = np.transpose(input_frames, (0, 4, 1, 2, 3)) + try: + exported_program.module()(input_frames) + except Exception: + tb.print_exc() + + +Automatic Speech Recognition +--------------- + +**Automatic Speech Recognition** (ASR) is the use of machine learning to transcribe spoken language into text. +`Whisper `__ is a Transformer based encoder-decoder model from OpenAI, which was trained on 680k hours of labelled data for ASR and speech translation. +The code below tries to export ``whisper-tiny`` model for ASR. + + +.. code:: python + + import torch + from transformers import WhisperProcessor, WhisperForConditionalGeneration + from datasets import load_dataset + + # load model + model = WhisperForConditionalGeneration.from_pretrained("openai/whisper-tiny") + + # dummy inputs for exporting the model + input_features = torch.randn(1,80, 3000) + attention_mask = torch.ones(1, 3000) + decoder_input_ids = torch.tensor([[1, 1, 1 , 1]]) * model.config.decoder_start_token_id + + model.eval() + + exported_program: torch.export.ExportedProgram= torch.export.export(model, args=(input_features, attention_mask, decoder_input_ids,)) + + + +Error: strict tracing with TorchDynamo +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: console + + torch._dynamo.exc.InternalTorchDynamoError: AttributeError: 'DynamicCache' object has no attribute 'key_cache' + + +By default ``torch.export`` traces your code using `TorchDynamo `__, a byte-code analysis engine, which symbolically analyzes your code and builds a graph. +This analysis provides a stronger guarantee about safety but not all Python code is supported. When we export the ``whisper-tiny`` model using the +default strict mode, it typically returns an error in Dynamo due to an unsupported feature. To understand why this errors in Dynamo, you can refer to this `GitHub issue `__. + +Solution +~~~~~~~~ + +To address the above error , ``torch.export`` supports the ``non_strict`` mode where the program is traced using the Python interpreter, which works similar to +PyTorch eager execution. The only difference is that all ``Tensor`` objects will be replaced by ``ProxyTensors``, which will record all their operations into +a graph. By using ``strict=False``, we are able to export the program. + +.. code:: python + + import torch + from transformers import WhisperProcessor, WhisperForConditionalGeneration + from datasets import load_dataset + + # load model + model = WhisperForConditionalGeneration.from_pretrained("openai/whisper-tiny") + + # dummy inputs for exporting the model + input_features = torch.randn(1,80, 3000) + attention_mask = torch.ones(1, 3000) + decoder_input_ids = torch.tensor([[1, 1, 1 , 1]]) * model.config.decoder_start_token_id + + model.eval() + + exported_program: torch.export.ExportedProgram= torch.export.export(model, args=(input_features, attention_mask, decoder_input_ids,), strict=False) + +Image Captioning +---------------- + +**Image Captioning** is the task of defining the contents of an image in words. In the context of gaming, Image Captioning can be used to enhance the +gameplay experience by dynamically generating text description of the various game objects in the scene, thereby providing the gamer with additional +details. `BLIP `__ is a popular model for Image Captioning `released by SalesForce Research `__. The code below tries to export BLIP with ``batch_size=1``. + + +.. code:: python + + import torch + from models.blip import blip_decoder + + device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') + image_size = 384 + image = torch.randn(1, 3,384,384).to(device) + caption_input = "" + + model_url = 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_capfilt_large.pth' + model = blip_decoder(pretrained=model_url, image_size=image_size, vit='base') + model.eval() + model = model.to(device) + + exported_program: torch.export.ExportedProgram= torch.export.export(model, args=(image,caption_input,), strict=False) + + + +Error: Cannot mutate tensors with frozen storage +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +While exporting a model, it might fail because the model implementation might contain certain Python operations which are not yet supported by ``torch.export``. +Some of these failures may have a workaround. BLIP is an example where the original model errors, which can be resolved by making a small change in the code. +``torch.export`` lists the common cases of supported and unsupported operations in `ExportDB `__ and shows how you can modify your code to make it export compatible. + +.. code:: console + + File "/BLIP/models/blip.py", line 112, in forward + text.input_ids[:,0] = self.tokenizer.bos_token_id + File "/anaconda3/envs/export/lib/python3.10/site-packages/torch/_subclasses/functional_tensor.py", line 545, in __torch_dispatch__ + outs_unwrapped = func._op_dk( + RuntimeError: cannot mutate tensors with frozen storage + + + +Solution +~~~~~~~~ + +Clone the `tensor `__ where export fails. + +.. code:: python + + text.input_ids = text.input_ids.clone() # clone the tensor + text.input_ids[:,0] = self.tokenizer.bos_token_id + +.. note:: + This constraint has been relaxed in PyTorch 2.7 nightlies. This should work out-of-the-box in PyTorch 2.7 + +Promptable Image Segmentation +----------------------------- + +**Image segmentation** is a computer vision technique that divides a digital image into distinct groups of pixels, or segments, based on their characteristics. +`Segment Anything Model (SAM) `__) introduced promptable image segmentation, which predicts object masks given prompts that indicate the desired object. `SAM 2 `__ is +the first unified model for segmenting objects across images and videos. The `SAM2ImagePredictor `__ class provides an easy interface to the model for prompting +the model. The model can take as input both point and box prompts, as well as masks from the previous iteration of prediction. Since SAM2 provides strong +zero-shot performance for object tracking, it can be used for tracking game objects in a scene. + + +The tensor operations in the predict method of `SAM2ImagePredictor `__ are happening in the `_predict `__ method. So, we try to export like this. + +.. code:: python + + ep = torch.export.export( + self._predict, + args=(unnorm_coords, labels, unnorm_box, mask_input, multimask_output), + kwargs={"return_logits": return_logits}, + strict=False, + ) + + +Error: Model is not of type ``torch.nn.Module`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``torch.export`` expects the module to be of type ``torch.nn.Module``. However, the module we are trying to export is a class method. Hence it errors. + +.. code:: console + + Traceback (most recent call last): + File "/sam2/image_predict.py", line 20, in + masks, scores, _ = predictor.predict( + File "/sam2/sam2/sam2_image_predictor.py", line 312, in predict + ep = torch.export.export( + File "python3.10/site-packages/torch/export/__init__.py", line 359, in export + raise ValueError( + ValueError: Expected `mod` to be an instance of `torch.nn.Module`, got . + + +Solution +~~~~~~~~ + +We write a helper class, which inherits from ``torch.nn.Module`` and call the ``_predict method`` in the ``forward`` method of the class. The complete code can be found `here `__. + +.. code:: python + + class ExportHelper(torch.nn.Module): + def __init__(self): + super().__init__() + + def forward(_, *args, **kwargs): + return self._predict(*args, **kwargs) + + model_to_export = ExportHelper() + ep = torch.export.export( + model_to_export, + args=(unnorm_coords, labels, unnorm_box, mask_input, multimask_output), + kwargs={"return_logits": return_logits}, + strict=False, + ) + +Conclusion +---------- + +In this tutorial, we have learned how to use ``torch.export`` to export models for popular use cases by addressing challenges through correct configuration and simple code modifications. +Once you are able to export a model, you can lower the ``ExportedProgram`` into your hardware using `AOTInductor `__ in case of servers and `ExecuTorch `__ in case of edge device. +To learn more about ``AOTInductor`` (AOTI), please refer to the `AOTI tutorial `__. +To learn more about ``ExecuTorch`` , please refer to the `ExecuTorch tutorial `__. From 8903ee177a5afc6e567aea25e5a39be1bd3feca4 Mon Sep 17 00:00:00 2001 From: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> Date: Tue, 21 Jan 2025 15:56:34 -0500 Subject: [PATCH 016/347] Add link to a good recipe example (#3243) Update recipe template link Co-authored-by: Svetlana Karslioglu --- CONTRIBUTING.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5a3ab42272c..c4038d168c3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -218,9 +218,8 @@ described in the preceding sections: - [NLP From Scratch: Generating Names with a Character-Level RNN Tutorial](https://pytorch.org/tutorials/intermediate/char_rnn_generation_tutorial.html) -If you are creating a recipe, we recommend that you use [this -template](https://github.com/pytorch/tutorials/blob/tutorials_refresh/recipes_source/recipes/example_recipe.py) -as a guide. +If you are creating a recipe, [this is a good +example.](https://github.com/pytorch/tutorials/blob/main/recipes_source/recipes/what_is_state_dict.py) # Submission Process # From fe351910f3cd27fc64991e3c0601019309a4c4f1 Mon Sep 17 00:00:00 2001 From: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> Date: Tue, 21 Jan 2025 15:57:16 -0500 Subject: [PATCH 017/347] Fix broken links in cpp_autograd.rst (#3240) Fix broken links in cpp_autograd.rst --- advanced_source/cpp_autograd.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/advanced_source/cpp_autograd.rst b/advanced_source/cpp_autograd.rst index d09f877e5a2..51e5e0b358f 100644 --- a/advanced_source/cpp_autograd.rst +++ b/advanced_source/cpp_autograd.rst @@ -255,9 +255,9 @@ Out: [ CPUFloatType{3,4} ] Please see the documentation for ``torch::autograd::backward`` -(`link `_) +(`link `_) and ``torch::autograd::grad`` -(`link `_) +(`link `_) for more information on how to use them. Using custom autograd function in C++ @@ -394,9 +394,9 @@ C++ using the following table: +--------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Python | C++ | +================================+========================================================================================================================================================================+ -| ``torch.autograd.backward`` | ``torch::autograd::backward`` (`link `_) | +| ``torch.autograd.backward`` | ``torch::autograd::backward`` (`link `_) | +--------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| ``torch.autograd.grad`` | ``torch::autograd::grad`` (`link `_) | +| ``torch.autograd.grad`` | ``torch::autograd::grad`` (`link `_) | +--------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | ``torch.Tensor.detach`` | ``torch::Tensor::detach`` (`link `_) | +--------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ From 8fab8bf9c0681c00336acda68c698120398728a9 Mon Sep 17 00:00:00 2001 From: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> Date: Tue, 21 Jan 2025 16:46:48 -0500 Subject: [PATCH 018/347] Fix a broken link in rpc_tutorial.rst (#3237) Fixing broken links in RPC Tutorial --- intermediate_source/rpc_tutorial.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/intermediate_source/rpc_tutorial.rst b/intermediate_source/rpc_tutorial.rst index 835e6f0649f..dd8af47e62a 100644 --- a/intermediate_source/rpc_tutorial.rst +++ b/intermediate_source/rpc_tutorial.rst @@ -59,7 +59,7 @@ Distributed Reinforcement Learning using RPC and RRef ----------------------------------------------------- This section describes steps to build a toy distributed reinforcement learning -model using RPC to solve CartPole-v1 from `OpenAI Gym `__. +model using RPC to solve CartPole-v1 from `OpenAI Gym `__. The policy code is mostly borrowed from the existing single-thread `example `__ as shown below. We will skip details of the ``Policy`` design, and focus on RPC @@ -156,7 +156,7 @@ send commands. Applications don't need to worry about the lifetime of ``RRefs``. The owner of each ``RRef`` maintains a reference counting map to track its lifetime, and guarantees the remote data object will not be deleted as long as there is any live user of that ``RRef``. Please refer to the ``RRef`` -`design doc `__ for details. +`design doc `__ for details. .. code:: python @@ -531,7 +531,7 @@ the given arguments (i.e., ``lr=0.05``). In the training loop, it first creates a distributed autograd context, which will help the distributed autograd engine to find gradients and involved RPC send/recv functions. The design details of the distributed autograd engine can -be found in its `design note `__. +be found in its `design note `__. Then, it kicks off the forward pass as if it is a local model, and run the distributed backward pass. For the distributed backward, you only need to specify a list of roots, in this case, it is the loss ``Tensor``. From df9e22606bf78dc3570755e242f6f8fa6be51fec Mon Sep 17 00:00:00 2001 From: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> Date: Tue, 21 Jan 2025 17:21:33 -0500 Subject: [PATCH 019/347] Fix a broken link in pytorch_with_examples.rst (#3238) Fix link in pytorch-with-examples Co-authored-by: Svetlana Karslioglu --- beginner_source/pytorch_with_examples.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beginner_source/pytorch_with_examples.rst b/beginner_source/pytorch_with_examples.rst index 6705b5b21a4..e5642dfa139 100644 --- a/beginner_source/pytorch_with_examples.rst +++ b/beginner_source/pytorch_with_examples.rst @@ -149,7 +149,7 @@ which will be optimized during learning. In TensorFlow, packages like `Keras `__, -`TensorFlow-Slim `__, +`TensorFlow-Slim `__, and `TFLearn `__ provide higher-level abstractions over raw computational graphs that are useful for building neural networks. From 84ac70ae260c25ab0088be17adc7f0d3eee2349d Mon Sep 17 00:00:00 2001 From: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> Date: Wed, 22 Jan 2025 14:19:16 -0500 Subject: [PATCH 020/347] Update .lycheeignore (#3247) * Update .lycheeignore Adding a link to ignore * Update .lycheeignore --- .lycheeignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.lycheeignore b/.lycheeignore index 994f2871212..ed70fffc2ad 100644 --- a/.lycheeignore +++ b/.lycheeignore @@ -12,3 +12,6 @@ https://pytorch.org/tutorials/beginner/colab/n # Ignore local host link from intermediate_source/tensorboard_tutorial.rst http://localhost:6006 + +# Ignore local host link from recipes_source/deployment_with_flask.rst +http://localhost:5000/predict From 85d0fa9df7d0c88a580142cbf59e95c554263068 Mon Sep 17 00:00:00 2001 From: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> Date: Thu, 23 Jan 2025 01:02:32 -0500 Subject: [PATCH 021/347] Update ddp_series_multinode.rst (#3248) Edit author link --- intermediate_source/ddp_series_multinode.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intermediate_source/ddp_series_multinode.rst b/intermediate_source/ddp_series_multinode.rst index 5717589bdaa..8746eb19bbd 100644 --- a/intermediate_source/ddp_series_multinode.rst +++ b/intermediate_source/ddp_series_multinode.rst @@ -6,7 +6,7 @@ training** \|\| `minGPT Training `__ Multinode Training ================== -Authors: `Suraj Subramanian `__ +Authors: `Suraj Subramanian `__ .. grid:: 2 From 3c565ca8123bcba84f530820ef968db5bff14556 Mon Sep 17 00:00:00 2001 From: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> Date: Thu, 23 Jan 2025 02:18:05 -0500 Subject: [PATCH 022/347] Update ddp_series_minGPT.rst (#3246) --- intermediate_source/ddp_series_minGPT.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intermediate_source/ddp_series_minGPT.rst b/intermediate_source/ddp_series_minGPT.rst index 259db3623c6..743568ae18b 100644 --- a/intermediate_source/ddp_series_minGPT.rst +++ b/intermediate_source/ddp_series_minGPT.rst @@ -6,7 +6,7 @@ training `__ \|\| **minGPT Training** Training “real-world” models with DDP ===================================== -Authors: `Suraj Subramanian `__ +Authors: `Suraj Subramanian `__ .. grid:: 2 From 15547173ea4825faeb18c99a1f4eae2aecf31b65 Mon Sep 17 00:00:00 2001 From: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> Date: Thu, 23 Jan 2025 10:57:19 -0500 Subject: [PATCH 023/347] Update dynamic_quantization_bert_tutorial.rst (#3239) Update links in dynamic quantization bert tutorial --- intermediate_source/dynamic_quantization_bert_tutorial.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/intermediate_source/dynamic_quantization_bert_tutorial.rst b/intermediate_source/dynamic_quantization_bert_tutorial.rst index e515f53a1df..786ef11f3b2 100644 --- a/intermediate_source/dynamic_quantization_bert_tutorial.rst +++ b/intermediate_source/dynamic_quantization_bert_tutorial.rst @@ -138,7 +138,7 @@ the following helper functions: one for converting the text examples into the feature vectors; The other one for measuring the F1 score of the predicted result. -The `glue_convert_examples_to_features `_ function converts the texts into input features: +The `glue_convert_examples_to_features `_ function converts the texts into input features: - Tokenize the input sequences; - Insert [CLS] in the beginning; @@ -147,7 +147,7 @@ The `glue_convert_examples_to_features `_ function has the compute metrics with +The `glue_compute_metrics `_ function has the compute metrics with the `F1 score `_, which can be interpreted as a weighted average of the precision and recall, where an F1 score reaches its best value at 1 and worst score at 0. The @@ -273,7 +273,7 @@ We load the tokenizer and fine-tuned BERT sequence classifier model 2.3 Define the tokenize and evaluation function ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -We reuse the tokenize and evaluation function from `HuggingFace `_. +We reuse the tokenize and evaluation function from `HuggingFace `_. .. code:: python From 3b1257d2644161e0ec1972a2b0b5298c2306b135 Mon Sep 17 00:00:00 2001 From: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> Date: Thu, 23 Jan 2025 13:35:14 -0500 Subject: [PATCH 024/347] Update .lycheeignore (#3253) Adding https://www.uber.com/blog/deep-neuroevolution/ to ignore, link valid but not passing --- .lycheeignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.lycheeignore b/.lycheeignore index ed70fffc2ad..3d86ae872de 100644 --- a/.lycheeignore +++ b/.lycheeignore @@ -15,3 +15,6 @@ http://localhost:6006 # Ignore local host link from recipes_source/deployment_with_flask.rst http://localhost:5000/predict + +# Ignore local host link from advanced_source/cpp_frontend.rst +https://www.uber.com/blog/deep-neuroevolution/ From 903f7af9ca7594fe53b0dc1ba6e7be4f1ab9f9ce Mon Sep 17 00:00:00 2001 From: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> Date: Thu, 23 Jan 2025 14:58:28 -0500 Subject: [PATCH 025/347] Update cpp_frontend.rst (#3245) Update broken links --- advanced_source/cpp_frontend.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/advanced_source/cpp_frontend.rst b/advanced_source/cpp_frontend.rst index de22fbf05a1..d31be00c632 100644 --- a/advanced_source/cpp_frontend.rst +++ b/advanced_source/cpp_frontend.rst @@ -57,7 +57,7 @@ the right tool for the job. Examples for such environments include: Multiprocessing is an alternative, but not as scalable and has significant shortcomings. C++ has no such constraints and threads are easy to use and create. Models requiring heavy parallelization, like those used in `Deep - Neuroevolution `_, can benefit from + Neuroevolution `_, can benefit from this. - **Existing C++ Codebases**: You may be the owner of an existing C++ application doing anything from serving web pages in a backend server to @@ -662,7 +662,7 @@ Defining the DCGAN Modules We now have the necessary background and introduction to define the modules for the machine learning task we want to solve in this post. To recap: our task is to generate images of digits from the `MNIST dataset -`_. We want to use a `generative adversarial +`_. We want to use a `generative adversarial network (GAN) `_ to solve this task. In particular, we'll use a `DCGAN architecture From 5a5edfc611c22efcc3dc5192212d0df3a12aa30e Mon Sep 17 00:00:00 2001 From: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> Date: Thu, 23 Jan 2025 16:23:02 -0500 Subject: [PATCH 026/347] Update torchserve_with_ipex.rst (#3249) Update to the tuning guide and launch script links Co-authored-by: Mark Saroufim --- intermediate_source/torchserve_with_ipex.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/intermediate_source/torchserve_with_ipex.rst b/intermediate_source/torchserve_with_ipex.rst index 1a11b4180f4..23d91f50cb6 100644 --- a/intermediate_source/torchserve_with_ipex.rst +++ b/intermediate_source/torchserve_with_ipex.rst @@ -379,8 +379,8 @@ For interested readers, please check out the following documents: - `CPU specific optimizations `_ - `Maximize Performance of Intel® Software Optimization for PyTorch* on CPU `_ -- `Performance Tuning Guide `_ -- `Launch Script Usage Guide `_ +- `Performance Tuning Guide `_ +- `Launch Script Usage Guide `_ - `Top-down Microarchitecture Analysis Method `_ - `Configuring oneDNN for Benchmarking `_ - `Intel® VTune™ Profiler `_ From 37e0b1ee04b199b375aa474302c5198ce7dec27f Mon Sep 17 00:00:00 2001 From: Pian Pawakapan Date: Thu, 23 Jan 2025 15:28:12 -0800 Subject: [PATCH 027/347] [BE][export] add data-dependent section to export tutorial (#3244) --- en-wordlist.txt | 6 +- intermediate_source/torch_export_tutorial.py | 185 +++++++++++++++++++ 2 files changed, 190 insertions(+), 1 deletion(-) diff --git a/en-wordlist.txt b/en-wordlist.txt index 7c2ed6c398c..b56df45df0c 100644 --- a/en-wordlist.txt +++ b/en-wordlist.txt @@ -81,6 +81,8 @@ FX FX's FairSeq Fastpath +FakeTensor +FakeTensors FFN FloydHub FloydHub's @@ -368,6 +370,8 @@ downsample downsamples dropdown dtensor +dtype +dtypes duration elementwise embeddings @@ -615,6 +619,7 @@ triton uint UX umap +unbacked uncomment uncommented underflowing @@ -651,7 +656,6 @@ RecSys TorchRec sharding TBE -dtype EBC sharder hyperoptimized diff --git a/intermediate_source/torch_export_tutorial.py b/intermediate_source/torch_export_tutorial.py index 9acacf53629..c992eefa9fc 100644 --- a/intermediate_source/torch_export_tutorial.py +++ b/intermediate_source/torch_export_tutorial.py @@ -629,6 +629,191 @@ def forward(self, x, y): "bool_val": None, } +###################################################################### +# Data-dependent errors +# --------------------- +# +# While trying to export models, you have may have encountered errors like "Could not guard on data-dependent expression", or Could not extract specialized integer from data-dependent expression". +# These errors exist because ``torch.export()`` compiles programs using FakeTensors, which symbolically represent their real tensor counterparts. While these have equivalent symbolic properties +# (e.g. sizes, strides, dtypes), they diverge in that FakeTensors do not contain any data values. While this avoids unnecessary memory usage and expensive computation, it does mean that export may be +# unable to out-of-the-box compile parts of user code where compilation relies on data values. In short, if the compiler requires a concrete, data-dependent value in order to proceed, it will error out, +# complaining that the value is not available. +# +# Data-dependent values appear in many places, and common sources are calls like ``item()``, ``tolist()``, or ``torch.unbind()`` that extract scalar values from tensors. +# How are these values represented in the exported program? In the `Constraints/Dynamic Shapes `_ +# section, we talked about allocating symbols to represent dynamic input dimensions. +# The same happens here: we allocate symbols for every data-dependent value that appears in the program. The important distinction is that these are "unbacked" symbols, +# in contrast to the "backed" symbols allocated for input dimensions. The `"backed/unbacked" `_ +# nomenclature refers to the presence/absence of a "hint" for the symbol: a concrete value backing the symbol, that can inform the compiler on how to proceed. +# +# In the input shape symbol case (backed symbols), these hints are simply the sample input shapes provided, which explains why control-flow branching is determined by the sample input properties. +# For data-dependent values, the symbols are taken from FakeTensor "data" during tracing, and so the compiler doesn't know the actual values (hints) that these symbols would take on. +# +# Let's see how these show up in exported programs: + +class Foo(torch.nn.Module): + def forward(self, x, y): + a = x.item() + b = y.tolist() + return b + [a] + +inps = ( + torch.tensor(1), + torch.tensor([2, 3]), +) +ep = export(Foo(), inps) +print(ep) + +###################################################################### +# The result is that 3 unbacked symbols (notice they're prefixed with "u", instead of the usual "s" for input shape/backed symbols) are allocated and returned: +# 1 for the ``item()`` call, and 1 for each of the elements of ``y`` with the ``tolist()`` call. +# Note from the range constraints field that these take on ranges of ``[-int_oo, int_oo]``, not the default ``[0, int_oo]`` range allocated to input shape symbols, +# since we have no information on what these values are - they don't represent sizes, so don't necessarily have positive values. + +###################################################################### +# Guards, torch._check() +# ^^^^^^^^^^^^^^^^^^^^^^ +# +# But the case above is easy to export, because the concrete values of these symbols aren't used in any compiler decision-making; all that's relevant is that the return values are unbacked symbols. +# The data-dependent errors highlighted in this section are cases like the following, where `data-dependent guards `_ are encountered: + +class Foo(torch.nn.Module): + def forward(self, x, y): + a = x.item() + if a // 2 >= 5: + return y + 2 + else: + return y * 5 + +###################################################################### +# Here we actually need the "hint", or the concrete value of ``a`` for the compiler to decide whether to trace ``return y + 2`` or ``return y * 5`` as the output. +# Because we trace with FakeTensors, we don't know what ``a // 2 >= 5`` actually evaluates to, and export errors out with "Could not guard on data-dependent expression ``u0 // 2 >= 5 (unhinted)``". +# +# So how do we export this toy model? Unlike ``torch.compile()``, export requires full graph compilation, and we can't just graph break on this. Here are some basic options: +# +# 1. Manual specialization: we could intervene by selecting the branch to trace, either by removing the control-flow code to contain only the specialized branch, or using ``torch.compiler.is_compiling()`` to guard what's traced at compile-time. +# 2. ``torch.cond()``: we could rewrite the control-flow code to use ``torch.cond()`` so we don't specialize on a branch. +# +# While these options are valid, they have their pitfalls. Option 1 sometimes requires drastic, invasive rewrites of the model code to specialize, and ``torch.cond()`` is not a comprehensive system for handling data-dependent errors. +# As we will see, there are data-dependent errors that do not involve control-flow. +# +# The generally recommended approach is to start with ``torch._check()`` calls. While these give the impression of purely being assert statements, they are in fact a system of informing the compiler on properties of symbols. +# While a ``torch._check()`` call does act as an assertion at runtime, when traced at compile-time, the checked expression is sent to the symbolic shapes subsystem for reasoning, and any symbol properties that follow from the expression being true, +# are stored as symbol properties (provided it's smart enough to infer those properties). So even if unbacked symbols don't have hints, if we're able to communicate properties that are generally true for these symbols via +# ``torch._check()`` calls, we can potentially bypass data-dependent guards without rewriting the offending model code. +# +# For example in the model above, inserting ``torch._check(a >= 10)`` would tell the compiler that ``y + 2`` can always be returned, and ``torch._check(a == 4)`` tells it to return ``y * 5``. +# See what happens when we re-export this model. + +class Foo(torch.nn.Module): + def forward(self, x, y): + a = x.item() + torch._check(a >= 10) + torch._check(a <= 60) + if a // 2 >= 5: + return y + 2 + else: + return y * 5 + +inps = ( + torch.tensor(32), + torch.randn(4), +) +ep = export(Foo(), inps) +print(ep) + +###################################################################### +# Export succeeds, and note from the range constraints field that ``u0`` takes on a range of ``[10, 60]``. +# +# So what information do ``torch._check()`` calls actually communicate? This varies as the symbolic shapes subsystem gets smarter, but at a fundamental level, these are generally true: +# +# 1. Equality with non-data-dependent expressions: ``torch._check()`` calls that communicate equalities like ``u0 == s0 + 4`` or ``u0 == 5``. +# 2. Range refinement: calls that provide lower or upper bounds for symbols, like the above. +# 3. Some basic reasoning around more complicated expressions: inserting ``torch._check(a < 4)`` will typically tell the compiler that ``a >= 4`` is false. Checks on complex expressions like ``torch._check(a ** 2 - 3 * a <= 10)`` will typically get you past identical guards. +# +# As mentioned previously, ``torch._check()`` calls have applicability outside of data-dependent control flow. For example, here's a model where ``torch._check()`` insertion +# prevails while manual specialization & ``torch.cond()`` do not: + +class Foo(torch.nn.Module): + def forward(self, x, y): + a = x.item() + return y[a] + +inps = ( + torch.tensor(32), + torch.randn(60), +) +export(Foo(), inps) + +###################################################################### +# Here is a scenario where ``torch._check()`` insertion is required simply to prevent an operation from failing. The export call will fail with +# "Could not guard on data-dependent expression ``-u0 > 60``", implying that the compiler doesn't know if this is a valid indexing operation - +# if the value of ``x`` is out-of-bounds for ``y`` or not. Here, manual specialization is too prohibitive, and ``torch.cond()`` has no place. +# Instead, informing the compiler of ``u0``'s range is sufficient: + +class Foo(torch.nn.Module): + def forward(self, x, y): + a = x.item() + torch._check(a >= 0) + torch._check(a <= y.shape[0]) + return y[a] + +inps = ( + torch.tensor(32), + torch.randn(60), +) +ep = export(Foo(), inps) +print(ep) + +###################################################################### +# Specialized values +# ^^^^^^^^^^^^^^^^^^ +# +# Another category of data-dependent error happens when the program attempts to extract a concrete data-dependent integer/float value +# while tracing. This looks something like "Could not extract specialized integer from data-dependent expression", and is analogous to +# the previous class of errors - if these occur when attempting to evaluate concrete integer/float values, data-dependent guard errors arise +# with evaluating concrete boolean values. +# +# This error typically occurs when there is an explicit or implicit ``int()`` cast on a data-dependent expression. For example, this list comprehension +# has a `range()` call that implicitly does an ``int()`` cast on the size of the list: + +class Foo(torch.nn.Module): + def forward(self, x, y): + a = x.item() + b = torch.cat([y for y in range(a)], dim=0) + return b + int(a) + +inps = ( + torch.tensor(32), + torch.randn(60), +) +export(Foo(), inps, strict=False) + +###################################################################### +# For these errors, some basic options you have are: +# +# 1. Avoid unnecessary ``int()`` cast calls, in this case the ``int(a)`` in the return statement. +# 2. Use ``torch._check()`` calls; unfortunately all you may be able to do in this case is specialize (with ``torch._check(a == 60)``). +# 3. Rewrite the offending code at a higher level. For example, the list comprehension is semantically a ``repeat()`` op, which doesn't involve an ``int()`` cast. The following rewrite avoids data-dependent errors: + +class Foo(torch.nn.Module): + def forward(self, x, y): + a = x.item() + b = y.unsqueeze(0).repeat(a, 1) + return b + a + +inps = ( + torch.tensor(32), + torch.randn(60), +) +ep = export(Foo(), inps, strict=False) +print(ep) + +###################################################################### +# Data-dependent errors can be much more involved, and there are many more options in your toolkit to deal with them: ``torch._check_is_size()``, ``guard_size_oblivious()``, or real-tensor tracing, as starters. +# For more in-depth guides, please refer to the `Export Programming Model `_, +# or `Dealing with GuardOnDataDependentSymNode errors `_. + ###################################################################### # Custom Ops # ---------- From 4b1d9fb029c11fe51ed31f5ee72c9e12bbfa153d Mon Sep 17 00:00:00 2001 From: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> Date: Thu, 23 Jan 2025 20:30:39 -0500 Subject: [PATCH 028/347] Update FSDP_tutorial.rst (#3252) Link no longer exists so giving credit to creator instead --- intermediate_source/FSDP_tutorial.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intermediate_source/FSDP_tutorial.rst b/intermediate_source/FSDP_tutorial.rst index ce104889860..8e5217c64a8 100644 --- a/intermediate_source/FSDP_tutorial.rst +++ b/intermediate_source/FSDP_tutorial.rst @@ -11,7 +11,7 @@ It also comes with considerable engineering complexity to handle the training of `PyTorch FSDP `__, released in PyTorch 1.11 makes this easier. In this tutorial, we show how to use `FSDP APIs `__, for simple MNIST models that can be extended to other larger models such as `HuggingFace BERT models `__, -`GPT 3 models up to 1T parameters `__ . The sample DDP MNIST code has been borrowed from `here `__. +`GPT 3 models up to 1T parameters `__ . The sample DDP MNIST code courtesy of `Patrick Hu `_. How FSDP works From 2a30921062c976c57e7f71db02524de75e898872 Mon Sep 17 00:00:00 2001 From: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> Date: Fri, 24 Jan 2025 10:08:05 -0500 Subject: [PATCH 029/347] Update fx_graph_mode_ptq_static.rst (#3255) Update to utility link --- prototype_source/fx_graph_mode_ptq_static.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prototype_source/fx_graph_mode_ptq_static.rst b/prototype_source/fx_graph_mode_ptq_static.rst index 0c4f8065e37..da16d04dbce 100644 --- a/prototype_source/fx_graph_mode_ptq_static.rst +++ b/prototype_source/fx_graph_mode_ptq_static.rst @@ -253,7 +253,7 @@ of the observers for activation and weight. ``QConfigMapping`` contains mapping Utility functions related to ``qconfig`` can be found in the `qconfig `_ file -while those for ``QConfigMapping`` can be found in the `qconfig_mapping ` +while those for ``QConfigMapping`` can be found in the `qconfig_mapping ` .. code:: python From d4c1e740f1f05b73fc0c48781ae1cfc6a198a932 Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Fri, 24 Jan 2025 10:17:42 -0800 Subject: [PATCH 030/347] Pull 2.6 binaries (#3234) * Pull 2.6 binaries * Update --- .ci/docker/requirements.txt | 4 ++-- .jenkins/build.sh | 6 ++++-- .jenkins/validate_tutorials_built.py | 5 ++++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.ci/docker/requirements.txt b/.ci/docker/requirements.txt index 6babb63e786..56df738f96c 100644 --- a/.ci/docker/requirements.txt +++ b/.ci/docker/requirements.txt @@ -28,8 +28,8 @@ tensorboard jinja2==3.1.3 pytorch-lightning torchx -torchrl==0.5.0 -tensordict==0.5.0 +torchrl==0.6.0 +tensordict==0.6.0 ax-platform>=0.4.0 nbformat>=5.9.2 datasets diff --git a/.jenkins/build.sh b/.jenkins/build.sh index e6ecaf6712a..8eca78ae346 100755 --- a/.jenkins/build.sh +++ b/.jenkins/build.sh @@ -22,8 +22,10 @@ sudo apt-get install -y pandoc #Install PyTorch Nightly for test. # Nightly - pip install --pre torch torchvision torchaudio -f https://download.pytorch.org/whl/nightly/cu102/torch_nightly.html # Install 2.5 to merge all 2.4 PRs - uncomment to install nightly binaries (update the version as needed). -# pip uninstall -y torch torchvision torchaudio torchtext torchdata -# pip3 install torch==2.5.0 torchvision torchaudio --no-cache-dir --index-url https://download.pytorch.org/whl/test/cu124 +sudo pip uninstall -y torch torchvision torchaudio torchtext torchdata +sudo pip3 install torch==2.6.0 torchvision --no-cache-dir --index-url https://download.pytorch.org/whl/test/cu124 +sudo pip uninstall -y fbgemm-gpu torchrec +sudo pip3 install fbgemm-gpu==1.1.0 torchrec==1.0.0 --no-cache-dir --index-url https://download.pytorch.org/whl/test/cu124 # Install two language tokenizers for Translation with TorchText tutorial python -m spacy download en_core_web_sm diff --git a/.jenkins/validate_tutorials_built.py b/.jenkins/validate_tutorials_built.py index c3bf4c5534b..665b3b48e3b 100644 --- a/.jenkins/validate_tutorials_built.py +++ b/.jenkins/validate_tutorials_built.py @@ -51,7 +51,10 @@ "intermediate_source/flask_rest_api_tutorial", "intermediate_source/text_to_speech_with_torchaudio", "intermediate_source/tensorboard_profiler_tutorial", # reenable after 2.0 release. - "intermediate_source/torch_export_tutorial" # reenable after 2940 is fixed. + "intermediate_source/torch_export_tutorial", # reenable after 2940 is fixed. + "advanced_source/pendulum", + "beginner_source/onnx/export_simple_model_to_onnx_tutorial", + "beginner_source/onnx/onnx_registry_tutorial" ] def tutorial_source_dirs() -> List[Path]: From 5786e9790e0a4886872f4e2e0806f10267443a98 Mon Sep 17 00:00:00 2001 From: Angela Yi Date: Fri, 24 Jan 2025 12:21:07 -0800 Subject: [PATCH 031/347] Update aoti tutorial (#3224) Co-authored-by: Svetlana Karslioglu --- recipes_source/torch_export_aoti_python.py | 194 +++++++++++++-------- 1 file changed, 123 insertions(+), 71 deletions(-) diff --git a/recipes_source/torch_export_aoti_python.py b/recipes_source/torch_export_aoti_python.py index 312491b660f..c0cbb7e2800 100644 --- a/recipes_source/torch_export_aoti_python.py +++ b/recipes_source/torch_export_aoti_python.py @@ -3,7 +3,7 @@ """ .. meta:: :description: An end-to-end example of how to use AOTInductor for Python runtime. - :keywords: torch.export, AOTInductor, torch._inductor.aot_compile, torch._export.aot_load + :keywords: torch.export, AOTInductor, torch._inductor.aoti_compile_and_package, aot_compile, torch._export.aoti_load_package ``torch.export`` AOTInductor Tutorial for Python runtime (Beta) =============================================================== @@ -14,19 +14,18 @@ # # .. warning:: # -# ``torch._inductor.aot_compile`` and ``torch._export.aot_load`` are in Beta status and are subject to backwards compatibility -# breaking changes. This tutorial provides an example of how to use these APIs for model deployment using Python runtime. +# ``torch._inductor.aoti_compile_and_package`` and +# ``torch._inductor.aoti_load_package`` are in Beta status and are subject +# to backwards compatibility breaking changes. This tutorial provides an +# example of how to use these APIs for model deployment using Python +# runtime. # -# It has been shown `previously `__ how AOTInductor can be used -# to do Ahead-of-Time compilation of PyTorch exported models by creating -# a shared library that can be run in a non-Python environment. -# -# -# In this tutorial, you will learn an end-to-end example of how to use AOTInductor for Python runtime. -# We will look at how to use :func:`torch._inductor.aot_compile` along with :func:`torch.export.export` to generate a -# shared library. Additionally, we will examine how to execute the shared library in Python runtime using :func:`torch._export.aot_load`. -# You will learn about the speed up seen in the first inference time using AOTInductor, especially when using -# ``max-autotune`` mode which can take some time to execute. +# It has been shown `previously +# `__ how +# AOTInductor can be used to do Ahead-of-Time compilation of PyTorch exported +# models by creating an artifact that can be run in a non-Python environment. +# In this tutorial, you will learn an end-to-end example of how to use +# AOTInductor for Python runtime. # # **Contents** # @@ -36,115 +35,169 @@ ###################################################################### # Prerequisites # ------------- -# * PyTorch 2.4 or later +# * PyTorch 2.6 or later # * Basic understanding of ``torch.export`` and AOTInductor # * Complete the `AOTInductor: Ahead-Of-Time Compilation for Torch.Export-ed Models `_ tutorial ###################################################################### # What you will learn # ---------------------- -# * How to use AOTInductor for python runtime. -# * How to use :func:`torch._inductor.aot_compile` along with :func:`torch.export.export` to generate a shared library -# * How to run a shared library in Python runtime using :func:`torch._export.aot_load`. -# * When do you use AOTInductor for python runtime +# * How to use AOTInductor for Python runtime. +# * How to use :func:`torch._inductor.aoti_compile_and_package` along with :func:`torch.export.export` to generate a compiled artifact +# * How to load and run the artifact in a Python runtime using :func:`torch._export.aot_load`. +# * When to you use AOTInductor with a Python runtime ###################################################################### # Model Compilation # ----------------- # -# We will use the TorchVision pretrained `ResNet18` model and TorchInductor on the -# exported PyTorch program using :func:`torch._inductor.aot_compile`. +# We will use the TorchVision pretrained ``ResNet18`` model as an example. # -# .. note:: +# The first step is to export the model to a graph representation using +# :func:`torch.export.export`. To learn more about using this function, you can +# check out the `docs `_ or the +# `tutorial `_. # -# This API also supports :func:`torch.compile` options like ``mode`` -# This means that if used on a CUDA enabled device, you can, for example, set ``"max_autotune": True`` -# which leverages Triton based matrix multiplications & convolutions, and enables CUDA graphs by default. +# Once we have exported the PyTorch model and obtained an ``ExportedProgram``, +# we can apply :func:`torch._inductor.aoti_compile_and_package` to AOTInductor +# to compile the program to a specified device, and save the generated contents +# into a ".pt2" artifact. # -# We also specify ``dynamic_shapes`` for the batch dimension. In this example, ``min=2`` is not a bug and is -# explained in `The 0/1 Specialization Problem `__ - +# .. note:: +# +# This API supports the same available options that :func:`torch.compile` +# has, such as ``mode`` and ``max_autotune`` (for those who want to enable +# CUDA graphs and leverage Triton based matrix multiplications and +# convolutions) import os import torch +import torch._inductor from torchvision.models import ResNet18_Weights, resnet18 model = resnet18(weights=ResNet18_Weights.DEFAULT) model.eval() with torch.inference_mode(): + inductor_configs = {} - # Specify the generated shared library path - aot_compile_options = { - "aot_inductor.output_path": os.path.join(os.getcwd(), "resnet18_pt2.so"), - } if torch.cuda.is_available(): device = "cuda" - aot_compile_options.update({"max_autotune": True}) + inductor_configs["max_autotune"] = True else: device = "cpu" model = model.to(device=device) example_inputs = (torch.randn(2, 3, 224, 224, device=device),) - # min=2 is not a bug and is explained in the 0/1 Specialization Problem - batch_dim = torch.export.Dim("batch", min=2, max=32) exported_program = torch.export.export( model, example_inputs, - # Specify the first dimension of the input x as dynamic - dynamic_shapes={"x": {0: batch_dim}}, ) - so_path = torch._inductor.aot_compile( - exported_program.module(), - example_inputs, - # Specify the generated shared library path - options=aot_compile_options + path = torch._inductor.aoti_compile_and_package( + exported_program, + package_path=os.path.join(os.getcwd(), "resnet18.pt2"), + inductor_configs=inductor_configs ) +###################################################################### +# The result of :func:`aoti_compile_and_package` is an artifact "resnet18.pt2" +# which can be loaded and executed in Python and C++. +# +# The artifact itself contains a bunch of AOTInductor generated code, such as +# a generated C++ runner file, a shared library compiled from the C++ file, and +# CUDA binary files, aka cubin files, if optimizing for CUDA. +# +# Structure-wise, the artifact is a structured ``.zip`` file, with the following +# specification: +# +# .. code:: +# . +# ├── archive_format +# ├── version +# ├── data +# │ ├── aotinductor +# │ │ └── model +# │ │ ├── xxx.cpp # AOTInductor generated cpp file +# │ │ ├── xxx.so # AOTInductor generated shared library +# │ │ ├── xxx.cubin # Cubin files (if running on CUDA) +# │ │ └── xxx_metadata.json # Additional metadata to save +# │ ├── weights +# │ │ └── TBD +# │ └── constants +# │ └── TBD +# └── extra +# └── metadata.json +# +# We can use the following command to inspect the artifact contents: +# +# .. code:: bash +# +# $ unzip -l resnet18.pt2 +# +# .. code:: +# +# Archive: resnet18.pt2 +# Length Date Time Name +# --------- ---------- ----- ---- +# 1 01-08-2025 16:40 version +# 3 01-08-2025 16:40 archive_format +# 10088 01-08-2025 16:40 data/aotinductor/model/cagzt6akdaczvxwtbvqe34otfe5jlorktbqlojbzqjqvbfsjlge4.cubin +# 17160 01-08-2025 16:40 data/aotinductor/model/c6oytfjmt5w4c7onvtm6fray7clirxt7q5xjbwx3hdydclmwoujz.cubin +# 16616 01-08-2025 16:40 data/aotinductor/model/c7ydp7nocyz323hij4tmlf2kcedmwlyg6r57gaqzcsy3huneamu6.cubin +# 17776 01-08-2025 16:40 data/aotinductor/model/cyqdf46ordevqhiddvpdpp3uzwatfbzdpl3auj2nx23uxvplnne2.cubin +# 10856 01-08-2025 16:40 data/aotinductor/model/cpzfebfgrusqslui7fxsuoo4tvwulmrxirc5tmrpa4mvrbdno7kn.cubin +# 14608 01-08-2025 16:40 data/aotinductor/model/c5ukeoz5wmaszd7vczdz2qhtt6n7tdbl3b6wuy4rb2se24fjwfoy.cubin +# 11376 01-08-2025 16:40 data/aotinductor/model/csu3nstcp56tsjfycygaqsewpu64l5s6zavvz7537cm4s4cv2k3r.cubin +# 10984 01-08-2025 16:40 data/aotinductor/model/cp76lez4glmgq7gedf2u25zvvv6rksv5lav4q22dibd2zicbgwj3.cubin +# 14736 01-08-2025 16:40 data/aotinductor/model/c2bb5p6tnwz4elgujqelsrp3unvkgsyiv7xqxmpvuxcm4jfl7pc2.cubin +# 11376 01-08-2025 16:40 data/aotinductor/model/c6eopmb2b4ngodwsayae4r5q6ni3jlfogfbdk3ypg56tgpzhubfy.cubin +# 11624 01-08-2025 16:40 data/aotinductor/model/chmwe6lvoekzfowdbiizitm3haiiuad5kdm6sd2m6mv6dkn2zk32.cubin +# 15632 01-08-2025 16:40 data/aotinductor/model/c3jop5g344hj3ztsu4qm6ibxyaaerlhkzh2e6emak23rxfje6jam.cubin +# 25472 01-08-2025 16:40 data/aotinductor/model/chaiixybeiuuitm2nmqnxzijzwgnn2n7uuss4qmsupgblfh3h5hk.cubin +# 139389 01-08-2025 16:40 data/aotinductor/model/cvk6qzuybruhwxtfblzxiov3rlrziv5fkqc4mdhbmantfu3lmd6t.cpp +# 27 01-08-2025 16:40 data/aotinductor/model/cvk6qzuybruhwxtfblzxiov3rlrziv5fkqc4mdhbmantfu3lmd6t_metadata.json +# 47195424 01-08-2025 16:40 data/aotinductor/model/cvk6qzuybruhwxtfblzxiov3rlrziv5fkqc4mdhbmantfu3lmd6t.so +# --------- ------- +# 47523148 18 files + ###################################################################### # Model Inference in Python # ------------------------- # -# Typically, the shared object generated above is used in a non-Python environment. In PyTorch 2.3, -# we added a new API called :func:`torch._export.aot_load` to load the shared library in the Python runtime. -# The API follows a structure similar to the :func:`torch.jit.load` API . You need to specify the path -# of the shared library and the device where it should be loaded. +# To load and run the artifact in Python, we can use :func:`torch._inductor.aoti_load_package`. # -# .. note:: -# In the example above, we specified ``batch_size=1`` for inference and it still functions correctly even though we specified ``min=2`` in -# :func:`torch.export.export`. - import os import torch +import torch._inductor -device = "cuda" if torch.cuda.is_available() else "cpu" -model_so_path = os.path.join(os.getcwd(), "resnet18_pt2.so") +model_path = os.path.join(os.getcwd(), "resnet18.pt2") -model = torch._export.aot_load(model_so_path, device) -example_inputs = (torch.randn(1, 3, 224, 224, device=device),) +compiled_model = torch._inductor.aoti_load_package(model_path) +example_inputs = (torch.randn(2, 3, 224, 224, device=device),) with torch.inference_mode(): - output = model(example_inputs) + output = compiled_model(example_inputs) + ###################################################################### -# When to use AOTInductor for Python Runtime -# ------------------------------------------ +# When to use AOTInductor with a Python Runtime +# --------------------------------------------- # -# One of the requirements for using AOTInductor is that the model shouldn't have any graph breaks. -# Once this requirement is met, the primary use case for using AOTInductor Python Runtime is for -# model deployment using Python. -# There are mainly two reasons why you would use AOTInductor Python Runtime: +# There are mainly two reasons why one would use AOTInductor with a Python Runtime: # -# - ``torch._inductor.aot_compile`` generates a shared library. This is useful for model -# versioning for deployments and tracking model performance over time. +# - ``torch._inductor.aoti_compile_and_package`` generates a singular +# serialized artifact. This is useful for model versioning for deployments +# and tracking model performance over time. # - With :func:`torch.compile` being a JIT compiler, there is a warmup -# cost associated with the first compilation. Your deployment needs to account for the -# compilation time taken for the first inference. With AOTInductor, the compilation is -# done offline using ``torch.export.export`` & ``torch._indutor.aot_compile``. The deployment -# would only load the shared library using ``torch._export.aot_load`` and run inference. +# cost associated with the first compilation. Your deployment needs to +# account for the compilation time taken for the first inference. With +# AOTInductor, the compilation is done ahead of time using +# ``torch.export.export`` and ``torch._inductor.aoti_compile_and_package``. +# At deployment time, after loading the model, running inference does not +# have any additional cost. # # # The section below shows the speedup achieved with AOTInductor for first inference @@ -185,7 +238,7 @@ def timed(fn): torch._dynamo.reset() -model = torch._export.aot_load(model_so_path, device) +model = torch._inductor.aoti_load_package(model_path) example_inputs = (torch.randn(1, 3, 224, 224, device=device),) with torch.inference_mode(): @@ -217,8 +270,7 @@ def timed(fn): # ---------- # # In this recipe, we have learned how to effectively use the AOTInductor for Python runtime by -# compiling and loading a pretrained ``ResNet18`` model using the ``torch._inductor.aot_compile`` -# and ``torch._export.aot_load`` APIs. This process demonstrates the practical application of -# generating a shared library and running it within a Python environment, even with dynamic shape -# considerations and device-specific optimizations. We also looked at the advantage of using +# compiling and loading a pretrained ``ResNet18`` model. This process +# demonstrates the practical application of generating a compiled artifact and +# running it within a Python environment. We also looked at the advantage of using # AOTInductor in model deployments, with regards to speed up in first inference time. From c27636a2ddf0887b3af0a00bc689cc1bb84941f6 Mon Sep 17 00:00:00 2001 From: Richard Zou Date: Fri, 24 Jan 2025 15:26:22 -0500 Subject: [PATCH 032/347] Update user-defined triton kernels tutorial with new torch.library.triton_op (#3227) Update user-defined triton kernels tutorial with new torch.library.triton_op The new API is a more advanced complement to the existing APIs. --------- Co-authored-by: Svetlana Karslioglu --- ...ile_user_defined_triton_kernel_tutorial.py | 213 +++++++++++++++++- 1 file changed, 210 insertions(+), 3 deletions(-) diff --git a/recipes_source/torch_compile_user_defined_triton_kernel_tutorial.py b/recipes_source/torch_compile_user_defined_triton_kernel_tutorial.py index 7d183af6fd1..7f3e6fbf6f8 100644 --- a/recipes_source/torch_compile_user_defined_triton_kernel_tutorial.py +++ b/recipes_source/torch_compile_user_defined_triton_kernel_tutorial.py @@ -140,17 +140,224 @@ def add_fn(x, y): print(f"Vector addition of\nX:\t{x}\nY:\t{y}\nis equal to\n{out}") ###################################################################### -# Composibility and Limitations +# Composability +# ------------------------------------------------------------------- +# +# User-defined Triton kernels do not automatically support all PyTorch +# subsystems. This can be seen in the following use cases: +# +# - Adding a CPU fallback +# - Adding a ``FlopCounter`` formula +# - Composing with Tensor Subclasses +# +# To compose with additional PyTorch subsystems, use ``torch.library.triton_op``. +# +# ``triton_op is`` a structured way of defining a custom operator that is backed by one +# or more Triton kernels: like regular custom operators (``torch.library.custom_op``), +# you are able to specify the interactions with PyTorch subsystems via ``torch.library``. +# However, unlike ``torch.library.custom_op``, which creates opaque callables with respect to +# ``torch.compile``, ``torch.compile`` traces into ``triton_op`` to apply optimizations. +# +# Here’s a chart of which API to use when integrating Triton kernels with PyTorch. +# +# .. list-table:: +# :header-rows: 1 +# +# * - +# - Triton kernel (no explicit ``torch.library`` wrapper) +# - ``torch.library.triton_op`` +# - ``torch.library.custom_op`` +# * - Supports inference +# - Yes +# - Yes +# - Yes +# * - Supports training +# - In the majority of cases +# - Yes +# - Yes +# * - Supports ``torch.compile`` +# - Yes +# - Yes +# - Yes +# * - Supports ``torch.compile(fullgraph=True)`` +# - In the majority of cases +# - In the majority of cases +# - In all cases +# * - Does torch.compile trace into the implementation? +# - Yes +# - Yes +# - No +# * - Supports AOTInductor +# - Yes +# - Yes +# - No +# * - Supports PyTorch Subsystems like FlopCounterMode, CPU Fallback, Tensor Subclasses +# - No +# - Yes +# - Yes + +###################################################################### +# Wrapping Triton kernels with ``triton_op`` +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# Use ``torch.library.triton_op`` to wrap a function that may invoke one or more Triton kernels. +# Use ``torch.library.wrap_triton`` to wrap the calls to the Triton kernel. + +from torch.library import triton_op, wrap_triton + +@triton_op("mylib::mysin", mutates_args={}) +def mysin(x: torch.Tensor) -> torch.Tensor: + out = torch.empty_like(x) + n_elements = x.numel() + wrap_triton(sin_kernel)[(n_elements,)](x, out, n_elements, BLOCK_SIZE=4) + return out + +@triton.jit +def sin_kernel( + in_ptr0, + out_ptr, + n_elements, + BLOCK_SIZE: "tl.constexpr", +): + pid = tl.program_id(axis=0) + block_start = pid * BLOCK_SIZE + offsets = block_start + tl.arange(0, BLOCK_SIZE) + mask = offsets < n_elements + x = tl.load(in_ptr0 + offsets, mask=mask) + output = tl.sin(x) + tl.store(out_ptr + offsets, output, mask=mask) + +def sin_triton(x): + out = torch.empty_like(x) + n_elements = x.numel() + sin_kernel[(n_elements,)](x, out, n_elements, BLOCK_SIZE=4) + return out + +###################################################################### +# You can invoke the ``triton_op`` in one of the following two ways. + +x = torch.randn(3, device="cuda") +y = mysin(x) +z = torch.ops.mylib.mysin.default(x) + +assert torch.allclose(y, x.sin()) +assert torch.allclose(z, x.sin()) + +###################################################################### +# The resulting ``triton_op`` works with ``torch.compile`` and ``AOTInductor``. + +y = torch.compile(mysin)(x) +assert torch.allclose(y, x.sin()) + +###################################################################### +# Adding training support +# ^^^^^^^^^^^^^^^^^^^^^^^ +# +# Use ``register_autograd`` to add an autograd formula for the ``triton_op``. +# Prefer this to using ``torch.autograd.Function`` (which has various composability footguns +# with ``torch.compile``). + +def backward(ctx, grad_output): + x, = ctx.saved_tensors + return grad_input * x.cos() + +def setup_context(ctx, inputs, output): + x, = inputs + ctx.save_for_backward(x) + +mysin.register_autograd(backward, setup_context=setup_context) + +###################################################################### +# Note that the backward must be a composition of PyTorch-understood operators. +# If you want the backward to call Triton kernels, then those must be wrapped in ``triton_op`` as well: + +@triton.jit +def cos_kernel( + in_ptr0, + out_ptr, + n_elements, + BLOCK_SIZE: "tl.constexpr", +): + pid = tl.program_id(axis=0) + block_start = pid * BLOCK_SIZE + offsets = block_start + tl.arange(0, BLOCK_SIZE) + mask = offsets < n_elements + x = tl.load(in_ptr0 + offsets, mask=mask) + output = tl.cos(x) + tl.store(out_ptr + offsets, output, mask=mask) + +@triton_op("mylib::mycos", mutates_args={}) +def mycos(x: torch.Tensor) -> torch.Tensor: + out = torch.empty_like(x) + n_elements = x.numel() + wrap_triton(cos_kernel)[(n_elements,)](x, out, n_elements, BLOCK_SIZE=4) + return out + +def backward(ctx, grad_output): + x, = ctx.saved_tensors + return grad_input * mycos(x) + +def setup_context(ctx, inputs, output): + x, = inputs + ctx.save_for_backward(x) + +mysin.register_autograd(backward, setup_context=setup_context) + +###################################################################### +# Adding a CPU Fallback +# ^^^^^^^^^^^^^^^^^^^^^ +# Triton kernels don’t run on CPU. Use ``register_kernel`` to add a CPU (or any other device) fallback for the ``triton_op``: + +@mysin.register_kernel("cpu") +def _(x): + return torch.sin(x) + +x = torch.randn(3) +y = mysin(x) +assert torch.allclose(y, x.sin()) + +###################################################################### +# The fallback must be composed of PyTorch operators. + +###################################################################### +# Adding a FlopCounter Formula +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# To specify how many flops the triton kernel reports under PyTorch's flop counter, +# use ``register_flop_formula``. + +from torch.utils.flop_counter import FlopCounterMode, register_flop_formula + +@register_flop_formula(torch.ops.mylib.mysin) +def _(x_shape): + numel = 1 + for s in x_shape: + numel *= s + return numel + +x = torch.randn(3, device="cuda") + +######################################################### +# ``FlopCounterMode`` requires `tabulate `__. +# Before running the code below, make sure you have ``tabulate`` installed or install by +# running ``pip install tabulate``. +# +# >>> with FlopCounterMode() as flop_counter: +# >>> y = mysin(x) + +###################################################################### +# Limitations # -------------------------------------------------------------------- # # As of PyTorch 2.3, the support for user-defined Triton kernels in ``torch.compile`` # includes dynamic shapes, ``torch.autograd.Function``, JIT inductor, and AOT inductor. # You can use these features together to build complex, high-performance models. # +# PyTorch 2.6 added ``torch.library.triton_op``, which adds support for +# user-defined Triton kernels in tensor subclasses and other advanced features. +# # However, there are certain limitations to be aware of: # -# * **Tensor Subclasses:** Currently, there is no support for -# tensor subclasses and other advanced features. # * **Triton Features:** While ``triton.heuristics`` can be used either standalone or # before ``triton.autotune``, it cannot be used after ``triton.autotune``. This # implies that if ``triton.heuristics`` and ``triton.autotune`` are to be used From db9a86712a278b2c9dc11adae10015e091e6a824 Mon Sep 17 00:00:00 2001 From: mikaylagawarecki Date: Fri, 24 Jan 2025 16:51:52 -0500 Subject: [PATCH 033/347] Validate transformer tutorial builds against 2.6 (#3202) Co-authored-by: Svetlana Karslioglu --- .jenkins/validate_tutorials_built.py | 1 - .../transformer_building_blocks.py | 391 ++++++++++++------ 2 files changed, 258 insertions(+), 134 deletions(-) diff --git a/.jenkins/validate_tutorials_built.py b/.jenkins/validate_tutorials_built.py index 665b3b48e3b..99492b0719f 100644 --- a/.jenkins/validate_tutorials_built.py +++ b/.jenkins/validate_tutorials_built.py @@ -25,7 +25,6 @@ "intermediate_source/mnist_train_nas", # used by ax_multiobjective_nas_tutorial.py "intermediate_source/fx_conv_bn_fuser", "intermediate_source/_torch_export_nightly_tutorial", # does not work on release - "intermediate_source/transformer_building_blocks", # does not work on release "advanced_source/super_resolution_with_onnxruntime", "advanced_source/usb_semisup_learn", # fails with CUDA OOM error, should try on a different worker "prototype_source/fx_graph_mode_ptq_dynamic", diff --git a/intermediate_source/transformer_building_blocks.py b/intermediate_source/transformer_building_blocks.py index 932be472e89..7d2c67356e1 100644 --- a/intermediate_source/transformer_building_blocks.py +++ b/intermediate_source/transformer_building_blocks.py @@ -142,7 +142,7 @@ # that arise due to different sequence lengths within a batch. Since there is # no ``query_padding_mask`` in ``nn.MHA``, users have to take care to mask/slice # the outputs appropriately to account for query sequence lengths. ``NestedTensor`` -# cleanly removes the need for this sort of error-prone padding masks. +# cleanly removes the need for this sort of error-prone padding masks. # # * **Memory** # Instead of materializing a dense ``[B, S, D]`` tensor with a ``[B, S]`` @@ -163,6 +163,7 @@ import torch.nn as nn import torch.nn.functional as F + class MultiHeadAttention(nn.Module): """ Computes multi-head attention. Supports nested or padded tensors. @@ -177,6 +178,7 @@ class MultiHeadAttention(nn.Module): dropout (float, optional): Dropout probability. Default: 0.0 bias (bool, optional): Whether to add bias to input projection. Default: True """ + def __init__( self, E_q: int, @@ -195,23 +197,25 @@ def __init__( self.dropout = dropout self._qkv_same_embed_dim = E_q == E_k and E_q == E_v if self._qkv_same_embed_dim: - self.packed_proj = nn.Linear(E_q, E_total * 3, bias=bias, **factory_kwargs) + self.packed_proj = nn.Linear(E_q, E_total * 3, bias=bias, **factory_kwargs) else: - self.q_proj = nn.Linear(E_q, E_total, bias=bias, **factory_kwargs) - self.k_proj = nn.Linear(E_k, E_total, bias=bias, **factory_kwargs) - self.v_proj = nn.Linear(E_v, E_total, bias=bias, **factory_kwargs) + self.q_proj = nn.Linear(E_q, E_total, bias=bias, **factory_kwargs) + self.k_proj = nn.Linear(E_k, E_total, bias=bias, **factory_kwargs) + self.v_proj = nn.Linear(E_v, E_total, bias=bias, **factory_kwargs) E_out = E_q self.out_proj = nn.Linear(E_total, E_out, bias=bias, **factory_kwargs) assert E_total % nheads == 0, "Embedding dim is not divisible by nheads" self.E_head = E_total // nheads self.bias = bias - def forward(self, - query: torch.Tensor, - key: torch.Tensor, - value: torch.Tensor, - attn_mask=None, - is_causal=False) -> torch.Tensor: + def forward( + self, + query: torch.Tensor, + key: torch.Tensor, + value: torch.Tensor, + attn_mask=None, + is_causal=False, + ) -> torch.Tensor: """ Forward pass; runs the following process: 1. Apply input projection @@ -235,12 +239,20 @@ def forward(self, result = self.packed_proj(query) query, key, value = torch.chunk(result, 3, dim=-1) else: - q_weight, k_weight, v_weight = torch.chunk(self.packed_proj.weight, 3, dim=0) + q_weight, k_weight, v_weight = torch.chunk( + self.packed_proj.weight, 3, dim=0 + ) if self.bias: - q_bias, k_bias, v_bias = torch.chunk(self.packed_proj.bias, 3, dim=0) + q_bias, k_bias, v_bias = torch.chunk( + self.packed_proj.bias, 3, dim=0 + ) else: q_bias, k_bias, v_bias = None, None, None - query, key, value = F.linear(query, q_weight, q_bias), F.linear(key, k_weight, k_bias), F.linear(value, v_weight, v_bias) + query, key, value = ( + F.linear(query, q_weight, q_bias), + F.linear(key, k_weight, k_bias), + F.linear(value, v_weight, v_bias), + ) else: query = self.q_proj(query) @@ -259,7 +271,8 @@ def forward(self, # Step 3. Run SDPA # (N, nheads, L_t, E_head) attn_output = F.scaled_dot_product_attention( - query, key, value, dropout_p=self.dropout, is_causal=is_causal) + query, key, value, dropout_p=self.dropout, is_causal=is_causal + ) # (N, nheads, L_t, E_head) -> (N, L_t, nheads, E_head) -> (N, L_t, E_total) attn_output = attn_output.transpose(1, 2).flatten(-2) @@ -280,6 +293,7 @@ def forward(self, import numpy as np + def zipf_sentence_lengths(alpha: float, batch_size: int) -> torch.Tensor: # generate fake corpus by unigram Zipf distribution # from wikitext-2 corpus, we get rank "." = 3, "!" = 386, "?" = 858 @@ -292,6 +306,7 @@ def zipf_sentence_lengths(alpha: float, batch_size: int) -> torch.Tensor: word = np.random.zipf(alpha) return torch.tensor(sentence_lengths) + # Generate a batch of semi-realistic data using Zipf distribution for sentence lengths # in the form of nested tensors with the jagged layout. def gen_batch(N, E_q, E_k, E_v, device, dtype=torch.float32, query_seq_len_1=False): @@ -302,30 +317,41 @@ def gen_batch(N, E_q, E_k, E_v, device, dtype=torch.float32, query_seq_len_1=Fal # dimension and works with torch.compile. The batch items each have shape (B, S*, D) # where B = batch size, S* = ragged sequence length, and D = embedding dimension. if query_seq_len_1: - query = torch.nested.nested_tensor([ - torch.randn(1, E_q, dtype=dtype, device=device) - for l in sentence_lengths - ], layout=torch.jagged) + query = torch.nested.nested_tensor( + [torch.randn(1, E_q, dtype=dtype, device=device) for l in sentence_lengths], + layout=torch.jagged, + ) else: - query = torch.nested.nested_tensor([ - torch.randn(l.item(), E_q, dtype=dtype, device=device) - for l in sentence_lengths - ], layout=torch.jagged) - - key = torch.nested.nested_tensor([ - torch.randn(s.item(), E_k, dtype=dtype, device=device) - for s in sentence_lengths - ], layout=torch.jagged) - - value = torch.nested.nested_tensor([ - torch.randn(s.item(), E_v, dtype=dtype, device=device) - for s in sentence_lengths - ], layout=torch.jagged) + query = torch.nested.nested_tensor( + [ + torch.randn(l.item(), E_q, dtype=dtype, device=device) + for l in sentence_lengths + ], + layout=torch.jagged, + ) + + key = torch.nested.nested_tensor( + [ + torch.randn(s.item(), E_k, dtype=dtype, device=device) + for s in sentence_lengths + ], + layout=torch.jagged, + ) + + value = torch.nested.nested_tensor( + [ + torch.randn(s.item(), E_v, dtype=dtype, device=device) + for s in sentence_lengths + ], + layout=torch.jagged, + ) return query, key, value, sentence_lengths -import timeit + import math +import timeit + def benchmark(func, *args, **kwargs): torch.cuda.synchronize() @@ -336,6 +362,7 @@ def benchmark(func, *args, **kwargs): end = timeit.default_timer() return output, (end - begin), torch.cuda.max_memory_allocated() + ############################################################################## # We will now demonstrate the performance improvements of using nested tensors # in the ``MultiheadAttention`` layer + compile for self attention. We compare this against @@ -347,71 +374,94 @@ def benchmark(func, *args, **kwargs): nheads = 8 dropout = 0.0 bias = True -device='cuda' +device = "cuda" torch.manual_seed(6) query, key, value, sentence_lengths = gen_batch(N, E_q, E_k, E_v, device) S = sentence_lengths.max().item() -print(f"Total sequence length in nested query {sentence_lengths.sum().item()}, max sequence length {S}") +print( + f"Total sequence length in nested query {sentence_lengths.sum().item()}, max sequence length {S}" +) padded_query, padded_key, padded_value = ( t.to_padded_tensor(0.0) for t in (query, key, value) ) torch.manual_seed(6) -mha_layer = MultiHeadAttention(E_q, E_k, E_v, E_total, nheads, dropout=dropout, bias=bias, device='cuda') +mha_layer = MultiHeadAttention( + E_q, E_k, E_v, E_total, nheads, dropout=dropout, bias=bias, device="cuda" +) torch.manual_seed(6) -vanilla_mha_layer = nn.MultiheadAttention(E_q, nheads, dropout=dropout, batch_first=True, bias=bias, device='cuda') +vanilla_mha_layer = nn.MultiheadAttention( + E_q, nheads, dropout=dropout, batch_first=True, bias=bias, device="cuda" +) # ``nn.MultiheadAttention`` uses a non conventional initialization for layers, so do this for exact parity :( -mha_layer.out_proj.weight = nn.Parameter(vanilla_mha_layer.out_proj.weight.clone().detach()) -mha_layer.packed_proj.weight = nn.Parameter(vanilla_mha_layer.in_proj_weight.clone().detach()) +mha_layer.out_proj.weight = nn.Parameter( + vanilla_mha_layer.out_proj.weight.clone().detach() +) +mha_layer.packed_proj.weight = nn.Parameter( + vanilla_mha_layer.in_proj_weight.clone().detach() +) mha_layer.out_proj.bias = nn.Parameter(vanilla_mha_layer.out_proj.bias.clone().detach()) -mha_layer.packed_proj.bias = nn.Parameter(vanilla_mha_layer.in_proj_bias.clone().detach()) +mha_layer.packed_proj.bias = nn.Parameter( + vanilla_mha_layer.in_proj_bias.clone().detach() +) new_mha_layer = torch.compile(mha_layer) # warmup compile nested_result_warmup = new_mha_layer(query, query, query, is_causal=True) # benchmark -nested_result, nested_time, nested_peak_memory = benchmark(new_mha_layer, query, query, query, is_causal=True) +nested_result, nested_time, nested_peak_memory = benchmark( + new_mha_layer, query, query, query, is_causal=True +) padded_nested_result = nested_result.to_padded_tensor(0.0) # For the vanilla ``nn.MultiheadAttention``, we need to construct the ``key_padding_mask`` # Further, ``nn.MultiheadAttention`` forces one to materialize the ``attn_mask`` even if using ``is_causal`` src_key_padding_mask = torch.where(padded_query == 0.0, -math.inf, 0)[:, :, 0] -attn_mask = torch.empty((N, S, S), device=device).fill_(float('-inf')) +attn_mask = torch.empty((N, S, S), device=device).fill_(float("-inf")) for i, s in enumerate(sentence_lengths): attn_mask[i, :s, :s] = nn.Transformer.generate_square_subsequent_mask(s) -attn_mask = attn_mask.unsqueeze(1).expand(N, nheads, S, S).reshape(N*nheads, S, S) +attn_mask = attn_mask.unsqueeze(1).expand(N, nheads, S, S).reshape(N * nheads, S, S) vanilla_mha_layer = torch.compile(vanilla_mha_layer) # warmup compile -warmup_vanilla_result = vanilla_mha_layer(padded_query, - padded_query, - padded_query, - attn_mask=attn_mask, - key_padding_mask=src_key_padding_mask, - need_weights=False, - is_causal=True) +warmup_vanilla_result = vanilla_mha_layer( + padded_query, + padded_query, + padded_query, + attn_mask=attn_mask, + key_padding_mask=src_key_padding_mask, + need_weights=False, + is_causal=True, +) # benchmark -(padded_result, _), padded_time, padded_peak_memory = benchmark(vanilla_mha_layer, - padded_query, - padded_query, - padded_query, - key_padding_mask=src_key_padding_mask, - need_weights=False, - attn_mask=attn_mask, - is_causal=True) +(padded_result, _), padded_time, padded_peak_memory = benchmark( + vanilla_mha_layer, + padded_query, + padded_query, + padded_query, + key_padding_mask=src_key_padding_mask, + need_weights=False, + attn_mask=attn_mask, + is_causal=True, +) print(f"{padded_time=:.5f}, padded_peak_memory={padded_peak_memory/1e9:.2f} GB") print(f"{nested_time=:.5f}, nested_peak_memory={nested_peak_memory/1e9:.2f} GB") -print("Max difference between vanilla and nested result", (padded_result - padded_nested_result).abs().max().item()) +print( + "Max difference between vanilla and nested result", + (padded_result - padded_nested_result).abs().max().item(), +) print(f"Nested speedup: {(padded_time/nested_time):.2f}") -print(f"Nested peak memory reduction {((padded_peak_memory - nested_peak_memory)/1e9):.2f} GB") +print( + f"Nested peak memory reduction {((padded_peak_memory - nested_peak_memory)/1e9):.2f} GB" +) ###################################################################################### # For reference, here are some sample outputs on A100: -# +# # .. code:: # # padded_time=0.03454, padded_peak_memory=4.14 GB @@ -426,24 +476,54 @@ def benchmark(func, *args, **kwargs): # padding-specific step: remove output projection bias from padded entries for fair comparison padded_result[i, entry_length:, :] = 0.0 -_, padded_bw_time, padded_bw_peak_mem = benchmark(lambda : padded_result.sum().backward()) -_, nested_bw_time, nested_bw_peak_mem = benchmark(lambda : padded_nested_result.sum().backward()) +_, padded_bw_time, padded_bw_peak_mem = benchmark( + lambda: padded_result.sum().backward() +) +_, nested_bw_time, nested_bw_peak_mem = benchmark( + lambda: padded_nested_result.sum().backward() +) print(f"{padded_bw_time=:.5f}, padded_bw_peak_mem={padded_bw_peak_mem/1e9:.2f} GB") print(f"{nested_bw_time=:.5f}, nested_bw_peak_mem={nested_bw_peak_mem/1e9:.2f} GB") print(f"Nested backward speedup: {(padded_bw_time/nested_bw_time):.2f}") -print(f"Nested backward peak memory reduction {((padded_bw_peak_mem - nested_bw_peak_mem)/1e9):.2f} GB") +print( + f"Nested backward peak memory reduction {((padded_bw_peak_mem - nested_bw_peak_mem)/1e9):.2f} GB" +) -print("Difference in out_proj.weight.grad", (mha_layer.out_proj.weight.grad - vanilla_mha_layer.out_proj.weight.grad).abs().max().item()) -print("Difference in packed_proj.weight.grad", (mha_layer.packed_proj.weight.grad - vanilla_mha_layer.in_proj_weight.grad).abs().max().item()) -print("Difference in out_proj.bias.grad", (mha_layer.out_proj.bias.grad - vanilla_mha_layer.out_proj.bias.grad).abs().max().item()) -print("Difference in packed_proj.bias.grad", (mha_layer.packed_proj.bias.grad - vanilla_mha_layer.in_proj_bias.grad).abs().max().item()) +print( + "Difference in out_proj.weight.grad", + (mha_layer.out_proj.weight.grad - vanilla_mha_layer.out_proj.weight.grad) + .abs() + .max() + .item(), +) +print( + "Difference in packed_proj.weight.grad", + (mha_layer.packed_proj.weight.grad - vanilla_mha_layer.in_proj_weight.grad) + .abs() + .max() + .item(), +) +print( + "Difference in out_proj.bias.grad", + (mha_layer.out_proj.bias.grad - vanilla_mha_layer.out_proj.bias.grad) + .abs() + .max() + .item(), +) +print( + "Difference in packed_proj.bias.grad", + (mha_layer.packed_proj.bias.grad - vanilla_mha_layer.in_proj_bias.grad) + .abs() + .max() + .item(), +) ################################################################################## # Sample outputs on A100: # # .. code:: -# +# # padded_bw_time=2.09337, padded_bw_peak_mem=5.10 GB # nested_bw_time=0.01452, nested_bw_peak_mem=3.24 GB # Nested backward speedup: 144.13 @@ -477,10 +557,10 @@ def benchmark(func, *args, **kwargs): # classified the modifications into layer type, layer ordering, and modifications # to the attention score. We trust that changing layer type and layer ordering # (such as swapping ``LayerNorm`` for ``RMSNorm``) is fairly straightforward. -# +# # In this section, we will discuss various functionalities using the # aforementioned building blocks, including the following: -# +# # * Cross Attention # * Fully masked rows no longer cause NaNs # * Modifying attention score: ALiBi with FlexAttention and NJT @@ -501,8 +581,12 @@ def benchmark(func, *args, **kwargs): query, _, _, q_len = gen_batch(N, E_q, E_k, E_v, device) _, key, value, kv_len = gen_batch(N, E_q, E_k, E_v, device) -print(f"Total sequence length in nested query {q_len.sum().item()}, max sequence length {q_len.max().item()}") -print(f"Total sequence length in nested key/value {kv_len.sum().item()}, max sequence length {kv_len.max().item()}") +print( + f"Total sequence length in nested query {q_len.sum().item()}, max sequence length {q_len.max().item()}" +) +print( + f"Total sequence length in nested key/value {kv_len.sum().item()}, max sequence length {kv_len.max().item()}" +) out = new_mha_layer(query, key, value, is_causal=False) ######################################################################################## @@ -519,29 +603,40 @@ def benchmark(func, *args, **kwargs): # warmup compile warmup_nested_result = new_mha_layer(query, key, value, is_causal=False) -warmup_vanilla_result = vanilla_mha_layer(padded_query, - padded_key, - padded_value, - key_padding_mask=key_padding_mask, - need_weights=False, - is_causal=False) - -nested_result, nested_time, nested_peak_memory = benchmark(new_mha_layer, query, key, value, is_causal=False) -(padded_result, _), padded_time, padded_peak_memory = benchmark(vanilla_mha_layer, - padded_query, - padded_key, - padded_value, - key_padding_mask=key_padding_mask, - need_weights=False, - is_causal=False) +warmup_vanilla_result = vanilla_mha_layer( + padded_query, + padded_key, + padded_value, + key_padding_mask=key_padding_mask, + need_weights=False, + is_causal=False, +) + +nested_result, nested_time, nested_peak_memory = benchmark( + new_mha_layer, query, key, value, is_causal=False +) +(padded_result, _), padded_time, padded_peak_memory = benchmark( + vanilla_mha_layer, + padded_query, + padded_key, + padded_value, + key_padding_mask=key_padding_mask, + need_weights=False, + is_causal=False, +) padded_nested_result = nested_result.to_padded_tensor(0.0) for i, entry_length in enumerate(q_len): # padding-specific step: remove output projection bias from padded entries for fair comparison padded_result[i, entry_length:, :] = 0.0 -print("Max difference between vanilla and nested result", (padded_result - padded_nested_result).abs().max().item()) +print( + "Max difference between vanilla and nested result", + (padded_result - padded_nested_result).abs().max().item(), +) print(f"Nested speedup: {(padded_time/nested_time):.2f}") -print(f"Nested peak memory reduction {((padded_peak_memory - nested_peak_memory)/1e9):.2f} GB") +print( + f"Nested peak memory reduction {((padded_peak_memory - nested_peak_memory)/1e9):.2f} GB" +) ################################################################################## # Sample outputs on A100: @@ -556,15 +651,16 @@ def benchmark(func, *args, **kwargs): ################################################################################ # Fully masked rows no longer cause NaNs # -------------------------------------- -# +# # There has been a long standing issue with ``nn.MultiheadAttention`` and # ``scaled_dot_product_attention`` where if a row was fully masked out, the output # of the attention layer would be NaN. See `issue `_. # This is because the softmax over an empty set is undefined. -# +# # Thanks to `this PR `_ -# this is no longer the case. Instead, fully masked rows in ``scaled_dot_product_attention``. -# For cases where ``nn.MHA`` does not employ the "fast-path", this will also apply. +# this is no longer the case. Instead, the output corresponding to fully masked rows +# in ``scaled_dot_product_attention`` will be 0. For cases where ``nn.MHA`` does +# not employ the "fast-path", this will also apply. # # Using a custom MHA layer with NJTs is strongly recommended over the # existing "fast-path" in ``nn.MultiheadAttention`` as NJT's ability to model raggedness @@ -583,6 +679,7 @@ def benchmark(func, *args, **kwargs): from torch.nn.attention.flex_attention import flex_attention + def generate_alibi_bias(H: int): """Returns an alibi bias score_mod given the number of heads H Args: @@ -590,22 +687,21 @@ def generate_alibi_bias(H: int): Returns: alibi_bias: alibi bias score_mod """ + def alibi_mod(score, b, h, q_idx, kv_idx): scale = torch.exp2(-((h + 1) * 8.0 / H)) bias = (q_idx - kv_idx) * scale return score + bias + return alibi_mod + query, key, value, _ = gen_batch(N, E_q, E_k, E_v, device) n_heads, D = 8, E_q // 8 alibi_score_mod = generate_alibi_bias(n_heads) -query = ( - query.unflatten(-1, [n_heads, D]).transpose(1, 2).detach().requires_grad_() -) +query = query.unflatten(-1, [n_heads, D]).transpose(1, 2).detach().requires_grad_() key = key.unflatten(-1, [n_heads, D]).transpose(1, 2).detach().requires_grad_() -value = ( - value.unflatten(-1, [n_heads, D]).transpose(1, 2).detach().requires_grad_() -) +value = value.unflatten(-1, [n_heads, D]).transpose(1, 2).detach().requires_grad_() out_flex2 = flex_attention(query, key, value, score_mod=alibi_score_mod) ############################################################################### @@ -619,69 +715,72 @@ def alibi_mod(score, b, h, q_idx, kv_idx): from torch.nn.attention.flex_attention import create_nested_block_mask + def causal_mask(b, h, q_idx, kv_idx): return q_idx >= kv_idx + query, key, value, _ = gen_batch(N, E_q, E_k, E_v, device) block_mask = create_nested_block_mask(causal_mask, 1, 1, query, _compile=True) -query = ( - query.unflatten(-1, [n_heads, D]).transpose(1, 2).detach().requires_grad_() -) +query = query.unflatten(-1, [n_heads, D]).transpose(1, 2).detach().requires_grad_() key = key.unflatten(-1, [n_heads, D]).transpose(1, 2).detach().requires_grad_() -value = ( - value.unflatten(-1, [n_heads, D]).transpose(1, 2).detach().requires_grad_() -) +value = value.unflatten(-1, [n_heads, D]).transpose(1, 2).detach().requires_grad_() out_flex = flex_attention(query, key, value, block_mask=block_mask) ############################################################################### # Packed Projection # ----------------- -# +# # Packed projection is a technique that makes use of the fact that when the input # for projection (matrix multiplications) are the same (self-attention), we can pack the projection # weights and biases into single tensors. It is especially useful when the individual # projections are memory bound rather than compute bound. There are # two examples that we will demonstrate here: -# +# # * Input projection for MultiheadAttention # * SwiGLU activation in feed-forward network of Transformer Layer -# +# # Input projection for MultiheadAttention # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # When doing self-attention, the ``query``, ``key``, and ``value`` -# are the same tensor. Each of these tensors is projected with a +# are the same tensor. Each of these tensors is projected with a # ``Linear(E_q, E_total)`` layer. Instead, we can pack this into one layer, # which is what we do in the MultiheadAttention layer above. -# +# # Let us compare the performance of the packed projection against the usual method: + class InputProjection(nn.Module): def __init__(self, E_q, E_total, bias=False, device=None, dtype=None): - factory_kwargs = {'device': device, 'dtype': dtype} + factory_kwargs = {"device": device, "dtype": dtype} super().__init__() self.q_proj = nn.Linear(E_q, E_total, bias=bias, **factory_kwargs) self.k_proj = nn.Linear(E_q, E_total, bias=bias, **factory_kwargs) self.v_proj = nn.Linear(E_q, E_total, bias=bias, **factory_kwargs) - def forward(self, x): + def forward(self, x): return self.q_proj(x), self.k_proj(x), self.v_proj(x) + class PackedInputProjection(nn.Module): def __init__(self, E_q, E_total, bias=False, device=None, dtype=None): - factory_kwargs = {'device': device, 'dtype': dtype} + factory_kwargs = {"device": device, "dtype": dtype} super().__init__() self.packed_proj = nn.Linear(E_q, E_total * 3, bias=bias, **factory_kwargs) - def forward(self, query): + def forward(self, query): return torch.chunk(self.packed_proj(query), 3, dim=-1) + B, D, dtype = 256, 8192, torch.bfloat16 -torch.set_float32_matmul_precision('high') -in_proj = torch.compile(InputProjection(D, D, device='cuda', dtype=torch.bfloat16)) -packed_in_proj = torch.compile(PackedInputProjection(D, D, device='cuda', dtype=torch.bfloat16)) +torch.set_float32_matmul_precision("high") +in_proj = torch.compile(InputProjection(D, D, device="cuda", dtype=torch.bfloat16)) +packed_in_proj = torch.compile( + PackedInputProjection(D, D, device="cuda", dtype=torch.bfloat16) +) -q, _, _, sequence_lengths = gen_batch(B, D, D, D, device='cuda', dtype=torch.bfloat16) +q, _, _, sequence_lengths = gen_batch(B, D, D, D, device="cuda", dtype=torch.bfloat16) # warmup in_proj(q) @@ -691,7 +790,9 @@ def forward(self, query): (q_out, k_out, v_out), time, _ = benchmark(in_proj, q) (q_out, k_out, v_out), time_packed, _ = benchmark(packed_in_proj, q) # On my A100 prints 1.05x speedup -print(f"InputProjection: {time:5f} s, PackedInputProjection: {time_packed:5f} s, speedup: {time/time_packed:.2f}x") +print( + f"InputProjection: {time:5f} s, PackedInputProjection: {time_packed:5f} s, speedup: {time/time_packed:.2f}x" +) ################################################## # SwiGLU feed forward network of Transformer Layer @@ -699,9 +800,18 @@ def forward(self, query): # Swish-Gated Linear Unit (SwiGLU) is a non-linear activation function that is increasingly popular in the feed-forward # network of the transformer layer (e.g. Llama). A feed-forward network with SwiGLU activation is defined as: + class SwiGLUFFN(nn.Module): - def __init__(self, dim, hidden_dim, multiple_of, ffn_dim_multiplier=None, device=None, dtype=None): - factory_kwargs = {'device': device, 'dtype': dtype} + def __init__( + self, + dim, + hidden_dim, + multiple_of, + ffn_dim_multiplier=None, + device=None, + dtype=None, + ): + factory_kwargs = {"device": device, "dtype": dtype} super().__init__() hidden_dim = int(2 * hidden_dim / 3) # custom dim factor multiplier @@ -712,16 +822,26 @@ def __init__(self, dim, hidden_dim, multiple_of, ffn_dim_multiplier=None, device self.w1 = nn.Linear(dim, hidden_dim, bias=False, **factory_kwargs) self.w2 = nn.Linear(hidden_dim, dim, bias=False, **factory_kwargs) self.w3 = nn.Linear(dim, hidden_dim, bias=False, **factory_kwargs) - + def forward(self, x): return self.w2(F.silu(self.w1(x)) * self.w3(x)) + ######################################################################## -# An alternative way of implementing this that uses packed projection is +# An alternative way of implementing this that uses packed projection is + class PackedSwiGLUFFN(nn.Module): - def __init__(self, dim, hidden_dim, multiple_of, ffn_dim_multiplier=None, device=None, dtype=None): - factory_kwargs = {'device': device, 'dtype': dtype} + def __init__( + self, + dim, + hidden_dim, + multiple_of, + ffn_dim_multiplier=None, + device=None, + dtype=None, + ): + factory_kwargs = {"device": device, "dtype": dtype} super().__init__() hidden_dim = int(2 * hidden_dim / 3) # custom dim factor multiplier @@ -731,19 +851,22 @@ def __init__(self, dim, hidden_dim, multiple_of, ffn_dim_multiplier=None, device self.w13 = nn.Linear(dim, 2 * hidden_dim, bias=False, **factory_kwargs) self.w2 = nn.Linear(hidden_dim, dim, bias=False, **factory_kwargs) - + def forward(self, x): x1, x3 = torch.chunk(self.w13(x), 2, dim=-1) return self.w2(F.silu(x1) * x3) + ################################################################################ # We can compare the performance of the two implementations as follows # Depending on your hardware, you might see different results. On an A100 I see # 1.12x speedup for D=128. D = 128 -swigluffn = torch.compile(SwiGLUFFN(D, D * 4, 256, device='cuda', dtype=torch.bfloat16)) -packed_swigluffn = torch.compile(PackedSwiGLUFFN(D, D * 4, 256, device='cuda', dtype=torch.bfloat16)) +swigluffn = torch.compile(SwiGLUFFN(D, D * 4, 256, device="cuda", dtype=torch.bfloat16)) +packed_swigluffn = torch.compile( + PackedSwiGLUFFN(D, D * 4, 256, device="cuda", dtype=torch.bfloat16) +) q, _, _, sentence_lengths = gen_batch(D, D, D, D, device="cuda", dtype=torch.bfloat16) @@ -755,12 +878,14 @@ def forward(self, x): _, time, _ = benchmark(swigluffn, q) _, time_packed, _ = benchmark(packed_swigluffn, q) # On my A100 prints 1.08x speedup -print(f"SwiGLUFFN: {time} s, PackedSwiGLUFFN: {time_packed} s, speedup: {time/time_packed:.2f}x") +print( + f"SwiGLUFFN: {time} s, PackedSwiGLUFFN: {time_packed} s, speedup: {time/time_packed:.2f}x" +) ################################################################################ # Extended examples # ----------------- -# +# # We intend to update this tutorial to demonstrate more examples of how to use # the various performant building blocks such as KV-Caching, Grouped Query Attention # etc. Further, there are several good examples of using various performant building blocks to @@ -774,7 +899,7 @@ def forward(self, x): ################################################################################ # Conclusion # ---------- -# +# # In this tutorial, we have introduced the low level building blocks PyTorch # provides for writing transformer layers and demonstrated examples how to compose # them. It is our hope that this tutorial has educated the reader on the ease with From 733b1ecf2ef7830d2b8fb47fb030e5c0abb30699 Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Fri, 24 Jan 2025 13:57:13 -0800 Subject: [PATCH 034/347] Add torch.compiler.set_stance tutorial (#3260) * add torch.compiler.set_stance tutorial --------- Co-authored-by: William Wen --- recipes_source/recipes_index.rst | 7 + .../torch_compiler_set_stance_tutorial.py | 244 ++++++++++++++++++ 2 files changed, 251 insertions(+) create mode 100644 recipes_source/torch_compiler_set_stance_tutorial.py diff --git a/recipes_source/recipes_index.rst b/recipes_source/recipes_index.rst index b841d9ee759..632efebb5c5 100644 --- a/recipes_source/recipes_index.rst +++ b/recipes_source/recipes_index.rst @@ -122,6 +122,13 @@ Recipes are bite-sized, actionable examples of how to use specific PyTorch featu :link: ../recipes/torch_compile_backend_ipex.html :tags: Basics +.. customcarditem:: + :header: Dynamic Compilation Control with ``torch.compiler.set_stance`` + :card_description: Learn how to use torch.compiler.set_stance + :image: ../_static/img/thumbnails/cropped/generic-pytorch-logo.png + :link: ../recipes/torch_compiler_set_stance_tutorial.html + :tags: Compiler + .. customcarditem:: :header: Reasoning about Shapes in PyTorch :card_description: Learn how to use the meta device to reason about shapes in your model. diff --git a/recipes_source/torch_compiler_set_stance_tutorial.py b/recipes_source/torch_compiler_set_stance_tutorial.py new file mode 100644 index 00000000000..56b338db801 --- /dev/null +++ b/recipes_source/torch_compiler_set_stance_tutorial.py @@ -0,0 +1,244 @@ +# -*- coding: utf-8 -*- + +""" +Dynamic Compilation Control with ``torch.compiler.set_stance`` +========================================================================= +**Author:** `William Wen `_ +""" + +###################################################################### +# ``torch.compiler.set_stance`` is a ``torch.compiler`` API that +# enables you to change the behavior of ``torch.compile`` across different +# calls to your model without having to reapply ``torch.compile`` to your model. +# +# This recipe provides some examples on how to use ``torch.compiler.set_stance``. +# +# +# .. contents:: +# :local: +# +# Prerequisites +# --------------- +# +# - ``torch >= 2.6`` + +###################################################################### +# Description +# ----------- +# ``torch.compile.set_stance`` can be used as a decorator, context manager, or raw function +# to change the behavior of ``torch.compile`` across different calls to your model. +# +# In the example below, the ``"force_eager"`` stance ignores all ``torch.compile`` directives. + +import torch + + +@torch.compile +def foo(x): + if torch.compiler.is_compiling(): + # torch.compile is active + return x + 1 + else: + # torch.compile is not active + return x - 1 + + +inp = torch.zeros(3) + +print(foo(inp)) # compiled, prints 1 + +###################################################################### +# Sample decorator usage + + +@torch.compiler.set_stance("force_eager") +def bar(x): + # force disable the compiler + return foo(x) + + +print(bar(inp)) # not compiled, prints -1 + +###################################################################### +# Sample context manager usage + +with torch.compiler.set_stance("force_eager"): + print(foo(inp)) # not compiled, prints -1 + +###################################################################### +# Sample raw function usage + +torch.compiler.set_stance("force_eager") +print(foo(inp)) # not compiled, prints -1 +torch.compiler.set_stance("default") + +print(foo(inp)) # compiled, prints 1 + +###################################################################### +# ``torch.compile`` stance can only be changed **outside** of any ``torch.compile`` region. Attempts +# to do otherwise will result in an error. + + +@torch.compile +def baz(x): + # error! + with torch.compiler.set_stance("force_eager"): + return x + 1 + + +try: + baz(inp) +except Exception as e: + print(e) + + +@torch.compiler.set_stance("force_eager") +def inner(x): + return x + 1 + + +@torch.compile +def outer(x): + # error! + return inner(x) + + +try: + outer(inp) +except Exception as e: + print(e) + +###################################################################### +# Other stances include: +# - ``"default"``: The default stance, used for normal compilation. +# - ``"eager_on_recompile"``: Run code eagerly when a recompile is necessary. If there is cached compiled code valid for the input, it will still be used. +# - ``"fail_on_recompile"``: Raise an error when recompiling a function. +# +# See the ``torch.compiler.set_stance`` `doc page `__ +# for more stances and options. More stances/options may also be added in the future. + +###################################################################### +# Examples +# -------- + +###################################################################### +# Preventing recompilation +# ~~~~~~~~~~~~~~~~~~~~~~~~~~ +# +# Some models do not expect any recompilations - for example, you may always have inputs with the same shape. +# Since recompilations may be expensive, we may wish to error out when we attempt to recompile so we can detect and fix recompilation cases. +# The ``"fail_on_recompilation"`` stance can be used for this. + + +@torch.compile +def my_big_model(x): + return torch.relu(x) + + +# first compilation +my_big_model(torch.randn(3)) + +with torch.compiler.set_stance("fail_on_recompile"): + my_big_model(torch.randn(3)) # no recompilation - OK + try: + my_big_model(torch.randn(4)) # recompilation - error + except Exception as e: + print(e) + +###################################################################### +# If erroring out is too disruptive, we can use ``"eager_on_recompile"`` instead, +# which will cause ``torch.compile`` to fall back to eager instead of erroring out. +# This may be useful if we don't expect recompilations to happen frequently, but +# when one is required, we'd rather pay the cost of running eagerly over the cost of recompilation. + + +@torch.compile +def my_huge_model(x): + if torch.compiler.is_compiling(): + return x + 1 + else: + return x - 1 + + +# first compilation +print(my_huge_model(torch.zeros(3))) # 1 + +with torch.compiler.set_stance("eager_on_recompile"): + print(my_huge_model(torch.zeros(3))) # 1 + print(my_huge_model(torch.zeros(4))) # -1 + print(my_huge_model(torch.zeros(3))) # 1 + + +###################################################################### +# Measuring performance gains +# =========================== +# +# ``torch.compiler.set_stance`` can be used to compare eager vs. compiled performance +# without having to define a separate eager model. + + +# Returns the result of running `fn()` and the time it took for `fn()` to run, +# in seconds. We use CUDA events and synchronization for the most accurate +# measurements. +def timed(fn): + start = torch.cuda.Event(enable_timing=True) + end = torch.cuda.Event(enable_timing=True) + start.record() + result = fn() + end.record() + torch.cuda.synchronize() + return result, start.elapsed_time(end) / 1000 + + +@torch.compile +def my_gigantic_model(x, y): + x = x @ y + x = x @ y + x = x @ y + return x + + +inps = torch.randn(5, 5), torch.randn(5, 5) + +with torch.compiler.set_stance("force_eager"): + print("eager:", timed(lambda: my_gigantic_model(*inps))[1]) + +# warmups +for _ in range(3): + my_gigantic_model(*inps) + +print("compiled:", timed(lambda: my_gigantic_model(*inps))[1]) + + +###################################################################### +# Crashing sooner +# =============== +# +# Running an eager iteration first before a compiled iteration using the ``"force_eager"`` stance +# can help us to catch errors unrelated to ``torch.compile`` before attempting a very long compile. + + +@torch.compile +def my_humongous_model(x): + return torch.sin(x, x) + + +try: + with torch.compiler.set_stance("force_eager"): + print(my_humongous_model(torch.randn(3))) + # this call to the compiled model won't run + print(my_humongous_model(torch.randn(3))) +except Exception as e: + print(e) + +######################################## +# Conclusion +# -------------- +# In this recipe, we have learned how to use the ``torch.compiler.set_stance`` API +# to modify the behavior of ``torch.compile`` across different calls to a model +# without needing to reapply it. The recipe demonstrates using +# ``torch.compiler.set_stance`` as a decorator, context manager, or raw function +# to control compilation stances like ``force_eager``, ``default``, +# ``eager_on_recompile``, and "fail_on_recompile." +# +# For more information, see: `torch.compiler.set_stance API documentation `__. From 4a6f79ee78dead701f4d81663aeb83667a1ede0a Mon Sep 17 00:00:00 2001 From: "Yu, Guangye" <106960996+guangyey@users.noreply.github.com> Date: Fri, 24 Jan 2025 15:05:44 -0800 Subject: [PATCH 035/347] [1/N] Refine beginner tutorial by accelerator api (#3167) * refine build model tutorial by accelerator api --------- Co-authored-by: Svetlana Karslioglu --- beginner_source/basics/buildmodel_tutorial.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/beginner_source/basics/buildmodel_tutorial.py b/beginner_source/basics/buildmodel_tutorial.py index 987bc7c44a2..1806e80feb5 100644 --- a/beginner_source/basics/buildmodel_tutorial.py +++ b/beginner_source/basics/buildmodel_tutorial.py @@ -32,17 +32,10 @@ ############################################# # Get Device for Training # ----------------------- -# We want to be able to train our model on a hardware accelerator like the GPU or MPS, -# if available. Let's check to see if `torch.cuda `_ -# or `torch.backends.mps `_ are available, otherwise we use the CPU. - -device = ( - "cuda" - if torch.cuda.is_available() - else "mps" - if torch.backends.mps.is_available() - else "cpu" -) +# We want to be able to train our model on an `accelerator `__ +# such as CUDA, MPS, MTIA, or XPU. If the current accelerator is available, we will use it. Otherwise, we use the CPU. + +device = torch.accelerator.current_accelerator().type if torch.accelerator.is_available() else "cpu" print(f"Using {device} device") ############################################## From b9b16565c74181dd72ab342d4bbc36b0c038035b Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Fri, 24 Jan 2025 15:07:10 -0800 Subject: [PATCH 036/347] Add MTIA to wordlist (#3262) --- en-wordlist.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/en-wordlist.txt b/en-wordlist.txt index b56df45df0c..4757f48a6f2 100644 --- a/en-wordlist.txt +++ b/en-wordlist.txt @@ -159,6 +159,7 @@ Minifier MobileNet ModelABC MPS +MTIA Mypy NAS NCCL From 3f302a33ca96b8b497c2a020247f493f9537c53f Mon Sep 17 00:00:00 2001 From: "Yu, Guangye" <106960996+guangyey@users.noreply.github.com> Date: Fri, 24 Jan 2025 15:21:22 -0800 Subject: [PATCH 037/347] [2/N] Refine beginner tutorial by accelerator api (#3168) Co-authored-by: Svetlana Karslioglu --- beginner_source/basics/quickstart_tutorial.py | 14 ++++---------- beginner_source/basics/tensorqs_tutorial.py | 16 ++++++++-------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/beginner_source/basics/quickstart_tutorial.py b/beginner_source/basics/quickstart_tutorial.py index df7628081ba..5cce8dcfe9a 100644 --- a/beginner_source/basics/quickstart_tutorial.py +++ b/beginner_source/basics/quickstart_tutorial.py @@ -84,16 +84,10 @@ # To define a neural network in PyTorch, we create a class that inherits # from `nn.Module `_. We define the layers of the network # in the ``__init__`` function and specify how data will pass through the network in the ``forward`` function. To accelerate -# operations in the neural network, we move it to the GPU or MPS if available. - -# Get cpu, gpu or mps device for training. -device = ( - "cuda" - if torch.cuda.is_available() - else "mps" - if torch.backends.mps.is_available() - else "cpu" -) +# operations in the neural network, we move it to the `accelerator `__ +# such as CUDA, MPS, MTIA, or XPU. If the current accelerator is available, we will use it. Otherwise, we use the CPU. + +device = torch.accelerator.current_accelerator().type if torch.accelerator.is_available() else "cpu" print(f"Using {device} device") # Define model diff --git a/beginner_source/basics/tensorqs_tutorial.py b/beginner_source/basics/tensorqs_tutorial.py index 70a966d9f89..30e05cb10d0 100644 --- a/beginner_source/basics/tensorqs_tutorial.py +++ b/beginner_source/basics/tensorqs_tutorial.py @@ -99,20 +99,20 @@ # Operations on Tensors # ~~~~~~~~~~~~~~~~~~~~~~~ # -# Over 100 tensor operations, including arithmetic, linear algebra, matrix manipulation (transposing, +# Over 1200 tensor operations, including arithmetic, linear algebra, matrix manipulation (transposing, # indexing, slicing), sampling and more are # comprehensively described `here `__. # -# Each of these operations can be run on the GPU (at typically higher speeds than on a -# CPU). If you’re using Colab, allocate a GPU by going to Runtime > Change runtime type > GPU. +# Each of these operations can be run on the CPU and `Accelerator `__ +# such as CUDA, MPS, MTIA, or XPU. If you’re using Colab, allocate an accelerator by going to Runtime > Change runtime type > GPU. # -# By default, tensors are created on the CPU. We need to explicitly move tensors to the GPU using -# ``.to`` method (after checking for GPU availability). Keep in mind that copying large tensors +# By default, tensors are created on the CPU. We need to explicitly move tensors to the accelerator using +# ``.to`` method (after checking for accelerator availability). Keep in mind that copying large tensors # across devices can be expensive in terms of time and memory! -# We move our tensor to the GPU if available -if torch.cuda.is_available(): - tensor = tensor.to("cuda") +# We move our tensor to the current accelerator if available +if torch.accelerator.is_available(): + tensor = tensor.to(torch.accelerator.current_accelerator()) ###################################################################### From c2faee459836f6aadeb38325fa554f49748858ac Mon Sep 17 00:00:00 2001 From: "Yu, Guangye" <106960996+guangyey@users.noreply.github.com> Date: Fri, 24 Jan 2025 15:22:01 -0800 Subject: [PATCH 038/347] [3/N] Refine beginner tutorial by accelerator api (#3170) * [3/N] Refine beginner tutorial by accelerator api --------- Co-authored-by: albanD Co-authored-by: Svetlana Karslioglu --- beginner_source/chatbot_tutorial.py | 12 +++-- .../introyt/tensors_deeper_tutorial.py | 48 +++++++++---------- .../knowledge_distillation_tutorial.py | 6 ++- beginner_source/nn_tutorial.py | 25 +++++----- 4 files changed, 44 insertions(+), 47 deletions(-) diff --git a/beginner_source/chatbot_tutorial.py b/beginner_source/chatbot_tutorial.py index ee274866895..520c934d965 100644 --- a/beginner_source/chatbot_tutorial.py +++ b/beginner_source/chatbot_tutorial.py @@ -108,8 +108,10 @@ import json -USE_CUDA = torch.cuda.is_available() -device = torch.device("cuda" if USE_CUDA else "cpu") +# If the current `accelerator `__ is available, +# we will use it. Otherwise, we use the CPU. +device = torch.accelerator.current_accelerator().type if torch.accelerator.is_available() else "cpu" +print(f"Using {device} device") ###################################################################### @@ -1318,16 +1320,16 @@ def evaluateInput(encoder, decoder, searcher, voc): encoder_optimizer.load_state_dict(encoder_optimizer_sd) decoder_optimizer.load_state_dict(decoder_optimizer_sd) -# If you have CUDA, configure CUDA to call +# If you have an accelerator, configure it to call for state in encoder_optimizer.state.values(): for k, v in state.items(): if isinstance(v, torch.Tensor): - state[k] = v.cuda() + state[k] = v.to(device) for state in decoder_optimizer.state.values(): for k, v in state.items(): if isinstance(v, torch.Tensor): - state[k] = v.cuda() + state[k] = v.to(device) # Run training iterations print("Starting Training!") diff --git a/beginner_source/introyt/tensors_deeper_tutorial.py b/beginner_source/introyt/tensors_deeper_tutorial.py index d7293dfe295..2d5f4cb2963 100644 --- a/beginner_source/introyt/tensors_deeper_tutorial.py +++ b/beginner_source/introyt/tensors_deeper_tutorial.py @@ -632,34 +632,33 @@ # does this *without* changing ``a`` - you can see that when we print # ``a`` again at the end, it retains its ``requires_grad=True`` property. # -# Moving to GPU +# Moving to `Accelerator `__ # ------------- # -# One of the major advantages of PyTorch is its robust acceleration on -# CUDA-compatible Nvidia GPUs. (“CUDA” stands for *Compute Unified Device -# Architecture*, which is Nvidia’s platform for parallel computing.) So -# far, everything we’ve done has been on CPU. How do we move to the faster +# One of the major advantages of PyTorch is its robust acceleration on an +# `accelerator `__ +# such as CUDA, MPS, MTIA, or XPU. +# So far, everything we’ve done has been on CPU. How do we move to the faster # hardware? # -# First, we should check whether a GPU is available, with the +# First, we should check whether an accelerator is available, with the # ``is_available()`` method. # # .. note:: -# If you do not have a CUDA-compatible GPU and CUDA drivers -# installed, the executable cells in this section will not execute any -# GPU-related code. +# If you do not have an accelerator, the executable cells in this section will not execute any +# accelerator-related code. # -if torch.cuda.is_available(): - print('We have a GPU!') +if torch.accelerator.is_available(): + print('We have an accelerator!') else: print('Sorry, CPU only.') ########################################################################## -# Once we’ve determined that one or more GPUs is available, we need to put -# our data someplace where the GPU can see it. Your CPU does computation -# on data in your computer’s RAM. Your GPU has dedicated memory attached +# Once we’ve determined that one or more accelerators is available, we need to put +# our data someplace where the accelerator can see it. Your CPU does computation +# on data in your computer’s RAM. Your accelerator has dedicated memory attached # to it. Whenever you want to perform a computation on a device, you must # move *all* the data needed for that computation to memory accessible by # that device. (Colloquially, “moving the data to memory accessible by the @@ -669,8 +668,8 @@ # may do it at creation time: # -if torch.cuda.is_available(): - gpu_rand = torch.rand(2, 2, device='cuda') +if torch.accelerator.is_available(): + gpu_rand = torch.rand(2, 2, device=torch.accelerator.current_accelerator()) print(gpu_rand) else: print('Sorry, CPU only.') @@ -678,25 +677,22 @@ ########################################################################## # By default, new tensors are created on the CPU, so we have to specify -# when we want to create our tensor on the GPU with the optional +# when we want to create our tensor on the accelerator with the optional # ``device`` argument. You can see when we print the new tensor, PyTorch # informs us which device it’s on (if it’s not on CPU). # -# You can query the number of GPUs with ``torch.cuda.device_count()``. If -# you have more than one GPU, you can specify them by index: +# You can query the number of accelerators with ``torch.accelerator.device_count()``. If +# you have more than one accelerator, you can specify them by index, take CUDA for example: # ``device='cuda:0'``, ``device='cuda:1'``, etc. # # As a coding practice, specifying our devices everywhere with string # constants is pretty fragile. In an ideal world, your code would perform -# robustly whether you’re on CPU or GPU hardware. You can do this by +# robustly whether you’re on CPU or accelerator hardware. You can do this by # creating a device handle that can be passed to your tensors instead of a # string: # -if torch.cuda.is_available(): - my_device = torch.device('cuda') -else: - my_device = torch.device('cpu') +my_device = torch.accelerator.current_accelerator() if torch.accelerator.is_available() else torch.device('cpu') print('Device: {}'.format(my_device)) x = torch.rand(2, 2, device=my_device) @@ -718,12 +714,12 @@ # It is important to know that in order to do computation involving two or # more tensors, *all of the tensors must be on the same device*. The # following code will throw a runtime error, regardless of whether you -# have a GPU device available: +# have an accelerator device available, take CUDA for example: # # .. code-block:: python # # x = torch.rand(2, 2) -# y = torch.rand(2, 2, device='gpu') +# y = torch.rand(2, 2, device='cuda') # z = x + y # exception will be thrown # diff --git a/beginner_source/knowledge_distillation_tutorial.py b/beginner_source/knowledge_distillation_tutorial.py index 49ab9a134dc..19d1553e7a0 100644 --- a/beginner_source/knowledge_distillation_tutorial.py +++ b/beginner_source/knowledge_distillation_tutorial.py @@ -37,8 +37,10 @@ import torchvision.transforms as transforms import torchvision.datasets as datasets -# Check if GPU is available, and if not, use the CPU -device = torch.device("cuda" if torch.cuda.is_available() else "cpu") +# Check if the current `accelerator `__ +# is available, and if not, use the CPU +device = torch.accelerator.current_accelerator().type if torch.accelerator.is_available() else "cpu" +print(f"Using {device} device") ###################################################################### # Loading CIFAR-10 diff --git a/beginner_source/nn_tutorial.py b/beginner_source/nn_tutorial.py index 9a1ce8218e0..e04815bd27e 100644 --- a/beginner_source/nn_tutorial.py +++ b/beginner_source/nn_tutorial.py @@ -132,7 +132,7 @@ # we'll write `log_softmax` and use it. Remember: although PyTorch # provides lots of prewritten loss functions, activation functions, and # so forth, you can easily write your own using plain python. PyTorch will -# even create fast GPU or vectorized CPU code for your function +# even create fast accelerator or vectorized CPU code for your function # automatically. def log_softmax(x): @@ -827,28 +827,25 @@ def __iter__(self): fit(epochs, model, loss_func, opt, train_dl, valid_dl) ############################################################################### -# Using your GPU +# Using your `Accelerator `__ # --------------- # -# If you're lucky enough to have access to a CUDA-capable GPU (you can +# If you're lucky enough to have access to an accelerator such as CUDA (you can # rent one for about $0.50/hour from most cloud providers) you can -# use it to speed up your code. First check that your GPU is working in +# use it to speed up your code. First check that your accelerator is working in # Pytorch: -print(torch.cuda.is_available()) +# If the current accelerator is available, we will use it. Otherwise, we use the CPU. +device = torch.accelerator.current_accelerator().type if torch.accelerator.is_available() else "cpu" +print(f"Using {device} device") -############################################################################### -# And then create a device object for it: - -dev = torch.device( - "cuda") if torch.cuda.is_available() else torch.device("cpu") ############################################################################### -# Let's update ``preprocess`` to move batches to the GPU: +# Let's update ``preprocess`` to move batches to the accelerator: def preprocess(x, y): - return x.view(-1, 1, 28, 28).to(dev), y.to(dev) + return x.view(-1, 1, 28, 28).to(device), y.to(device) train_dl, valid_dl = get_data(train_ds, valid_ds, bs) @@ -856,9 +853,9 @@ def preprocess(x, y): valid_dl = WrappedDataLoader(valid_dl, preprocess) ############################################################################### -# Finally, we can move our model to the GPU. +# Finally, we can move our model to the accelerator. -model.to(dev) +model.to(device) opt = optim.SGD(model.parameters(), lr=lr, momentum=0.9) ############################################################################### From 8f0518cef4b81eda39068f95ee886543f5269509 Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Mon, 27 Jan 2025 12:29:48 -0800 Subject: [PATCH 039/347] Cherry-pick: [ONNX] Update dynamo_export tutorial (#3196) (#3265) Co-authored-by: Ti-Tai Wang --- .jenkins/validate_tutorials_built.py | 2 - _static/img/onnx/custom_addandround.png | Bin 0 -> 39031 bytes .../img/onnx/custom_addandround_function.png | Bin 34769 -> 0 bytes _static/img/onnx/custom_addandround_model.png | Bin 7544 -> 0 bytes _static/img/onnx/custom_aten_add_function.png | Bin 16332 -> 0 bytes _static/img/onnx/custom_aten_add_model.png | Bin 8613 -> 0 bytes _static/img/onnx/custom_aten_gelu_model.png | Bin 26439 -> 26058 bytes ...classifier_onnx_model_on_netron_web_ui.png | Bin 0 -> 61983 bytes ..._clossifier_onnx_modelon_netron_web_ui.png | Bin 97157 -> 0 bytes .../export_simple_model_to_onnx_tutorial.py | 10 +- beginner_source/onnx/intro_onnx.py | 2 +- .../onnx/onnx_registry_tutorial.py | 160 +----------------- 12 files changed, 15 insertions(+), 159 deletions(-) create mode 100644 _static/img/onnx/custom_addandround.png delete mode 100644 _static/img/onnx/custom_addandround_function.png delete mode 100644 _static/img/onnx/custom_addandround_model.png delete mode 100644 _static/img/onnx/custom_aten_add_function.png delete mode 100644 _static/img/onnx/custom_aten_add_model.png create mode 100644 _static/img/onnx/image_classifier_onnx_model_on_netron_web_ui.png delete mode 100755 _static/img/onnx/image_clossifier_onnx_modelon_netron_web_ui.png diff --git a/.jenkins/validate_tutorials_built.py b/.jenkins/validate_tutorials_built.py index 99492b0719f..21fab4eabfd 100644 --- a/.jenkins/validate_tutorials_built.py +++ b/.jenkins/validate_tutorials_built.py @@ -52,8 +52,6 @@ "intermediate_source/tensorboard_profiler_tutorial", # reenable after 2.0 release. "intermediate_source/torch_export_tutorial", # reenable after 2940 is fixed. "advanced_source/pendulum", - "beginner_source/onnx/export_simple_model_to_onnx_tutorial", - "beginner_source/onnx/onnx_registry_tutorial" ] def tutorial_source_dirs() -> List[Path]: diff --git a/_static/img/onnx/custom_addandround.png b/_static/img/onnx/custom_addandround.png new file mode 100644 index 0000000000000000000000000000000000000000..d4973ce6c2426f52ee70d0be43733f1a4883d66d GIT binary patch literal 39031 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 diff --git a/_static/img/onnx/custom_aten_add_model.png b/_static/img/onnx/custom_aten_add_model.png deleted file mode 100644 index e5ef1c71742ccff347d84c65c6b7f5d15e960654..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8613 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 D>9(|{ diff --git a/_static/img/onnx/custom_aten_gelu_model.png b/_static/img/onnx/custom_aten_gelu_model.png index 5b326690eb75f6297501cc760f9f590da2ca20ad..63186f308895829f0082e098fb6dac6a2eeafcf8 100644 GIT binary patch literal 26058 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 Jcu(-f{{kcykQ4v_ literal 26439 zcmeFZcUV(fw>}y~L8a+ddQ(wQdZc$01(BvGB~+!i5Rgu27C^dNq(~Q}2!W6QNhks7 z(wlThDAIckox9@R-|wFDopbNG=l91s_c`~j=iv!S)|zw8HRl}T9q&6v!k%cW(OqP_ z2m*oV)E_<21%b{^fIw$<&r<=P%>Pc~j`B0s6cs!zbC#+fM~A zyU(1zkJ{#F;H@Ef;E^PmwJmCCwN8)D$&^EXY{AlwuW2B0ZQ2!4JC)a+?nO@ji_C*S(|n6cbc3rr;Xuv$W?`fv+F zGx^tzy5B=KN9feBs7GQP`$#12I_t^Nz^X*E%OcK0CODPROTUNJeGbo~y}#GE@#l#` z?}(xbj%w2QLCgtvScFo*<4JmGmW9qeeJW5z*vQC8-nVZp4lA(cw|!GbkbVf%uiacq zg36{WlN04MvJd7{$S$9L$nMO>5zA~H_wmA(<+xr)3LXm4WLMyOtOG@$9el>T?;#QT z&MNKf@-j@eWg+~QO=xyY_cg=_Lf@>Wi!|1?m4x?P8x%j16Yp&&4K0&W@0_0O`Vd=L zQAbTZPf1$Hbmq56yOUA-j8*^E>Mqr_A^mjQBj}RFVg}+2h~tPb!m8sxm$rA3v+1%z z*;DdUsBn0_aM_!^o01+h{~}_#h}z3)hUxK}oLcyWw@g~mwQqA9(NG9JINvt)6jLN8 z+7Gtfo=6;+>N1xPBpe`&crV0KNwJSHB3b?VLJ)-e3nXa~BcJ{gy{@pWEgY=i=5+$0 z{sAZ>LguTjUxup=jygSTFgDF)5qG~U}^}ks88mBqk9PO{!v6ypv^FRAQ1Du(Mu#^ z^GDG=og%6=X{E(9wB4rS+v9U02FVBIzLM>6S(@~*6#p*TAo)R@UD zE)+IXFK78e(^N8RwE|D&Pj|ICMx(W^wnM}^o3dY^ldFfdu8*`5=H_*qHy>X!$DPv_ z;D$cU8@%c6u;IZ8Yu3^4Kld??r&E}Qco(nM+<<zEYiU+3C+I$HD}%vsFJ& zVh+5_ecRl0l9T&=&0fUO-#r=H4v@}{!Wasn$(9=Wkr(QdO%t#Ck|L@h;C$?Zh(j3X zAhtSmm{6oKC&JG0b_qk7*yePc>GWsVo7R`EyNaRc)%VusB1`*T>i#0w1j)hin<1&g zN21-+2mK`Z(anhChY`WH-Mg9__qyLy|N}gbld8cD277Xg^j%}r_4eSn7Bp* zale{_Ni1Fcz?6wsu$z&b8W+gLRewc6E0-zlTz1}!zq&1{lm<9&MQrT1meE8f?d zF)P|toLA8xIvzn+u2f5yz5B9p<8q4$OdWol^7>(m1CDdEiyQO-$vZh}-teo3JCZ@t zj8h`_k~VpC7xBW05UHyk8{-j+^~JAd>K`y47w=D|3ti|u*nD; zt^HmQo{p7&?^x9;2y*!VcT|(#U)fw=d6Vc~3+IU%XA^1v@a#Na5G6=94E-gqKf==0 z-{v`zGz>-vzjh3`_xf1)aW^8q9X{YnWv0jdLa!@n{MvUmX{*2pwdKIc@FTj&Z`gkS zH%v(H(X>|PHNbFx5cf0YIDKlgB2Gut4)glNXR&gr?!p8ps57fJJhkIax=4|9jp4pa zEhmF^#FoaDV}s83pM{d<16A4`r+vCc2#RMA_ns}mVYl4cwFDTjAG;)F9`{(l=}w*W zvq)p7;aH#H%WXLlE5X>w>Vt>Eua+n-fJ#f4-ob8l1iQ4Qg>9Cx(f{6F&s*Vei2~9* z4kaAPAL7#3NJ%vq3Tt_AddNnAU~%`*{nXDFF7@C$;riWHj6Xe7N&}x9JD{Q{5w8t| zU@nLg&Lt&d>;6qiTBhWpH?p{U>w{IBW;Rjq&9btlf(%I$6lUR2r)cSxA*-11qKU#a ziXd!(LCR+CntEu`s+ga^9fnDIAGPhn*no}b>@!Rue*@KRFZ?OoHvgHS{^573c4T-Zpp2st?KB4KGm>-(t|dk?MwI zjIaQKQq5vwPjB!B&WgEioYY}I3cfpRZ;YLK6B~h_9J$|^_69)0#H%f^h+k5w8s{efoVodU()i)ShXZFo>$|sV?mWJ3^4I6U@&+jz zJ9NNk{&TqO-`0h&MSdm6x@ zH?-mlRYMVgK0%=F9|ud+4M8sjEPmxjpm9y^q`6iXib#W$9OI^692&H z2f&909gkuJbLhALFvi##!P`7>-)Ftl8fAH0b>GKGa8zHukPwn+AZ<%HxDQsaHTz<% zCYY>`6E4TN`)~a6T`1p~`#Oj%x#`DF+JbESXd1P2N=Kl+b`9ZJRx>}qPrd-axn&ztkp#lw3C zPDPoRqiMN6^;>F;NkBatm}v%Y4Hf|?$U-~CoT+Zb+r@~s+CrnBuk`f() zxdp7L2@q}MOV_&?`ek*mB|oo-gUNPq$5UfFDu8aW<6E>$R@>}*4U7PjZL@6eL87r_ zL#gxmg)0orbu4T=0_;s5&1v0A79Cp~Z3udhyEA3TjMR218B~0ySY)(~$!ib!vie@l zKwoXJ#slpdy3!qg!l6$zMGD_k*y#@F zqh<3iDh;*G1+k01eCbIGnm@ucTQb~=5JiMXK{U9?%b26s%$8n&Ud1$>R*94QiijOY zjZieVB*Z5}dRd$^DM@&?yIp1lF$9M`L=ciBmIuG$uva@JhH})#53Yce`7k_Y#5hK` zn`Cp*7`|+{2-Wb8JKC8FI$G(@*q*?8E``Hd#%KkQEeA{}>iM)*baT~N2y z&w&9|EiEnmx02`pGq%JIn7r*MVPy1aSDHeJZX48G_Et!{VQT-PmQg7X^`wP8eQKr!*667QvB~= z59QFXi9@-@l)V)Po`hXk{8^Wfj58pYi2;#tnGWGO@1=JmH zv^9DkSft7cSU71lFE8&>9R&z_V_)mfBg~@!%ThA52@*!;HmI2|8G6V{|nsNza%L*GU&jt^@+sYiD(4j4>^nB z(g8C{AebPZqnv;d%x7lxZl+D3zxi-YYj?}V5ysbNkqu~)&K(bVo zl_S(9f+)aNO^XOR&`1I>Eg3Y!E`1X$K>YlEXUk%J;=auI?87hFPe)4)F+%vSueT2> z+%6iwLN>9D*}Akljy=4i04!e3@yeC9lU2IHG7k@g;j?8^(vOrc5FSve2;eZ44NB}@ z(FRy~BaE>{#T_vlC~01uWTw8=Rra<%j0ZUIJ`l$qp!3Yne8GLF`<0k(nqZndRv;)o zx-n78;@23Th(fq`ZqCC=zv`~go5aFJk}n$<%_m+G=FCs*REvkw%|0{cc591a!~lLK zRJW1K;90yg%sbs_-m)IPX8C?*;1<~W`zJ$mSv9o*`W8uOtv}A0j(|Mbr~670sLa`3 zu9*87ShU&oOi9WZBavq83?m8IzmoUS_Y^!7ZWg+Il{r#?tLCT(C$R5yg^7ucxe3QR z_{oZRPt0vRd(FgQO-#~RgwF9?5d95ckIlboYCbBUvYB}e{*r$;Q`TaL8E~q)OAo~| z(Y|`}Njqw4<-qU!4CQ$(SCuJz=Df#h+uM zcrz{iqk>X7>5Z#a8c}(Z5PY~T0$)>`O-bYwG}@Rko^H@OgF(T zb^rL(k9V6)Q6=F?y~|JTu}RV!U*;(}SdMYy_=@h4#613RJFTIBF2b z4{(l6i&Sgr(w^-ru4~x^S&M1@{)_Q4m{(R;tM|Qe@_tUUsi2H3vb}wzx9gaKxD_2y zO;>?k5%?kZ1mM2hkB?vro)3*_ouTC`B2MAbwpO&z-qHMPsTzp=G`+u|L%5--q~_g) z)VN02H!l*m5snnLv5FwvRk5_Pep}fPHh0zN5IZGooF1X2wc7Xm^h#?CET*4mJbqMn z-d{WX0b5Y z&3!}8lFU4E3ma8-t2gNcyUh z)7hY1Uhfqj~y_eI@8VfSky<3=uS*ilW>LceG zueTw&5f8=7y>d%r(@w4R1rtI{OuH7fIs%5g1GMxt8RuLJlKYBk-&{zZD(8*hvr?cN zedKEB?8@RG>*NVjV4=vMB+tovGiJ@mjefVOBQ&EKJ+>6yGwJ8rPQOa3nBM-cENnc zl4GuYG+I=~tMGXTwKm|1C-OmS&}r66fkg}5y?A`4u&^nZqIe1R0X*1tWnqiFb@|Cu<^vXUyCb+9a zVxJRS5R&xZRpY}eAN#e~5z+?@V=Q-hECvRfxL&M3Gw~=52=2k$z0J!T@j&zT+UNLf zCgWbe?nHHX{4Es?_9`9M{=VBnh$nl?bTtuERj|fM1RTEli3OXnF-70;D0D}X__hco zE`7WoZiuqc^~02|p98ty_%{%$2x;4u2Ki<^V6JqnN$3avxt5EW*n~H!g&G#E#rf## zxv^7(sF{Ks=a^dw3}KAiLX|n!E~JI{!K~z8sX?BQfV&pGj$VodIhzPpZ4-tP-#uG! z({K2phfOHFF>-|OBSkG&;8ULY4b&lYT{;m{mEwmfAE)s7tsyDeMy03#iT);1q}91$ zRnqXNTZu#FWH&Q!UQUIMUMGT8t4-ZNe*NycE`{>EVc zd%#8+TMUdGaTb7Z|3A$7&xt+V*@ZO$%Cxuue|^fJT{2}D5Mj%gt}ApwFP<^_YIHo7E{%D{AZK^WX`Xg~_>8eY1TO`|0}>4xiPd(xh%{G&(VZvosiHh1 zu{*1H&nE*%{K4VNvKcg7x@nCgXL_Px*(FjBH-X&aViH={=h@mRkeO^}#ihUoY>}8ZMFpKSLIVH7SnKHX^F0 zWDse>;=16|vwmkS`bt-rAr4J>l71$Lk_^f++V>Iz`@bVN!0VZ;Pgx_TL$gouM7aBy zp=Xtgs%Bn(6dDbAZZa-sX>|8PrU<^we}53vgZ*+qb59n2tqhNWW3qQ;39^ zDoLBYcgcon6~DJ?u~xqOvAstUfMhr+_Fb&&G!M8S(6jhfTH@&Z0RWMD3ARpSY4kjT zpF}!B7ztp>=#C;*(HAC@hqq0i{V&MYcn!gC!`KCp5C|#y7M|ah+wegvy`xB|YE6d5 z(u8L2rg30bI7@l{NqJ0rSRPyaIqzJZu2p8RdnZq$8=4DZ?=AlFSv$X}v3G)&7QxG7 z(oFBNd4Uq2Kaq*9U-M1=l&$|wI5w(4JJOg>usu5AX03JBmn1%e#t&5?4$l6R&`XDI*|k~AaP%d3waTLzxBax z=(Pc6JfMr57UyU5Q-`_lPXnc2MY|tDo(Lrl3D82jcMC<aQ)zHXP{Q=P8XE$n^4vWT-AYet0eaS5l0iL2_z*QhV}r^lGv?9`1)$^cVJH771Y zcQ<+8WI=4vNvFOFPuK5c4=auC-di)|sH>1oBNCY@zu!|QN8vOp@2$N=qFeK4P(`(( zKQVU|XKlZj)k7WX&3t4IW^(#3^QHe3Ww9@`8nThS9w_FPv!W-a!gj^}c5JTGYU#_F z`X?pVOAg~`MTCMqhX<^@=P4W(h?(NKX5;!P*dqVqyJ;fwC8rmtw{^M#%374NK&5hI z14R8oO}+xXaOtN$ySRK)LYIVBvBsRX#;jm6qe`rcUCq{?E7@~>BIzO=El=$e=bA$5 zpBVF&7hN5R1H?(MQMK_S_bANhQiwO^i+d3kzw>-lv+|=1?tX(pNqSnqMZb zzS1Yi`9TpjL0s{4?Jzi(ZWG&UK@aFhOl=rzt+1V~Bt&si?$mk@z9SWr@WjgeIt0C5 zmucmU^rtOf1|ILKHO|VIHVj(k?!6EIKwX;IFgekMC;c`jObcGRb3^X8Z6jts6>LeK zMAKaj#Y#fL#E%IF&Fau?ti#C`v%!mw$^=djREPYqBj~Ce0wR%VSLSTpN>gimERs7Y z1_51`h7=yM_1*L`7*(3&0GIZ+a6Q*Ur7(bL{+Qj!@|C4~oK0hK)(VJY+36}x0{t;> z>~c0tZ0w7BUof^!ojq##&`%}sKBwTDYWsoF@Qt%jA&w{t;ahp3^xk#r*Di=`s!-SF9cA4-!BeKx7kwl+Fn^@caF zBrkBS;p8OxFfO2ZMLD9wkiYRlJDx{m3Z!o$n zd`1~g7S=n+P|ckPDzfh?_KkuMm6h-+oV1EBNEnK&l<#igxnVR~zTo)bKpm|1Q7YvJ zvYyc`NWn24#pObswF4$?&RJMMy7f7#*`mmrc{7>L`Q7OG33}DW^4|ObtNu@1Rv>pp z0HtR@&PZ3V15JEL0B~Wb6&xVEK-Z}MU3N`$2mJq(T@!)qV6OtS8%Lh*Ui?v$U0Arn ziNrOKyBr{<37w#TJ~&tuCVuzz?=g7*EyzI*a)4X;pZB%i@|_>8AZ4ki#>VUt5)zQY zW3mRMY4+(8z}$E|vwWdzq9j6Pj*`B^_^B+x$(X56wJU7#KI-qZ7}Y`X=U&>G%T*iL z!iaBgdo65!XUTlbT99ZK8CgM>dB13GPO%a*r~VQf z>?@Xq5JPbRNdEPwto|`=;m|^_W4WAupSmdD)j8 z`2uF4=-%(WvpJ@g-KU3<#urkXGtA*VEJ^%`Q`|Bl5_5MyFL8Qd#badq}#WQ~>k2x_FPZ#23jaBc3z*)t zZJ?3=(L3`0!qhzfH-3mKT6Qr{`e?nrgbnol^)Mdp6%qJs3CIahPB6GgJgT$>WeDA@ zx$}|8)B=nAohH>i6IX`~4-ZG215K=G1U&n6ANSYiLg$H~Ak5DsBGbQPYyXo^TlQx( zb#CKC?ORJA5ADm?fzJZUo?-O|+fe#%^^t<|;wZpBOE``dte}2v{u-kITQBSaoV%SP zxo_F$NE+wJ{VO+^%)ug(R?vs+76ir!9^UpCc9HqGvYs4FQ8c4tcq2f|ai(5>V;p!K z#Nx95QF}ue)U8hjK<>EL2EU2*RsN*~%yez#MWJcS6FtfjrV*3$FW}7-3)gwI&?Z!) z1@)cXCU)$#f-}mM!z=~y!cn1IU2Ep2zlHm=R88;lTl3N+e!9d+{>ZduvJ)7Sr8@Ud=Y@4^Bi+5OQmL-W0wa_hMc{~Izx#f9bNgS{^kJ?3tb?z9x) ztIIEJ5u(vvcgDs1NfySN)pUgHBevrz9hFtQh)0Pm)8D3~N+L_W6@y z5kzR_(V@`kDkx+6_gHTFHqMK5zHfaPYq^=(nz+%}6C591ESn#q_t39!NX%(E9|CsQ z(t93v&U$GLa+$}#x|xQ|Si5LV7u&v!WM6klNl0~D%MOkQ)Vrg(Os!KX#e8o!rAOakVChyL;- zKvND!`J*4Fqc(;$*oAdP=SN1DVwgGPW=npnpC0()a9*K&h9D|ad4Z57fOw7{Kim!j zFQxROGG!~bu6O|o%2oSM^8|N&?$;nyW}U$82pNgeeDIRrG{e$#5o_=o zE~A!s3B(2`7PE-iXkt>?ny)7LR@pxCA1$V6+!}cgSlS@%Ox&V9kb{({^o8slAU{P~ zy3<0Mezb<5x+)JDq71kEj_wxZveBw=ChS!3OWo;|QxM=iNV$4UQ+dL-te|UDgLX-9 zQ*G*|s_j9!_O@IukdpBBtxOcZL37Z`_} zrpnX4>+v%*!ibpiDS4u58E6miaEF!st&b<)&*^U zBjoPaz@B9vY^?B*YMLiNo|WHwM$waia}XF zhYK(xa$I4M9v3d0>&CD)K6CEtLpr6?Zkd>dmF$O?J~C=EOGuN|pF(e=9KU`1bv(+DwBje|cf{}`wIiGgEIu@_Mylrzo(3L|y9o5$`lz1-C zNC<%cy6F}u)V#8&P3QqP9`p(L1UR~D$#0w=F#y;fXeOv?{;#|?PEBB`0>{6~Wp}5^ zQKSNm=sE@%|CM3oxklh*WMsS#U`MTg_I9@lPU3+tf++Pa5D0bhYyZXzRA-hr-&T7#=y zY=NT{iL$qaY!=s^LdxWw<+pkgC4IG@Gc&9{++aOkWhW=mYtf1Oh5pV4wY}x#G&_zF zDuA0EI6pltf=dDLE}4yzCi?&`^>f<|NWCcTr33)zCG~Z2ocdmT9*_h?-CPRfy6;3k z@eB1?Dsd3@Y(U$;O{N1Rgn1*tzbhXaD?H#7Rk?l%&8jJj=YA@>#S7;qO3eQgeIu6BBYYufS(z^B3HOK|EGz+S zk;Z?>nGhH5YwD6UnF|7$k`26_`6ySEo`*tBNbXjKgt%yk&X4eZAf*ZlDW}o!J@i=q zUsst+ zV5wGI1k9i^AXKUE#W{HhBZ*Yfy)_7k3x zVWyV`gVK?U{K0(I#;;hNrf5#NsVFJJk7$=->DfffO!Jxm*WF>;=1|4$FVY(gS4wz5 z%US?ELVXeuNlxPUb$eG#L!Uc!#EuJxo>spUFW3v@jgt#1I4g|E>!vCLVQw^x0%AtT zQtw*8h;aAqH%tFu zKUtg@6;KA8bz6KRpW;SXA~Un)g&#q7=-`Z!+W}+01J0E-}v3tI(_&v!RV-1UsECRj)K3_-HYk8J^Zxba5RYTmV*S#e3&r z%2h}6LnF5Dx1tqxhot9H+@`utmb-diAiCsQmN@$mjc5mIL2f#)Fw~;*Ggtr3UZCzl2XzgvbbEr&}J?j_9KVFs2DDn!zyz( zJW#kKZRk-6T6If5G&%akr=;E%xML8=n11jxcj|+RX7Hfi&A`!M8s?@Pt>j+cUV$Ps zn$n<`a{jBV3Mj!r^7*I%%Rq6cNl$fMz9j9$AJm)GQIt6Rx$uxQAX!UhoIgll$4_qa z2hVhb`BCPJoQTIKYBPg1Cp&pXMbTc>TdAlQwQt;`r9q1xNxSQ?tsu4+*;u2;l@;iNKS-cB)gQbV|Kw(U#`wL5e~q`1uk+^c#jCsYL6PX z7D*+PzHH*Yl-R3ZKit!|-)MwnodHfL#XA3~T4-LQIG*k^`*|U4by{J~M<>Ol?t=Y( zuF|o61=+*c>5Sh98bw*ERp^9d^d57#wSyO`x4V9)TfrAMDaMwNHC(W&4OJuIb6|H7 zyzkCq60&OZ@jothxL-;Rs?GvUfmZ6nf4~066<1>c@}9?F*LHAv*ewxjy2ACvK+TxG zXx2;IH4ERoc^t`MZU&sJoBbt@${f69=!)VqC$JF97b#sI40wuq?`+6f9yG)>2*uda zk$bO#o{>)So(@)<>ReM~szl3@KZKEkeBc1m(%Sl_r5eD^99PoI1C0kh3^8f&^3uqS zU5a6JKq{8ptQHS&p}n2r)gDyq+G7yk&}Al+1}UnwxFp9FA%`r#O~vqNBs#3GGnq|F zey=2lMnU}W$Tx~Pame%3ws(tKGvHcbmm<>pbU^6-hALveHKTIb9bWTHrF@fWw8mo_h1^!1_GCN}w@VA(6PQi82mQ7g(5k>a z1r=#A;ONDa@@La8c^&P^f!AQjNC9LLtJ~>{oNMz#JP+5Yu3zP6g5_nU==_=_?zO`? z?&ga9LB^sxCM2xebag*5<}-Js_wri7R43i=H&{Kl-+JGI0$E)PYg`eqQ#+*p+=$*+LL%A%&gT^oLtw)n(W#Q+?(Y!kt5+a@FNlT7u;p8XH5QbB%+-{JTZu zl{znc)`fG{zbChUTJ%&uKRq4Mlv;8UrR zvC9fKK7&_Zr%QNsl%QVf>9;r1pneG?G*eRAHc7+^M-)_pC8w$#r;|MjGRc<1o#71b zZF~5VP4h?QlE*DTuOp5p5*8EfIBzS+DV9VZ`Dcr~+v(k!9tl{3<)Asc`N#)7cDYiP zNqq6nN~pES7-AiGdE+(I zt4VKb;DMWC)$cJrB#-d|^Y~?@fcaMG$;Mk7*+{=T&fe?6-fLgWH&P{tVFgNtY1NFW zE|XfQ<<-v{3#6(`r@qGtQyQmeUeDBxGQ-^tlxrP<>7n#SpKG(qVB?S(d!M88K}>4T zH0LntxHTlL=|^70uI1C}?xoAzM}B@Q`2^HTj!3iAVAuqC+E%}iG3e??pr&;y#jR7l ztu0s0O!5a>rbs3^|tct2?mSqQO)MRQ#W^fX#KXuLF{+)V9)Z0h3LM-CCy)`m~J zrYM`se#K&Uj(36Tt?o}`D>)JaTpfPTP=7eN2FD{wkigWgw^8fl+)`Pjjh9OXx zqGX`uQCqOk{x7J14Iaoo6~7gfB?oYESySuv3|$TYIch^%9Tz&$g3{ykWv53Hre_jJ zC1tLA12VvNl_Ejdp+XF8BL@K>5FB9YD)wsNKuDv$nS5g(`X^bR5a#}V%e>)w=vAdz zSm5EPyADbBppx5vy9aQ3^FW2`Ki8DFM{v0`bGT_%5P@Kik^A0lvEAKz~iO8CaG%ICn7(-pB#_Y92@(QDv4Tc2Bscy>Ip#qLG*plGdL^A5Ym6e zaii0#S}e_X@!Q@~&B>DY!9s3HmOCS?df%o!SxRKKD?NQciB#UzsHV92XDKbvUH4`F z-K$nJ&et!DS0BxYG{*}W_a6NClc#T`v`<@Oi6bRoBf`8PCh%w)MY0$dQtQ4)*gUY_$@g|QMpaK%%RE(hX%V?7eekLr4y`dZnc{1o|vq66fY{ytC6B@fZ18o$V8q zj;cP{5PiUcXk{Jsm75t<^|*)i;wxA2<6==_&Jp2;W+ zyA%^fuf$?LDL;>&CTD^$%t1o>`nra zjW52)RQ!&97jVg1cr6i^-W9I4xQhSglHNojgo<-;7(OLzuW#RegaU?wpNY%Pw>Q6+ElTE zfa(%O%juE?Rm2Hlu_Dd#Z4p$Odjw^v$R2C)QhqFtH2C#lUzT|Gct(V6W49saMh_Ye z>|%1wiT-n2uYlkAR^m4PP^V8e88ZKiO$BlM#ipXeHrXJ7I(nO*-dGVZ2N_7zibetY ziL>rla7KB!6cqZT;}A6*9ZZ zDNP#fW`PX5oS6l3cG*y~`+L^N@ykH_hAr;aVFe`yY%z z6G6WjrRP9(4rH&Zx(E5zw`Rp$QfZ7#h>}$M6m4LAN%<_g%*%|}ruqk4SlKj~I)NHg zN{;+NMm(InNwlL*(zInACcZGHMTwDJ@bY$0$}=UR$-X4Mio^wSx2LoiN(6%!-@@0j z12V{Rg-b@7Hx4K-DU`Bn_)3`)xH9f)VV}3~#(0U@>yb)5B1Kj*B*5Jch0EbzS{cGJmI< z!Sf_J=SN}!K-tdBZ()TWFN^~Z6ehEAmYuB+*kNTanWYt?Dg5{N$ETQ#x|0+|d`o_A zEED7E@QceK573zoZY!}F2XKlO7qKec*+9gX``9V*RWdENNk)N0Mh$LW86ggQR znrjWJ!2aXc8@hLPmWRazJPg#k%AK+YZi==!D#`#Qjh?GxpW}A5835U6{&7P@wqJqNh zJ4JV*%*4ENJHS2uYUKEvm?V$;Uyx;FeODR#V$l*%*2-qF!N9x+3Bv$izfkv_OhEoC zxy^%e4REyRKS0W)PlyBKi<3YS?DIQ*8bHy{gGR1Xw&FH+(HF|{hs!0XS2L|FihkY$ z7nQyAjg3dAxM@IuqSpgZW5Dsdnh0=t|8sK?$TRhSn_Mx3HAhCB>l2A^>yicY^YdRE z4STxZ;%Kp<<7LBF8kM@l)F2Vt8PysaYkL(FD==NR5HbB3<4^YW%9U5XqhQAQmJ5#l z7VSTir>O-3FSRk7v*w=oKGkHA=C?tP#-+MXNFq(yDx!!Px}X-4qzCcUdreJxdViBpytcdHDgOh)GcNOMfU&` z!y%>Z2zek$0a$rJ(Gb+hO2vymk#Qmjh5)1`KWRX3#Wrh(iY%HNS)I7GsK65+l-T^KJQJd%J)Sqs%+)PYR#EOt*yg3x zI^1%s&;=rfDY0yKAyG!!5vD&$h%spH ztI-NKp8m`&9Amr+;XtO`6=OOcMX+qBHI{(v*va66949VO(TfK;Iv61E&JUgk$V?^c z=r{MjMbA>9U*>j2zAw=6k#+X|^)sGMNr*HulBj^bn?IGjk?x3e4ycv^5tzunzm!|$ zi4VJP1B5wHv8iDsFTloeK)>i}byWBau|ll6ym%wUMaX>{0zPrGyfL8HELQVlg#7I3 z@1Bt!G4$E@jJq&Qxdm>hZR`tRugV-@UPQ@4nodDo>bC`+DCiaWmSkc+`?9t)R{h4wv!o zqv@2+(DtId$Yi3DBClqpsOruNs~i>)8AmkZz#hS&k>)0x5sLjHawHHUnUm3WoFmuI z;l9anLRO}ES1M{hRKRip>l_wp#?BovARN9?_*srV==d&KG%4j)m>3EznQEmYHP3Yo zu`zVuL_0ZMolT@|t&enw1(y3=>GsL%Lg2ktZ{LNvtXE4aPbFr@>fD4QhXqdf4zx_U zx|DeQ$b+YM_WkZb39Y%v52F4{Njm;p1Nv9m&p%mW;KKh8f9*lprFiHB#2;i9^gpzq zz*+x^?6o2qpQS>q#qq%o1bP~HLTldJn4a!d zVdyX-N0UF^f{G$!0J&kB#(eMon8%>0>uUL2PQ7MZ+z5PknKgaC-4?A+Zg*3ctWWek ztlAxM+$i$G1HqN6V9TPK`9;iq!w_FT_RI93Fy>X@a(of+r_joyI;-(dA=Ayoat!Ge@FCdTWgOuC% zvJ&F)UNdUWpoZW1^solv81V5SbM@)*TJDTj)imeE4l+=IUSYetG%YtgO=0?jqW=`> zfD{vcx)lfvS(XMsCV=R5`Fq)0sRZFDDJGF2e$4vcT6`Oi5;&&YgIJ163q2u!t zuh55S@TnaQ;TDHaaz|q6VO~dDO12Up**9cl3veRB)RnUx5hFJG*Mx@6hA@nnar;8Y zMZFe36u0*^Gj;{pg8svYFAqcOcDaLBYGOm2 zuR$zG^}#|5)YAO+cE8ruW2ZQ2n5-l~BfslP$P5hK%Ki#$y8qX5anzg{ zvwHN<+;GTQz^Nu-V3E^fsnE57(SUqi#P7S*=~WMtL@%wKR;vT1^`!@4!51b*9`znV2gk}Gg?2tK4_~1k)mGFE;Ea1{9G7&iNelFVexUXAtE-wi#+S5pFl-X;`DzGT*dH3JY?I~$B68Y zhclw)$_9V$73IMj&?n)-Xhc3n+P6dy*GAP$3U065Ai@*hm+! zY0Rruuo!e>Xk1ngWMfq0VpTf{IM#P<+J7n$2z4TSHvR4Ia(RQ6#D$|~yi8bgcOr-O z>m8?l#&*TzSEjW`$N3z6N%Z%zPVpYF25NZzZa;QjYOR%}Klh2Zv#TbHtBRAbBzDB! zKq&n5Xw6`8E~>!)R$9P(GWg`{@;mpBd_&x6qBm@geb9Mz zMTT(FJMX-qp~uHlt$}dG{JE%@zJu-eh3x5JwgCs4PRFKQ(lcGi=yMEXtXbiFWHZ|h z{Fq*9-JZ12!k68VSkz{Pg+_t@WM|?|!f|FZaTb=ds?kj zQBe^nBJdQIt4tS>S-=(%6_8pW%%Frp$`HbkAdm#KiYSt+ltCbbib7Ea5kUxqAVZkL zJkMbsLI_g=A<5kb+rHkl-hJ!7_11ms<==DmIeYK(?fre<@AunZbeFqAdYMEy5zVE? zvb$8CB2tD`X2B8D@R)6?1soBx(wmIeP*NvDgn zAS9`aYxh2S*0g&yLod3d2woK3ADBp;9yMP3COmk^5Mk9b@Pi^e@cdoJj{APD1yhWE zsj_{{L;BoW;+ePaHQQ9FsXlvmf}EZsmBA9CW{+A&SIrh5%OVG?*>$Umh*(p@BpJoP zff`G?Qm4IhN4~+KoAd151hLX<5vW`1x z-|eOSg}Bde>Xomx> znQVs6b_#1v;Kt@K2W$;kPB(c*>h;j~{*WM)7kBs0JdrAww!Ue*TvPkNWR+9i5xUe-JXc?(H0*6h zlqG83z^8≷qC?qsQ|gdx7YU6w0<%*jLPscnV@?dgo42u3 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 WT|~IV3D8HOYsNP&=Y4nU-roWIfwd?A diff --git a/_static/img/onnx/image_classifier_onnx_model_on_netron_web_ui.png b/_static/img/onnx/image_classifier_onnx_model_on_netron_web_ui.png new file mode 100644 index 0000000000000000000000000000000000000000..6430e4943ffaa3f69940450d47764fceaf5b14d3 GIT binary patch literal 61983 zcmeFZcT^Nxw=Ldg1q4xv5+tc)BHyp-Bpoa}G^V zNlliV;qAin#rwYBz30B~jq%2B+&?%Rsp{&gU2CsB*PL_h4^JLTU&kWFg27@*$Ft$w+F6+9cvhR%}({L=Z5gbjXaP-D)6L*f7LA7Vn6A?)yZ34{+YG{<$PyQf=C zcdRi1Jd-_W>pFxLUtUwdo%=z}De|l}YH4X{F>D5&mEf_B9(uAo_S3C$Sa@@V;)$tg z25nYSk|>dm&6~=V5|`8C-BQzT3faEO`Gg!4ir=|L&+ZNk)}nGzH_moxC{I;aH@-_? zO>n|(cOr%#nZ2>O*@;N}kRlyqKHn98Fxu^&&NJyGp?`WXyuD)WfgT5OQ*=X&C}W_V zwJx{7<9KPqrN-s^n=70q9mIC+pVxy<_kysz#I=fyKGYrz7Q`6CV8O;$QM|^1WPHim z)-~|`{r$7chR{gIXnV&~p8WC|Lm_iwVq(;uK7A2!4kk+YBOjaBc&I?XpnS?*n~RG} z6W^9@;pA{_I8!;L#H5q7_#(Io9B>mI5zN~5E2YEP6%`{)*R>6s0|$v}e_cY17Bw=R zgS}>w=xL&p}mQBhr6W z_&ARDQ@ns<3>~<2NpS0QffPavvPDeoOxImgqyj=|-Hz6qO|F5V$N#5Ddrr)ol(80< zBp`mJ1B$vNe0wm#zRO+I1(8^BQ;Z1w%74@&NjRD-eP5yHoiukwzA(Gkt$Tvm190$d zOIJ}X)cwfQox~+Z57#gX#aH5MCmGxQb;rK$wa4y?JPsFNug9f&BpoU->Jyv8D&N;L zkfK+%?6=EP^#zl)S=o8c!(Qu1_ec&!pVUlcx=fUBp3DshEFoW;EsB4t6 zfI2N7#5zKV4%QhPfjcw;mn0m^m9D$pqhaDfCtMHC3l2>3%PrmyiM<_lY$sH7Kmji} zjHqqCpA7~U2izx%HpQT#$05P7piK_T`yjTXL!P>fjNRBf;0@-}V5nV}J+vHO(ceAZ z7MgI>jM_Qv?>0THs)cu6y#RyBNcJ?c*}SpRJszbd45VEE5y2x5gWdJJVgFJt z4mqTW+WYCZU3Yp|XJR&ju5m4gJ`oyv^RFS&G1D4W%#JB5FV9I$eFS2F{VyMHQ-i~^ zii=faxe@I04eGA0RovE-x%->*u~77!1%5tikzxPX{dnfSNUudfjY))0JQUZ1gM+A( zgQe~8f>(`9;I%Rtj+P-}{k}Zmb6Ae5-6<`|s;SYPIv6VFbX?T|GwbuqX{qDmsneP+ z-f0{zZtlhE%`T2|*dI~uxIFUkcv|EnEsdgKP=V1#O;>Mw!s8i zDjnAA?}`^Fbvtx$Tldo+&_CVQw-~Q@Ij0Wp)(gZsqRe_KKRx|k4H%Kd#YOh^#1ETY z0!qVru6fXOUi7|W#nuiD9lrf~wA1c{Bf*D99kcGSe0J%yW)0rgO|qthcPqOWe)_R; zM}kO^zhNH|&eR@CyFLtpf9qWpl$!q=*_fIUuC5*fMjb}~Z^`6-xm?C=x9z|rzWe{c zI$)4Y!@!jj{7Z3~zm7mX2>DwpxpFXJ}T0N-wD_);4{uRe5@@8QC{ww`|&X ztaZ*ybrDQlR!Q z$NP25wYdiMqbR%&Q}QIS?&llkY%ekp3?ZdQUrbNLfDa}8slboJ=W9qnT4yZ`bI-w9b&P!|KXlCSmcW1 z@G{=OD~x5Jv|Xc6Tb8F1PFj-G%wng;Y!M4r z{^_5gT*}0%`pK~4?ifb(lVh}2$CrfLa2w}fb=#A9wONH^A~kRRlXht zFXJYPHws@udu|Z;%q(GYTw}$|N-eA48(LT;jpF(B4hJn_;1<4}`@B6{M%jJ z+;tbh*_v;tc3nGhhFlzmag6E@twhxFR>)f$-JeOL6MY+cP2Bu%H=HfhC|6Ssufs$m z?qbeglXUmue}&~Nu@OJupRF<(>A0qn5I8?^9)h?@xo!+Ga5)%@cP^2~7NV zzS!vZQe$JP8A1D@t`-JMy8qV)HuB<`dZn89YU4v?-1D``nz@@`JPZJ8@LHd4ZlB+z zCk=?9Bj>Yy1c$@h+S@sxmCy&@K7?RnW5d2*k!K;sB463U!ot>VcXa}_idy2d8s{ar zeLGd~3Jf;GjhjMcK_=j!5UFGPVbW!Z+3jEJ5G8KU9uW#Gihj4-G zscCD+;#7v(@BK1he){=!Fg6U<*V8MhE^CjxRg7|WcDA7aS>Jd`$_C% zq@{gu$#|c!n8qs!?>{Ow=?tay5~os(V*5HCifjs^EJ#kKUL`vR`*MRCgBnL6mj?D& zPm_gQcQAW}h9)Ep9gRf#$)}qhRz1m*ejQ*0i0^vl`w10UM!O%go>B+YKH)da{mApU z?T&?2gUzo6D80pQ@TI(-!;x zSU#TU6JV7MEwdbbt`6>B1UoEbXs?U0uXb>GrxV=$vARZd0vJ_I+o5TrTXPqU4g1}5Cs<_7&*L)x{(XZBaHEU zT{==_Z2}shC5~%)#Q?}2i1r|(8z|lHx_;A~V=o#cF_jM$-ur&xk7NHb1iwkcsya%{ z`i&9=B!}MRbf~cA54R0+a0!VZw|fGw$L#;2R3&=p;&v2K;*pZz^z)jo;{?)#000Hg zK&$+!r8{GNoT?htg<|~-9^=O&zXB_7foRLQVQ)98JJk~rZCJA3N}1tkrE$XU2GAk| z==m=zh!-wTv_pejOE(CM-3!zwZEMu?9hSA-Vyz;7PUGX5 zxBJGDEBR*WhB%W0uxZUob9pr%`-J?; z_VssF_u5h%w>IROLGt@@<1QxI(NXcEbhJQS$XgR1Za4jz@5=NZ{(_-f9ZN3TpJ$@S z$pbC!CuT?p=&8qV%7UCB@j5BCzX_B4vG;9)(dv;f^1Fmx7T(-meJ9G~xeG6->4>o| zPUw`{CQ|dOYA94q<{Aa_-I#L!N^F;dkPMgO+lBRgqS#BVcB|2SwQd*41f0}a*TMwDtoyG=CS*xiSLbJ(6(G>f zG(5l3*44k9exvpT?a&)wd5A;O^CQ7fQmsaoWP25~`sG*71PGa1c zQpVn01fiC3Rg9;3aDwRYHfOiOypS#B00@N8;$m>0QR#@iC;e-f{3%y6wIKM3?dys0 zitxkRts<(1SH`b{+s25rmm$zPk9K0X5i!el87(n3;hN&y2YbwBr&2=e-)=z}tAhcY zc!~TUi?)9mijCt#tRsR09+O1|lFwYA0fT%I%s}X%mzNg` zlME6|QO_p{pAw5<7O0SX3(YdK{`7^|siSm47M&Ln!Crb!KQ1xrR0IPqnh_DB5op(@l0PQ`F$EJlMB&wCx}|&Ysk4o$Bg9=vjjrlo(40Q zn&}xH0RVeJZ?EeLPMc}t&;>-`M1Zhn1YJ(|V`H_i+Z4uxuZ1 zYiu0K2%leE5|ATMzdO6%)Xkdz%)OF2=Zb?d492!~6~&h&nvi4YVQv*ftC12bZXS4A zY2rqmDLGigU8W_{9y=bZjArnXYvXXVL!U45mDoUz6d|?Cp@^{ zH<|1EOyg)9G3royuotL;lq!L5n=1-WslRYfUmMD?Wxit{3|1M5w@I-n)y0d39wUSw zy89ijj16qRr?#NY5>F8JG;^vc;Whl_$52r%wf#fv9r|5Y(yK~;5TF&+&9XEiEW3vu%WHOHiQ?Sg~K!0oR0WpaHwiEXaf zS@B|}ADLRlsa7nNWv&Yaaek9Nk7|bfOd;q}{y<$PUSt2p(C}KI^1HIv4=M2BM%uQn zmyezB;zNv@tCS?&nbUK?8#eV#ip}wDn~i0Z?ew*mGY3F4)t3)j&Arr62!Azy$B07q zJ$334>?DwKy(LlUgfr)E~dXv&dG9T6dM1uj$WdN#7#T0|3nhWIpxXxYZ#UwcZwW z`jedFZ%^Oa)fDWH3rkFdvX^$qb(A?|8E8kp49*cfNoUy~ilFDr$nhB+7XGN-T%WID zt{7X#99);>S~i5M1cw)rI2=0Rr40t|Pmb*NXpgLL9f3MmH4seyn{Uc0Yv_a%wF@@- zRtvr7N^?rg5q5QRc5pS8?lHK~RO7RJJNr+fCz9Lddgz}OQ&s?6ic1pGp|We<%_Wpv zf+`Kh-chcl$7zG+x?{Ody3@{(sG8ESE_0h

qpexEa*@9d|v$%wVx$C@*XPNtHwcIu~$l; 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

t7{&z`!oQJz>!{C$IhBQI4UM2*`8)d0@ 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 literal 0 HcmV?d00001 diff --git a/_static/img/onnx/image_clossifier_onnx_modelon_netron_web_ui.png b/_static/img/onnx/image_clossifier_onnx_modelon_netron_web_ui.png deleted file mode 100755 index 0c29c16879844cc92704e332c124516db40b3864..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 97157 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�(54)QJwcAZ$uLgTwTz*FQxI(iN1GQ6SPR#_(XHh^c{egj8puTv?1 z3`0u7m$EMY^nlr$A>HC7=O~i`%Sdd0j(Xaa$R%g7W11?iV8!-T)KKS8B@{d}kEAyu zcgK=TA<=Xj^37Os?zSwQM2$@*2Ni(%j{v;czlp!%lyBu4yM9Un@N_&zn0=>L1sOU{MO6gu0$H~ zZrL3s*T7(e=v#1`=A!iK7kU^AKPIijK753h1a1lFlRl=@IO% z%Iu7isEnDK541#>O^9W&pxs$#90ChA+th!Vv0{c59ww>c`Oa*p(GcCgdqbUlQuTF> zEK)PCuK_QaoruDBP9vmFe#)aP9J`2Z0@=K}&8k6V!f=?=MV!T=9KzcV#1@W`QvyO5 zo~2dA89>Xm3j@G);GL;8Dh58Twsl+o9kUYM{RTY@*&77c{NIghja?5-~fGq5sL3HeHCm+o?= zT7-dD018b_y}A_jgnH#_lf=~+o^;KexCgr0$=VE*WxE>Ai03O0Jzkr|*(ljuwD8fe z5hUh~11ywuVf9;_Q@sXV-zIC70d??NHO9SX)R$h zAR%mh6LtyAf7ay2Srqmmg>x@R4PPMfj-1SSx@)s|ruocngwfPChFvhBX%eLS8>79W z_znqY!<>;09rL<-b|oPWXX6-lpR26>sPuyNSS(D-0Dt+oc9xs6Yt+QycuqD>XgA>T z8wE-z>NeUwYg~hqJ^U@cCCUhX!`H(IQ~zEn&W^Hd5o*=hok>oP<|J z*%?+b)}&|b%?SRnB>7{JlF0EhTO@8Dn0zRJS~Q1GNjV>rWeHlB5VSE6_0QsY3nHWc zybIYt-tm8Kbb%T1SJ5%xejgzAdJqRW^@ex!!*iK( zPLIqUKnMKa3;k#sg2!y(r_K|>fzH?dXy zE>C)E&P)G}Ivvp#ALDbt2??ASZBc$39^L~Mf>>A3G5hn@NsUcSPK8tnf|Ko;p`0fU z*C7L$*WNeAiW|c4O5Y5C6MfcTHR!}*M#n7qb4TP5Y-c$5&7_jf+iiQFN?7W2>f!re zU!eFRN`7wu{cH)y4Wb?vd0Ika8XVS|#SrfTezOg0HI`>bPc4zPki8ZKk8f`~<)7Qf zhtZ*4TL?8KeK?4JUNX%h?NJ7$Nww7Kul*nfGoFR6rN0Mh6LI)J9NZAkW_w9UYaTc| zJv>|`f%XC8zKNVymQcontoC;!Y~z~-{9Z4V21 zw5WaB*2dsY0T44GF5tCtZ@tgB*e+EP?4WXzsZf;U(YR|wO3H05E%P;_1RrgcVAz1O z6TH9FRK^682C7i)m}8^HiVOVp`}w+)6atKF1QGprQIlSD{fxeF7HUedTk^I(+C$!0 zlZvmKE8eZutp7c1Pn+CyH>a1XU8ieb{3TwMgM}w^Hk6RNU_ST~ub!U)LGX`d&>Pi^ z_c!FbQjr_St1oqY&Cul_eH9Elsu{=c@~* zE6y~UqtTD>r3Hz_om4U$Z$6~BWOL7bgudOA|6+4w>Jpl9kgfK@5;ZNgB6tQkt%lBL ze)~wLAH`^Wbw+@1EO7hZhX}M|renISSMv=)Y?GBJb2M z@R|E_e2aM7Tqp#J0*~SvG=)gsqll2-C>Z_kecu_-F?a8I^|sTZb`MgC;qa{GPS-X1m6J(AB>|~x)O`5o@fz?O)doO);Tu1tUvjXwl zh|k;77-41zPIB9tg%01uEOCS);fYx)CJFYfbAH}=fIsWrtv63jJ!Qc6DQgUie#SPz ze$u!yD#gZdX-$yt>biZ|!LYv{WVr^-{v-Ji*v28rvAi>3lBv=}8$(=cL~O;YrPdcw z;Y_JN)+fyUDx6w~9l;fzxz0Nd;_mKAP1&!P5r>>RfLzz{d=Oq)PYc+#BH3ghhN_a? z0XgZuMlPPYssE!5h=g|#2iI+V{W~+>#|BF^A{Intc+)#;xOLLHkiApuBGB;+9!tBv zAOM(R=G#^Q<}tqc(sy5r?C0%`xw+a?OW(8gHQXZs6n`C;ZMsnx7NG3RPyk^?!l}|j zia2uBr$1|w0?K~iOWgmW?ajlX{@?!ZHN17&B(({W{O*`8*#_NY5NB z#vDbbZI7!D6RJq!V&lsqOR&LYF1u2G1H)ZaM+G>cI9%^I1*snN+Pk6evzm?VtW3&tcM;A$OS_mVwlth zw^BDi9Zl2;O?69j+kqE8WPqxycaY&>WWv~zu5FWTof4EbHWjw7lG!;L)t5;Vik}f< zi(AKw6|+3fZwe;1dZK!#8&#as#p3y)ubY-b-DUPPFbuc{lzjZ|#8V}|nx^n;v*4xA zT23}lRW+m)UJj!ElfdKPd(`)?K~G%&X1Xv3;n5~gh`#vp6m}RxuI>i+w!i)aT$Jp| zZpmPsKrc-!$M!0hsMkx=a;dc!q5DvCMIKdEKN6Qzx0u`pIkQV@3X?W_>9MIj^XLvw zr?cDaU18C(K%ByKBahB}h{5XPK;d!iRa#U5n_XLiYs-LUPW0}s;qwk@*3#-XFpJ6` z-sveaZQIKBDVQYs@4j3O#+s@jjcTF)l_039o6-E`uz z0V5Ue^L=Ns_*XWkMRxkzM!ISue>x8y*|T~+wUch@ViUb8ZMSt?tg~(H^}haz=<*i9RiUf9NuBhn=Im z)r%fhm3_fsKj(X!Qh{qe&gXExTBf>@g{l0+S3}%PB8OaQ6{hf}!)>m< z&9(QYI*hwHH z^39{AW$OL+8&B9JPPt&)H%Bmgs*!D!k}2Y)&;x6$mN$I{rB&gy%hUu z7oiqgl088ms$KIz9qi*AY+{qt`uyEymTPB?VcRL&uC-S)lT%d`p&h@pHrJ;yCRjXyLK7DQ}eg`$0eflH3I#5CS>(=i~tMJC}5WGe~KF^{j+OW zURm1oAL7WeQnEtkw-$|^TZJxt#=r1p1w}n7`|BD@ww{GY>!ljYU>Jh8^HqXKXQL zbRL8<9Dw!OxI5J>UF!_qqjTQd5Xgtu#iTJ1FGLW zIx{IEtafFCV$?zP+wZ)u7?xLxolsTy8n2&$OcW31I?#{?TG$!u+4Bk0c$fSD!RTs4cj1WfoO! z4bH1u_;63mu6Y%WK_za#r2=*@&kF>49ci=kFde!G7CK!i7FY0n4^*I=0nZ*UoK^QK zI-4ft3hm2pReQi&^lqcR43u68s8V_<)^yEaIi7eWr*cAX#xY25GjBiz6_?fm!pGDj`1uC48WWx}D=Po$UU z8bSFqm&(*&d2uxOXsq1jTyMNID2nZYvljRXD+$C|W`^kbVu{8jV)pkzXCjuK_^BnV zaNV+lH&A$06XwBNGcb6}gG1TaRlh7ld!$fd7v{BPV@W!>54=_;+rQewYQs}`BkkDp z@2=MbNcA%eT3M!Q|FvFSYcJbcS=&k$7?bD6N)*|8Ci;XDM8I;x>o(%4XIV|LA6y=` z7+JGucT{M+p~Kdt2Y(At_#lBn9kax1gr{S8A~gcFJi%AFFpJ|?%YQAn%hLv9+=DO4 z<%62n`jVFUOv~VHuFnweV1r%ZXlPr+4ZuQ8*$I7`h}A|p1LzAN-e9)Iv}|a5T#z0f z?GC)P9f~{Cqa=xJ&Rmqc-rnSbdQ5~NweC8NyZvCCbm%>o{JIxk!CLA&Iv~PuWwDaKEEQ5p?UbC(=ta~ZE;eBqk)h};H^nB@b z2{vUC;<`(96XlU4%_9Xd{TuG1WU7jq%AUf+tOAa1eI6h-qoBFonvA$pTpkNv1N}f* z;T+rp4@OxYZBK;yFO`G-p$wMK)Pp*4HF+{J9Xjc?%yjF&vd7X}wE3_>nLY@m`)NAK zUY-6VOLT*9P(UhKRK5Qxh#arh)Kl6vz-eC+`m8ckzu@urcPRh7WXwEfE;Ic}MMlrM zMVghqxIQtxS6T60k+!)gyCg&-jLcOnBb8QzH@CnXGPuNl=$|W}Puogu{K^)KJu|F! zII%ila*2LD5$GG0fx8=o{j=6X-po>&LdfSv&a3^pN&4pAJa9c%A)2R@jiEjU5RwNi z3qv_`IfCx$JOWa{<3zeP`VaIy-N*fEaNbe04iPyg+?l=Y4Akf@)32^v7go5)WR~13 z?(4l%T)i_ZV+5mhrQF`KtrKe*lEqhdddXXJiudf|vT;9`CR$tn(Kk=9!EBRx7O5o* z5SU|b=iFZ2+nwJd03W9Sd;2N6=)ZF;&Qh&ZZn<(fnU9dlAJQEKg!nn@vzs^JW%{vb z3^jDF*IqIMFQh#v&enD;5Degk@n*?^rDPxih^6KqFc27S)cO{NOS>o#d7PWR##OdV z=4Qee55|$>w_=-ytb-g^*DlNLv-(t{C@q@$7p9i6qnDk4&pu1IB~vyjkg`W;Yr`qf zJ*8JQKflAm9)0+b{LQHgf9WiB?JQV|5Y(AJnC<_QmeO%2BQ8GWO3qH^^V2ygcJ-1u z@8uiYT{3*&BwKugzg&Hto(3I6B;+QwWvZL|3Wo$$z|L+FdYYejFqDtom_DQYuuNcO z5HpAQgQ<5uZJ?eQYRGk~%oQSq%(6^$kN+~wJluS9nV)Kb_D_4p%piMrrjHuj`sY}>29%1ZQECp2YMaT{1j*zyrzU_jL>zXEPBLDEyXr3s3m?9cMVj zzltE&&3tJ0gHKX5;n?A!f`I)i!9VFhSwjq2*PZQmx-O`xk8qf?<)HP@bNzGo78w8a z^h=(ts*tK8S0gFBbLiR?D96!;M@H1>W0x7O#@{A=fx^K%)A~It&>f&Cc%K0VI7J6^ zW4xCB9#KNWK^SUqblU)hyX1zje3NwwN`=$#ASQP=BLlRT%;B8R+G>+4jmlTl#jGxu zVTj-+j9r*cEK|sdosz*41gfLmPw#Z=!N`b^Mm&Uw$;O$NN0euq|1rYEZLrOx6q^My zi;7I>q}pBV0YTToZbb>}03xFWtSJAE;-VXdJQ`CcCnp^}TQgW2cOV@dAm@o0`Oh+X z|C5vMf5I7Qo`jY>vm{{}uxG>LMwpsD^n_0|2rq=oAnNr8Ke7?f`}2X}_R^+tV?x(; zq4s32?2>Klnc(z`%n0%^5lFpaWUwU=IFPX{;VS)xIe}|@w+dCS=Weu5Y#PCGTNkC? z!WfUUJ`AR2;aY3EWG;av>=xo&ncabxwruI2{YtW!bEhgp6;{rsM0Xbgv!z4^L9xyP z-*OR5b$xA$QYOqxwMC$SyL{5s{FNZtYx4P$XCpH(?8Z!3#OK0c+51vu3@lisY8#D^ z2Q&kqESq9}_3+o;W!eoxfIW-6X8f+)8Z>b$A(TV5N|(SY7h#C%13-8Xii*@)F2uE< z+xg|uR$$LBdOn7S1Q};thzhWBQJelyrN`gCl#vmT^UnR@lbH7bIuUV5Ey zVSpK)&QI848+27l>&AJn!nQ-P*{uA8fh^Y`X4lz00+4uzG*;tV(Wgk)wO$ zN=bx%F?w8)U}oY!bBgg#J9@*_VMvh?*Qw*9)N#@IxlnEh>NU^$4U1}VU{B~9R5vfJ zDIj;kh0!zrSoy(5X-nzgJ6mcqLHG|Uh6g=%xKzkA*1Eq{hvGGXh%;ejoM5zd;_3fA zG$!*{lxms{AA5?K0H? z=Pdp2ozCfF{l#5G2EPdHnw6;7of%^=Rp6e=+jr1fWh9Kj@V1?v>IO73=iXx=XiN2; zb+kJ5DrBcTfU_;tTw1+ejaWRM>2l9^gwW;3S;F}_^VHp(kw!DR+`^g3MgH}wQ8!qT z7*$gs#%)xcVkOkfPq9c)F_jqvqS8!^fjur+B=R#h``n883+=6fklj-|yzxFqtC?TG z)e*_~tuxw)|E@!r^ir2UtNBHM@Mu($JfUAo%Q)_dkZ5QP*nOO zy+SMP@AqwU<5%c?S0?R~R4hFNjD!iSg_WMcF*aXm1XY(mB<8I;005ixOF|U`DqhYp zaNnbm=6=c=UFk&p@y9A|Y~c&PHq*OJ70+J3Ow4lMi@k_6{#|cs?p#z*lxOWAueWqP zk;sphWK5Y$uV1EcCn%I!zvT2+xI{PM1JTln$(_D_J=uCp^6~NOfF;Wkl+<_+75c&I z7mjg8Fx<@fy z0o*buMm#XdOx3IB0dxemkE8z_L`Eqgk1J;!*p~NXlq|Fv`b%y!?cwvYl!_76x z6g{L1n^UtAj?2VJsaPKbC~w@(v_|#w%7k_5TVx31_pSNrc}Ar(E;Col0kl3)cmd&i z`%OSJHkL6;^T_DNi^>I^vRFytpMUZ16WUY!GiAi=BA8P7u8||t=72HIzST#%%He-N zx2A>Vj-mRdmFOVqG=E4#K6vt3!>ja4W#$YD8J8tN0Ct`!OPGXr^(>^K6y#Lz-b7hB zY}2a_VAC%8@A&x?n#C0A+Yn-a?a5HfL{ms;Q#}XxTvjd0$0N*VK*BBti|_d;>d{M- z>*)MfMU2=1#`IB_4)2PxJSQL6H@ac|Z(Pfoc}MQUuQjCrf9M1#X!uhp{xJM+Z$Km0P~ER;65Prnlu(Y`yP|;a^)ixI_gC7eo+qxr@!FdPk;ZVh9fDx@GD++ z^gLnrnZFt-(Pvu&}s88Kqk3DT;#wayOa}ueJ9OD^v0IxM)^@81Z*cVPT(PV z32!lAAgF4qw4a-oF(2T9PmvqblYKD=e z5k2p3C}0;LS!ge|(t|80p<=aiPfd70Ey(kd{}D-=*X>AuuLqr?fb+SS$1grrF3eN% z>z^~@%P%fvyL~9?pTSKHhhr5|Zs*e`?(LfW;Xil@{~n2$^$qCrKk)j%x&430>)ld_ z*rCo9K7P5@`>j`mZJS+1wN-mny1Ui7zvh~CXQa|K*;I2PIC&sO=0b8uKUlSfLsK?j zEn*f@ObWrd$7%1GIZX`+u4 z1)1d;(F&C+&^RnK#4iQK+rjclVPAo9`IIuhEv!i8IxajyaqR=PXq%M}3w0(P(i8}v zVAv~K37D^&1kkzugu|X&MW2BZ92MLFsn7ijO;2lw^=RE|tOG%TI>a*DVH-v1OtI}kEf4T_CrcZQl(*;x+jKA?1Q&ZoBmS{5y&ntEHzvrG%a%us-8R>}`Q zYDCprF`57UTVMXt^b&CL2(T?-#dKHZp@zfvmI|g+>CM9uaQ`gK!ZR~jd*gWNLbBK6 z28}<52y0x~16l3|U?yh9!VFr%8fIvWrgw}tj=wk0sqC2zhBZIVt)KtIx^J?dbVBGQ ze!HaT1#y0C-6sj#Z#`*IQhf_3Hka~`T)GX4;)4g1@U|kKfbU>s|D=8;XBFKL?&JOG z+h-GfYI~TW`f1@7qu}=j?=)OWJSXg^AJaXo@8ufe+mp?~07f#x2?KeXu@H^2rV5?{ z(?i`@R${}-O|8bcsTbnmj)O@NmkZc^d(z8RlzE3aQk5?0vobJl@kT0}_6KXZBWZk? zVKw>ShPi=9-M1XHi&6;92!f*#d988D9frT$ZB`anaw5gl%44$Zc+^|xd>oC2t3k~g zZ?ur12@QFsF|HTxt}x$y^X|J-CyQqe4~O{mikJ@kc%a{po$YmZEQ=LmJqB@eemckl zW@?JY<(c-MH|>caCKOxrDQ8CA zVLf6T_3TcAV7WiJefP*#+(6hI+t+#1*bivj&z;$1<=pHzy{)qZcRS&9!rnLs6}e|+ zLPvzj#~X`j+^fkvcKJN6@p@LkLzYuS3^b4De1^;lp1@R@#9%U`Mz!4peRI{&=b(6e zx3TH7HTq5M^T4QyNu@g5QjK}34D&0tXtN-8C#%Q#4OXSoj3=ZgHTdxCE0Mcf$(#q| zN<9z+|0Sfyu-W_B-o)(F>TbO13;X#J>(X$<6Q8KB31##MZ#(Q@ywici;wNY@NY;cA zpP=y0rjCmKQ@!uTB3jjhMebFN84k5z+(8i4tbxjGFCzgrZA+bE_5M&ck_|`kw`;)m zc=Kt?ts?s(Bb)5g=rwu_Z#0@HCZaR=GoODptE>zU&mBNAzkGb};N`l^v1V|j6}?5p z^yjoM{K+@2I47TFA#F@6e=!=BsGS$Tqo;)O)6$-nzI8PTXK5l*Kgpu8?jcTK4l@i4=7^lEKnH)zEd$OYs?cQg~t}co)4o7D^*^501lK z_QW0F3RV+cRH9ReC2fC?+3+taEypxGDS#gl`ausrbB~I0Dg4SI-8@%yh1ck#Q0z-s z*RxwtV#!gh@ZQk9g~UCF>kPNM+5ngjM>G)6q8dA9Wve%~2*t3#%T4oEi=7%sC8JpyEOy8A3R`mLtAw zeLL!XE98{nI{-<7UPvwA5LxYv-R&xLo47TUXRx=~Yu_m+s09tGE)x<*<45k`J9pAX zuym6QQ8cwG`MsYZmj@6&ay(*(NUl z?-}me`OrdLmRO~9p+bH{F^{gqVY2N@$^y@ zI6KWsXFDIg>nXJ`Jxxd`H4=E zMIO*-2&=RGBc&!;(Bk|ES-h-MblJ*O|MV?;Td!>Z^-jvXf_+zW(e2-~Bc(`?JlFAh z&X1xy$!tL$tr@&tl>$4uy$2A}q^fSnEj2-B1!VZj@b4Y{KBZQ@j=Bii!e_vPZ5gbH6V_X-d`{5u0ubJ^P z&W?Lpm-Xb@gDo1>S|||H2^+O=79FF|X3gdni)>0nc^v$PPam%+`YfyPNZhiY$pv2y zzZDsBp>&Qa;ekl7OZu*Pg6A1T*#v}(RhUMr!d)3y_v}RIb$?2cSa(a1u|pPPe1)kCIBqf zew?Wa_85~<_|6t)0^PFAmU~?79mpWT^$dh7Le)$ae@~bV z@!|HJRxla?>pT-I97hE6yw=zkE8!>PDIg|3ZWN~XMEpoeK5Z$i-4CvRdi*XARNHzq zWt2hRG2RaNBtZWAJR#&U{JzSg{qn&V}7F2CQME5Eil_XPr4o!JYrf4RWWeB)R+`~}a(=!hB! z&MP3xRiT(9yZqmx8a~tOoZ<4iboE3-DnwDFR7~H9zg_e&(iQDuwvk;Ml(WCm$OTBlGt@8pJ`}h(d12gF!XkneX#{!(8aZTovLLI zS3w7~?VnCJm$p;UdYivYP+y4|DORgo#HpSP5;ct@N+WEkx));mcKh&?%-FY-N>>1n z5ng5&1wlF*KmRZH2QX3TXCV)R<(+__< zJ#Cm?RtJRr=;ZI!9jSBIF#;Nfm9Evzhi%=|(R^eG{%-Y8id(ZDuMCKJeZ1lfI_uRf zZy>(H8C^li-uAuxbcqOK!E*n+r@w1TlHJRm$3^-9;c2?^W`mN`JG_rRXp1G3NQZR}>%UxXZezub#P!jOC<)w9o&Fll_;u|-(gJJ{ z=;a9xlZz3LYNzad&hDA`f+HslJ4CxG+Zy_6rJ+KJ$_iVY^h-S9tTdc8ZTyjz~K* z5_B804lw9b3i14H6eW;=aZG^>F*`^hXqZu;MV)aw(~P+l@%#v``#{dAHo5S7)o2{W zrQkeR(dC9qGF^wTdid%73|&cP#dEYQ$tdr13kvl~XHD_VsV{mke00t%ae&HDT)e_i zWXQ0{#+XkPumJZ?cYjxa#&7U?25)3WMuy|zjwBQYK&FSUv-x@%;k&%FF`i{PbV6J0 zk`5Gdv@Nw4H(@-l_=$1U2yS+A$0#2!#p4R(=F^M{e?qmvI68RfV4Do>s% zB3cp&Sp1n&dss&SB&ie3rH^3M1Bcp~<>m>6jly2^a*3YWUaRBHV@^G<4XUGHv&kN$ zm9i!{H?&Hb5AZMMi%vT!o{0Km?LqA_>)Ov7Znm{I^chC?Nd|Iq0hKT#S_N=7+R#0K zu&_51N&V2!p*sn-3i*Q`dpjdNwkVnn@?PENc3+|ibXJs}ww!Rh28QjNUAz3@`0i|V zjYJX8Gp}ux<9pRmQ+Uc;Gw->~1onM&)dK+Fr;5oD4mnxM#c;;KQO5nD*!X@lhBB@r zT|~e21^$K75$2cSCUIeCrOLv^`%nNgT;P0@ZjeNtGku?)u9Uji&WXNypq>`;ox}XP z)@N2=A!qWCt_`g>(=0@GV$%J**DVChySt$=gQKlhI$rQ+4OFfU%gV+ZgH6uo17$7m#@Zs zJ{J3zL3HoU{YzJ<6GY9-GDa?;w;RIocCXQOweM1YzFom=)~-+tdOzbjj8^CM9}iZw z{oFj$Cu~J3Y4e7y%z3}l&Rgy5x6S7{6=jY-V#N(!sx<+X@4-4nf0Hv67q)}-dR=i} z1+4q%X$DB^SxHjkOQ-o$jKz(dNKmFA#_-wl+x=R4kF}B~(XJrrcFN8E7VrC8Y(cCf z>%MAPNoNFGki%Mux|GW7GWz?)FowM1w;D6<#NbJP|HTb`NOaE3XSwob7riGz$HF5R z3-qRoP}Y)IZDVh}ae-E5243^tUuaXU3Io48^pj|2$Nz%@V+4ok|B(PwB8m2n+MLX~ zv=FdtWqOk`ANBYHLgyBUIk7fS)Az%c%s{sujq@NKDuoyT|KnMsRfHgz19ae!+01YBkzT*Te5CL@WNz}uAfWU`& zKwqD&Ur9jOLM3*^AeDc2Yk@5Ra2qQ<#eUW$+eA4{#d89{=tm*QSXjuna;^_L+f!_9 z5D6<;k=$w$-|w&`78!sN z^=R=Y61(CahlTm%4-~Tsoct$xWww4gP5Tf1^8hMfhb82}kjnO%4}kwHlDWu%x5|X` z^&@AA8;mjsY}x64K-<#|$(WyVSTz)5n{+u3X#5g-So6a5SN7#S@Sa!zFzk6JjgnI% z>MC|pa0Bv)ISRt5ve$nsmszRGsgZ4K#9u3~i^Lscqs=*s8KrN`WN>o+0KZT&+?sMY z!vio?>Qyb3S6618_rNA>MFN)zm+lXVPqVG1niZkQo+tV^D=J@Yrt<9mbi?t&1nY?hn# zww!k!zwF|i({tnbXnl#1!;zf(6|2%Cb^DhHR+iPPUpsg9-F!a@CX)ejWJ{l}UWJJk zv3&2{X^7&4m3rSKcew07d}@;1mic35l9XjtlGH3TTIq1#j=u&xW#VY0o)lU6^n#n> zoUFsg-)lI9zjDl~GP|ZmTlUTo%j&Wi)maal5DK!-KM&Nj(HNrlU-|zZ8HN2H+g6>2 zIdY{lbp@iEq2STt2y}i3bkDbqlC)=pu3J$esuAEkEX|7vYadu%CLWy-oz)K}3F69Z z64oT4U^D37&yV*=dOS(=f^!YMU@nVkKxzSe*x?5k4-gN|g0=}~hcCRzL2jTs_g5B? zbn7ztS0Q=*_xs}SAK8ibdX4@}nydIHrp1NwX$$xSQ7yW%8z@QA@o(=V9Uz#aBnh7# zcPl9M9_g3R@RI%`f{iS8h7^7*=4^Pmsg4JfAlPHr2vF0|_^wiD*_U4nt-?_#LJ0n! zZwtaB@Wh~+C=-Om!;tRoGualXsV!hj&K{)cIxdh0E@4JjbTtsMi;Mt(o*gLdgpIuG z=(WupNNL&B!&HY)nLw=9b4&PMR^YY3$tZR)0`Q|)h+#)X2SMS7HTyKwZNT({@``kV z-}FNv>xo%Ixh6(Gze?6fg*)fN9#RoJDlrV*lc5IKV(Cx*_HIA?uLT?h0`0geiZO1_(%sVcB{j3#b+bI}Rj@E&w{Cs3QNwS4~qUo(ZKut9u>W>6n!nb(9OTZq+@8vT>p7`V+dK7~Sb{akh2=QOtnZMG5Pw>A(O=$JK}( zXe(2zt`OvJa-K(PL0A!fs}qs^R42k_5b+$Qt1@RfPn-0X`~sy6X0xqqK`)Vg*;YN; zfr%F#C+M>LRV!ldlxalN2 zEnSKHpPCm>piVrB!`DBfwYs3FmVJEV#<9fz%~HU8>)vq zUiN79PRO`zjZR!#OVrf4>K-{)wPjT-U%mTiWu)Ob1&!UT%X0(13&n+3w~H={er4V= zjI6p=T^SK8l+sDu$iBGYCivvmo9;q$6BD-xtI6vO**dstJGVXMp;oMko4dQ-kj_;a zw+VNTZHHfBoCEHAmFx34PxW?faA$Hfb!4wkrzH-oL%VMK7{Th))#%bLk_k6Z(2IWo zHj%B?HoWxhEYYnJrp%ohY93s3q+ma=g+@b_BH12*IrjK~Yay{?lO#7tVK8?g)eltk zmpV^27RT%%0ATj$^=>I6W^`X?zPE^7vE6ukM07VXa@7)bm5exyb`>jdoJaAk{oKq0 zZd?6;wjN)@iV#vz;u(Y`KP+~i^#GsB+JOvQbk%^^SKvs^+!fta!hV+biU^Gc8<%@2 zh%{xM1Z$mibTi!H@?qc$m(*s#`ce-NVO=A8ZUC@Yr`#SJDW5T49tvK+vOGdzRDN#t z_85H>TzST*$HON#PJwxW`=HY<*2{_BLoj?6R?j-Q#J7@nI*X=1 zF~k6KM^07x_&D$4Sv7vK+J6KrK2aZ%w5tPh>a#EUZeB^=RFp?Cm$qG)okoueih$Q| zKocSdrpWaTs<*+*Tb9>49P$WEOmjPo%i7{!_clcLScg((XM3YVQ+guHuOwDmOm=tN+M92_Pn}G3e)O)Lq{w&1hEpmC3@NUgfs<1!C+I_3U>* z@T{k@r(rLWbJrFRrQL70sQ8a6yV&xV4BC2(&-A13d8+uu&6U7^uGXApF#1F-dTk^6 zqYUf8hMja`Nmxew%G7A$)036zI(pk8y>vA!l{wrk)MSa!|LTI1(W zGnY@E3+9c$gz{Y|NNZ01(d^;-#q443C6P~;o7zHev(jl-lWSADV&>f8%~5u_f`D2v zd)23<8>`g+`4gJ($wv8C_Ph$wx`{q{JSny_#Z`e+ zk)*NU6*(p$XX1XM&n=G|dS1_;|L7fT4JlLL7*qr*WHJIMrCO~deO^)AF-)?^|(zGu=tDS?trOKLJ=3%gy80=kB^vK_>cS|Pk`Ej?q zWqBpscBfTx@&E1dZi~XMX%&~8Iv=Ee z@T&G^?N;52d@dz;>BOoez(+R^UZC<$`Y($yGj6+{!^@Sx?~G-k8@!fwyo)v-;}P*r@kJx^)1l)Ls_y1NJige26_>dr( z$$f8_*Z3`0I5wW++U4;4K%8B}zI}hhY87+S-o{LO*%jW`>aU5D`jT#yp^}?nL>KiM zPy5kSH6SlX@Mo2qOQNi8gpR~k(ku{(9$H^FTiQ}*Wu+pqd)<(3MA*3ln8e(g) zAi%*CBuS;*epWY72H`NMGai8^(&eu39a}&m@0@=f8lP3no7?rBqFJK4`eV>KIHVYh zku*bCkKsE$6uXM9%hn-50=sLyRop8SiV?b|G;_8A{+yNLHfwG; z>h&@1&KZ+S=*~0>;+49VHxOm#<`&V3pCkZ^7dBMf4KDDvYK8KI)X7d z0b6ZZjb>91xt-0DV^m9FN#2ng;k)1984cy)CvBJ=PHHY#TSV0@CY4iV@4ATc8B&}KnKRr4UBvXQ1<7{TEX@?qK=3qo2HA2^act+6$}My z`jKyYMw7Xy{&bIdlVmeiW*f;g)Ab__nepQ?BVSAIpxu~QMoC9RP&sM$%eGA}yIqV# z^$EHS17ppYDiU&sFP`+R_u_R;1Kx}^ki1T&;{Sf%-!9KTjZa^Ku71L7G(7~Bz&cnM zivR6FrMxN{NiE>LNSMXBWd1Av{o4ipr#+M;IlGb+FEwQ_;~i0Vm6%lL>1@V&(vO-R zFM~%%i(0xom!X1&f8kpXNpynI04m&bX(8_enGaDSCTfu9@&KmA#g_d=wOn$fmtv3J(XW5Y?J)XUI$46gK3iyhaFmKve830cZ z&=w{ft)?LJ%?xU#h8$z4fqFpKfY%ppe?0G^3_ci@vLv;pAb^YjE@TTBfIKtr6k7f9 zxCof(C=eEnzXF>QFn0sDqM$0Q;{tD!JIo9hR68vP%epPWzhxhOg{gMwp%n;;hw+a% zBeD0L^gc?Xd3Qn~Dp_*GG|p?;WAvk(O9ru0Z%)iAom0ueo@I9Z84**g5O~6 z^4{<~2Zk~gSCFx`(GoCj5NtwrlhdY`w20aIkGoQ|9a^at@ZUVZrP>_U*Tn#!9{ddJ z28({Uqh&!I4dv42p}`<(1S@w(YEhi8t83t`ei{z|G#&O`CAq!9Q$|A-lc@jb<4)D? zCUK;+wBrPrhs=jVloWc3fmSuK5%Yxa-oxTB-}$3*nZnYvUlxh&A)e@F+AI@*guZyj zZi~z?W&61++2^LZyDAMF0yh?3Gw|fKy1~mg9shtn0-_;F44L-JEKQdkRB3Vl=FRdr z{Cw5oMdDy;>gnf`y9*>WDumm{B=%iTRd8R+G|b@8Yl?~oe|U3$Q_=jD{=8sT2~_-= zI->ogGt_L$l_;NiaLVHteCghsrWOmjW{tvv*;@{^a_|*|L+^>P-Gf^WMqGy+NuvXh z&#v^g@%B4(Y08%bCvK}A%nt{mbDwj-<94>>}pf2W@9xWe~f z_)x6O@bc>##|)H{=Be7i#vDr$8j_@{uEda|AF>GZs(B?iagg{xe0_sza$?WWYj@Cz za2R{?<+$h{Bx!WG`m|2Qvm~B)5|x4%dhu6gJ03)`#wxSIZY@ku{x`qRrNkFP(oRU) zeKfQX_C`fwGG-x8rx>1to8Vu-@y|769|p&SY7}bSgRbZ@sy+BVoLgG~r{N6bKV?Tc z!0fg}02HzhTWg;~dX9Xh5Hp&FlM)3319(JrYf8amng9`=^FL5<4w{&m1q8Be8W5ii zy|2HWfUGCQ@c+^0!}@kN5jZbtPiwvHL2mP83u?V{-e`n9m&{Wp`EwPo-iMPs`y_`S4Xk-WFRI4jj`=4Ed%EivE5Us=D?HgOmh5i2DO+IS-7Y#N$!qDjk zTIBSiSnD5Qk^-dBY=iOM(43t}5N%DC@UvOb(mTk#NFaZrK@-z38dKL`R5~oP`Eg0y z8_mW-SwFkq_tu&1;k6C=O%ONNiTc2G1M8PmSdUj?XT;0~pEq)mL^*>fP}PdLbc#y2 z8QUuk3;hwh1)hsp81fcIL`0MnZlu|!*-W&61C-&`FHedYqQPDn>O1!bA&BLE-J`K#%RmP@E}Fo{^*gIq%*@N=WszE`r|s8<$A{lsFSs zxlpoeKpE^ArZi8nJCzNuU5?atIjqSb=me%96Bd%DQ)GxE=;ui-Q0|$)1v;JjPunJ( z-T|CEh~hpkACN()uu z`tu&Yk_+d(2!m;{{Ot2i@GS%(N<2zHg9z(dE)y$~|DSdjOjWtCC0mC@JS)W^#jN=j z#64kQuX90ltYjv?aljr(`ZuhkQ_&4;bW{Wv!;I75!V!QKDaXUe18Y!E5&ZOjv)|ZY z;Jt|>!ym}Z`brzIs=D|LEC~cMs#6b8y$y=RF$@&x50dm>zsNsRVf^X7ep_tW`j zcPo9zga`r#?#dQu2miZj-BNBY(m9|^ddYAT-z5dW#7QO=0w zRidHZB0ogNKRup_e|07w6~0iAJ!pu9Fe)fPY@O+Ta0X|srC*jLGktg^=eQ^A_4EOL z=y#}2m1`+1YT423$HS}7ZbU`?Ms0vpEJ=mRHrF6HnBg&AJv@0*5JuIER`_JlhcjM{mn#WYg4{9=a_SEIYH(-fin7sh_f$c`= z^ld#86-zBP;ya+TPN&eb!5(S9Genqj-s@Gl%j~}X+x6EDNj~zP+%zdiQ*@ysc{K1y z;L*1O4346(D)vm)5-{TL`&V_WN^9lJPh+O(w>eGpA(b0D+bO~!>j7x`iGI2+P(RuR zmQUUkxGCLn@S}VL*j&&zWaKXwIj7&g47x8%&SG-uO*l1IMIg{oP=5au-iimzbuszd z=7VDWK~Q=)1%7q`dVQftXC|jk&M!UIQv_C{jY469NOeW4_m+U3TAPcu^6#tU2p=sB z0I(6$P4u7N@VsX)%rgA-*=s}FmIhX<&3b-Xei-8y`J?{SfZ5XHd8F#lylFKZAMwTK z*sN{vpV+ZdnJu04*em4{XXWEkg96(UBUnyuGW$_W<3ts^(P`&cuxYCS->gHU9h2g-|h*!LSHfI3GQ}iXD zaqylCEbW&)KdVqVFSOKjZ9z5rjsfx3WSoz#B2U>D4OWhZB2dthNXDV_%=Hr-_rQ`_ z3QH;jnnt`C>?mb@HLX`Y{jIZy5>3T?v0nHBn)zTiKS^w-7ZY@Nq7@6XL_SsZ#Mz43 zqW*!^4NGN>uneCyoJa^UEyt1a&#;1t_GXHT$4X+dbvC7E(Yfm@9y=O8Y`!Y5(bgRA zb%nOx6Tbq0q<(r0-G|WH+vzbS7_lV|K^Tt9NPr+%hi_Ve3hPRW6M4Ow!adZxRt@p- z&%h`zaM9Ngws@=t{u)DFWi_5qz#$)}yBv2Z_J+b?{M)d_2v_X0kv+ z)LS#t+weMJ3xlmh%fK^F`1p)l1w-z6mCKlEZ@H6yt_oGMe+}P&p8wvS`lO;k6+Fsp zQD{>YgyD9kl?drExo=4-1@>1h?fY9;&=F482a_Di%3oGqtOf?|XgIdLbkO~JymwKI z4GC!#Jz@qjpSp_5@#2I5Lb0_qNYVK~#I2B$<-KJ)`Kv5{)JqYL3FvGYZA9UHYrE*R ztx>f|kDf%s>Ns_HXU1do%0tl;4flX$uQ+$&W%Asd2HaWGUlbc4JP+;uX5Kw(C1svG zzXA0!KYHnAb79%TQVkH-y%f)0Bu)MN+epb#K^!t){<<`RWd5BKmWuuC{tj&SNAO6I zRryPYx(xzgMHH$ioUR{mQs5&o#@RXd4HCZcTQv39!P?w|>+>9P~={?<{CX@Ifq>VnF+r%wJcA<4v>%0)d)eS zMMDN0Lr)6~71zv#liO%A@iW0iyI3vghaO0DEs>_f$@djrr z(}z^jDIdvspm%cPk>Kaew-wC(MytWL#}+E*-EvF>KVXInPXpi4w*7uZTGXA!Nl8f~ z0U*=V{&uOty>vVyW*$6~Gicmlr(y~n*?_r%SE09~(w(0}T^Xxz*q7PIzVD4=G9cn{ ztn9&603;Efo>B|#RP;p6(Z?nQXpc=y5|*OZBdv zK_tne;U>)R#*``hO*aKtmU(n;eKZ?ar`C&z`JV!rVHGBaeLr27noB6L@155-&^#t< z3B+^oV+a_aSVB;;9T7N}=xq5JwRU2pRapa!tQ5;(vj{Ej7$_GPzy`-6by#Hm0f}Y_ z3S7~k(?s+$PVF~PBA0LNL+icbf#+|){>u-(@OsMmcp3BO!5|I-K`MfHXA#;Dhm)J= zfNK>n9d$PE@1S`!oT(f0dpp(TE-ZjHzPW`8jIET&1xnVPP3zxbB07weEAhe`J|6y& zWGuqA*G8*%`m6ikkeE){TAq3gig}Prz_KP(kD0H+P^yYMSoF&xBgj}RN$sQa+&!Dl z&RT^tHi0g3Xi24b@Fj04jFX!@SSWP`-E=K4U4zk2F%-t4R+hr5)H#vbE-;>D zjkf(@H`Q?w9?w)qg4Z^~h}bSWyq(fC(T(@3kLyZUs}KLjpAm`~y|xUeDaclV#Je#M zNM-9Q7R(@j8)Rj{mHl=(sdj}+oY{X_HC}r8rl4Jgxc*L}xm zq`NIoT2xHJ{Jlp0tEismy$dWflO)^4y|eHVLEBHI z{USqwLQ|ym6}bel|3%z;M@6}<-JS)CXdz-iP*B0G5(O1Ol#Br-XOOG{N-P8kl7kye zfJhXOEIE{tp-2X@NX|J)&N=t93itWW>Aqig-*N94*FW|^b|Fwz@4MET&z!$0MDGRv zVI&4@*dnIQD6NB962M9Tf5vA#1j8-Axf$z_kCSajVcPx3!Sm0~jA`YzSQYLi`{g4h9OcR%rht z*C~a^q^!e5Lz^@7-+0ov@4;5N`#xA-b&)debOsY?$VEuX(Xd+Lq(F;dXgZ*&p^&ne zqT!Bc0qG7=$+I`wX~sJ#vnNldYUVWrR8eQ!my$YSN_yn~((aad?C*s)dIA+JrOpTT zVASJ5YRnhW;EnzTSUIve*t%NW@|7?`Ya{Jq5VBO5ULH&X{X#@>C_l0CR8vOs>9*7i ztI1J#qnh4>@~*C$1O$Jg=sVuUycVi5!CucQ7Rys<5%LJ^EcC^&PnL1WFb>*^{vf2Q zBK;cB(Kr6hJ$5c6K2(KSHvHoZpTg3N^Lk@M*L~3 zHc;aleMg3{5HsXI<;tfkpq?f5>0EULbCRI!6Ar6lWX!)lQqo(v_>1f9iwl z1Bp*k*M8my4=~y}VeY+@YDwzS!mIHS4PR{9k?X4l6-`aIMFm#!h$92vO7$|qBX2y3 z_oFi>1^bXC7<4KnoxwLluSd|Gg1Uli`sBjWoZfHrx2B4{Fq1eT^lBSD(&y00pqH%- z=XbL*6#*@|BH_FL7R|{OW>Mn5%65|B$1^-TdNfVc`g>+D=D{6NI*pixax!E1dDp5@kk7z6tO@|$F7`A`jR8z&X~2+0ZbxZJPOvL7yg z4QTFyWfVsTx!F>l`M<#&lvspHtbf|4+g#BfCpyKzP7!67ayF3M<&E+ONAv_d<(a>6 z8&r!#e^-7Ic2l@C-V+^>7m5Iy@UM4#qy&30|=4l8b-Tm)JnQNRp1&jG30g?u=& zDq~0zPd}02%>&irt2w)zHlVvQE;+ANw)Cmve)+3 z27x{b_F;56+{NgQnJj|JNNTUGLO0i`S`s;_wLNS6F{)_{wm3^Ny6CiR&fGpy+8@PG z&J5OLKDXx>Q;#%(A`tVk9o8&1z|Pt|a|^Q}b6B{X=hk5=?ny*<*4;Xnty_Kw)~SvV zxKDHpl)mjgUIq8SzrZ{EF;7boGlZpUY*@e!4_KX#b76;`EzOa1SMFiu25h(>YH! zxLdAbk;KIt_PB$5N>caTR?)z_!Zev2k3p(Pj{O#{4rwVKm(Lg+!tVc9(hRKA!o7S4 z3@JtN%fo=8$N*L7KA6SY^@B>ENo4RcJFPo@xg`5zYbD_Xsyck9LGtMq=aJ%#$EuHP zAK+0O5=?02&#^4sklyL8mC@Z(De=j%uE-#Qts6D6m!Sh9ebA$!iucP*SJ&yoR!}Q=e-zMJ)dWRD ze6ucn5q~_pDR)0G(y(v@+-v3wpjk0j()E0^jbCPTzqpz9_VnGXwKY25&hdA_PQNm@AC>M82I$*1DK zz`;uwCiHDWwJyBF5_?^${8&;etu9hU8hYROeF8P&A;dqY;>b>vs>o$Q>?AL)Yn1b$ zu^f{O$*qSapIjb+&)ymlznv$S-hss6({KEoX9hO3?|L6jH*7-h*A$RhtQP{t8uKY$ zd|EdMLw0hfPH%zv3iYqyt4B#W23Ka_Xc247?!G*QnlP5O!9CV-_T?#sWY*$cA-baHj5HxvZUo79Mrrmbk&!ZVIfc~w``*OWVgX|-=iX!MeQJp6 zdD~AIr#5@fZiTfRP`puWgBgBr;lSpZslkBh!4k9d77mb1; zLCT72_v`U15J+?Wc>M3hsZL&;VH+_`ej|kQU_(HdRJZ_j=k_}FbSMj>%7d}vPQ>3e z=G##c>4~c=VqXUFx{kYc5v(cPCPNDFIl?Ib@A@&r(;>EaA>KoFL zd`CD|D(PM38_bqrk796!B}gytmFj(8d0H|qO0tbqHP-U9kLCB;O?8~ZPg%N zv56LYtnufG==8(QPnak2tHp)Q*tX;k$XzL2mTk8yjBgkvs6=Y7YT1`&YcQL+_s;dO zT?{kw+WhiqLY#5f?Z=*|0d_oQ*_ZUKp7niRhH-`(J*rFNJU4k+nasA?#ayFsSY^3U z`;+pKF=?a|*yrkg3f^JDVwF9TnLoj8*ljFZow$$R_Qe-m9EZxK>EjwX3HG;k91|=r zv`^gqqCR6GAEzE+g%Y6awjYF@i#oL#{F(OXHP6$w1gMhkXffM!Gzb~!{Ak^tSbej6 zccVM%7CCSCN6T}#hqcm`GXhhaGx3FaK>(zCTG%3|yoZ~LOa;5?>!`pUi4t?wp6eYt zR$JC^V;~IEM{|*;es48Q-h2Q%8`A-%EQNPr-tS2}#>`W5orj$In~(HTy!MU?^z%3~ z8e=uVzckSjiR1Q*z>kox+IF<)nK2K?`JN=3aEjnI=`9j;{!2c$Ho|dPEu*=Dc}3#| z?xyMZfw*kdZjw10B`F1dAjc&5Lt zbA!n;b_50_*&w+J<4F2ggPH$?0?5yJZ?@oLR3Ao*g{KJAjVDlxs2N5mOC55ja_FEh zGXDPUt4@HyTlMj_pcmQrHfv+&1{Ljw2I0_v&B6)n%j}p~JRJ#3MC8Z{t@~DTpvDJE z)2~|K%(@BVIi>Wn$3vkMCv78f9D(m`?p#Ch1~j=oOynGo`6a(sy(}w2+|QRyI}HsI z!BADS5SVkY^)N?e1088mxck(@zhL5bi?UGx9=no@_OToh#gX1D6h6zt$EjL&;f|L;n5 zH4e0tr{z~F%?REB`#AiD-oD*cXF$sq$S4pdO2udq724KHYcCx>(N`li`3a#w4C)kslb0rJJUeEvJ)J*pUn#9 z!~~Q>^3&&pFsRF{`#LIJt~GlGK08EF;vA*XF9=v`*@WW;z$OMwAVA_(^et+51ol#> zX1L4OSg|k7%BXw&U}1|zL*0{PNAPpuPJ4C3EBFR$aq>B-ZDcTu-(<#I70ZUgNu8vA z$x99dswCMP!_+I`>#OO9BAS;Pg=U*A(>F_>RBk<4A9f)0?yKk9vVd7?P-n|fz5v;z zlklQ#^wRod-g05v2DUlK3)Pov)oHu|BZWGWbDZEOYxjm#|G9VGGLknA<{2<#O$oig zPsyCwGXn>*bSPz1!)q4~&{X4f0Skq@2-Zxi;1yY+wNGDb15wY`9Nb~LPr&^8SibYS ztNBvWYLs$ku#40mX#$P71KlLmAZ-pHC8wPX0 z21q#N3+LJha$XDK%dx8RXI-gG-c;-Vv}%Yxm>;hjb?hM7rZ}Ig{DbgMOB1(mmKR0U(nKUO!?3{MkVM7H zhk@{7Yx;|;d}e1$%|m!>w4M2im*Am(I{rg#suGh?_Ko(O{tB& zM3qKuA`IGyC`??5ZwTI*rLB@z53 zdL6bfkgT>oR_+>OTM8rUp?+N-PPorIZoM4{mBXQ^UNu2&N z1#9lg@VV`A5k;1JJ)zo~N=IW-mO(uWqsk>%jy;0CnKfQ8Y?4Jc%{H8CI$w0z^Xjk9 zz({YZ@=X3D#(cA?iO|5)#&gwPolD*YNXnQ6W1MS7F$ zHQSWID+#&9o}ro;>?!5A7^|@?Tv1d0am)4f=X1ffFo%(Al}>Q4HVUikSTAE)daO#8 zj*U~UQW!tr71#(Wt8dKK?9zykfsA%k-D}zO_%qTQ$4Dvd!UV?TgMDQFww#Yy%m1q% z|JUj~qqBQ7yvxuy<~Q}Yt6@5!Oo2OFTXThWwCaTn_h#kmeZBVH%eTr! z%5T5WPEO4m)yC968P81!?CGVbt9!E17j7beiCoi5(NZR-SSIMnU;jMF)w)59m}uZ2FdGTAx~Wnu=B#q_Ra3bjVjGK?Xi zX-*M*>!O{;uVZ~Dc!`x5QB(yA}BRed0vK#2K(5jMIkTS(>tOM4OO#SV#^h$a`jlU4G(f({3 zuF#wB{ED7x714}ld42ZtrM4q`?9LO{EpW8q9kSk$ot8eirg<~`6B6R&*77Z{b`}Uq z?plDEBFrdERaJNi3cJQ!F{}H!Rpn3IAT+N$uNIA_*^v!X6(Odb;)awiVhy)G{erst z4H&8n2F};5FM&-7Fvp$V<8+;B$6yJrk$yMWs;B^Ge`STw$#++b*ML$;V0NV$j3`lW z2*0)*vLvdy*RBal@T?NV6Ftbw?pHHodS@OzdnTCyovH)@1Uj6nFehStX)&z?=7 zzBgp-#pQvosfq7M-N@*VPhHH+uFZ9rrnL8Yv;Q{hQWZO@aGbu*$BZa-e4t*G{@whU$cb@s(XDhKQ92n}>@ ztjw6fW-uKc5ews$_{3)?=G%=Uj;MO{w)!(ms&JpLR@rQJd9*ODpn6T4+9U!DnR7~z zLzy;3QeP*~`ySJF*&}cOT8kzNc%kfpR~sDe5Ic79OeD}joif)t>^?PJHqd&`VLt-q z+6;!xKJq#Lc8IBmQ*}|SCGQEB;iB%E_%Q&$ry^uWoBP-RlW4Ppskc-a{2f(47l8xz zxr{gI8dP(@ra(wh76#HT_vMD|d|<*A@;betDD$@3vw(cmIKla<81`iISp!C@Pdlj5 zuzTt?bNJJV`X{U>oRKLvFU!zA^W#3#chvhR%JjAImasO~YyX{<$Z2#(fKsOo?v}6_fl0yvwvQ*2q>hCgmjSR7{hyQF!Hi`YLQE^jM%POGrokyT#gOdzM z$YIy19}iePdR9V0p4->mXqS2&V zZA7!ue^xmmbB^aoOHD6hXo0P=Qd|t5Me1FinIvA>o{Qs#C(*3yT04i{{BMnj#F@^V znKNp$H>-vws&1xlRCuI76j{G*qUzJTQ#ZJDHm3JjXI*#?8g!%!*Bt1U`XkU*PgcyP zA%A%W25HHa!TQY|6Q4>zRSsddXZ@6I=Dg844P{w2#T#MvTAsgmU~JBA7-~rxslJwv z`^@p_AOTmg#C*kRcI#VmvQ*e9gQ9(fGS(5|Fl~1cT;q1A8*|$^SlpqXM=_*h-xtv> zg(0v$57fhS2af%?tuIK~&DeR0dUk?I&GC27V&I<0wK>9(gif?0x!*0t%d3^T6W{rm zkWv7%RTBZZ98@%mJO4f=#X(ZQlqcAW`4u@^Er6(}AlTs@kIDQbvg3|oDx;w5*lguY zh~B9Pph#Q^u@DkfQId#B;|p!jy5KUuj+AD>%Q|4fgh@9CtK4gc4`+SI=>`b*9Ds}n zTdt1$opt<<#h|YDjWREiOzz>K?wYhW!!bc;1zRztr_6sGX#q`(0F4&NIo@fRHTlU; z=C5;9ljn-qgo2QNF~QHyMLuj9)Dw-NeX3k>UIZ|5m3h zS=_f4|Z(zOIF`EhA%m{BWBi0 z-!;sV@p};=&pm6p`4^wb3&MoqItX|{ni5Jk+b`vj2b^jz@fKBJT+$5Y>E53{W zRVZH~j*b`w=euQp*AoH2a&ww@=V4EGOaOqf2v(4nD|if9qL9PN;(+b$*^JxVcJ-nw zamK`Hen*?a-$}s*7pP*;nZ5Craw(4%`{G*qlB^btg5uqxddr~!cCmLekhVH{m3J&+aVDy`~E0hM9rOdP7 zM7MIy9`Aip6Ef`fJfPCRfK6w^3APOcb4Qgl&68;2{l;kMctyG2^uF^Lk*kVP zK;3e>s6OqbuKJP8OV`}c3#_{f0sTlyb%GQVwfj`PkNkPrVjh?!u33bjbh-X-zrq@S ziHX&goFaY5$n?epYG=*S^WrQ8f$tRZ`sgfnXiyv@LO+OLw7?vm;#%;c{Ove%UHxT? zaz*b@NWm8?AOUmcGF*Nj0wRck0GPmCu1WF|m{8cG1`W(CS%GRZT80AfgE!SjP-G;} z%Xxnjs(K_=FTk+;FpDQ1?t(zZA#r#pUEnqWEvN$FY&FO^ZWk*q)NKX6jWwqMtMtg1 zB5V4e?k|J!G30Vewv;k;Z4Up2@ONDTXAa}Rsioe{X9DyP=GYx@YtlFX%fp-ufk z3qSA}x}h+P%5GAJDcMrZ9V@`x4}aLy3Cq)0@1iwt*uVW%fsFlS^C+d&UVaCivY13FDc2DuAFwd`Bkc-=QT$X&w! zRET}7+VaihR7G+oQzQ(T;o;BKBHoWM5r33B->l`i%>pZLn+pKb6n?hTTo22>=?Tv- zYNF>ssmO%-$IIrkU>p{?0DD=j&7@q|rbbT}dra6K1-GzU81U%dOFMtwA5 zGUWTMD0sn;gEB3DoGp~kDrsvmUf9aCckP$WBxhLS^x8N*K9E^ftuTSY7L37hr|pFZ z!h;1AK7rzySBygX>Fp`0_rHA8-_`*v1+<;W2#{kwzr0Mp^%d-WnWQMjI#tOWfldY; zu%V#I`OC|{w4s?=urgb7SOBa|5AP4U*@6QuaRnxVxnJxOMAs98q5$vG2GSBlgE2;x zkF~r2g6h(Qm^_XUnBiQ#wLS%$9><8`1P(G66#y_NzK3THZAh0CUbNimWrk(B>CTEP zSUu{nEcTMlbE$9B=fNE)E());33T3ldPnNoe(q8n4ZC~>^Yb5L$8Js}DW}J- zdhu_qFoV&*_h9NYsqO;NJ_ae&u~n#@4Ij5Q~_?9c4M*Da0+N!9BVX%oK}{Ei9dkw z*gvko7B>Hy*VZ~u%Sgb@>ai^p(6QIC*rGG;aJF__npIbc6?KnDc5JLWYAF!lr9-eb zMK^ov`eO3nl5{`>ibV;$7jS8j-G#O0(y2jg}E!-G9&}e_L z)~Bh@0K+`gf!$<(HO|eDtQI8dpT#TCU}*j)og#bAakc;D!ti;dg3>csHOb>N-wZve zfPUnm^f@$p4Q&x1TcR{wD2jG$y;4_M2Z7)2$|IP_V0Uw`0y6rG3DB#;N4zp7;^t{} z;s!G2m*&2%0~){kKI}CINhhH-zKr9y#G^nC4H{Xs?JQg*hkMTs5J$=s^Cq)Nt;0;t z8`r49je%>q%1$-un=T2)K*ro_^Ye~%!qScr6+iA?E#S5@6{=F$SI_WUn-tyt4Vj!* zBQ3S9H5fJ&OWHp~ znFJK@Y}nU#!m^++v9A$o7=pUaNh^+dw8Lb)!8AEO?LxD({*|f*V~u8e?_QOqszwEU zk?l{eY0M;%o=QfEo|_{Y8QG?HTe4@~Ji&;t$KpvH<_j3$*J7JQOP(tJbYfaK1}oYmi>JGm$W08h9S_RY=$rCxrXWhyr)~)6UwW~ zIF=d%_H;`*K!|OoS=_tonbMg>@V%FEV0_L9{Lo&NwF9hpjopw|j>e zL_#F3DsJ|5dS#RX!*tV$qPvV{O`cm`>K6?$$ezlzK=Gn`81hhn1;~?$xB(N$Sv*V@lO$Fx~zZ$pYz5Z6oFHu%@udQ*vC0418nUobt~o` z$1c5fOF=PMT3K0HfZVVFoL?(05Le3sX?VP!FSSx#>=N~lV`%NtH-{dlX`HVT1A#3= zK97=32Q2Dc$BzdzjBeP&HN7^Ci9GYBGvlEb8M8nxi@;EZe!ZSS@kXn&^;HtaRH3z@ zW1(~v%9$FV7J69JD9b1%aJk}p4k6{8J= zKKJb`$Wd1~hfoYfFrnqSsi~^hh@fVOZihvwkW&Q*cM#^LEPH(4E7MsOJ#!ENK(WS9@$q=ML>2K1riIQuIj+U#$|SrxG+|(^E+n zCv8FDh27o-(u^eU)6pVDOv!K7Gq@y{HlEG?bB) zCNG9yqOkk4#P%kCrz4xMS4xxGJpotm4Q$>M#l<7PmtiXcjsfr}Hf^6PU=FmHDN;`9~!XB;MBa)o?;43M;uu}mL2U|&P3#@;;2Lkh|`Fkj8p$=2hr~o$m@aC_Vda$$J~~S`!B|L8%L|=0SG1Z3OIdf z4U)GV;dbq^k$uVgc)+YpRL+FGp@nAfF*@$0~&?!zf|B(7Cy9cv3oPNuL8Z~W)130fnOq#|i zsh8&zEpdR|cJ)lwVv<)c6dX)^0q(Ydo5*{6E_mnt8{pw_(U-3UGpFx@GJ#zS)%4@d zPJ`_(*~*@M2VcP;-s^}*bA3cr#mGkZd7#>xzQ$Qg2R5#9g#wcd`iTI_QRfAfMFVB- znZJ-aZ9%_tI03owV38{Ur z!;k(fH`Y9zvlx?$m}7*MlyB|cO0aqv@{{2#sekSdI@7FjAWnp8b#I~D09-^WU-;pe z3LhpOEz6ne*!LPWK@4+ajDgFI4qk(3<(Y=Cs~|RRll{Lc)oUzUdi34)D+WpMh^$XwU5o< zDAjIkvWm8R`lR4VwnO9So~U$PaEJA&tAh}|EL4lY-vwy%%X#JxnGyiTw>4FxOXT8khMmX2?fSOU03s-YdinBjf6{9K7rpaiaoV5 z3-GPwW=;$A$!Tfc@kX8L`fuTUc@AM0j!!>sHJFqz=6+8Y1H^@RzRe5{lYy?`rz5LI}lnF5uC^A&>bqa6zB zW$`Wv__5(Mq;-+FSQ)_d2^BA+b9mPx^zCV?)c~w~66H9grMdMu{3hhIDd7w(_j=dE zP9Q|E#z_gnCc+@bErrZ==C1Y_O7SGzcW<|Wk!zi`d%d$L(`PBDy6B79>88X(Q`e`1 zo3LM=)&^Le8_Y*D^<9&O_E7LS>smjTddDMwIU-ASr&S-2N=v}z+I|E?mP}yFw8MyD z5L96_Q*-)l={Es>VnCk{H-lWgF&|WHg6?e)p#VWL|r` z7N42L-t7}-ItOnx5*bbF&9Ksg`Sh`@;F5o+%1#f6pwI9m4|HHZ^_JcYcsgF8p)ESf z%EQ_bU`r;V2wO4ZD$^p{Lyerg6v-Dy9Xe*dycl)whGlQGJA|3uNWrfuR(d+)by+kQ&%db<8~BY5B$V@IHj9d~LQW_poF_ zV2bxQ!$nMmyscM4(_xY7xA>#oPxKbleSgpnP7>$PQ!&o%klDQUo0gPGi zQRbx=AvH%Q`I`lxBSp*vn)v}qjPC5n*NT%ro`FCKsKeJKh?Fi=Hi}7(S z6-6!6s%nVQ0jm%r0t0IL-(0Ib2&hmN|Iy+Ned}G4MUd+!;kfx3GIRMqw~Oi^4Ke}t zdPsqB;YfU#*b)s}IX`=9?|sw%deF5nI)7a9Qeo2}Fr0)6IohRGPpy}4ph{J>>%!E5 z7Yx*Ap!fbsmjefWMZ7K#|io&IJ;?IcW@ia(KhtGoi= zDZ8ON(yC?B>sKyUP4n2pZ-K|4W1-vzru}P!nv+-QzMEzA9uiLdA}mwtt=n&-HF-S= z{P{jzD$YL_pT#ZF^3#a(9jbaWF_ThOIZTlg+qQhI6uNStc;Qgz$jR9_&DR*Y8*Y(h z9Zw*680zIDex!7d{nY=xrKZ}N)i~be0U$HXx^k~LvuX!lCp+j);z#-h+*WPi$r|7O z(ki^JE+*=M?{6-ENtm$H%RI>i@Wy2!MnR2goa_WnsxHb3Wxv);`2kvCOlHsZv`>V$ zh0AFNkqGm%6%2X-h4)H*CIakCr!6YZW_92S=E_!2=RHXB*Ntxlp94os`b&rdaZ9$} z?9X2k+#J0tT*aW{rL61NX__;<@?E&d$t+;Ld)%H6v290;P6kMvuNL5*hPZBMyy!;M zFom-a0&s=`U|%Ed)Ue?CcED?)LC>pQPc8zM`802Fn0s%Q6B7aI71=+uUMmKh!kS}! zFYjrF;zRXU8g1~7v(Drb(GS$iZ#h5VS*|vAL@jqlTU~;HCYvKH`)5bqFP9hozP&eY zcIB{36>aJ4k?u)4F;PIi9s9XwII={E#I1OfGwF0VKyxWfs7s#n+e~LdezA>gHrjTZ ztAHwv+YDmnYx=iIzEIr z`mnO%H19X~3Y?KPN9bH#q&7fCRj766AoV)XJf;F$zew-dvAO3QZ}m;-*UV`V+fh-u zht{gldP|E=!NjM<=;4E?4ka?qB;_UQPiro6{%TEJD8$m0&RobGe4t7A7nTiWCUdw= zraH3bl21v~0-!{ON%qVeQ)fu2NK+qXei8AlmSebWO;_myWpk#`Gc#)bZN7bcG~0jQ ziio*5ix@rNnWkIEIbtUEs3%TrtXyE_W4*W>c6|FFch0w&-r{BE3lNbSIMbWeYr`=4 z*6ErcTTt_2$jTQ-;JP)TxOU`-d$?Nj6DhLWc(6 z29nPD-`eaQg5MKu_GHgOR8!IjAE_(YB9}T^<(x*{h=NHcMaZFp^vC)a_tqv z9s{RJizE2Y=VLgrdUV)jjSK~EtsC#&yYc+vyJwUE-#v9(mfq!=>T}qfnatJ468iba zH<@yB6b$((R&JFmm$yEtQl_0*N&PrZh>m5{UO798bxUiTls;Ko6seoYu7hci6fH=f zKPlME170ao0E)9Jrc6Fnc$oJpUPM&%kz;%PxA_;@asIksLgE|y`r6lFdfz) z6IU4Tk0ZN75;mUUXVpFYDT0^GmyLuiu5FuD1HYm~=h%vEf z6FenE9q44d-^`W!rZKxNV!w|Y)a{9iX6$jF16TIrsFJ^ePq);(T)%s@{rg)xuR7^B zl+Dd-mz$)cuvo`S=dhMd$`7XpgG{HA-qY%;=@&b5 zd|UpVZu<3^Te0kw+QM&|?SkHNHLs^Yo8PCF+V6n8=w4or{Hyc2QnceXNlSB_%Za&E3%R)x`g zWIM}eg}$IkKXsCB2()TGZVJ18_eH)F0q#1INy*A5U0eDmGL@Y^!;$%#xu1GsxlH*z zlBva9l`dh6;@R<6Gw!!xU3hJ0ocOLKb*2fg@gl!o%Bd)vp>M=c!>=&RKQ#Rtwq;Am zunh#L?c32$BZR@A@HqLs>~zk)?!P;lj!Bu~^V^%mLv$i!d^T(I%6Pk;z94i&*>*&L z;ujbzrrn!5-s9I9jeBUNo9*@>>?w5*Z`4Dx<>Ctg>Mi7v7BHKN?&Vjb;5)S;^4w&` z3pcPNym>jCvSj0zP0^qohZ@AWK}`#Yfr0Y$?dYan`Ry~_g&dR(0pU>j(>`6ivz<%k z60aA25kMPWp?+tcKksN4{vvf4HcASosWKJV$mCS=FF#9&nst@6|88S?|N3h3QB!%z z(;F6b%f~x$Q|~UZO1ur+ZciDS(Nevhd78tqdB4bw(G`gnw`H9|;fVJ-tK|%(S4j|4 z>)%ZK`n46##=g*(74e19nC9>L!vInM6)5kUrdI)>orjO8cleM(>#6oJ;lpfnuZRg> z>ix#xHA5$y;b&Bq%3|8Xa}vaJSb~GZ4|a#zp`-Kl411BiL8KH8IO9&~fRbke6szPP{>np zYAbm*SIF*%mBG%94Uev)n0b@~g;5b2_4qLEM^z69lnSnx0?^gY?Ue709rkl|xP~1v z`^iDVIO8kH`2~OzoyFS|#i$Pg)hI6aIy(dR{s_#{cI>GuG`M`IVnnuYfV;a6s6YK> zo4RBe0&){+`A_)As&m^#y|vRz9FYk&Hz+V1(& zk`<8{)EmkfoK2vsIIR#C9dk*(|L|&HFf5l;rDGcC0@~!xQ*lSYeop=H-fKopEVL;t zcU&V#ML0C7=4(VnGs5?)jz3v5ZL$yH?*m$CHYGI2LcuXZ&Sa0!>38cwisjGE+(rN& z@{>|LByEN;+nek$WKWb5C}=qWsgJ4RIGAA$B#d^J53&%$cmdszy`&5J zCpI)G;e?U_Aw1^r(CU1=T1FX1#>4OXLa*qGInl#JbuzGZJIKOfFTe?~7Qx7Xe*+C9 zy+&#yX1IO{+ATh9ES-ca2-c&cwAb0L3&7yo6a*d4`xj~Ti@`3?)R)cUk&OuIui_;X zFVfGf*u(ObRpwrPYvwecrGY^6b;N?8gd!lA@g2<>8ojRx;7V{V!ZyJSnL%Ci+?>g# zQQY5HtuP#Bpaq$EQ}vOidoYn*7KJTOGuZ1){NFFNuRi?lF)S%eeK1MsdyxL>GI*t1 zv3e7O)qD;#ZL%9jsic`D<$W)(RsJg!P~~E*@irif<-y8ERPC`;)eW|el}9t|p9bYW zctJEeKlh6a&R4gWQ$0t9t_U3FXt)9NdS@fiFbs3yg}tn3zPF#7ZGL5GjFb&(;i0X=$$bz?r%(qDIRxA$$wIT|U*>klJ%$}N$RrR@z zWcS5aonpV-!UcDbLgP!H6sX7ov0caS8wvKVmhoz0@Fm(LP7sagvOE4a+bdKE(rwW} zBOd7X+`XEOBCVG7lRjs`?9Cj{-n))*r2559S;;2xMac)P^at-OpiB@Lirh9%dGSnL z?aGfA*|@Kx=0(+YypEUW)r#6yTtP$%c)DXp)#UtJFdQQ74;c#Vu>;3b#ywQYzE)z2 zeYi^gD;f~=MJ2czJf*7k;0&AsTw*$c3QCT!P~e9h#v~n;*#4>-rDfNUe~R}zFsDi$ za#NKaaW!m6+ z$PSO{jl1kQ)Hb-$;AFTHGJ)6&Zw>&Mnb5Ahw%Qej$5iRUDW4PelNpcmT8mog^uPPL z>R!Pf40x?VquG6GeCYu5$3CuMux$2rabd6fIRti7EjfC$u%pr{X7XW6a4&U2NS;0= zR@M&ewUf>}Gdoku^${OO+Z-n}daIrk2Z*ae4?#*aK+t)ovf{T}I$LTFrmDJ;ip%4u(_@^-&p>tmW})+4+Ay*=|a?S zjMGpqQ~c16f9s}0*6bxec~*igb`5cYaZSzQ?oe-z|6P>F(g_fOoDtdV%|5fyCFZ?G zjpl1TD@Cft1j`0dFzwYTXs-CZXMEPA)o<=7Y;$a9Ek3mCLke=WZbyUc*GVd9}8L));Ed@q?ZMTP;v-#Z|fP z-gmVQJ5VaTnefp;MSBJNDfkizTgx#JGKZr_!@xe6#ln(`e_Q@L`WV?#Q=>ob4Et*d z6U&*KlKvPHjE`T|WjZi-UX%J7&$)5I?7Zn~;MHL!-E;ADVCeuYRyq2fkY!;@9We{% zb4CpnGNHwd#V`4-S3GxTO&k9 z*5>p-qY+SEV=Z`)hl!g(Fil5R{4|e|QsLJ$H&wDP+lfg_95dkhSFH=x400Q^2!W$= z7$^~lr=5F&)`vHf!KkqSmb8nwc8Hw7CN3WqXBJVPx4i(Xuem3Rx<&^R*P(V<(HgR3 z<%++<5Am4~{CM+AX5#Nh!13W^T%nHK0kl*)0)E(=0vzoL{GhS|HoP^hLQG?0Y3ka{ z!b@R`;4cx>=c2j2bH`Os4Uh=`EKOz^PioTwvN$ur@jvpS7X~$LfUv?LEfdHYWs?3f zBy*^QAjH4}FTI$%aG_}NM-G5?I$&j=2{bzxc7D}rjq7--?1b`~SNiX0 zTc;b89YVE1kAYJYNFg06sp`XVI`&gM2$IlM=^md?4BDYjyF%ym ztfg;MpZ3>z)EtPgi32xcSz)n|PRr2Ey_-gyE4rtFmZu>TA=bQg*k$HcW@5EJKrD?yPO|>7c#C(-nRaVs+UXE$#alIppqkV{md~l~^_?%rf`U=D*MJ-LU!K$-l1%P=Er?e*MwV5T#v3?3>gQMs zU9G7GwY)|fNbAs^$UtE7yw*~KZIrLuS8aEj8B_ISjG)GPz7R0&{ zY1}JX>`d{F-@$&7%lK{hj(FN}k?@%ffs-OR`q%~v`?z*`0IBD}QA>aJABX>+Nt0#P z_Rx`-Zof$IP*aZf6cQ%Hj^3#HcnN&Xl=hPC&zr}R%b~{`;*xdC68>EPBg85w6F3wkRmgHDsS*^D|JHTe6#%=`aO!~W9cC4}!2{IZ5B*#xCYC=C*)nP2?u zhmLyLciy!f&O*-5a0f6}4StgUZ=U_XpJ0}ti`KacMjqfw>pj8)GL=^rz;~dHz)FD3 zKWKfr7#QA@P-*cQ%IVkuJS0g{5Z-k#(h?QO0iRn>NgW!bU(URGWCsgIg5EzAyyTk@ z%Jv4*WkB2oQrqfTJ))OY`5PnMHy@(O;9k&W>`(@bn&1=}^7s0=NFojDUev)|PXwFm z&D9dFsOlO5TncC)5LtDx(c5#^tS`?P5xe{FVC%=h-n;)ul9CmgeDt1%ItqkOhc356 zQ-u2)27ihp?rkiG#R%sS54QILE@S&MqM&#a9_K^H!y`)lQxzI3-V7=;Fu-{WeRV;S zxBO(!AD9(WS|-!}U4wt-f#{t-AN{fLOEFb=bw$9}U=+^o2Zgx;am)P{?<05OC)c-K z&?N{THQXs|xi5CoHYh}uLXGH6Y;boyLw0AkWFr_%fr}s%R*y_O5YOoS$j*2cifJ+x z{)3TMR==_6wB#GPTMsKpB%uPOifAyP!H8r~P=A9LQ6zZ@4mX|uW!?vn-D!k|qWF&7 z`h7A7sXx;;jQklw`pcFXebdyU#6$+j(>VS|#p`qIFWO7mCZKU!HNsi!v$!8qJB;L) zD`tEDgO5jsk)sh+UVJ$LxrDk7#2W?r?Z1F`_x|apNTsf7Ku16@>J-2{Q|?-@r*f7V znoXKE|8eAV?xzJu{wC3p&rwwp0qZ`hkL69(S2~B6Dwd&f=)^lMv7=N#5hX@WNOVzW zV;KxGLMc}$mMozLQUeuysV=0IIP6*h+C4qaM>My3r6K&3XhF|J;wQ1MMKVw}GTa)K zRH!@$?@fbFfAdza$=2qpwO}V9Y|q& zGXSvfNXms_ZU7s_Qv7lZ_KKemh0|FFpjroLGEkXqH)exjXO1B)C7KKH(18$skwaXOm0>$b7T#w}Fp-|=Jp3jbEw#HBgVKSV&XG_duvnq@ zy51o3M2)qB4E%C%4M;4tj8g4pZY&dfP%;HB`tiRBKjBUXAKnWE?+wg0;lo7VZhey7 zI{+7yrU0f4KIy|GdRWZ)&jXI`t!H=$TL}34C-D_y)a zIs5x9=o5p={n)!yL&f!4|k&%jIc|<1ekQ^Tg@d8w4`Yva$)9iXcq-E zGGIGUQ8F?P%Qi2=v)u_3+S9TyV#ivkAM-#n^ZK;HWp)0Elnj&kq00J8rpmWNK*O&t zg4s#FZIGtLYvVnb%+s9b7>E=0uTWL|5Cs7d<;n@5hxyqv4kP@_!2j>S<&QBT`YQT@ z3v9?x;qu1jJ0)+fiz6~%*r6S42?Ok zPkwy%%aF%)6IqvEp0P6kEiUytI*9yNVZzST=BP41-1Spy8MbH1LD2C)u*s!ax3~qt z;l1M%DN_HPVP5!AQ*8aE#~_@kpIl0H0&`SHwrQscbdx}&NTrjAkn^$va~?q<9Jqa@ z9RF8s?;X|T+O_)z5O4vC1w=uL1?fc>MLLLrfE4M{A|ldJx|GlqQ4vt8g7i)Zy@#SG zqV$e*rH9^oIrBlkGu|`CH_qN;k8iJk-nCvr5|Zb+?|IL8UB9ad3o8)sTdBoQB!+}m z1YHF?pE0Hz+$-K8!_@mUTtfmL5GKf6=omo?2`pQ%*&y5(l0~KI2L3jVtVb| zOn*s^)56dopsI`y4NByw_B413FNQsmf_O%tj+it4?}_Bq(yv%E?}_z*c1?PF4_@tX zG=;>OBBrH**8=Kilyo#!FZbJrub`y+NIwKU`fwaBllBGZR6Q|SQ+u|m!6yqLhq;hQ z48@2?vVF>vR+;|A1f`7dop4;ck z{{Amm_VQ87L@6ZlR+B-*J{eI(t|Pgm&`j;-ZPdnRfqEZx`Ob%Hwf`?!u2mBWQgE-y zRuOQlfx3}Nru0iq)EvBpw~l}&)2j<&9OV!Q&pzQe)7_*Zv8D;N<`--5i~;@a(@^G6 zS+BG8UKqTU*4^VGV<%t|e~CyO74|vXCNA0!Lfhj5@BUvE#v7IQ7QKLPu0N4wCBK2U zfaRz`um8Z)(Lh@dBxx1mLmwi+79bRDMIxOLciRe$c-)hkf3gi1Fc69WW+jUvU)LDa z8(_6GUmv9^X+&fj0NKM9-N9(K{|mtdC7At!H6!q(tIl*zT8VM4O=vR?6WSEiknYuz zO$PLd%{2^;O| z#I$!1T<~SfJ_H$s1_p~xvkUXyTK;z|F@V>;ck@p=Laguhi(*%FTh+|jsfXqOm6;zq zifV1VA<^^^{%D5e|2gfuoUyhS)Xre?xu>%z;XM1(<5eSkYA0H7aXHZ&Y$!Lh5^1|= z?_wNQ#%VYF4jV7Um{UFp%G^`t%FBHK2)DjqFUXem1lDO$y^dl+ye3f*gdRLW;%t?| zf~VIhrG)QQqb)*oIAH5lRfu8ve)wvyN#9RjxvG%DQ-6gmL)oXm_NI*jXWCu5pu^Y* zb^1YpXzt0ouY%m(S#TT%8qnUn;6c*>_kp>XroLyZ4EZ>TeOEm-G|MYh)LA}O$T-2G zUF^1Ii60A5vK+iG3KZZF_hbvsLH@%?1DmzZqU7=nZn=AIHdwcorT$DKSG`QC?{NaH zYnM@Bp*NoTt)N=v()H`$FxU$Q{sB`3Qam!+6&IG5!MpjjUE}<+?=GsXA-7%GoP*m$hia7S@^PFqHRRVvzA8Fr&8ZP@M zgB?-4)=_&MeWc(Q@L7P6U7GqA{k4a;VM}2LHMxVJ5bCWq(ivf8Eax~)=z*~B zhKJV>^fHL~6Sn8I{##8ty__7AG&Y`l9vO8jhfJI3ma9A0Cs+P>z0C--rX^fy)x*1SL|Y(`*pjpl77OTb zMWU-eLN=i@Y-r_;;;7_%Wz0vNmcM)1=fG!IC@Lr@xDEluKfwoThD5SKr-(N;)E1HP z3J-g9Nc=#WRl)ilb0A!SpwR8I9R?MUzJu4g9XC$0#X>O_2Me~hB48=F5!@LMh(gK! z$}^RLrt0OK1L7uF-PQjFw!dRhJBr4FUJ6 z7WXFY(&5Qw1O(e}fB&&k8v3$7*VzcI{}LK-_|`6_+kgHm^i(!r$%!_6W-UM~cY5qF z^6E8RVZe)Z7-B--P1DZDL#*Msv9dsT5lcMtpCgl&sc7kixOddDe@1DFY5p>|epe8? z)-KD?Kx7&63Y>-~3b`Px>kk*bma=p0Vd~(#bnNxReb0ye2{ref&hK?5@K97Z7gd4w zX>B$|tI|JC@u$7kLiCm%$oq20K^~!*`y`F@H*Z7f{%8_QRc59k_~Tlmxc}2vO)tOD zv9q?|_XkwIz6$#8UP!^4jd2-z1H^N?LWRt?f?9kc3co&d{4Ks z<63VN|Lfm|Hp=3Z;NtsBD`{y4R8^^|Dn&bVoM3iC1L)8ERX|(h!Hkj*ij**AU7Irx zc}8vR0}=WDX#Px=51X30@eKY)XL>!&WcMM+)*^Y=B~FtJjb&XY|1{hI_LZ zfhq_Y*F5)Nfb#=r8tznf7g{mjTABkPm`d3>@YWfFEGT=z&}R#r|KPdLKX*()5}R|a z5k{NkEJh1!n(9gS(QKs9%Rd3S?Cxufq6?4V&F5uivd4opo1?yT=35usU_GRZKTw8Y zHa*7*fY#(FjJMRydvL32lHRMs3d1Un@_sZCdq6|FJSXPpXeyXs`xIE>9DkwYpUe0P zmNY#(N*<#bV4)g#aEN_pxPJh#;9u6%{{>K*M^6|Cw{xZ7JZEc=WyXM#Uitp;S_Glf z^f9VD%{pLTgq}!jYoChyE z^sxyvmHrVw(mIooBQ2&RylhP3CM!ZE=+$} zg3`RUI+22ZJ18$jGZh~GMGI1-ej4ogE-!ogzognl@?Hlsi=G=7Oy=>tUydGp6CY{F1v|34VfU8Cd~YcoO-I1efc-EzEyHeAm*Z z6eE^VZyLVhW35(^Hw6laLDlWU)1r5MWWX@QsQp=C+izU(sN$E@ffE`?1`^9IgY*qK zdnvB{_gr#$UZS`koE*##xi5n)fHv*_pG|%%{vdHvVw(?H_0rW~>QM}rcptQI%)lmL zXM+TG$?Sqn-v&JQNVuN!=BoQQZic|4<3#c5kbibqrz0wt=vbI_a$(DWsXZuwinsYE z>9*0mtvlKmDzC6Zx&L~#>9J>r>Cl%aE;YA4xFLdWA%c+3#^ihrJ)A?-GAIfb{@AUF zDX)Qpa^_{ob2tfeoBo20^2vtV*Ow;`f83zee)F?OS^=6On6PcMdJXi_3o8x|M4dB$ zBo9DNg)oRPMbdBeeU|m|9{BBi=uplkXZCn9KoO<`B%WCig69Kdrr`xOGgAq1ddq*B z4Zd&H-HpT>S@{ONlHM1DMywHRq);@K^nwR_JhiKO0}hV^R;ZtJ<&K1rZ(tF38l6a8 z)6vo@>p_x62Hj?G^Mc;x;Sz79M)7S}L@7QOnI!G$hs| zSyP=1iG9J7kjvCo?d9ciX%+l;qY)Eevomv{O*d{0)_6V2m(oAks5ikbmoXw znUU21E*os_PU<>dz?$-*92o{b8N3@tfIbSz>M#v^9bk~yH4IEF=Ql2RW8^ElOXu0v z$PHDA*YN*KUAKVgsIscF)|DYAaRq*A8bKgcxPwa(Ayv&YUqzWWgvA^H>V9^;|FMe> z8__HY+(eV15E^+zqDWBM4i<4cll{-cKnm-5sd$WNmhuC8gpQnKtR^@0ebwn@)yl3` zv17nJ_y1@uq{ITSnsX9VoEj-ls_Jj8-dvWpgVQ+|{54RlDcV-7b?YzDct<3&7>x_Q z{>9U%^#i#P(w}@__oe2>zVV>>ioXzhcyB!D>YOd)9{`}{4E+sKL^Xusj3VBOnr19( zrw5~Iy9JmM<_UK6NyVv53|+g$HwPd8Ubo0HqY;OOzO86eA$4EmH>Mp-Ewj4>W0Bx_ zFBA@~+;kl4>?1BC-fB&A0{lTkFuQb5Zm7qTDw4pF_UI}gyYFLxI1?kpmQT^ytrXARNbTET<&?gAI}Y$Y55J4Kym z&#DrofG#F~r+*w{?0{ijL+zgRoejF~ zmpkQlJd=?YcSK})#l=+L^yU*{syu6K~PNJWNV?W-Qntuf?3Iy2(>) zxA5Ekby~3?zhS4m(R3jxw5+}U-qntl58dcl-OGgQ>|EK^1@vr@d;<{=`uk}pcB!F# zfQcn~TpxZE6)tP2N(M%wfBsD#^_h6`AIEj)$ieVA^==iS&#UaYh#3q294GUiD(=#T zwe32igDIc z{)^e@BR_D-<)uh<5?^)EHmR=YhJoE)c6ZAlH16rDu|hmpn^wD=g#yl3|3OA4ob+3= zV`1J)ByQcMdCw_b3O71r{Yk7s)8F8J66#xjnM0Zc^dES=>6%s|e`B7cxPlb4352IF zF(;OgHpOg0{%VR|#Z$ZgmW=)@@qdnt{_cNGMu(G_5pF@Rf$%laWnpMb+Hm;uJ+qi` zqXxwC|8oHw`QPcerzS7Du8qLqS4AtZ>_9BJxx@8$nVI_dk@b?969T#Ya~*C z^puD<)wv5q9;Oe;TI`NEet$E1&efRapJ=ln?pMA+C)mtR#QZ)Inp<(@!cn&85|{bh z*Pmx?I9IkTpj$dhY#k!!-SLmh7xp|u%C*)soop)iW(ZyqTHEyL#qE<)$Lm5vK-{rU z(&99*aduWo=w%fwWU~+Xv8(U36dU5evkg0*Yg&(cMEzR82g~U-N+g8cCOUs1Qci6< zmaO%5JH=D{Pp$N~_>GeeGlV76{_H8yZWp+dpi(Ch&6FZ=aW2l+dM=>-RoEE~ z%5kG3nbMp6!~4ZCiv%4%xrEbgdT|v*>8#ig7uOh@80LE zk0v?|Cdqws@4aH@9F=b-BM|qkFp6t$KYQE>W*@9-@zbR4a^%`tiM!Tzdj8MS>!ur`yinNykW)oMx( z-$AUUyiy1CizxdbLzlYSBENe34BDPLR}B%<^XLb2G<}{AYQGT{7t=J8&00C6c{+h^xPw!$+mfybWzJ%7HIXj)GM(URrev(wwk~0_YZN|?r`Los}%%<|@ z3K0E!UtDTRJ3l*1`mOxEQ1lD+Osr`@l<=Dte|4SCTZZFqJ+Cb@i+IP)&Ct}yoN_Ku z#zIJ1o7c<2$W`KHi&Jse#kptew;te1f^t9zIE=&Lgp8ESPm~mnr*3{UD^Vz9EYAxm zR;Ifl?7O+u8E5qms?+~LkT~tK^Z^H=8@EAp;}GU-Bban`Lr1wfBZabQm?7n)`MImA z`UG-V{pOaA+|Om-_(?d+5nstR_PS1gK5}35l>UXbtDPQ+-Fj|`rZXae4ccjs1PW$) z_DDtqY^;ilLiIH2-}tVTs!l`ks4f1EeQ**SVH=xVYOMJ?xNIpp4@>v1##EK|k!43; z8ua8e7oB0Xvw76I85k`Ef4u8)z(yG4_a{Cwb}}vwit#-8dc5yAr3pyIBMy= z*T(L)KUtMRF3DBQ?y`mG3#?SAQL&UmcQLCg1X6LYhM}bQ-a~mFqZzhyjyC7_)PmFL z?UYGC1==H%33j3r8P`-JcnvN0EZdJ!t8UHGM6%0ERwh}^qNuJYlB(exvm_dwQ-nRH zgMStpThMuhpM6a>{jE$Gu}Qp_Ah8<86@OZD#2%+X z#~OEjKa-EM^j3+ALK-o9BDBUNK)X|=m;R2tTxW)UOG8Sy)_R zbN1y!H(uc9Q&ysV@6sM1Jr#3QSw-;R3jFZq;X`E?WjypQSWdA0)4jz7M1AO#8SOw~&IZSi+6Q9CAd`|SQOGnnCXiMMIj4lX8~Zp+XMRP(rnlho_#1;d*`%`E1s* zc`WSbn#q@)&Di@R0N`|K1+hS-(zXP_}d`+RVRIXAI&0xE*mKxe7ok^ zuXlRnF}#$%D^oCkw5PxL7TiNlNJMp=rr59jxyN2J$Kh}%3|$(vc4LSiGh_;WYTZTH zg!Mcycwzo5B*^DC^lU=h5&cLq35tKSNFewLP7;|VW#0Zgyw-ze{>GV9x5j#XuxsRp z3@h73iU>$knL%d$YV9%&9|SDa&(6fN>>_h8{bFl7I2Sr2llSm&rJ3Cys-7beeCYX* z%XUcIGZN1FzE7ru*>&t4e*`^@$dLi-0GO7o*@Qu}71`50!PQu!>spQt3}(BAXpq+~k+y)w z*aIxfHN}Qsz<7tSYSfr6Lk_)ZN-{hDn+*D^DVHM55bfYL9C~xtlxo95 zbV(TNOnzaPthbf2Sc7NYtkf>g3O(#l)*Q{Und1zX^La8rjZ-Zg&~?woEKJ*_xMc?R z{Ahwx4c#}<^fN|RR6d$xL|`6@dm%aRU(8_}Szj33i@px#)lo$C2kbJ7Q6wS)>Oy?X z*_((qU{2&L0EyTM@mr3|@uy$6Pg0}2eZp3JJW4NU-o%Nk*#O_R=vSeDmh6Mvj8B`; zsxH*~r8}d~Nw!I`Nqe-;+emOzFNnBrG)S!3GMe_b>W6D78a!nY!&>yOCn}fgxxT!2 zfMF(Z@HwIRAZr)pyRO)-m+w{|xDcdr5p*XfzNu3*Za{yOe`5BCbe+Oy4IISi5UDbM{(TyExLFw23VZ)=MKEFZBrLvPnxrRi^cATD}Ax{kHW9 zK8jA{W7OxVTp#V!Q03XbmY`BCXYQuTA>Rpow`m!7nd)RSJ>1=6wBCJcb5AT5v+YRN zdQo~sKPa4)GS*$8y-GkmANB7?rRY6v>gn=JN(oE7nAxV& zI?vKlaV1qhZpOf+Vc3h8`K^S#sM zi|sc061UZ8mX*4z>a`u(lyqO1t2@6g8#}PVJ~73W-t|Lh%TC?@G+5=Q*3pthL8`^W3 zl1#a#-fS(MvYIdntSF!7y}=VBY%7lrbI-$C=0h|mpLS

pbI^ntSHowirir*2dI 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 J`^J6W{|E65OeX*U diff --git a/beginner_source/onnx/export_simple_model_to_onnx_tutorial.py b/beginner_source/onnx/export_simple_model_to_onnx_tutorial.py index 760c40ab43c..5a8ac9c538a 100644 --- a/beginner_source/onnx/export_simple_model_to_onnx_tutorial.py +++ b/beginner_source/onnx/export_simple_model_to_onnx_tutorial.py @@ -7,7 +7,7 @@ Export a PyTorch model to ONNX ============================== -**Author**: `Thiago Crepaldi `_ +**Author**: `Ti-Tai Wang `_ and `Xavier Dupré `_ .. note:: As of PyTorch 2.1, there are two versions of ONNX Exporter. @@ -127,7 +127,7 @@ def forward(self, x): # Once Netron is open, we can drag and drop our ``my_image_classifier.onnx`` file into the browser or select it after # clicking the **Open model** button. # -# .. image:: ../../_static/img/onnx/image_clossifier_onnx_modelon_netron_web_ui.png +# .. image:: ../../_static/img/onnx/image_classifier_onnx_model_on_netron_web_ui.png # :width: 50% # # @@ -155,7 +155,7 @@ def forward(self, x): import onnxruntime -onnx_input = onnx_program.adapt_torch_inputs_to_onnx(torch_input) +onnx_input = [torch_input] print(f"Input length: {len(onnx_input)}") print(f"Sample input: {onnx_input}") @@ -166,7 +166,8 @@ def to_numpy(tensor): onnxruntime_input = {k.name: to_numpy(v) for k, v in zip(ort_session.get_inputs(), onnx_input)} -onnxruntime_outputs = ort_session.run(None, onnxruntime_input) +# onnxruntime returns a list of outputs +onnxruntime_outputs = ort_session.run(None, onnxruntime_input)[0] #################################################################### # 7. Compare the PyTorch results with the ones from the ONNX Runtime @@ -179,7 +180,6 @@ def to_numpy(tensor): # Before comparing the results, we need to convert the PyTorch's output to match ONNX's format. torch_outputs = torch_model(torch_input) -torch_outputs = onnx_program.adapt_torch_outputs_to_onnx(torch_outputs) assert len(torch_outputs) == len(onnxruntime_outputs) for torch_output, onnxruntime_output in zip(torch_outputs, onnxruntime_outputs): diff --git a/beginner_source/onnx/intro_onnx.py b/beginner_source/onnx/intro_onnx.py index ec625ec78ff..194f971261c 100644 --- a/beginner_source/onnx/intro_onnx.py +++ b/beginner_source/onnx/intro_onnx.py @@ -7,7 +7,7 @@ ==================== Authors: -`Thiago Crepaldi `_, +`Ti-Tai Wang `_ and `Xavier Dupré `_ `Open Neural Network eXchange (ONNX) `_ is an open standard format for representing machine learning models. The ``torch.onnx`` module provides APIs to diff --git a/beginner_source/onnx/onnx_registry_tutorial.py b/beginner_source/onnx/onnx_registry_tutorial.py index 0f64ba9c4d4..56c1d0c99a3 100644 --- a/beginner_source/onnx/onnx_registry_tutorial.py +++ b/beginner_source/onnx/onnx_registry_tutorial.py @@ -31,148 +31,15 @@ # # In this tutorial, we will cover three scenarios that require extending the ONNX registry with custom operators: # -# * Unsupported ATen operators # * Custom operators with existing ONNX Runtime support # * Custom operators without ONNX Runtime support # -# Unsupported ATen operators -# -------------------------- -# -# Although the ONNX exporter team does their best efforts to support all ATen operators, some of them -# might not be supported yet. In this section, we will demonstrate how you can add -# unsupported ATen operators to the ONNX Registry. -# -# .. note:: -# The steps to implement unsupported ATen operators are the same to replace the implementation of an existing -# ATen operator with a custom implementation. -# Because we don't actually have an unsupported ATen operator to use in this tutorial, we are going to leverage -# this and replace the implementation of ``aten::add.Tensor`` with a custom implementation the same way we would -# if the operator was not present in the ONNX Registry. -# -# When a model cannot be exported to ONNX due to an unsupported operator, the ONNX exporter will show an error message -# similar to: -# -# .. code-block:: python -# -# RuntimeErrorWithDiagnostic: Unsupported FX nodes: {'call_function': ['aten.add.Tensor']}. -# -# The error message indicates that the fully qualified name of unsupported ATen operator is ``aten::add.Tensor``. -# The fully qualified name of an operator is composed of the namespace, operator name, and overload following -# the format ``namespace::operator_name.overload``. -# -# To add support for an unsupported ATen operator or to replace the implementation for an existing one, we need: -# -# * The fully qualified name of the ATen operator (e.g. ``aten::add.Tensor``). -# This information is always present in the error message as show above. -# * The implementation of the operator using `ONNX Script `__. -# ONNX Script is a prerequisite for this tutorial. Please make sure you have read the -# `ONNX Script tutorial `_ -# before proceeding. -# -# Because ``aten::add.Tensor`` is already supported by the ONNX Registry, we will demonstrate how to replace it with a -# custom implementation, but keep in mind that the same steps apply to support new unsupported ATen operators. -# -# This is possible because the :class:`OnnxRegistry` allows users to override an operator registration. -# We will override the registration of ``aten::add.Tensor`` with our custom implementation and verify it exists. -# import torch import onnxruntime import onnxscript from onnxscript import opset18 # opset 18 is the latest (and only) supported version for now -class Model(torch.nn.Module): - def forward(self, input_x, input_y): - return torch.ops.aten.add(input_x, input_y) # generates a aten::add.Tensor node - -input_add_x = torch.randn(3, 4) -input_add_y = torch.randn(3, 4) -aten_add_model = Model() - - -# Now we create a ONNX Script function that implements ``aten::add.Tensor``. -# The function name (e.g. ``custom_aten_add``) is displayed in the ONNX graph, so we recommend to use intuitive names. -custom_aten = onnxscript.values.Opset(domain="custom.aten", version=1) - -# NOTE: The function signature must match the signature of the unsupported ATen operator. -# https://github.com/pytorch/pytorch/blob/main/aten/src/ATen/native/native_functions.yaml -# NOTE: All attributes must be annotated with type hints. -@onnxscript.script(custom_aten) -def custom_aten_add(input_x, input_y, alpha: float = 1.0): - input_y = opset18.Mul(input_y, alpha) - return opset18.Add(input_x, input_y) - - -# Now we have everything we need to support unsupported ATen operators. -# Let's register the ``custom_aten_add`` function to ONNX registry, and export the model to ONNX again. -onnx_registry = torch.onnx.OnnxRegistry() -onnx_registry.register_op( - namespace="aten", op_name="add", overload="Tensor", function=custom_aten_add - ) -print(f"aten::add.Tensor is supported by ONNX registry: \ - {onnx_registry.is_registered_op(namespace='aten', op_name='add', overload='Tensor')}" - ) -export_options = torch.onnx.ExportOptions(onnx_registry=onnx_registry) -onnx_program = torch.onnx.dynamo_export( - aten_add_model, input_add_x, input_add_y, export_options=export_options - ) - -###################################################################### -# Now let's inspect the model and verify the model has a ``custom_aten_add`` instead of ``aten::add.Tensor``. -# The graph has one graph node for ``custom_aten_add``, and inside of it there are four function nodes, one for each -# operator, and one for constant attribute. -# - -# graph node domain is the custom domain we registered -assert onnx_program.model_proto.graph.node[0].domain == "custom.aten" -assert len(onnx_program.model_proto.graph.node) == 1 -# graph node name is the function name -assert onnx_program.model_proto.graph.node[0].op_type == "custom_aten_add" -# function node domain is empty because we use standard ONNX operators -assert {node.domain for node in onnx_program.model_proto.functions[0].node} == {""} -# function node name is the standard ONNX operator name -assert {node.op_type for node in onnx_program.model_proto.functions[0].node} == {"Add", "Mul", "Constant"} - - -###################################################################### -# This is how ``custom_aten_add_model`` looks in the ONNX graph using Netron: -# -# .. image:: /_static/img/onnx/custom_aten_add_model.png -# :width: 70% -# :align: center -# -# Inside the ``custom_aten_add`` function, we can see the three ONNX nodes we -# used in the function (``CastLike``, ``Add``, and ``Mul``), and one ``Constant`` attribute: -# -# .. image:: /_static/img/onnx/custom_aten_add_function.png -# :width: 70% -# :align: center -# -# This was all that we needed to register the new ATen operator into the ONNX Registry. -# As an additional step, we can use ONNX Runtime to run the model, and compare the results with PyTorch. -# - - -# Use ONNX Runtime to run the model, and compare the results with PyTorch -onnx_program.save("./custom_add_model.onnx") -ort_session = onnxruntime.InferenceSession( - "./custom_add_model.onnx", providers=['CPUExecutionProvider'] - ) - -def to_numpy(tensor): - return tensor.detach().cpu().numpy() if tensor.requires_grad else tensor.cpu().numpy() - -onnx_input = onnx_program.adapt_torch_inputs_to_onnx(input_add_x, input_add_y) -onnxruntime_input = {k.name: to_numpy(v) for k, v in zip(ort_session.get_inputs(), onnx_input)} -onnxruntime_outputs = ort_session.run(None, onnxruntime_input) - -torch_outputs = aten_add_model(input_add_x, input_add_y) -torch_outputs = onnx_program.adapt_torch_outputs_to_onnx(torch_outputs) - -assert len(torch_outputs) == len(onnxruntime_outputs) -for torch_output, onnxruntime_output in zip(torch_outputs, onnxruntime_outputs): - torch.testing.assert_close(torch_output, torch.tensor(onnxruntime_output)) - ###################################################################### # Custom operators with existing ONNX Runtime support @@ -262,12 +129,11 @@ def custom_aten_gelu(input_x, approximate: str = "none"): def to_numpy(tensor): return tensor.detach().cpu().numpy() if tensor.requires_grad else tensor.cpu().numpy() -onnx_input = onnx_program.adapt_torch_inputs_to_onnx(input_gelu_x) +onnx_input = [input_gelu_x] onnxruntime_input = {k.name: to_numpy(v) for k, v in zip(ort_session.get_inputs(), onnx_input)} -onnxruntime_outputs = ort_session.run(None, onnxruntime_input) +onnxruntime_outputs = ort_session.run(None, onnxruntime_input)[0] torch_outputs = aten_gelu_model(input_gelu_x) -torch_outputs = onnx_program.adapt_torch_outputs_to_onnx(torch_outputs) assert len(torch_outputs) == len(onnxruntime_outputs) for torch_output, onnxruntime_output in zip(torch_outputs, onnxruntime_outputs): @@ -369,25 +235,17 @@ def custom_addandround(input_x): # assert onnx_program.model_proto.graph.node[0].domain == "test.customop" -assert onnx_program.model_proto.graph.node[0].op_type == "custom_addandround" -assert onnx_program.model_proto.functions[0].node[0].domain == "test.customop" -assert onnx_program.model_proto.functions[0].node[0].op_type == "CustomOpOne" -assert onnx_program.model_proto.functions[0].node[1].domain == "test.customop" -assert onnx_program.model_proto.functions[0].node[1].op_type == "CustomOpTwo" +assert onnx_program.model_proto.graph.node[0].op_type == "CustomOpOne" +assert onnx_program.model_proto.graph.node[1].domain == "test.customop" +assert onnx_program.model_proto.graph.node[1].op_type == "CustomOpTwo" ###################################################################### -# This is how ``custom_addandround_model`` ONNX graph looks using Netron: -# -# .. image:: /_static/img/onnx/custom_addandround_model.png -# :width: 70% -# :align: center -# -# Inside the ``custom_addandround`` function, we can see the two custom operators we -# used in the function (``CustomOpOne``, and ``CustomOpTwo``), and they are from module -# ``test.customop``: +# This is how ``custom_addandround_model`` ONNX graph looks using Netron. +# We can see the two custom operators we used in the function (``CustomOpOne``, and ``CustomOpTwo``), +# and they are from module ``test.customop``: # -# .. image:: /_static/img/onnx/custom_addandround_function.png +# .. image:: /_static/img/onnx/custom_addandround.png # # Custom Ops Registration in ONNX Runtime # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 8604b4be1e044b0cd48722ceca5565fd64b9d205 Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Mon, 27 Jan 2025 14:27:52 -0800 Subject: [PATCH 040/347] Cherry-pick: Fix torchrl scripts for PT 2.6 TorchRL>=0.6 (#3199) (#3266) Fixes #3195 Fixing TorchRL scripts for Pytorch 2.6 release Co-authored-by: Vincent Moens --- .jenkins/validate_tutorials_built.py | 1 - advanced_source/coding_ddpg.py | 2 +- advanced_source/pendulum.py | 2 +- intermediate_source/dqn_with_rnn_tutorial.py | 2 +- intermediate_source/reinforcement_ppo.py | 4 ++-- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.jenkins/validate_tutorials_built.py b/.jenkins/validate_tutorials_built.py index 21fab4eabfd..7d87331481a 100644 --- a/.jenkins/validate_tutorials_built.py +++ b/.jenkins/validate_tutorials_built.py @@ -51,7 +51,6 @@ "intermediate_source/text_to_speech_with_torchaudio", "intermediate_source/tensorboard_profiler_tutorial", # reenable after 2.0 release. "intermediate_source/torch_export_tutorial", # reenable after 2940 is fixed. - "advanced_source/pendulum", ] def tutorial_source_dirs() -> List[Path]: diff --git a/advanced_source/coding_ddpg.py b/advanced_source/coding_ddpg.py index c634932971b..3ee4ddc39bd 100644 --- a/advanced_source/coding_ddpg.py +++ b/advanced_source/coding_ddpg.py @@ -893,7 +893,7 @@ def make_recorder(actor_model_explore, transform_state_dict, record_interval): record_frames=1000, policy_exploration=actor_model_explore, environment=environment, - exploration_type=ExplorationType.MEAN, + exploration_type=ExplorationType.DETERMINISTIC, record_interval=record_interval, ) return recorder_obj diff --git a/advanced_source/pendulum.py b/advanced_source/pendulum.py index fae3635de1c..3084fe8312b 100644 --- a/advanced_source/pendulum.py +++ b/advanced_source/pendulum.py @@ -604,7 +604,7 @@ def __init__(self, td_params=None, seed=None, device="cpu"): env, # ``Unsqueeze`` the observations that we will concatenate UnsqueezeTransform( - unsqueeze_dim=-1, + dim=-1, in_keys=["th", "thdot"], in_keys_inv=["th", "thdot"], ), diff --git a/intermediate_source/dqn_with_rnn_tutorial.py b/intermediate_source/dqn_with_rnn_tutorial.py index 6ea09559392..bcc484f0a00 100644 --- a/intermediate_source/dqn_with_rnn_tutorial.py +++ b/intermediate_source/dqn_with_rnn_tutorial.py @@ -433,7 +433,7 @@ exploration_module.step(data.numel()) updater.step() - with set_exploration_type(ExplorationType.MODE), torch.no_grad(): + with set_exploration_type(ExplorationType.DETERMINISTIC), torch.no_grad(): rollout = env.rollout(10000, stoch_policy) traj_lens.append(rollout.get(("next", "step_count")).max().item()) diff --git a/intermediate_source/reinforcement_ppo.py b/intermediate_source/reinforcement_ppo.py index ec2dc0a488d..b25a6f8c8ac 100644 --- a/intermediate_source/reinforcement_ppo.py +++ b/intermediate_source/reinforcement_ppo.py @@ -419,8 +419,8 @@ in_keys=["loc", "scale"], distribution_class=TanhNormal, distribution_kwargs={ - "min": env.action_spec.space.low, - "max": env.action_spec.space.high, + "low": env.action_spec.space.low, + "high": env.action_spec.space.high, }, return_log_prob=True, # we'll need the log-prob for the numerator of the importance weights From 15f7cb348f1ae8fc02567e66cbafcc8433a56fb4 Mon Sep 17 00:00:00 2001 From: Angela Yi Date: Mon, 27 Jan 2025 15:47:40 -0800 Subject: [PATCH 041/347] Fix export tutorial (#3130) * Fix export tutorial --------- Co-authored-by: Svetlana Karslioglu --- .jenkins/validate_tutorials_built.py | 1 - intermediate_source/torch_export_tutorial.py | 366 +++++++++---------- 2 files changed, 165 insertions(+), 202 deletions(-) diff --git a/.jenkins/validate_tutorials_built.py b/.jenkins/validate_tutorials_built.py index 7d87331481a..ed790a18872 100644 --- a/.jenkins/validate_tutorials_built.py +++ b/.jenkins/validate_tutorials_built.py @@ -50,7 +50,6 @@ "intermediate_source/flask_rest_api_tutorial", "intermediate_source/text_to_speech_with_torchaudio", "intermediate_source/tensorboard_profiler_tutorial", # reenable after 2.0 release. - "intermediate_source/torch_export_tutorial", # reenable after 2940 is fixed. ] def tutorial_source_dirs() -> List[Path]: diff --git a/intermediate_source/torch_export_tutorial.py b/intermediate_source/torch_export_tutorial.py index c992eefa9fc..3ca6d09a52f 100644 --- a/intermediate_source/torch_export_tutorial.py +++ b/intermediate_source/torch_export_tutorial.py @@ -45,17 +45,18 @@ # .. code-block:: python # # export( -# f: Callable, +# mod: torch.nn.Module, # args: Tuple[Any, ...], # kwargs: Optional[Dict[str, Any]] = None, # *, # dynamic_shapes: Optional[Dict[str, Dict[int, Dim]]] = None # ) -> ExportedProgram # -# ``torch.export.export()`` traces the tensor computation graph from calling ``f(*args, **kwargs)`` +# ``torch.export.export()`` traces the tensor computation graph from calling ``mod(*args, **kwargs)`` # and wraps it in an ``ExportedProgram``, which can be serialized or executed later with -# different inputs. Note that while the output ``ExportedGraph`` is callable and can be -# called in the same way as the original input callable, it is not a ``torch.nn.Module``. +# different inputs. To execute the ``ExportedProgram`` we can call ``.module()`` +# on it to return a ``torch.nn.Module`` which is callable, just like the +# original program. # We will detail the ``dynamic_shapes`` argument later in the tutorial. import torch @@ -80,30 +81,15 @@ def forward(self, x, y): # # The ``graph`` attribute is an `FX graph `__ # traced from the function we exported, that is, the computation graph of all PyTorch operations. -# The FX graph has some important properties: +# The FX graph is in "ATen IR" meaning that it contains only "ATen-level" operations. # -# - The operations are "ATen-level" operations. -# - The graph is "functionalized", meaning that no operations are mutations. +# The ``graph_signature`` attribute gives a more detailed description of the +# input and output nodes in the exported graph, describing which ones are +# parameters, buffers, user inputs, or user outputs. # -# The ``graph_module`` attribute is the ``GraphModule`` that wraps the ``graph`` attribute -# so that it can be ran as a ``torch.nn.Module``. +# The ``range_constraints`` attributes will be covered later. print(exported_mod) -print(exported_mod.graph_module) - -###################################################################### -# The printed code shows that FX graph only contains ATen-level ops (such as ``torch.ops.aten``) -# and that mutations were removed. For example, the mutating op ``torch.nn.functional.relu(..., inplace=True)`` -# is represented in the printed code by ``torch.ops.aten.relu.default``, which does not mutate. -# Future uses of input to the original mutating ``relu`` op are replaced by the additional new output -# of the replacement non-mutating ``relu`` op. -# -# Other attributes of interest in ``ExportedProgram`` include: -# -# - ``graph_signature`` -- the inputs, outputs, parameters, buffers, etc. of the exported graph. -# - ``range_constraints`` -- constraints, covered later - -print(exported_mod.graph_signature) ###################################################################### # See the ``torch.export`` `documentation `__ @@ -163,32 +149,16 @@ def forward(self, x): except Exception: tb.print_exc() -###################################################################### -# - unsupported Python language features (e.g. throwing exceptions, match statements) - -class Bad4(torch.nn.Module): - def forward(self, x): - try: - x = x + 1 - raise RuntimeError("bad") - except: - x = x + 2 - return x - -try: - export(Bad4(), (torch.randn(3, 3),)) -except Exception: - tb.print_exc() ###################################################################### # Non-Strict Export # ----------------- # -# To trace the program, ``torch.export`` uses TorchDynamo, a byte code analysis -# engine, to symbolically analyze the Python code and build a graph based on the -# results. This analysis allows ``torch.export`` to provide stronger guarantees -# about safety, but not all Python code is supported, causing these graph -# breaks. +# To trace the program, ``torch.export`` uses TorchDynamo by default, a byte +# code analysis engine, to symbolically analyze the Python code and build a +# graph based on the results. This analysis allows ``torch.export`` to provide +# stronger guarantees about safety, but not all Python code is supported, +# causing these graph breaks. # # To address this issue, in PyTorch 2.3, we introduced a new mode of # exporting called non-strict mode, where we trace through the program using the @@ -197,16 +167,6 @@ def forward(self, x): # ``strict=False`` flag. # # Looking at some of the previous examples which resulted in graph breaks: -# -# - Accessing tensor data with ``.data`` now works correctly - -class Bad2(torch.nn.Module): - def forward(self, x): - x.data[0, 0] = 3 - return x - -bad2_nonstrict = export(Bad2(), (torch.randn(3, 3),), strict=False) -print(bad2_nonstrict.module()(torch.ones(3, 3))) ###################################################################### # - Calling unsupported functions (such as many built-in functions) traces @@ -223,22 +183,6 @@ def forward(self, x): print(bad3_nonstrict) print(bad3_nonstrict.module()(torch.ones(3, 3))) -###################################################################### -# - Unsupported Python language features (such as throwing exceptions, match -# statements) now also get traced through. - -class Bad4(torch.nn.Module): - def forward(self, x): - try: - x = x + 1 - raise RuntimeError("bad") - except: - x = x + 2 - return x - -bad4_nonstrict = export(Bad4(), (torch.randn(3, 3),), strict=False) -print(bad4_nonstrict.module()(torch.ones(3, 3))) - ###################################################################### # However, there are still some features that require rewrites to the original @@ -252,17 +196,16 @@ def forward(self, x): # But these need to be expressed using control flow ops. For example, # we can fix the control flow example above using the ``cond`` op, like so: -from functorch.experimental.control_flow import cond - class Bad1Fixed(torch.nn.Module): def forward(self, x): def true_fn(x): return torch.sin(x) def false_fn(x): return torch.cos(x) - return cond(x.sum() > 0, true_fn, false_fn, [x]) + return torch.cond(x.sum() > 0, true_fn, false_fn, [x]) exported_bad1_fixed = export(Bad1Fixed(), (torch.randn(3, 3),)) +print(exported_bad1_fixed) print(exported_bad1_fixed.module()(torch.ones(3, 3))) print(exported_bad1_fixed.module()(-torch.ones(3, 3))) @@ -280,25 +223,27 @@ def false_fn(x): # For more details about ``cond``, check out the `cond documentation `__. ###################################################################### -# .. -# [NOTE] map is not documented at the moment -# We can also use ``map``, which applies a function across the first dimension -# of the first tensor argument. -# -# from functorch.experimental.control_flow import map -# -# def map_example(xs): -# def map_fn(x, const): -# def true_fn(x): -# return x + const -# def false_fn(x): -# return x - const -# return control_flow.cond(x.sum() > 0, true_fn, false_fn, [x]) -# return control_flow.map(map_fn, xs, torch.tensor([2.0])) -# -# exported_map_example= export(map_example, (torch.randn(4, 3),)) -# inp = torch.cat((torch.ones(2, 3), -torch.ones(2, 3))) -# print(exported_map_example(inp)) +# We can also use ``map``, which applies a function across the first dimension +# of the first tensor argument. + +from torch._higher_order_ops.map import map as torch_map + +class MapModule(torch.nn.Module): + def forward(self, xs, y, z): + def body(x, y, z): + return x + y + z + + return torch_map(body, xs, y, z) + +inps = (torch.ones(6, 4), torch.tensor(5), torch.tensor(4)) +exported_map_example = export(MapModule(), inps) +print(exported_map_example) +print(exported_map_example.module()(*inps)) + +###################################################################### +# Other control flow ops include ``while_loop``, ``associative_scan``, and +# ``scan``. For more documentation on each operator, please refer to +# `this page `__. ###################################################################### # Constraints/Dynamic Shapes @@ -337,7 +282,10 @@ def forward( model = DynamicModel() ep = export(model, (w, x, y, z)) model(w, x, torch.randn(3, 4), torch.randn(12)) -ep.module()(w, x, torch.randn(3, 4), torch.randn(12)) +try: + ep.module()(w, x, torch.randn(3, 4), torch.randn(12)) +except Exception: + tb.print_exc() ###################################################################### # Basic concepts: symbols and guards @@ -466,7 +414,10 @@ def forward( # static guard is emitted on a dynamically-marked dimension: dynamic_shapes["w"] = (Dim.AUTO, Dim.DYNAMIC) -export(model, (w, x, y, z), dynamic_shapes=dynamic_shapes) +try: + export(model, (w, x, y, z), dynamic_shapes=dynamic_shapes) +except Exception: + tb.print_exc() ###################################################################### # Static guards also aren't always inherent to the model; they can also come from user specifications. In fact, a common pitfall leading to shape @@ -476,7 +427,10 @@ def forward( dynamic_shapes["w"] = (Dim.AUTO, Dim.AUTO) dynamic_shapes["x"] = (Dim.STATIC,) dynamic_shapes["y"] = (Dim.AUTO, Dim.DYNAMIC) -export(model, (w, x, y, z), dynamic_shapes=dynamic_shapes) +try: + export(model, (w, x, y, z), dynamic_shapes=dynamic_shapes) +except Exception: + tb.print_exc() ###################################################################### # Here you might ask why export "specializes", i.e. why we resolve this static/dynamic conflict by going with the static route. The answer is because @@ -494,7 +448,7 @@ def __init__(self): def forward(self, w, x, y, z): assert w.shape[0] <= 512 - torch._check(x.shape[0] >= 16) + torch._check(x.shape[0] >= 4) if w.shape[0] == x.shape[0] + 2: x0 = x + y x1 = self.l(w) @@ -510,8 +464,10 @@ def forward(self, w, x, y, z): "y": (Dim.AUTO, Dim.AUTO), "z": (Dim.AUTO,), } -ep = export(DynamicModel(), (w, x, y, z), dynamic_shapes=dynamic_shapes) -print(ep) +try: + ep = export(DynamicModel(), (w, x, y, z), dynamic_shapes=dynamic_shapes) +except Exception: + tb.print_exc() ###################################################################### # Each of these statements emits an additional guard, and the exported program shows the changes; ``s0`` is eliminated in favor of ``s2 + 2``, @@ -540,7 +496,10 @@ def forward(self, w, x, y, z): "input": (Dim.AUTO, Dim.STATIC), }, ) -ep.module()(torch.randn(2, 4)) +try: + ep.module()(torch.randn(2, 4)) +except Exception: + tb.print_exc() ###################################################################### # Named Dims @@ -594,14 +553,17 @@ def forward(self, x, y): return w + torch.ones(4) dx, dy, d1 = torch.export.dims("dx", "dy", "d1") -ep = export( - Foo(), - (torch.randn(6, 4), torch.randn(6, 4)), - dynamic_shapes={ - "x": (dx, d1), - "y": (dy, d1), - }, -) +try: + ep = export( + Foo(), + (torch.randn(6, 4), torch.randn(6, 4)), + dynamic_shapes={ + "x": (dx, d1), + "y": (dy, d1), + }, + ) +except Exception: + tb.print_exc() ###################################################################### # The expectation with suggested fixes is that the user can interactively copy-paste the changes into their dynamic shapes specification, and successfully export afterwards. @@ -743,7 +705,10 @@ def forward(self, x, y): torch.tensor(32), torch.randn(60), ) -export(Foo(), inps) +try: + export(Foo(), inps) +except Exception: + tb.print_exc() ###################################################################### # Here is a scenario where ``torch._check()`` insertion is required simply to prevent an operation from failing. The export call will fail with @@ -755,7 +720,7 @@ class Foo(torch.nn.Module): def forward(self, x, y): a = x.item() torch._check(a >= 0) - torch._check(a <= y.shape[0]) + torch._check(a < y.shape[0]) return y[a] inps = ( @@ -787,7 +752,10 @@ def forward(self, x, y): torch.tensor(32), torch.randn(60), ) -export(Foo(), inps, strict=False) +try: + export(Foo(), inps, strict=False) +except Exception: + tb.print_exc() ###################################################################### # For these errors, some basic options you have are: @@ -818,28 +786,26 @@ def forward(self, x, y): # Custom Ops # ---------- # -# ``torch.export`` can export PyTorch programs with custom operators. -# -# Currently, the steps to register a custom op for use by ``torch.export`` are: +# ``torch.export`` can export PyTorch programs with custom operators. Please +# refer to `this page `__ +# on how to author a custom operator in either C++ or Python. # -# - Define the custom op using ``torch.library`` (`reference `__) -# as with any other custom op +# The following is an example of registering a custom operator in python to be +# used by ``torch.export``. The important thing to note is that the custom op +# must have a `FakeTensor kernel `__. @torch.library.custom_op("my_custom_library::custom_op", mutates_args={}) -def custom_op(input: torch.Tensor) -> torch.Tensor: +def custom_op(x: torch.Tensor) -> torch.Tensor: print("custom_op called!") return torch.relu(x) -###################################################################### -# - Define a ``"Meta"`` implementation of the custom op that returns an empty -# tensor with the same shape as the expected output - -@custom_op.register_fake +@custom_op.register_fake def custom_op_meta(x): + # Returns an empty tensor with the same shape as the expected output return torch.empty_like(x) ###################################################################### -# - Call the custom op from the code you want to export using ``torch.ops`` +# Here is an example of exporting a program with the custom op. class CustomOpExample(torch.nn.Module): def forward(self, x): @@ -848,30 +814,27 @@ def forward(self, x): x = torch.cos(x) return x -###################################################################### -# - Export the code as before - exported_custom_op_example = export(CustomOpExample(), (torch.randn(3, 3),)) -exported_custom_op_example.graph_module.print_readable() +print(exported_custom_op_example) print(exported_custom_op_example.module()(torch.randn(3, 3))) ###################################################################### -# Note in the above outputs that the custom op is included in the exported graph. -# And when we call the exported graph as a function, the original custom op is called, -# as evidenced by the ``print`` call. -# -# If you have a custom operator implemented in C++, please refer to -# `this document `__ -# to make it compatible with ``torch.export``. +# Note that in the ``ExportedProgram``, the custom operator is included in the graph. ###################################################################### -# Decompositions -# -------------- +# IR/Decompositions +# ----------------- # -# The graph produced by ``torch.export`` by default returns a graph containing -# only functional ATen operators. This functional ATen operator set (or "opset") contains around 2000 -# operators, all of which are functional, that is, they do not -# mutate or alias inputs. You can find a list of all ATen operators +# The graph produced by ``torch.export`` returns a graph containing only +# `ATen operators `__, which are the +# basic unit of computation in PyTorch. As there are over 3000 ATen operators, +# export provides a way to narrow down the operator set used in the graph based +# on certain characteristics, creating different IRs. +# +# By default, export produces the most generic IR which contains all ATen +# operators, including both functional and non-functional operators. A functional +# operator is one that does not contain any mutations or aliasing of the inputs. +# You can find a list of all ATen operators # `here `__ # and you can inspect if an operator is functional by checking # ``op._schema.is_mutable``, for example: @@ -880,77 +843,78 @@ def forward(self, x): print(torch.ops.aten.add_.Tensor._schema.is_mutable) ###################################################################### -# By default, the environment in which you want to run the exported graph -# should support all ~2000 of these operators. -# However, you can use the following API on the exported program -# if your specific environment is only able to support a subset of -# the ~2000 operators. -# -# .. code-block:: python -# -# def run_decompositions( -# self: ExportedProgram, -# decomposition_table: Optional[Dict[torch._ops.OperatorBase, Callable]] -# ) -> ExportedProgram -# -# ``run_decompositions`` takes in a decomposition table, which is a mapping of -# operators to a function specifying how to reduce, or decompose, that operator -# into an equivalent sequence of other ATen operators. -# -# The default decomposition table for ``run_decompositions`` is the -# `Core ATen decomposition table `__ -# which will decompose the all ATen operators to the -# `Core ATen Operator Set `__ -# which consists of only ~180 operators. +# This generic IR can be used to train in eager PyTorch Autograd. This IR can be +# more explicitly reached through the API ``torch.export.export_for_training``, +# which was introduced in PyTorch 2.5, but calling ``torch.export.export`` +# should produce the same graph as of PyTorch 2.6. -class M(torch.nn.Module): - def __init__(self): +class DecompExample(torch.nn.Module): + def __init__(self) -> None: super().__init__() - self.linear = torch.nn.Linear(3, 4) + self.conv = torch.nn.Conv2d(1, 3, 1, 1) + self.bn = torch.nn.BatchNorm2d(3) def forward(self, x): - return self.linear(x) + x = self.conv(x) + x = self.bn(x) + return (x,) + +ep_for_training = torch.export.export_for_training(DecompExample(), (torch.randn(1, 1, 3, 3),)) +print(ep_for_training.graph) + +###################################################################### +# We can then lower this exported program to an operator set which only contains +# functional ATen operators through the API ``run_decompositions``, which +# decomposes the ATen operators into the ones specified in the decomposition +# table, and functionalizes the graph. By specifying an empty set, we're only +# performing functionalization, and does not do any additional decompositions. +# This results in an IR which contains ~2000 operators (instead of the 3000 +# operators above), and is ideal for inference cases. -ep = export(M(), (torch.randn(2, 3),)) -print(ep.graph) +ep_for_inference = ep_for_training.run_decompositions(decomp_table={}) +print(ep_for_inference.graph) -core_ir_ep = ep.run_decompositions() -print(core_ir_ep.graph) +###################################################################### +# As we can see, the previously mutable operator, +# ``torch.ops.aten.add_.default`` has now been replaced with +# ``torch.ops.aten.add.default``, a l operator. ###################################################################### -# Notice that after running ``run_decompositions`` the -# ``torch.ops.aten.t.default`` operator, which is not part of the Core ATen -# Opset, has been replaced with ``torch.ops.aten.permute.default`` which is part -# of the Core ATen Opset. -# -# Most ATen operators already have decompositions, which are located -# `here `__. -# If you would like to use some of these existing decomposition functions, -# you can pass in a list of operators you would like to decompose to the -# `get_decompositions `__ -# function, which will return a decomposition table using existing -# decomposition implementations. +# We can also further lower this exported program to an operator set which only +# contains the +# `Core ATen Operator Set `__, +# which is a collection of only ~180 operators. This IR is optimal for backends +# who do not want to reimplement all ATen operators. -class M(torch.nn.Module): - def __init__(self): - super().__init__() - self.linear = torch.nn.Linear(3, 4) +from torch.export import default_decompositions - def forward(self, x): - return self.linear(x) +core_aten_decomp_table = default_decompositions() +core_aten_ep = ep_for_training.run_decompositions(decomp_table=core_aten_decomp_table) +print(core_aten_ep.graph) + +###################################################################### +# We now see that ``torch.ops.aten.conv2d.default`` has been decomposed +# into ``torch.ops.aten.convolution.default``. This is because ``convolution`` +# is a more "core" operator, as operations like ``conv1d`` and ``conv2d`` can be +# implemented using the same op. + +###################################################################### +# We can also specify our own decomposition behaviors: + +my_decomp_table = torch.export.default_decompositions() -ep = export(M(), (torch.randn(2, 3),)) -print(ep.graph) +def my_awesome_custom_conv2d_function(x, weight, bias, stride=[1, 1], padding=[0, 0], dilation=[1, 1], groups=1): + return 2 * torch.ops.aten.convolution(x, weight, bias, stride, padding, dilation, False, [0, 0], groups) -from torch._decomp import get_decompositions -decomp_table = get_decompositions([torch.ops.aten.t.default, torch.ops.aten.transpose.int]) -core_ir_ep = ep.run_decompositions(decomp_table) -print(core_ir_ep.graph) +my_decomp_table[torch.ops.aten.conv2d.default] = my_awesome_custom_conv2d_function +my_ep = ep_for_training.run_decompositions(my_decomp_table) +print(my_ep.graph) ###################################################################### -# If there is no existing decomposition function for an ATen operator that you would -# like to decompose, feel free to send a pull request into PyTorch -# implementing the decomposition! +# Notice that instead of ``torch.ops.aten.conv2d.default`` being decomposed +# into ``torch.ops.aten.convolution.default``, it is now decomposed into +# ``torch.ops.aten.convolution.default`` and ``torch.ops.aten.mul.Tensor``, +# which matches our custom decomposition rule. ###################################################################### # ExportDB @@ -1024,18 +988,18 @@ def forward(self, x): ###################################################################### # .. code-block:: python # -# import torch._export # import torch._inductor # # # Note: these APIs are subject to change -# # Compile the exported program to a .so using ``AOTInductor`` +# # Compile the exported program to a PT2 archive using ``AOTInductor`` # with torch.no_grad(): -# so_path = torch._inductor.aot_compile(ep.module(), [inp]) +# pt2_path = torch._inductor.aoti_compile_and_package(ep) # # # Load and run the .so file in Python. # # To load and run it in a C++ environment, see: # # https://pytorch.org/docs/main/torch.compiler_aot_inductor.html -# res = torch._export.aot_load(so_path, device="cuda")(inp) +# aoti_compiled = torch._inductor.aoti_load_package(pt2_path) +# res = aoti_compiled(inp) ###################################################################### # Conclusion From 80989d2cc0d0e4980ee0f65310bf97aabd30cc43 Mon Sep 17 00:00:00 2001 From: "Yu, Guangye" <106960996+guangyey@users.noreply.github.com> Date: Mon, 27 Jan 2025 15:50:00 -0800 Subject: [PATCH 042/347] [WIP][4/N] Refine beginner tutorial by accelerator api (#3190) Co-authored-by: Svetlana Karslioglu --- .../examples_autograd/polynomial_autograd.py | 6 +++++- beginner_source/fgsm_tutorial.py | 12 ++++-------- beginner_source/transfer_learning_tutorial.py | 6 +++++- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/beginner_source/examples_autograd/polynomial_autograd.py b/beginner_source/examples_autograd/polynomial_autograd.py index 9c992d2ca4d..5e2fdc4101d 100755 --- a/beginner_source/examples_autograd/polynomial_autograd.py +++ b/beginner_source/examples_autograd/polynomial_autograd.py @@ -17,8 +17,12 @@ import torch import math +# We want to be able to train our model on an `accelerator `__ +# such as CUDA, MPS, MTIA, or XPU. If the current accelerator is available, we will use it. Otherwise, we use the CPU. + dtype = torch.float -device = "cuda" if torch.cuda.is_available() else "cpu" +device = torch.accelerator.current_accelerator().type if torch.accelerator.is_available() else "cpu" +print(f"Using {device} device") torch.set_default_device(device) # Create Tensors to hold input and outputs. diff --git a/beginner_source/fgsm_tutorial.py b/beginner_source/fgsm_tutorial.py index 9bdf52d84b4..a991fe85627 100644 --- a/beginner_source/fgsm_tutorial.py +++ b/beginner_source/fgsm_tutorial.py @@ -125,14 +125,9 @@ # `pytorch/examples/mnist `__. # For simplicity, download the pretrained model `here `__. # -# - ``use_cuda`` - boolean flag to use CUDA if desired and available. -# Note, a GPU with CUDA is not critical for this tutorial as a CPU will -# not take much time. -# epsilons = [0, .05, .1, .15, .2, .25, .3] pretrained_model = "data/lenet_mnist_model.pth" -use_cuda=True # Set random seed for reproducibility torch.manual_seed(42) @@ -184,9 +179,10 @@ def forward(self, x): ])), batch_size=1, shuffle=True) -# Define what device we are using -print("CUDA Available: ",torch.cuda.is_available()) -device = torch.device("cuda" if use_cuda and torch.cuda.is_available() else "cpu") +# We want to be able to train our model on an `accelerator `__ +# such as CUDA, MPS, MTIA, or XPU. If the current accelerator is available, we will use it. Otherwise, we use the CPU. +device = torch.accelerator.current_accelerator().type if torch.accelerator.is_available() else "cpu" +print(f"Using {device} device") # Initialize the network model = Net().to(device) diff --git a/beginner_source/transfer_learning_tutorial.py b/beginner_source/transfer_learning_tutorial.py index de7a178bd7d..8a344d3d88a 100644 --- a/beginner_source/transfer_learning_tutorial.py +++ b/beginner_source/transfer_learning_tutorial.py @@ -98,7 +98,11 @@ dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']} class_names = image_datasets['train'].classes -device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") +# We want to be able to train our model on an `accelerator `__ +# such as CUDA, MPS, MTIA, or XPU. If the current accelerator is available, we will use it. Otherwise, we use the CPU. + +device = torch.accelerator.current_accelerator().type if torch.accelerator.is_available() else "cpu" +print(f"Using {device} device") ###################################################################### # Visualize a few images From 15ef01526439c09469ec8566e1247e9e6fbc1295 Mon Sep 17 00:00:00 2001 From: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> Date: Tue, 28 Jan 2025 09:04:29 -0500 Subject: [PATCH 043/347] Update lychee.toml (#3267) Excluding image folders --- lychee.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lychee.toml b/lychee.toml index 26fefcfbc5b..b07496f7876 100644 --- a/lychee.toml +++ b/lychee.toml @@ -1 +1 @@ -exclude_path = [".jenkins/build.sh"] +exclude_path = [".jenkins/build.sh", "_static/img/", "_static/images/"] From b76570de3624a2a2f71b7d91b981fbec1ce7e305 Mon Sep 17 00:00:00 2001 From: "Jane (Yuan) Xu" <31798555+janeyx99@users.noreply.github.com> Date: Tue, 28 Jan 2025 12:54:52 -0500 Subject: [PATCH 044/347] Recommend cpython agnosticism in cpp custom op tutorial (#3250) * Recommend python agnosticism in cpp custom op tutorial * forgot to delete a line * Fixed tutorial to be clearer and to recommend other path * Switch to commits instead of master * Formatting code blocks * polish + i missed some code blocks earlier * Adjust advice based on Sam Gross's knowlede --- advanced_source/cpp_custom_ops.rst | 187 +++++++++++++++++++++++------ 1 file changed, 150 insertions(+), 37 deletions(-) diff --git a/advanced_source/cpp_custom_ops.rst b/advanced_source/cpp_custom_ops.rst index 9dc06daa6f4..512c39b2a68 100644 --- a/advanced_source/cpp_custom_ops.rst +++ b/advanced_source/cpp_custom_ops.rst @@ -62,20 +62,78 @@ Using ``cpp_extension`` is as simple as writing the following ``setup.py``: setup(name="extension_cpp", ext_modules=[ - cpp_extension.CppExtension("extension_cpp", ["muladd.cpp"])], - cmdclass={'build_ext': cpp_extension.BuildExtension}) + cpp_extension.CppExtension( + "extension_cpp", + ["muladd.cpp"], + # define Py_LIMITED_API with min version 3.9 to expose only the stable + # limited API subset from Python.h + extra_compile_args={"cxx": ["-DPy_LIMITED_API=0x03090000"]}, + py_limited_api=True)], # Build 1 wheel across multiple Python versions + cmdclass={'build_ext': cpp_extension.BuildExtension}, + options={"bdist_wheel": {"py_limited_api": "cp39"}} # 3.9 is minimum supported Python version + ) If you need to compile CUDA code (for example, ``.cu`` files), then instead use `torch.utils.cpp_extension.CUDAExtension `_. Please see `extension-cpp `_ for an example for how this is set up. -Starting with PyTorch 2.6, you can now build a single wheel for multiple CPython -versions (similar to what you would do for pure python packages). In particular, -if your custom library adheres to the `CPython Stable Limited API -`_ or avoids CPython entirely, you -can build one Python agnostic wheel against a minimum supported CPython version -through setuptools' ``py_limited_api`` flag, like so: +The above example represents what we refer to as a CPython agnostic wheel, meaning we are +building a single wheel that can be run across multiple CPython versions (similar to pure +Python packages). CPython agnosticism is desirable in minimizing the number of wheels your +custom library needs to support and release. The minimum version we'd like to support is +3.9, since it is the oldest supported version currently, so we use the corresponding hexcode +and specifier throughout the setup code. We suggest building the extension in the same +environment as the minimum CPython version you'd like to support to minimize unknown behavior, +so, here, we build the extension in a CPython 3.9 environment. When built, this single wheel +will be runnable in any CPython environment 3.9+. To achieve this, there are three key lines +to note. + +The first is the specification of ``Py_LIMITED_API`` in ``extra_compile_args`` to the +minimum CPython version you would like to support: + +.. code-block:: python + + extra_compile_args={"cxx": ["-DPy_LIMITED_API=0x03090000"]}, + +Defining the ``Py_LIMITED_API`` flag helps verify that the extension is in fact +only using the `CPython Stable Limited API `_, +which is a requirement for the building a CPython agnostic wheel. If this requirement +is not met, it is possible to build a wheel that looks CPython agnostic but will crash, +or worse, be silently incorrect, in another CPython environment. Take care to avoid +using unstable CPython APIs, for example APIs from libtorch_python (in particular +pytorch/python bindings,) and to only use APIs from libtorch (ATen objects, operators +and the dispatcher). We strongly recommend defining the ``Py_LIMITED_API`` flag to +help ascertain the extension is compliant and safe as a CPython agnostic wheel. Note that +defining this flag is not a full guarantee that the built wheel is CPython agnostic, but +it is better than the wild wild west. There are several caveats mentioned in the +`Python docs `_, +and you should test and verify yourself that the wheel is truly agnostic for the relevant +CPython versions. + +The second and third lines specifying ``py_limited_api`` inform setuptools that you intend +to build a CPython agnostic wheel and will influence the naming of the wheel accordingly: + +.. code-block:: python + + setup(name="extension_cpp", + ext_modules=[ + cpp_extension.CppExtension( + ..., + py_limited_api=True)], # Build 1 wheel across multiple Python versions + ..., + options={"bdist_wheel": {"py_limited_api": "cp39"}} # 3.9 is minimum supported Python version + ) + +It is necessary to specify ``py_limited_api=True`` as an argument to CppExtension/ +CUDAExtension and also as an option to the ``"bdist_wheel"`` command with the minimal +supported CPython version (in this case, 3.9). Consequently, the ``setup`` in our +tutorial would build one properly named wheel that could be installed across multiple +CPython versions ``>=3.9``. + +If your extension uses CPython APIs outside the stable limited set, then you cannot +build a CPython agnostic wheel! You should build one wheel per CPython version instead, +like so: .. code-block:: python @@ -86,28 +144,10 @@ through setuptools' ``py_limited_api`` flag, like so: ext_modules=[ cpp_extension.CppExtension( "extension_cpp", - ["python_agnostic_code.cpp"], - py_limited_api=True)], + ["muladd.cpp"])], cmdclass={'build_ext': cpp_extension.BuildExtension}, - options={"bdist_wheel": {"py_limited_api": "cp39"}} ) -Note that you must specify ``py_limited_api=True`` both within ``setup`` -and also as an option to the ``"bdist_wheel"`` command with the minimal supported -Python version (in this case, 3.9). This ``setup`` would build one wheel that could -be installed across multiple Python versions ``python>=3.9``. Please see -`torchao `_ for an example. - -.. note:: - - You must verify independently that the built wheel is truly Python agnostic. - Specifying ``py_limited_api`` does not check for any guarantees, so it is possible - to build a wheel that looks Python agnostic but will crash, or worse, be silently - incorrect, in another Python environment. Take care to avoid using unstable CPython - APIs, for example APIs from libtorch_python (in particular pytorch/python bindings,) - and to only use APIs from libtorch (aten objects, operators and the dispatcher). - For example, to give access to custom ops from Python, the library should register - the ops through the dispatcher (covered below!). Defining the custom op and adding backend implementations --------------------------------------------------------- @@ -252,16 +292,89 @@ matters (importing in the wrong order will lead to an error). To use the custom operator with hybrid Python/C++ registrations, we must first load the C++ library that holds the custom operator definition -and then call the ``torch.library`` registration APIs. This can happen in one -of two ways: - -1. If you're following this tutorial, importing the Python C extension module - we created will load the C++ custom operator definitions. -2. If your C++ custom operator is located in a shared library object, you can - also use ``torch.ops.load_library("/path/to/library.so")`` to load it. This - is the blessed path for Python agnosticism, as you will not have a Python C - extension module to import. See `torchao __init__.py `_ - for an example. +and then call the ``torch.library`` registration APIs. This can happen in +three ways: + + +1. The first way to load the C++ library that holds the custom operator definition + is to define a dummy Python module for _C. Then, in Python, when you import the + module with ``import _C``, the ``.so`` files corresponding to the extension will + be loaded and the ``TORCH_LIBRARY`` and ``TORCH_LIBRARY_IMPL`` static initializers + will run. One can create a dummy Python module with ``PYBIND11_MODULE`` like below, + but you will notice that this does not compile with ``Py_LIMITED_API``, because + ``pybind11`` does not promise to only use the stable limited CPython API! With + the below code, you sadly cannot build a CPython agnostic wheel for your extension! + (Foreshadowing: I wonder what the second way is ;) ). + +.. code-block:: cpp + + // in, say, not_agnostic/csrc/extension_BAD.cpp + #include + + PYBIND11_MODULE("_C", m) {} + +.. code-block:: python + + # in, say, extension/__init__.py + from . import _C + +2. In this tutorial, because we value being able to build a single wheel across multiple + CPython versions, we will replace the unstable ``PYBIND11`` call with stable API calls. + The below code compiles with ``-DPy_LIMITED_API=0x03090000`` and successfully creates + a dummy Python module for our ``_C`` extension so that it can be imported from Python. + See `extension_cpp/__init__.py `_ + and `extension_cpp/csrc/muladd.cpp `_ + for more details: + +.. code-block:: cpp + + #include + + extern "C" { + /* Creates a dummy empty _C module that can be imported from Python. + The import from Python will load the .so consisting of this file + in this extension, so that the TORCH_LIBRARY static initializers + below are run. */ + PyObject* PyInit__C(void) + { + static struct PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "_C", /* name of module */ + NULL, /* module documentation, may be NULL */ + -1, /* size of per-interpreter state of the module, + or -1 if the module keeps state in global variables. */ + NULL, /* methods */ + }; + return PyModule_Create(&module_def); + } + } + +.. code-block:: python + + # in, say, extension/__init__.py + from . import _C + +3. If you want to avoid ``Python.h`` entirely in your C++ custom operator, you may + use ``torch.ops.load_library("/path/to/library.so")`` in Python to load the ``.so`` + file(s) compiled from the extension. Note that, with this method, there is no ``_C`` + Python module created for the extension so you cannot call ``import _C`` from Python. + Instead of relying on the import statement to trigger the custom operators to be + registered, ``torch.ops.load_library("/path/to/library.so")`` will do the trick. + The challenge then is shifted towards understanding where the ``.so`` files are + located so that you can load them, which is not always trivial: + +.. code-block:: python + + import torch + from pathlib import Path + + so_files = list(Path(__file__).parent.glob("_C*.so")) + assert ( + len(so_files) == 1 + ), f"Expected one _C*.so file, found {len(so_files)}" + torch.ops.load_library(so_files[0]) + + from . import ops Adding training (autograd) support for an operator From a75c79159724d39d088c5f90e1d78aa6314396c2 Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Wed, 29 Jan 2025 09:07:11 -0800 Subject: [PATCH 045/347] Update spellcheck to run only on changed files (#3233) .rst, .md, and .py file changes are included for testing purposes only and will not be merged. Mering despite of the lint errors, could be fixed in followup PRs I guess --- .github/workflows/spelling.yml | 141 +++++++++++++++++- .pyspelling.yml | 52 ++++++- README.md | 4 +- beginner_source/colab.rst | 6 +- .../introyt/tensors_deeper_tutorial.py | 4 +- 5 files changed, 191 insertions(+), 16 deletions(-) diff --git a/.github/workflows/spelling.yml b/.github/workflows/spelling.yml index 07c86ed4a28..9556c8c3683 100644 --- a/.github/workflows/spelling.yml +++ b/.github/workflows/spelling.yml @@ -5,16 +5,149 @@ on: push: branches: - main + jobs: pyspelling: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - name: Check for skip label and get changed files + id: check-files + uses: actions/github-script@v6 + with: + script: | + let skipCheck = false; + let changedFiles = []; + + if (context.eventName === 'pull_request') { + // Check for skip label + const { data: labels } = await github.rest.issues.listLabelsOnIssue({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number + }); + skipCheck = labels.some(label => label.name === 'skip-spell-check'); + + if (!skipCheck) { + // Get changed files in PR + const { data: files } = await github.rest.pulls.listFiles({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number + }); + + changedFiles = files + .filter(file => file.filename.match(/\.(py|rst|md)$/)) + .map(file => file.filename); + } + } else { + // For push events, we'll still need to use git diff + // We'll handle this after checkout + } + + core.setOutput('skip', skipCheck.toString()); + core.setOutput('files', changedFiles.join('\n')); + core.setOutput('is-pr', (context.eventName === 'pull_request').toString()); + + - uses: actions/checkout@v4 + if: steps.check-files.outputs.skip != 'true' + with: + fetch-depth: 0 + + - name: Get changed files for push event + if: | + steps.check-files.outputs.skip != 'true' && + steps.check-files.outputs.is-pr != 'true' + id: push-files + run: | + CHANGED_FILES=$(git diff --name-only HEAD^..HEAD -- '*.py' '*.rst' '*.md') + echo "files<> $GITHUB_OUTPUT + echo "$CHANGED_FILES" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Check if relevant files changed + if: steps.check-files.outputs.skip != 'true' + id: check + run: | + if [ "${{ steps.check-files.outputs.is-pr }}" == "true" ]; then + FILES="${{ steps.check-files.outputs.files }}" + else + FILES="${{ steps.push-files.outputs.files }}" + fi + + if [ -z "$FILES" ]; then + echo "skip=true" >> $GITHUB_OUTPUT + echo "No relevant files changed (*.py, *.rst, *.md), skipping spell check" + else + echo "skip=false" >> $GITHUB_OUTPUT + echo "Found changed files to check:" + echo "$FILES" + fi + - uses: actions/setup-python@v4 + if: | + steps.check-files.outputs.skip != 'true' && + steps.check.outputs.skip != 'true' with: python-version: '3.9' cache: 'pip' - - run: pip install pyspelling - - run: sudo apt-get install aspell aspell-en - - run: pyspelling + - name: Install dependencies + if: | + steps.check-files.outputs.skip != 'true' && + steps.check.outputs.skip != 'true' + run: | + pip install pyspelling + sudo apt-get install aspell aspell-en + + - name: Run spell check on each file + id: spellcheck + if: | + steps.check-files.outputs.skip != 'true' && + steps.check.outputs.skip != 'true' + run: | + if [ "${{ steps.check-files.outputs.is-pr }}" == "true" ]; then + mapfile -t FILES <<< "${{ steps.check-files.outputs.files }}" + else + mapfile -t FILES <<< "${{ steps.push-files.outputs.files }}" + fi + + # Check each file individually + FINAL_EXIT_CODE=0 + SPELLCHECK_LOG="" + for file in "${FILES[@]}"; do + if [ -n "$file" ]; then + echo "Checking spelling in $file" + python3 -c "import yaml; config = yaml.safe_load(open('.pyspelling.yml')); new_matrix = [matrix.copy() for matrix in config['matrix'] if (('python' in matrix['name'].lower() and '$file'.endswith('.py')) or ('rest' in matrix['name'].lower() and '$file'.endswith('.rst')) or ('markdown' in matrix['name'].lower() and '$file'.endswith('.md'))) and not matrix.update({'sources': ['$file']})]; config['matrix'] = new_matrix; yaml.dump(config, open('temp_config.yml', 'w'))" + + if OUTPUT=$(pyspelling -c temp_config.yml 2>&1); then + echo "No spelling errors found in $file" + else + FINAL_EXIT_CODE=1 + echo "Spelling errors found in $file:" + echo "$OUTPUT" + SPELLCHECK_LOG+="### $file\n$OUTPUT\n\n" + fi + fi + done + + # Save the results to GITHUB_OUTPUT + echo "spell_failed=$FINAL_EXIT_CODE" >> $GITHUB_OUTPUT + echo "spell_log<> $GITHUB_OUTPUT + echo "$SPELLCHECK_LOG" >> $GITHUB_OUTPUT + echo "SPELLEOF" >> $GITHUB_OUTPUT + + if [ $FINAL_EXIT_CODE -ne 0 ]; then + echo "Spell check failed! See above for details." + echo + echo "Here are a few tips:" + echo "- All PyTorch API objects must be in double backticks or use an intersphinx directive." + echo " Example: ``torch.nn``, :func:" + echo "- Consult en-wordlist.txt for spellings of some of the words." + echo " You can add a word to en-wordlist.txt if:" + echo " 1) It's a common abbreviation, like RNN." + echo " 2) It's a word widely accepted in the industry." + echo "- Please do not add words like 'dtype', 'torch.nn.Transformer' to pass spellcheck." + echo " Instead wrap it in double backticks or use an intersphinx directive." + echo + exit 1 + fi diff --git a/.pyspelling.yml b/.pyspelling.yml index 1afe6dbb45e..bce797e6559 100644 --- a/.pyspelling.yml +++ b/.pyspelling.yml @@ -2,10 +2,7 @@ spellchecker: aspell matrix: - name: python sources: - - beginner_source/*.py - - intermediate_source/*.py - - advanced_source/*.py - - recipes_source/*/*.py + - "**/*.py" dictionary: wordlists: - en-wordlist.txt @@ -56,7 +53,7 @@ matrix: - pyspelling.filters.url: - name: reST sources: - - beginner_source/*.rst + - "**/*.rst" dictionary: wordlists: - en-wordlist.txt @@ -119,3 +116,48 @@ matrix: - open: '\.\.\s+(image|include|only)::' close: '$' - pyspelling.filters.url: +- name: markdown + sources: + - '**/*.md' + dictionary: + wordlists: + - en-wordlist.txt + pipeline: + - pyspelling.filters.markdown: + markdown_extensions: + - markdown.extensions.extra: + - markdown.extensions.admonition: + - markdown.extensions.codehilite: + - markdown.extensions.meta: + - markdown.extensions.tables: + - markdown.extensions.toc: + - pyspelling.filters.html: + comments: false + ignores: + - code + - pre + - tt + - img + - a + - table + - thead + - tbody + - th + - tr + - td + - pyspelling.filters.context: + context_visible_first: true + delimiters: + # Ignore code blocks + - open: '```[a-z]*\n' + close: '```\n' + # Ignore inline code + - open: '`' + close: '`' + # Ignore links + - open: '\[([^]]*)\]' + close: '\([^)]*\)' + # Ignore HTML comments + - open: '' + - pyspelling.filters.url: diff --git a/README.md b/README.md index af84d9ebe79..f17f7f5e3d9 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ All the tutorials are now presented as sphinx style documentation at: # Asking a question -If you have a question about a tutorial, post in https://dev-discuss.pytorch.org/ rather than creating an issue in this repo. Your question will be answered much faster on the dev-discuss forum. +If you hve a qestion about a tutorial, post in https://dev-discuss.pytorch.org/ rather than creating an issue in this repo. Your question will be answered much faster on the dev-discuss forum. # Submitting an issue @@ -20,7 +20,7 @@ You can submit the following types of issues: We use sphinx-gallery's [notebook styled examples](https://sphinx-gallery.github.io/stable/tutorials/index.html) to create the tutorials. Syntax is very simple. In essence, you write a slightly well formatted Python file and it shows up as an HTML page. In addition, a Jupyter notebook is autogenerated and available to run in Google Colab. -Here is how you can create a new tutorial (for a detailed description, see [CONTRIBUTING.md](./CONTRIBUTING.md)): +Here is how you can ceate a new tutorial (for a detailed description, see [CONTRIBUTING.md](./CONTRIBUTING.md)): NOTE: Before submitting a new tutorial, read [PyTorch Tutorial Submission Policy](./tutorial_submission_policy.md). diff --git a/beginner_source/colab.rst b/beginner_source/colab.rst index 812255704e7..329f4884666 100644 --- a/beginner_source/colab.rst +++ b/beginner_source/colab.rst @@ -10,8 +10,8 @@ run PyTorch tutorials in Google Colab. PyTorch Version in Google Colab ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When you are running a tutorial that requires a version of PyTorch that has -just been released, that version might not be yet available in Google Colab. +Wen you are running a tutorial that requires a version of PyTorch that has +jst been released, that version might not be yet available in Google Colab. To check that you have the required ``torch`` and compatible domain libraries installed, run ``!pip list``. @@ -27,7 +27,7 @@ Using Tutorial Data from Google Drive in Colab ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We've added a new feature to tutorials that allows users to open the -notebook associated with a tutorial in Google Colab. You may need to +ntebook associated with a tutorial in Google Colab. You may need to copy data to your Google drive account to get the more complex tutorials to work. diff --git a/beginner_source/introyt/tensors_deeper_tutorial.py b/beginner_source/introyt/tensors_deeper_tutorial.py index 2d5f4cb2963..cf24abd04ff 100644 --- a/beginner_source/introyt/tensors_deeper_tutorial.py +++ b/beginner_source/introyt/tensors_deeper_tutorial.py @@ -44,7 +44,7 @@ ########################################################################## -# Let’s unpack what we just did: +# Let’s upack what we just did: # # - We created a tensor using one of the numerous factory methods # attached to the ``torch`` module. @@ -85,7 +85,7 @@ ######################################################################### -# The factory methods all do just what you’d expect - we have a tensor +# The fctory methods all do just what you’d expect - we have a tensor # full of zeros, another full of ones, and another with random values # between 0 and 1. # From 721623711f3900cc3e1ef0bc4c75ab8e877928c8 Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Wed, 29 Jan 2025 09:46:36 -0800 Subject: [PATCH 046/347] Update what's new in tutorials for 2.6 release (#3269) * Update what's new in tutorials --- index.rst | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/index.rst b/index.rst index 91194d6f480..25fffe86bff 100644 --- a/index.rst +++ b/index.rst @@ -3,13 +3,12 @@ Welcome to PyTorch Tutorials **What's new in PyTorch tutorials?** -* `Compiled Autograd: Capturing a larger backward graph for torch.compile `__ -* `Reducing torch.compile cold start compilation time with regional compilation `__ -* `Introduction to TorchRec `__ -* `(prototype) Flight Recorder for Debugging Stuck Jobs `__ -* `(prototype) How to use TorchInductor on Windows CPU `__ -* `(prototype) Using Max-Autotune Compilation on CPU for Better Performance `__ -* `(prototype) Autoloading Out-of-Tree Extension `__ +* `Dynamic Compilation Control with torch.compiler.set_stance `__ +* `Accelerating PyTorch Transformers by replacing nn.Transformer with Nested Tensors and torch.compile() `__ +* `Understanding the torch.export Flow and Solutions to Common Challenges `__ +* Updated `torch.export Tutorial `__ with automatic dynamic shapes ``Dim.AUTO`` +* Updated `torch.export AOTInductor Tutorial for Python runtime `__ +* Updated `Using User-Defined Triton Kernels with torch.compile `__ with new ``torch.library.triton_op`` .. raw:: html From 90a7da8bc3e0d44d3431c163f27d7ba245abffa1 Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Wed, 29 Jan 2025 15:37:24 -0800 Subject: [PATCH 047/347] Move back to use stable binaries 2.6 (#3270) * Move back to use stable binaries 2.6 --- .ci/docker/requirements.txt | 4 ++-- .jenkins/build.sh | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.ci/docker/requirements.txt b/.ci/docker/requirements.txt index 56df738f96c..c0328356336 100644 --- a/.ci/docker/requirements.txt +++ b/.ci/docker/requirements.txt @@ -14,7 +14,7 @@ tqdm==4.66.1 numpy==1.24.4 matplotlib librosa -torch==2.5 +torch==2.6 torchvision torchdata networkx @@ -70,4 +70,4 @@ semilearn==0.3.2 torchao==0.5.0 segment_anything==1.0 torchrec==1.0.0; platform_system == "Linux" -fbgemm-gpu==1.0.0; platform_system == "Linux" +fbgemm-gpu==1.1.0; platform_system == "Linux" diff --git a/.jenkins/build.sh b/.jenkins/build.sh index 8eca78ae346..4a869d35a75 100755 --- a/.jenkins/build.sh +++ b/.jenkins/build.sh @@ -22,10 +22,10 @@ sudo apt-get install -y pandoc #Install PyTorch Nightly for test. # Nightly - pip install --pre torch torchvision torchaudio -f https://download.pytorch.org/whl/nightly/cu102/torch_nightly.html # Install 2.5 to merge all 2.4 PRs - uncomment to install nightly binaries (update the version as needed). -sudo pip uninstall -y torch torchvision torchaudio torchtext torchdata -sudo pip3 install torch==2.6.0 torchvision --no-cache-dir --index-url https://download.pytorch.org/whl/test/cu124 -sudo pip uninstall -y fbgemm-gpu torchrec -sudo pip3 install fbgemm-gpu==1.1.0 torchrec==1.0.0 --no-cache-dir --index-url https://download.pytorch.org/whl/test/cu124 +# sudo pip uninstall -y torch torchvision torchaudio torchtext torchdata +# sudo pip3 install torch==2.6.0 torchvision --no-cache-dir --index-url https://download.pytorch.org/whl/test/cu124 +# sudo pip uninstall -y fbgemm-gpu torchrec +# sudo pip3 install fbgemm-gpu==1.1.0 torchrec==1.0.0 --no-cache-dir --index-url https://download.pytorch.org/whl/test/cu124 # Install two language tokenizers for Translation with TorchText tutorial python -m spacy download en_core_web_sm From 0a93a8b4c5c4a3325f6b67d439e1197150756ab0 Mon Sep 17 00:00:00 2001 From: Usama Ahmed <53372259+0ssamaak0@users.noreply.github.com> Date: Thu, 30 Jan 2025 23:04:01 +0300 Subject: [PATCH 048/347] change torch.cuda.amp.GradScaler to torch.GradScaler("cuda") (#3257) * change torch.cuda.amp.GradScaler to torch.GradScaler("cuda") * changing torch.GradScaler to torch.amp.GradScaler --------- Co-authored-by: Svetlana Karslioglu --- recipes_source/recipes/amp_recipe.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/recipes_source/recipes/amp_recipe.py b/recipes_source/recipes/amp_recipe.py index b8a4d942333..91ce19a93a9 100644 --- a/recipes_source/recipes/amp_recipe.py +++ b/recipes_source/recipes/amp_recipe.py @@ -150,7 +150,7 @@ def make_model(in_size, out_size, num_layers): # The same ``GradScaler`` instance should be used for the entire convergence run. # If you perform multiple convergence runs in the same script, each run should use # a dedicated fresh ``GradScaler`` instance. ``GradScaler`` instances are lightweight. -scaler = torch.cuda.amp.GradScaler() +scaler = torch.amp.GradScaler("cuda") for epoch in range(0): # 0 epochs, this section is for illustration only for input, target in zip(data, targets): @@ -182,7 +182,7 @@ def make_model(in_size, out_size, num_layers): net = make_model(in_size, out_size, num_layers) opt = torch.optim.SGD(net.parameters(), lr=0.001) -scaler = torch.cuda.amp.GradScaler(enabled=use_amp) +scaler = torch.amp.GradScaler("cuda" ,enabled=use_amp) start_timer() for epoch in range(epochs): From 13d365c8268e41f6f4dc12d9f416d5c571c410ee Mon Sep 17 00:00:00 2001 From: mikaylagawarecki Date: Mon, 3 Feb 2025 11:37:23 -0500 Subject: [PATCH 049/347] Remove line about nightlies in transformer building blocks tutorial (#3271) --- intermediate_source/transformer_building_blocks.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/intermediate_source/transformer_building_blocks.py b/intermediate_source/transformer_building_blocks.py index 7d2c67356e1..36b2019f19f 100644 --- a/intermediate_source/transformer_building_blocks.py +++ b/intermediate_source/transformer_building_blocks.py @@ -3,8 +3,6 @@ ============================================================================================================= **Author:** `Mikayla Gawarecki `_ -.. note:: - This tutorial currently requires you to use the PyTorch nightly build. .. grid:: 2 From 1cedb080766a055bdef8351cc6d3786d1f0c3226 Mon Sep 17 00:00:00 2001 From: Vincent Moens Date: Wed, 19 Feb 2025 16:13:04 +0000 Subject: [PATCH 050/347] Add pageable/pinned tensor to cuda reliability note in pinmem tutorial (#3261) --- intermediate_source/pinmem_nonblock.py | 55 ++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/intermediate_source/pinmem_nonblock.py b/intermediate_source/pinmem_nonblock.py index fa69507a0e7..14d178ab882 100644 --- a/intermediate_source/pinmem_nonblock.py +++ b/intermediate_source/pinmem_nonblock.py @@ -108,7 +108,7 @@ # # .. _pinned_memory_async_sync: # -# When executing a copy from a host (e.g., CPU) to a device (e.g., GPU), the CUDA toolkit offers modalities to do these +# When executing a copy from a host (such as, CPU) to a device (such as, GPU), the CUDA toolkit offers modalities to do these # operations synchronously or asynchronously with respect to the host. # # In practice, when calling :meth:`~torch.Tensor.to`, PyTorch always makes a call to @@ -512,12 +512,54 @@ def pin_copy_to_device_nonblocking(*tensors): # # Until now, we have operated under the assumption that asynchronous copies from the CPU to the GPU are safe. # This is generally true because CUDA automatically handles synchronization to ensure that the data being accessed is -# valid at read time. -# However, this guarantee does not extend to transfers in the opposite direction, from GPU to CPU. -# Without explicit synchronization, these transfers offer no assurance that the copy will be complete at the time of -# data access. Consequently, the data on the host might be incomplete or incorrect, effectively rendering it garbage: +# valid at read time __whenever the tensor is in pageable memory__. # +# However, in other cases we cannot make the same assumption: when a tensor is placed in pinned memory, mutating the +# original copy after calling the host-to-device transfer may corrupt the data received on GPU. +# Similarly, when a transfer is achieved in the opposite direction, from GPU to CPU, or from any device that is not CPU +# or GPU to any device that is not a CUDA-handled GPU (such as, MPS), there is no guarantee that the data read on GPU is +# valid without explicit synchronization. +# +# In these scenarios, these transfers offer no assurance that the copy will be complete at the time of +# data access. Consequently, the data on the host might be incomplete or incorrect, effectively rendering it garbage. +# +# Let's first demonstrate this with a pinned-memory tensor: +DELAY = 100000000 +try: + i = -1 + for i in range(100): + # Create a tensor in pin-memory + cpu_tensor = torch.ones(1024, 1024, pin_memory=True) + torch.cuda.synchronize() + # Send the tensor to CUDA + cuda_tensor = cpu_tensor.to("cuda", non_blocking=True) + torch.cuda._sleep(DELAY) + # Corrupt the original tensor + cpu_tensor.zero_() + assert (cuda_tensor == 1).all() + print("No test failed with non_blocking and pinned tensor") +except AssertionError: + print(f"{i}th test failed with non_blocking and pinned tensor. Skipping remaining tests") +###################################################################### +# Using a pageable tensor always works: +# + +i = -1 +for i in range(100): + # Create a tensor in pin-memory + cpu_tensor = torch.ones(1024, 1024) + torch.cuda.synchronize() + # Send the tensor to CUDA + cuda_tensor = cpu_tensor.to("cuda", non_blocking=True) + torch.cuda._sleep(DELAY) + # Corrupt the original tensor + cpu_tensor.zero_() + assert (cuda_tensor == 1).all() +print("No test failed with non_blocking and pageable tensor") + +###################################################################### +# Now let's demonstrate that CUDA to CPU also fails to produce reliable outputs without synchronization: tensor = ( torch.arange(1, 1_000_000, dtype=torch.double, device="cuda") @@ -551,9 +593,8 @@ def pin_copy_to_device_nonblocking(*tensors): ###################################################################### -# The same considerations apply to copies from the CPU to non-CUDA devices, such as MPS. # Generally, asynchronous copies to a device are safe without explicit synchronization only when the target is a -# CUDA-enabled device. +# CUDA-enabled device and the original tensor is in pageable memory. # # In summary, copying data from CPU to GPU is safe when using ``non_blocking=True``, but for any other direction, # ``non_blocking=True`` can still be used but the user must make sure that a device synchronization is executed before From 1437a216cdf9e40ffbe33370569530d58028debd Mon Sep 17 00:00:00 2001 From: chaochen1111 <157326293+chaochen1111@users.noreply.github.com> Date: Thu, 20 Feb 2025 09:14:38 -0800 Subject: [PATCH 051/347] Fix typo `size` to `world_size` (#3274) --- intermediate_source/dist_tuto.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intermediate_source/dist_tuto.rst b/intermediate_source/dist_tuto.rst index 9a004aa67b4..1b622aa2770 100644 --- a/intermediate_source/dist_tuto.rst +++ b/intermediate_source/dist_tuto.rst @@ -72,7 +72,7 @@ the following template. mp.get_context("spawn") else: mp.set_start_method("spawn") - for rank in range(size): + for rank in range(world_size): p = mp.Process(target=init_process, args=(rank, world_size, run)) p.start() processes.append(p) From 863290e3c4db14abbbd0009d2b94977bfe7d4629 Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Mon, 24 Feb 2025 08:23:04 -0800 Subject: [PATCH 052/347] Update en-wordlist.txt (#3276) Add: - DeviceMesh - intra - composability - HSDP --- en-wordlist.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/en-wordlist.txt b/en-wordlist.txt index 4757f48a6f2..ea2ed6f77d2 100644 --- a/en-wordlist.txt +++ b/en-wordlist.txt @@ -64,6 +64,7 @@ DeepLabV DeepMind DeiT DenseNet +DeviceMesh Dialogs DyNet EOS @@ -110,6 +111,7 @@ HVP Hao HistoEnc HistoEncoder +HSDP Hugging Face IMDB IOT @@ -333,6 +335,7 @@ codegen colorbar compilable composable +composability concat conda config @@ -424,6 +427,7 @@ inferencing initializations inlined interpretable +intra invariance io iter From a543d0515e789acc9663d8358fd4eb6892b017d3 Mon Sep 17 00:00:00 2001 From: Vivien Cabannes Date: Mon, 24 Feb 2025 17:42:34 +0100 Subject: [PATCH 053/347] fixing typo "sequance->sequence" (#3275) * fixing typo "sequance->sequence" --------- Co-authored-by: Svetlana Karslioglu --- recipes_source/distributed_device_mesh.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/recipes_source/distributed_device_mesh.rst b/recipes_source/distributed_device_mesh.rst index 17ce3c4859e..cf87ecf16a9 100644 --- a/recipes_source/distributed_device_mesh.rst +++ b/recipes_source/distributed_device_mesh.rst @@ -31,7 +31,7 @@ Users can also easily manage the underlying process_groups/devices for multi-dim Why DeviceMesh is Useful ------------------------ DeviceMesh is useful when working with multi-dimensional parallelism (i.e. 3-D parallel) where parallelism composability is required. For example, when your parallelism solutions require both communication across hosts and within each host. -The image above shows that we can create a 2D mesh that connects the devices within each host, and connects each device with its counterpart on the other hosts in a homogenous setup. +The image above shows that we can create a 2D mesh that connects the devices within each host, and connects each device with its counterpart on the other hosts in a homogeneous setup. Without DeviceMesh, users would need to manually set up NCCL communicators, cuda devices on each process before applying any parallelism, which could be quite complicated. The following code snippet illustrates a hybrid sharding 2-D Parallel pattern setup without :class:`DeviceMesh`. @@ -150,7 +150,7 @@ Then, run the following `torch elastic/torchrun `__ +- `2D parallel combining Tensor/Sequence Parallel with FSDP `__ - `Composable PyTorch Distributed with PT2 `__ From 707cfcf976a6a1a1cf2b425b7e39dc02be7af9f8 Mon Sep 17 00:00:00 2001 From: Oguz Ulgen Date: Thu, 27 Feb 2025 08:10:40 -0800 Subject: [PATCH 054/347] Add a new tutorial for compile time caching (#3277) * Add a new tutorial for compile time caching --------- Co-authored-by: Svetlana Karslioglu --- en-wordlist.txt | 4 + recipes_source/recipes_index.rst | 11 +- ...compile_caching_configuration_tutorial.rst | 78 +++++++++++++ .../torch_compile_caching_tutorial.rst | 105 +++++++++++------- 4 files changed, 158 insertions(+), 40 deletions(-) create mode 100644 recipes_source/torch_compile_caching_configuration_tutorial.rst diff --git a/en-wordlist.txt b/en-wordlist.txt index ea2ed6f77d2..9f318cb99f2 100644 --- a/en-wordlist.txt +++ b/en-wordlist.txt @@ -691,3 +691,7 @@ XPU XPUs impl overrideable +TorchServe +Inductor’s +onwards +recompilations diff --git a/recipes_source/recipes_index.rst b/recipes_source/recipes_index.rst index 632efebb5c5..f136c4b9c67 100644 --- a/recipes_source/recipes_index.rst +++ b/recipes_source/recipes_index.rst @@ -348,11 +348,20 @@ Recipes are bite-sized, actionable examples of how to use specific PyTorch featu .. customcarditem:: :header: Compile Time Caching in ``torch.compile`` - :card_description: Learn how to configure compile time caching in ``torch.compile`` + :card_description: Learn how to use compile time caching in ``torch.compile`` :image: ../_static/img/thumbnails/cropped/generic-pytorch-logo.png :link: ../recipes/torch_compile_caching_tutorial.html :tags: Model-Optimization +.. Compile Time Caching Configurations + +.. customcarditem:: + :header: Compile Time Caching Configurations + :card_description: Learn how to configure compile time caching in ``torch.compile`` + :image: ../_static/img/thumbnails/cropped/generic-pytorch-logo.png + :link: ../recipes/torch_compile_caching_configuration_tutorial.html + :tags: Model-Optimization + .. Reducing Cold Start Compilation Time with Regional Compilation .. customcarditem:: diff --git a/recipes_source/torch_compile_caching_configuration_tutorial.rst b/recipes_source/torch_compile_caching_configuration_tutorial.rst new file mode 100644 index 00000000000..21565d0562d --- /dev/null +++ b/recipes_source/torch_compile_caching_configuration_tutorial.rst @@ -0,0 +1,78 @@ +Compile Time Caching Configuration +========================================================= +**Authors:** `Oguz Ulgen `_ and `Sam Larsen `_ + +Introduction +------------------ + +PyTorch Compiler implements several caches to reduce compilation latency. +This recipe demonstrates how you can configure various parts of the caching in ``torch.compile``. + +Prerequisites +------------------- + +Before starting this recipe, make sure that you have the following: + +* Basic understanding of ``torch.compile``. See: + + * `torch.compiler API documentation `__ + * `Introduction to torch.compile `__ + * `Compile Time Caching in torch.compile `__ + +* PyTorch 2.4 or later + +Inductor Cache Settings +---------------------------- + +Most of these caches are in-memory, only used within the same process, and are transparent to the user. An exception is caches that store compiled FX graphs (``FXGraphCache``, ``AOTAutogradCache``). These caches allow Inductor to avoid recompilation across process boundaries when it encounters the same graph with the same Tensor input shapes (and the same configuration). The default implementation stores compiled artifacts in the system temp directory. An optional feature also supports sharing those artifacts within a cluster by storing them in a Redis database. + +There are a few settings relevant to caching and to FX graph caching in particular. +The settings are accessible via environment variables listed below or can be hard-coded in the Inductor’s config file. + +TORCHINDUCTOR_FX_GRAPH_CACHE +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This setting enables the local FX graph cache feature, which stores artifacts in the host’s temp directory. Setting it to ``1`` enables the feature while any other value disables it. By default, the disk location is per username, but users can enable sharing across usernames by specifying ``TORCHINDUCTOR_CACHE_DIR`` (below). + +TORCHINDUCTOR_AUTOGRAD_CACHE +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This setting extends ``FXGraphCache`` to store cached results at the ``AOTAutograd`` level, rather than at the Inductor level. Setting it to ``1`` enables this feature, while any other value disables it. +By default, the disk location is per username, but users can enable sharing across usernames by specifying ``TORCHINDUCTOR_CACHE_DIR`` (below). +``TORCHINDUCTOR_AUTOGRAD_CACHE`` requires ``TORCHINDUCTOR_FX_GRAPH_CACHE`` to work. The same cache dir stores cache entries for ``AOTAutogradCache`` (under ``{TORCHINDUCTOR_CACHE_DIR}/aotautograd``) and ``FXGraphCache`` (under ``{TORCHINDUCTOR_CACHE_DIR}/fxgraph``). + +TORCHINDUCTOR_CACHE_DIR +~~~~~~~~~~~~~~~~~~~~~~~~ +This setting specifies the location of all on-disk caches. By default, the location is in the system temp directory under ``torchinductor_``, for example, ``/tmp/torchinductor_myusername``. + +Note that if ``TRITON_CACHE_DIR`` is not set in the environment, Inductor sets the ``Triton`` cache directory to this same temp location, under the Triton sub-directory. + +TORCHINDUCTOR_FX_GRAPH_REMOTE_CACHE +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This setting enables the remote FX graph cache feature. The current implementation uses ``Redis``. ``1`` enables caching, and any other value disables it. The following environment variables configure the host and port of the Redis server: + +``TORCHINDUCTOR_REDIS_HOST`` (defaults to ``localhost``) +``TORCHINDUCTOR_REDIS_PORT`` (defaults to ``6379``) + +.. note:: + + Note that if Inductor locates a remote cache entry, it stores the compiled artifact in the local on-disk cache; that local artifact would be served on subsequent runs on the same machine. + +TORCHINDUCTOR_AUTOGRAD_REMOTE_CACHE +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Similar to ``TORCHINDUCTOR_FX_GRAPH_REMOTE_CACHE``, this setting enables the remote ``AOTAutogradCache`` feature. The current implementation uses Redis. Setting it to ``1`` enables caching, while any other value disables it. The following environment variables are used to configure the host and port of the ``Redis`` server: +* ``TORCHINDUCTOR_REDIS_HOST`` (defaults to ``localhost``) +* ``TORCHINDUCTOR_REDIS_PORT`` (defaults to ``6379``) + +`TORCHINDUCTOR_AUTOGRAD_REMOTE_CACHE`` requires ``TORCHINDUCTOR_FX_GRAPH_REMOTE_CACHE`` to be enabled in order to function. The same Redis server can be used to store both AOTAutograd and FXGraph cache results. + +TORCHINDUCTOR_AUTOTUNE_REMOTE_CACHE +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This setting enables a remote cache for ``TorchInductor``’s autotuner. Similar to remote FX graph cache, the current implementation uses Redis. Setting it to ``1`` enables caching, while any other value disables it. The same host / port environment variables mentioned above apply to this cache. + +TORCHINDUCTOR_FORCE_DISABLE_CACHES +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Set this value to ``1`` to disable all Inductor caching. This setting is useful for tasks like experimenting with cold-start compile times or forcing recompilation for debugging purposes. + +Conclusion +------------- +In this recipe, we have learned how to configure PyTorch Compiler's caching mechanisms. Additionally, we explored the various settings and environment variables that allow users to configure and optimize these caching features according to their specific needs. + diff --git a/recipes_source/torch_compile_caching_tutorial.rst b/recipes_source/torch_compile_caching_tutorial.rst index 3c024828f9f..ebc831cdb90 100644 --- a/recipes_source/torch_compile_caching_tutorial.rst +++ b/recipes_source/torch_compile_caching_tutorial.rst @@ -1,12 +1,16 @@ Compile Time Caching in ``torch.compile`` ========================================================= -**Authors:** `Oguz Ulgen `_ and `Sam Larsen `_ +**Author:** `Oguz Ulgen `_ Introduction ------------------ -PyTorch Inductor implements several caches to reduce compilation latency. -This recipe demonstrates how you can configure various parts of the caching in ``torch.compile``. +PyTorch Compiler provides several caching offerings to reduce compilation latency. +This recipe will explain these offerings in detail to help users pick the best option for their use case. + +Check out `Compile Time Caching Configurations `__ for how to configure these caches. + +Also check out our caching benchmark at `PT CacheBench Benchmarks `__. Prerequisites ------------------- @@ -17,60 +21,83 @@ Before starting this recipe, make sure that you have the following: * `torch.compiler API documentation `__ * `Introduction to torch.compile `__ + * `Triton language documentation `__ * PyTorch 2.4 or later -Inductor Cache Settings ----------------------------- +Caching Offerings +--------------------- + +``torch.compile`` provides the following caching offerings: + +* End to end caching (also known as ``Mega-Cache``) +* Modular caching of ``TorchDynamo``, ``TorchInductor``, and ``Triton`` + +It is important to note that caching validates that the cache artifacts are used with the same PyTorch and Triton version, as well as, same GPU when device is set to be cuda. + +``torch.compile`` end-to-end caching (``Mega-Cache``) +------------------------------------------------------------ + +End to end caching, from here onwards referred to ``Mega-Cache``, is the ideal solution for users looking for a portable caching solution that can be stored in a database and can later be fetched possibly on a separate machine. + +``Mega-Cache`` provides two compiler APIs: + +* ``torch.compiler.save_cache_artifacts()`` +* ``torch.compiler.load_cache_artifacts()`` + +The intended use case is after compiling and executing a model, the user calls ``torch.compiler.save_cache_artifacts()`` which will return the compiler artifacts in a portable form. Later, potentially on a different machine, the user may call ``torch.compiler.load_cache_artifacts()`` with these artifacts to pre-populate the ``torch.compile`` caches in order to jump-start their cache. + +Consider the following example. First, compile and save the cache artifacts. + +.. code-block:: python + + @torch.compile + def fn(x, y): + return x.sin() @ y + + a = torch.rand(100, 100, dtype=dtype, device=device) + b = torch.rand(100, 100, dtype=dtype, device=device) + + result = fn(a, b) + + artifacts = torch.compiler.save_cache_artifacts() + + # Now, potentially store these artifacts in a database + +Later, you can jump-start the cache by the following: -Most of these caches are in-memory, only used within the same process, and are transparent to the user. An exception is caches that store compiled FX graphs (FXGraphCache, AOTAutogradCache). These caches allow Inductor to avoid recompilation across process boundaries when it encounters the same graph with the same Tensor input shapes (and the same configuration). The default implementation stores compiled artifacts in the system temp directory. An optional feature also supports sharing those artifacts within a cluster by storing them in a Redis database. +.. code-block:: python -There are a few settings relevant to caching and to FX graph caching in particular. -The settings are accessible via environment variables listed below or can be hard-coded in Inductor’s config file. + # Potentially download/fetch the artifacts from the database + assert artifacts is not None + artifact_bytes, cache_info = artifacts -TORCHINDUCTOR_FX_GRAPH_CACHE -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This setting enables the local FX graph cache feature, i.e., by storing artifacts in the host’s temp directory. ``1`` enables, and any other value disables it. By default, the disk location is per username, but users can enable sharing across usernames by specifying ``TORCHINDUCTOR_CACHE_DIR`` (below). + torch.compiler.load_cache_artifacts(artifact_bytes) -TORCHINDUCTOR_AUTOGRAD_CACHE -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This setting extends FXGraphCache to store cached results at the AOTAutograd level, instead of at the Inductor level. ``1`` enables, and any other value disables it. -By default, the disk location is per username, but users can enable sharing across usernames by specifying ``TORCHINDUCTOR_CACHE_DIR`` (below). -`TORCHINDUCTOR_AUTOGRAD_CACHE` requires `TORCHINDUCTOR_FX_GRAPH_CACHE` to work. The same cache dir stores cache entries for AOTAutogradCache (under `{TORCHINDUCTOR_CACHE_DIR}/aotautograd`) and FXGraphCache (under `{TORCHINDUCTOR_CACHE_DIR}/fxgraph`). +This operation populates all the modular caches that will be discussed in the next section, including ``PGO``, ``AOTAutograd``, ``Inductor``, ``Triton``, and ``Autotuning``. -TORCHINDUCTOR_CACHE_DIR -~~~~~~~~~~~~~~~~~~~~~~~~ -This setting specifies the location of all on-disk caches. By default, the location is in the system temp directory under ``torchinductor_``, for example, ``/tmp/torchinductor_myusername``. -Note that if ``TRITON_CACHE_DIR`` is not set in the environment, Inductor sets the Triton cache directory to this same temp location, under the Triton subdirectory. +Modular caching of ``TorchDynamo``, ``TorchInductor``, and ``Triton`` +----------------------------------------------------------- -TORCHINDUCTOR_FX_GRAPH_REMOTE_CACHE -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This setting enables the remote FX graph cache feature. The current implementation uses Redis. ``1`` enables caching, and any other value disables it. The following environment variables configure the host and port of the Redis server: +The aforementioned ``Mega-Cache`` is composed of individual components that can be used without any user intervention. By default, PyTorch Compiler comes with local on-disk caches for ``TorchDynamo``, ``TorchInductor``, and ``Triton``. These caches include: -``TORCHINDUCTOR_REDIS_HOST`` (defaults to ``localhost``) -``TORCHINDUCTOR_REDIS_PORT`` (defaults to ``6379``) +* ``FXGraphCache``: A cache of graph-based IR components used in compilation. +* ``TritonCache``: A cache of Triton-compilation results, including ``cubin`` files generated by ``Triton`` and other caching artifacts. +* ``InductorCache``: A bundle of ``FXGraphCache`` and ``Triton`` cache. +* ``AOTAutogradCache``: A cache of joint graph artifacts. +* ``PGO-cache``: A cache of dynamic shape decisions to reduce number of recompilations. -Note that if Inductor locates a remote cache entry, it stores the compiled artifact in the local on-disk cache; that local artifact would be served on subsequent runs on the same machine. +All these cache artifacts are written to ``TORCHINDUCTOR_CACHE_DIR`` which by default will look like ``/tmp/torchinductor_myusername``. -TORCHINDUCTOR_AUTOGRAD_REMOTE_CACHE -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Like TORCHINDUCTOR_FX_GRAPH_REMOTE_CACHE, this setting enables the remote AOT AutogradCache feature. The current implementation uses Redis. ``1`` enables caching, and any other value disables it. The following environment variables configure the host and port of the Redis server: -``TORCHINDUCTOR_REDIS_HOST`` (defaults to ``localhost``) -``TORCHINDUCTOR_REDIS_PORT`` (defaults to ``6379``) -`TORCHINDUCTOR_AUTOGRAD_REMOTE_CACHE`` depends on `TORCHINDUCTOR_FX_GRAPH_REMOTE_CACHE` to be enabled to work. The same Redis server can store both AOTAutograd and FXGraph cache results. +Remote Caching +---------------- -TORCHINDUCTOR_AUTOTUNE_REMOTE_CACHE -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This setting enables a remote cache for Inductor’s autotuner. As with the remote FX graph cache, the current implementation uses Redis. ``1`` enables caching, and any other value disables it. The same host / port environment variables listed above apply to this cache. +We also provide a remote caching option for users who would like to take advantage of a Redis based cache. Check out `Compile Time Caching Configurations `__ to learn more about how to enable the Redis-based caching. -TORCHINDUCTOR_FORCE_DISABLE_CACHES -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Set this value to ``1`` to disable all Inductor caching. This setting is useful for tasks like experimenting with cold-start compile times or forcing recompilation for debugging purposes. Conclusion ------------- In this recipe, we have learned that PyTorch Inductor's caching mechanisms significantly reduce compilation latency by utilizing both local and remote caches, which operate seamlessly in the background without requiring user intervention. -Additionally, we explored the various settings and environment variables that allow users to configure and optimize these caching features according to their specific needs. From e8dc3532ab0a703d3e2c3b6c73f7b816cf683ce2 Mon Sep 17 00:00:00 2001 From: Oguz Ulgen Date: Thu, 27 Feb 2025 15:00:12 -0800 Subject: [PATCH 055/347] Fix caching tutorial order (#3278) --- recipes_source/torch_compile_caching_tutorial.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/recipes_source/torch_compile_caching_tutorial.rst b/recipes_source/torch_compile_caching_tutorial.rst index ebc831cdb90..12fcad4163f 100644 --- a/recipes_source/torch_compile_caching_tutorial.rst +++ b/recipes_source/torch_compile_caching_tutorial.rst @@ -62,16 +62,17 @@ Consider the following example. First, compile and save the cache artifacts. artifacts = torch.compiler.save_cache_artifacts() - # Now, potentially store these artifacts in a database + assert artifacts is not None + artifact_bytes, cache_info = artifacts + + # Now, potentially store artifact_bytes in a database + # You can use cache_info for logging Later, you can jump-start the cache by the following: .. code-block:: python # Potentially download/fetch the artifacts from the database - assert artifacts is not None - artifact_bytes, cache_info = artifacts - torch.compiler.load_cache_artifacts(artifact_bytes) This operation populates all the modular caches that will be discussed in the next section, including ``PGO``, ``AOTAutograd``, ``Inductor``, ``Triton``, and ``Autotuning``. From 469d95b8107f29882dffef9f19d05d51da3945eb Mon Sep 17 00:00:00 2001 From: Oguz Ulgen Date: Thu, 27 Feb 2025 20:52:07 -0800 Subject: [PATCH 056/347] Add megacache to index (#3279) --- index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/index.rst b/index.rst index 25fffe86bff..55c7ce63048 100644 --- a/index.rst +++ b/index.rst @@ -9,6 +9,7 @@ Welcome to PyTorch Tutorials * Updated `torch.export Tutorial `__ with automatic dynamic shapes ``Dim.AUTO`` * Updated `torch.export AOTInductor Tutorial for Python runtime `__ * Updated `Using User-Defined Triton Kernels with torch.compile `__ with new ``torch.library.triton_op`` +* Updated `Compile Time Caching in torch.compile `__ with new ``Mega-Cache`` .. raw:: html From cb2e4edaab5d5acb5cf8d9e035511100f8fd2c7c Mon Sep 17 00:00:00 2001 From: Ti-Tai Wang Date: Wed, 5 Mar 2025 15:36:14 -0800 Subject: [PATCH 057/347] [ONNX] Update API to torch.onnx.export(..., dynamo=True) (#3223) --------- Co-authored-by: Svetlana Karslioglu --- _static/img/onnx/custom_addandround.png | Bin 39031 -> 0 bytes _static/img/onnx/custom_aten_gelu_model.png | Bin 26058 -> 0 bytes beginner_source/onnx/README.txt | 6 +- ...ort_control_flow_model_to_onnx_tutorial.py | 185 +++++++++ .../export_simple_model_to_onnx_tutorial.py | 68 +-- beginner_source/onnx/intro_onnx.py | 28 +- .../onnx/onnx_registry_tutorial.py | 392 ++++++++---------- beginner_source/onnx/onnx_toc.txt | 3 +- 8 files changed, 417 insertions(+), 265 deletions(-) delete mode 100644 _static/img/onnx/custom_addandround.png delete mode 100644 _static/img/onnx/custom_aten_gelu_model.png create mode 100644 beginner_source/onnx/export_control_flow_model_to_onnx_tutorial.py diff --git a/_static/img/onnx/custom_addandround.png b/_static/img/onnx/custom_addandround.png deleted file mode 100644 index d4973ce6c2426f52ee70d0be43733f1a4883d66d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39031 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 Jcu(-f{{kcykQ4v_ diff --git a/beginner_source/onnx/README.txt b/beginner_source/onnx/README.txt index 6a598f9b9f5..96004a239ea 100644 --- a/beginner_source/onnx/README.txt +++ b/beginner_source/onnx/README.txt @@ -10,5 +10,9 @@ ONNX https://pytorch.org/tutorials/beginner/onnx/export_simple_model_to_onnx_tutorial.html 3. onnx_registry_tutorial.py - Extending the ONNX Registry + Extending the ONNX exporter operator support https://pytorch.org/tutorials/beginner/onnx/onnx_registry_tutorial.html + +4. export_control_flow_model_to_onnx_tutorial.py + Export a model with control flow to ONNX + https://pytorch.org/tutorials/beginner/onnx/export_control_flow_model_to_onnx_tutorial.html \ No newline at end of file diff --git a/beginner_source/onnx/export_control_flow_model_to_onnx_tutorial.py b/beginner_source/onnx/export_control_flow_model_to_onnx_tutorial.py new file mode 100644 index 00000000000..617a1e94a36 --- /dev/null +++ b/beginner_source/onnx/export_control_flow_model_to_onnx_tutorial.py @@ -0,0 +1,185 @@ +# -*- coding: utf-8 -*- +""" +`Introduction to ONNX `_ || +`Exporting a PyTorch model to ONNX `_ || +`Extending the ONNX exporter operator support `_ || +**`Export a model with control flow to ONNX** + +Export a model with control flow to ONNX +======================================== + +**Author**: `Xavier Dupré `_ +""" + + +############################################################################### +# Overview +# -------- +# +# This tutorial demonstrates how to handle control flow logic while exporting +# a PyTorch model to ONNX. It highlights the challenges of exporting +# conditional statements directly and provides solutions to circumvent them. +# +# Conditional logic cannot be exported into ONNX unless they refactored +# to use :func:`torch.cond`. Let's start with a simple model +# implementing a test. +# +# What you will learn: +# +# - How to refactor the model to use :func:`torch.cond` for exporting. +# - How to export a model with control flow logic to ONNX. +# - How to optimize the exported model using the ONNX optimizer. +# +# Prerequisites +# ~~~~~~~~~~~~~ +# +# * ``torch >= 2.6`` + + +import torch + +############################################################################### +# Define the Models +# ----------------- +# +# Two models are defined: +# +# ``ForwardWithControlFlowTest``: A model with a forward method containing an +# if-else conditional. +# +# ``ModelWithControlFlowTest``: A model that incorporates ``ForwardWithControlFlowTest`` +# as part of a simple MLP. The models are tested with +# a random input tensor to confirm they execute as expected. + +class ForwardWithControlFlowTest(torch.nn.Module): + def forward(self, x): + if x.sum(): + return x * 2 + return -x + + +class ModelWithControlFlowTest(torch.nn.Module): + def __init__(self): + super().__init__() + self.mlp = torch.nn.Sequential( + torch.nn.Linear(3, 2), + torch.nn.Linear(2, 1), + ForwardWithControlFlowTest(), + ) + + def forward(self, x): + out = self.mlp(x) + return out + + +model = ModelWithControlFlowTest() + + +############################################################################### +# Exporting the Model: First Attempt +# ---------------------------------- +# +# Exporting this model using torch.export.export fails because the control +# flow logic in the forward pass creates a graph break that the exporter cannot +# handle. This behavior is expected, as conditional logic not written using +# :func:`torch.cond` is unsupported. +# +# A try-except block is used to capture the expected failure during the export +# process. If the export unexpectedly succeeds, an ``AssertionError`` is raised. + +x = torch.randn(3) +model(x) + +try: + torch.export.export(model, (x,), strict=False) + raise AssertionError("This export should failed unless PyTorch now supports this model.") +except Exception as e: + print(e) + +############################################################################### +# Using :func:`torch.onnx.export` with JIT Tracing +# ---------------------------------------- +# +# When exporting the model using :func:`torch.onnx.export` with the dynamo=True +# argument, the exporter defaults to using JIT tracing. This fallback allows +# the model to export, but the resulting ONNX graph may not faithfully represent +# the original model logic due to the limitations of tracing. + + +onnx_program = torch.onnx.export(model, (x,), dynamo=True) +print(onnx_program.model) + + +############################################################################### +# Suggested Patch: Refactoring with :func:`torch.cond` +# -------------------------------------------- +# +# To make the control flow exportable, the tutorial demonstrates replacing the +# forward method in ``ForwardWithControlFlowTest`` with a refactored version that +# uses :func:`torch.cond``. +# +# Details of the Refactoring: +# +# Two helper functions (identity2 and neg) represent the branches of the conditional logic: +# * :func:`torch.cond`` is used to specify the condition and the two branches along with the input arguments. +# * The updated forward method is then dynamically assigned to the ``ForwardWithControlFlowTest`` instance within the model. A list of submodules is printed to confirm the replacement. + +def new_forward(x): + def identity2(x): + return x * 2 + + def neg(x): + return -x + + return torch.cond(x.sum() > 0, identity2, neg, (x,)) + + +print("the list of submodules") +for name, mod in model.named_modules(): + print(name, type(mod)) + if isinstance(mod, ForwardWithControlFlowTest): + mod.forward = new_forward + +############################################################################### +# Let's see what the FX graph looks like. + +print(torch.export.export(model, (x,), strict=False)) + +############################################################################### +# Let's export again. + +onnx_program = torch.onnx.export(model, (x,), dynamo=True) +print(onnx_program.model) + + +############################################################################### +# We can optimize the model and get rid of the model local functions created to capture the control flow branches. + +onnx_program.optimize() +print(onnx_program.model) + +############################################################################### +# Conclusion +# ---------- +# +# This tutorial demonstrates the challenges of exporting models with conditional +# logic to ONNX and presents a practical solution using :func:`torch.cond`. +# While the default exporters may fail or produce imperfect graphs, refactoring the +# model's logic ensures compatibility and generates a faithful ONNX representation. +# +# By understanding these techniques, we can overcome common pitfalls when +# working with control flow in PyTorch models and ensure smooth integration with ONNX workflows. +# +# Further reading +# --------------- +# +# The list below refers to tutorials that ranges from basic examples to advanced scenarios, +# not necessarily in the order they are listed. +# Feel free to jump directly to specific topics of your interest or +# sit tight and have fun going through all of them to learn all there is about the ONNX exporter. +# +# .. include:: /beginner_source/onnx/onnx_toc.txt +# +# .. toctree:: +# :hidden: +# \ No newline at end of file diff --git a/beginner_source/onnx/export_simple_model_to_onnx_tutorial.py b/beginner_source/onnx/export_simple_model_to_onnx_tutorial.py index 5a8ac9c538a..1676e4adc06 100644 --- a/beginner_source/onnx/export_simple_model_to_onnx_tutorial.py +++ b/beginner_source/onnx/export_simple_model_to_onnx_tutorial.py @@ -2,18 +2,19 @@ """ `Introduction to ONNX `_ || **Exporting a PyTorch model to ONNX** || -`Extending the ONNX Registry `_ +`Extending the ONNX exporter operator support `_ || +`Export a model with control flow to ONNX `_ Export a PyTorch model to ONNX ============================== -**Author**: `Ti-Tai Wang `_ and `Xavier Dupré `_ +**Author**: `Ti-Tai Wang `_, `Justin Chu `_, `Thiago Crepaldi `_. .. note:: - As of PyTorch 2.1, there are two versions of ONNX Exporter. + As of PyTorch 2.5, there are two versions of ONNX Exporter. - * ``torch.onnx.dynamo_export`` is the newest (still in beta) exporter based on the TorchDynamo technology released with PyTorch 2.0 - * ``torch.onnx.export`` is based on TorchScript backend and has been available since PyTorch 1.2.0 + * ``torch.onnx.export(..., dynamo=True)`` is the newest (still in beta) exporter using ``torch.export`` and Torch FX to capture the graph. It was released with PyTorch 2.5 + * ``torch.onnx.export`` uses TorchScript and has been available since PyTorch 1.2.0 """ @@ -21,7 +22,7 @@ # In the `60 Minute Blitz `_, # we had the opportunity to learn about PyTorch at a high level and train a small neural network to classify images. # In this tutorial, we are going to expand this to describe how to convert a model defined in PyTorch into the -# ONNX format using TorchDynamo and the ``torch.onnx.dynamo_export`` ONNX exporter. +# ONNX format using the ``torch.onnx.export(..., dynamo=True)`` ONNX exporter. # # While PyTorch is great for iterating on the development of models, the model can be deployed to production # using different formats, including `ONNX `_ (Open Neural Network Exchange)! @@ -47,8 +48,7 @@ # # .. code-block:: bash # -# pip install onnx -# pip install onnxscript +# pip install --upgrade onnx onnxscript # # 2. Author a simple image classifier model # ----------------------------------------- @@ -62,17 +62,16 @@ import torch.nn.functional as F -class MyModel(nn.Module): - +class ImageClassifierModel(nn.Module): def __init__(self): - super(MyModel, self).__init__() + super().__init__() self.conv1 = nn.Conv2d(1, 6, 5) 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): + def forward(self, x: torch.Tensor): x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2)) x = F.max_pool2d(F.relu(self.conv2(x)), 2) x = torch.flatten(x, 1) @@ -81,6 +80,7 @@ def forward(self, x): x = self.fc3(x) return x + ###################################################################### # 3. Export the model to ONNX format # ---------------------------------- @@ -88,9 +88,19 @@ def forward(self, x): # Now that we have our model defined, we need to instantiate it and create a random 32x32 input. # Next, we can export the model to ONNX format. -torch_model = MyModel() -torch_input = torch.randn(1, 1, 32, 32) -onnx_program = torch.onnx.dynamo_export(torch_model, torch_input) +torch_model = ImageClassifierModel() +# Create example inputs for exporting the model. The inputs should be a tuple of tensors. +example_inputs = (torch.randn(1, 1, 32, 32),) +onnx_program = torch.onnx.export(torch_model, example_inputs, dynamo=True) + +###################################################################### +# 3.5. (Optional) Optimize the ONNX model +# --------------------------------------- +# +# The ONNX model can be optimized with constant folding, and elimination of redundant nodes. +# The optimization is done in-place, so the original ONNX model is modified. + +onnx_program.optimize() ###################################################################### # As we can see, we didn't need any code change to the model. @@ -102,13 +112,14 @@ def forward(self, x): # Although having the exported model loaded in memory is useful in many applications, # we can save it to disk with the following code: -onnx_program.save("my_image_classifier.onnx") +onnx_program.save("image_classifier_model.onnx") ###################################################################### # You can load the ONNX file back into memory and check if it is well formed with the following code: import onnx -onnx_model = onnx.load("my_image_classifier.onnx") + +onnx_model = onnx.load("image_classifier_model.onnx") onnx.checker.check_model(onnx_model) ###################################################################### @@ -124,7 +135,7 @@ def forward(self, x): # :align: center # # -# Once Netron is open, we can drag and drop our ``my_image_classifier.onnx`` file into the browser or select it after +# Once Netron is open, we can drag and drop our ``image_classifier_model.onnx`` file into the browser or select it after # clicking the **Open model** button. # # .. image:: ../../_static/img/onnx/image_classifier_onnx_model_on_netron_web_ui.png @@ -155,18 +166,17 @@ def forward(self, x): import onnxruntime -onnx_input = [torch_input] -print(f"Input length: {len(onnx_input)}") -print(f"Sample input: {onnx_input}") +onnx_inputs = [tensor.numpy(force=True) for tensor in example_inputs] +print(f"Input length: {len(onnx_inputs)}") +print(f"Sample input: {onnx_inputs}") -ort_session = onnxruntime.InferenceSession("./my_image_classifier.onnx", providers=['CPUExecutionProvider']) +ort_session = onnxruntime.InferenceSession( + "./image_classifier_model.onnx", providers=["CPUExecutionProvider"] +) -def to_numpy(tensor): - return tensor.detach().cpu().numpy() if tensor.requires_grad else tensor.cpu().numpy() +onnxruntime_input = {input_arg.name: input_value for input_arg, input_value in zip(ort_session.get_inputs(), onnx_inputs)} -onnxruntime_input = {k.name: to_numpy(v) for k, v in zip(ort_session.get_inputs(), onnx_input)} - -# onnxruntime returns a list of outputs +# ONNX Runtime returns a list of outputs onnxruntime_outputs = ort_session.run(None, onnxruntime_input)[0] #################################################################### @@ -179,7 +189,7 @@ def to_numpy(tensor): # For that, we need to execute the PyTorch model with the same input and compare the results with ONNX Runtime's. # Before comparing the results, we need to convert the PyTorch's output to match ONNX's format. -torch_outputs = torch_model(torch_input) +torch_outputs = torch_model(*example_inputs) assert len(torch_outputs) == len(onnxruntime_outputs) for torch_output, onnxruntime_output in zip(torch_outputs, onnxruntime_outputs): @@ -209,4 +219,4 @@ def to_numpy(tensor): # # .. toctree:: # :hidden: -# \ No newline at end of file +# diff --git a/beginner_source/onnx/intro_onnx.py b/beginner_source/onnx/intro_onnx.py index 194f971261c..2fb73a86c15 100644 --- a/beginner_source/onnx/intro_onnx.py +++ b/beginner_source/onnx/intro_onnx.py @@ -1,13 +1,14 @@ """ **Introduction to ONNX** || `Exporting a PyTorch model to ONNX `_ || -`Extending the ONNX Registry `_ +`Extending the ONNX exporter operator support `_ || +`Export a model with control flow to ONNX `_ Introduction to ONNX ==================== Authors: -`Ti-Tai Wang `_ and `Xavier Dupré `_ +`Ti-Tai Wang `_, `Thiago Crepaldi `_. `Open Neural Network eXchange (ONNX) `_ is an open standard format for representing machine learning models. The ``torch.onnx`` module provides APIs to @@ -19,21 +20,20 @@ including Microsoft's `ONNX Runtime `_. .. note:: - Currently, there are two flavors of ONNX exporter APIs, - but this tutorial will focus on the ``torch.onnx.dynamo_export``. + Currently, you can choose either through `TorchScript https://pytorch.org/docs/stable/jit.html`_ or + `ExportedProgram https://pytorch.org/docs/stable/export.html`_ to export the model to ONNX by the + boolean parameter dynamo in `torch.onnx.export `_. + In this tutorial, we will focus on the ``ExportedProgram`` approach. -The TorchDynamo engine is leveraged to hook into Python's frame evaluation API and dynamically rewrite its -bytecode into an `FX graph `_. -The resulting FX Graph is polished before it is finally translated into an -`ONNX graph `_. - -The main advantage of this approach is that the `FX graph `_ is captured using -bytecode analysis that preserves the dynamic nature of the model instead of using traditional static tracing techniques. +When setting ``dynamo=True``, the exporter will use `torch.export `_ to capture an ``ExportedProgram``, +before translating the graph into ONNX representations. This approach is the new and recommended way to export models to ONNX. +It works with PyTorch 2.0 features more robustly, has better support for newer ONNX operator sets, and consumes less resources +to make exporting larger models possible. Dependencies ------------ -PyTorch 2.1.0 or newer is required. +PyTorch 2.5.0 or newer is required. The ONNX exporter depends on extra Python packages: @@ -58,8 +58,6 @@ import onnxscript print(onnxscript.__version__) - from onnxscript import opset18 # opset 18 is the latest (and only) supported version for now - import onnxruntime print(onnxruntime.__version__) @@ -78,4 +76,4 @@ .. toctree:: :hidden: -""" +""" \ No newline at end of file diff --git a/beginner_source/onnx/onnx_registry_tutorial.py b/beginner_source/onnx/onnx_registry_tutorial.py index 56c1d0c99a3..e82bc6257de 100644 --- a/beginner_source/onnx/onnx_registry_tutorial.py +++ b/beginner_source/onnx/onnx_registry_tutorial.py @@ -1,14 +1,14 @@ # -*- coding: utf-8 -*- - """ `Introduction to ONNX `_ || `Exporting a PyTorch model to ONNX `_ || -**Extending the ONNX Registry** +**Extending the ONNX exporter operator support** || +`Export a model with control flow to ONNX `_ -Extending the ONNX Registry -=========================== +Extending the ONNX Exporter Operator Support +============================================ -**Authors:** Ti-Tai Wang (titaiwang@microsoft.com) +**Authors:** `Ti-Tai Wang `_, `Justin Chu `_ """ @@ -16,288 +16,242 @@ # Overview # -------- # -# This tutorial is an introduction to ONNX registry, which empowers users to implement new ONNX operators -# or even replace existing operators with a new implementation. +# This tutorial describes how you can create ONNX implementation for unsupported PyTorch operators +# or replace existing implementation with your own. +# +# We will cover three scenarios that require extending the ONNX exporter's operator support: +# +# * Overriding the implementation of an existing PyTorch operator +# * Using custom ONNX operators +# * Supporting a custom PyTorch operator +# +# What you will learn: +# +# - How to override or add support for PyTorch operators in ONNX. +# - How to integrate custom ONNX operators for specialized runtimes. +# - How to implement and translate custom PyTorch operators to ONNX. +# +# Prerequisites +# ~~~~~~~~~~~~~ +# +# Before starting this tutorial, make sure you have completed the following prerequisites: +# +# * ``torch >= 2.6`` +# * The target PyTorch operator +# * Completed the +# `ONNX Script tutorial `_ +# before proceeding +# * The implementation of the operator using `ONNX Script `__ +# +# Overriding the implementation of an existing PyTorch operator +# ------------------------------------------------------------- # -# During the model export to ONNX, the PyTorch model is lowered to an intermediate -# representation composed of `ATen operators `_. -# While ATen operators are maintained by PyTorch core team, it is the responsibility of the ONNX exporter team -# to independently implement each of these operators to ONNX through `ONNX Script `_. -# The users can also replace the behavior implemented by the ONNX exporter team with their own implementation -# to fix bugs or improve performance for a specific ONNX runtime. +# Although the ONNX exporter team does their best efforts to support all PyTorch operators, some of them +# might not be supported yet. In this section, we will demonstrate how you can add +# unsupported PyTorch operators to the ONNX Registry. # -# The ONNX Registry manages the mapping between PyTorch operators and the ONNX operators counterparts and provides -# APIs to extend the registry. +# .. note:: +# The steps to implement unsupported PyTorch operators are the same as those for replacing the implementation of an existing +# PyTorch operator with a custom one. +# Because we don't actually have an unsupported PyTorch operator to use in this tutorial, we are going to leverage +# this and replace the implementation of ``torch.ops.aten.add.Tensor`` with a custom implementation the same way we would +# if the operator was not implemented by the ONNX exporter. +# +# When a model cannot be exported to ONNX due to an unsupported operator, the ONNX exporter will show an error message +# similar to: # -# In this tutorial, we will cover three scenarios that require extending the ONNX registry with custom operators: +# .. code-block:: python # -# * Custom operators with existing ONNX Runtime support -# * Custom operators without ONNX Runtime support +# No decompositions registered for [...] # +# The error message indicates that the unsupported PyTorch operator is ``torch.ops.aten.add.Tensor``. +# The operator is of type ````, and this operator is what we will use as the +# target to register our custom implementation. import torch -import onnxruntime import onnxscript -from onnxscript import opset18 # opset 18 is the latest (and only) supported version for now +# Opset 18 is the standard supported version as of PyTorch 2.6 +from onnxscript import opset18 as op + + +# Create a model that uses the operator torch.ops.aten.add.Tensor +class Model(torch.nn.Module): + def forward(self, input_x, input_y): + return torch.ops.aten.add.Tensor(input_x, input_y) + + +# NOTE: The function signature (including parameter names) must match the signature of the unsupported PyTorch operator. +# https://github.com/pytorch/pytorch/blob/main/aten/src/ATen/native/native_functions.yaml +# All attributes must be annotated with type hints. +def custom_aten_add(self, other, alpha: float = 1.0): + if alpha != 1.0: + alpha = op.CastLike(alpha, other) + other = op.Mul(other, alpha) + # To distinguish the custom implementation from the builtin one, we switch the order of the inputs + return op.Add(other, self) + + +x = torch.tensor([1.0]) +y = torch.tensor([2.0]) + +# Then we provide the custom implementation to the ONNX exporter as a ``custom_translation_table``. +onnx_program = torch.onnx.export( + Model().eval(), + (x, y), + dynamo=True, + custom_translation_table={ + torch.ops.aten.add.Tensor: custom_aten_add, + }, +) +# Optimize the ONNX graph to remove redundant nodes +onnx_program.optimize() ###################################################################### -# Custom operators with existing ONNX Runtime support -# --------------------------------------------------- -# -# In this case, the user creates a model with standard PyTorch operators, but the ONNX runtime -# (e.g. Microsoft's ONNX Runtime) can provide a custom implementation for that kernel, effectively replacing the -# existing implementation in the ONNX Registry. Another use case is when the user wants to use a custom implementation -# of an existing ONNX operator to fix a bug or improve performance of a specific operator. -# To achieve this, we only need to register the new implementation with the existing ATen fully qualified name. -# -# In the following example, we use the ``com.microsoft.Gelu`` from ONNX Runtime, -# which is not the same ``Gelu`` from ONNX spec. Thus, we register the Gelu with -# the namespace ``com.microsoft`` and operator name ``Gelu``. +# Now let's inspect the model and verify the model is using the custom implementation. + +print(onnx_program.model) + +###################################################################### +# The translation is using our custom implementation: In node ``node_Add_0``, ``input_y`` now +# comes first, and ``input_x`` comes second. # -# Before we begin, let's check whether ``aten::gelu.default`` is really supported by the ONNX registry. +# We can use ONNX Runtime to run the model and verify the results by calling +# the :class:`torch.onnx.ONNXProgram` directly on the input tensors. -onnx_registry = torch.onnx.OnnxRegistry() -print(f"aten::gelu.default is supported by ONNX registry: \ - {onnx_registry.is_registered_op(namespace='aten', op_name='gelu', overload='default')}") +result = onnx_program(x, y)[0] +torch.testing.assert_close(result, torch.tensor([3.0])) ###################################################################### -# In our example, ``aten::gelu.default`` operator is supported by the ONNX registry, -# so :meth:`onnx_registry.is_registered_op` returns ``True``. +# Using custom ONNX operators +# --------------------------- +# +# In this case, we create a model with standard PyTorch operators, but the runtime +# (such as Microsoft's ONNX Runtime) can provide a custom implementation for that kernel, effectively replacing the +# existing implementation. +# +# In the following example, we use the ``com.microsoft.Gelu`` operator provided by ONNX Runtime, +# which is not the same ``Gelu`` from ONNX spec. + -class CustomGelu(torch.nn.Module): +class GeluModel(torch.nn.Module): def forward(self, input_x): return torch.ops.aten.gelu(input_x) -# com.microsoft is an official ONNX Runtime namspace -custom_ort = onnxscript.values.Opset(domain="com.microsoft", version=1) -# NOTE: The function signature must match the signature of the unsupported ATen operator. +# Create a namespace for the custom operator using ONNX Script +# ``com.microsoft`` is an official ONNX Runtime namespace +microsoft_op = onnxscript.values.Opset(domain="com.microsoft", version=1) + +# NOTE: The function signature (including parameter names) must match the signature of the unsupported PyTorch operator. # https://github.com/pytorch/pytorch/blob/main/aten/src/ATen/native/native_functions.yaml # NOTE: All attributes must be annotated with type hints. -@onnxscript.script(custom_ort) -def custom_aten_gelu(input_x, approximate: str = "none"): - # We know com.microsoft::Gelu is supported by ONNX Runtime - # It's only not supported by ONNX - return custom_ort.Gelu(input_x) +# The function must be scripted using the ``@onnxscript.script()`` decorator when +# using operators from custom domains. This may be improved in future versions. +from onnxscript import FLOAT + +@onnxscript.script(microsoft_op) +def custom_aten_gelu(self: FLOAT, approximate: str = "none") -> FLOAT: + return microsoft_op.Gelu(self) -onnx_registry = torch.onnx.OnnxRegistry() -onnx_registry.register_op( - namespace="aten", op_name="gelu", overload="default", function=custom_aten_gelu) -export_options = torch.onnx.ExportOptions(onnx_registry=onnx_registry) -aten_gelu_model = CustomGelu() -input_gelu_x = torch.randn(3, 3) +onnx_program = torch.onnx.export( + GeluModel().eval(), + (x,), + dynamo=True, + custom_translation_table={ + torch.ops.aten.gelu.default: custom_aten_gelu, + }, +) -onnx_program = torch.onnx.dynamo_export( - aten_gelu_model, input_gelu_x, export_options=export_options - ) +# Optimize the ONNX graph to remove redundant nodes +onnx_program.optimize() ###################################################################### # Let's inspect the model and verify the model uses op_type ``Gelu`` # from namespace ``com.microsoft``. # -# .. note:: -# :func:`custom_aten_gelu` does not exist in the graph because -# functions with fewer than three operators are inlined automatically. -# - -# graph node domain is the custom domain we registered -assert onnx_program.model_proto.graph.node[0].domain == "com.microsoft" -# graph node name is the function name -assert onnx_program.model_proto.graph.node[0].op_type == "Gelu" +print(onnx_program.model) ###################################################################### -# The following diagram shows ``custom_aten_gelu_model`` ONNX graph using Netron, -# we can see the ``Gelu`` node from module ``com.microsoft`` used in the function: -# -# .. image:: /_static/img/onnx/custom_aten_gelu_model.png -# -# That is all we need to do. As an additional step, we can use ONNX Runtime to run the model, -# and compare the results with PyTorch. -# - -onnx_program.save("./custom_gelu_model.onnx") -ort_session = onnxruntime.InferenceSession( - "./custom_gelu_model.onnx", providers=['CPUExecutionProvider'] - ) - -def to_numpy(tensor): - return tensor.detach().cpu().numpy() if tensor.requires_grad else tensor.cpu().numpy() - -onnx_input = [input_gelu_x] -onnxruntime_input = {k.name: to_numpy(v) for k, v in zip(ort_session.get_inputs(), onnx_input)} -onnxruntime_outputs = ort_session.run(None, onnxruntime_input)[0] +# Similar to the previous example, we can use ONNX Runtime to run the model and verify the results. -torch_outputs = aten_gelu_model(input_gelu_x) +result = onnx_program(x)[0] +torch.testing.assert_close(result, torch.ops.aten.gelu(x)) -assert len(torch_outputs) == len(onnxruntime_outputs) -for torch_output, onnxruntime_output in zip(torch_outputs, onnxruntime_outputs): - torch.testing.assert_close(torch_output, torch.tensor(onnxruntime_output)) ###################################################################### -# Custom operators without ONNX Runtime support -# --------------------------------------------- +# Supporting a custom PyTorch operator +# ------------------------------------ # -# In this case, the operator is not supported by any ONNX runtime, but we -# would like to use it as custom operator in ONNX graph. Therefore, we need to implement -# the operator in three places: -# -# 1. PyTorch FX graph -# 2. ONNX Registry -# 3. ONNX Runtime +# In this case, the operator is an operator that is user implemented and registered to PyTorch. # # In the following example, we would like to use a custom operator # that takes one tensor input, and returns one output. The operator adds # the input to itself, and returns the rounded result. # -# -# Custom Ops Registration in PyTorch FX Graph (Beta) -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -# -# Firstly, we need to implement the operator in PyTorch FX graph. -# This can be done by using ``torch._custom_op``. -# - -# NOTE: This is a beta feature in PyTorch, and is subject to change. -from torch._custom_op import impl as custom_op - -@custom_op.custom_op("mylibrary::addandround_op") -def addandround_op(tensor_x: torch.Tensor) -> torch.Tensor: - ... - -@addandround_op.impl_abstract() -def addandround_op_impl_abstract(tensor_x): - return torch.empty_like(tensor_x) - -@addandround_op.impl("cpu") -def addandround_op_impl(tensor_x): - return torch.round(tensor_x + tensor_x) # add x to itself, and round the result +# Firstly, we assume the custom operator is implemented and registered with ``torch.library.custom_op()``. +# You can refer to `Creating new custom ops in Python `_ +# for a detailed guide on how to create custom operators. -torch._dynamo.allow_in_graph(addandround_op) -class CustomFoo(torch.nn.Module): - def forward(self, tensor_x): - return addandround_op(tensor_x) +# Define and use the operator in PyTorch +@torch.library.custom_op("mylibrary::add_and_round_op", mutates_args=()) +def add_and_round_op(input: torch.Tensor) -> torch.Tensor: + return torch.round(input + input) -input_addandround_x = torch.randn(3) -custom_addandround_model = CustomFoo() - -###################################################################### -# -# Custom Ops Registration in ONNX Registry -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -# -# For the step 2 and 3, we need to implement the operator in ONNX registry. -# In this example, we will implement the operator in ONNX registry -# with the namespace ``test.customop`` and operator name ``CustomOpOne``, -# and ``CustomOpTwo``. These two ops are registered and built in -# `cpu_ops.cc `__. -# +@add_and_round_op.register_fake +def _add_and_round_op_fake(tensor_x): + return torch.empty_like(tensor_x) -custom_opset = onnxscript.values.Opset(domain="test.customop", version=1) +class AddAndRoundModel(torch.nn.Module): + def forward(self, input): + return add_and_round_op(input) -# NOTE: The function signature must match the signature of the unsupported ATen operator. -# https://github.com/pytorch/pytorch/blob/main/aten/src/ATen/native/native_functions.yaml -# NOTE: All attributes must be annotated with type hints. -@onnxscript.script(custom_opset) -def custom_addandround(input_x): - # The same as opset18.Add(x, x) - add_x = custom_opset.CustomOpOne(input_x, input_x) - # The same as opset18.Round(x, x) - round_x = custom_opset.CustomOpTwo(add_x) - # Cast to FLOAT to match the ONNX type - return opset18.Cast(round_x, to=1) +# Implement the custom operator in ONNX using ONNX Script +def onnx_add_and_round(input): + return op.Round(op.Add(input, input)) -onnx_registry = torch.onnx.OnnxRegistry() -onnx_registry.register_op( - namespace="mylibrary", op_name="addandround_op", overload="default", function=custom_addandround - ) -export_options = torch.onnx.ExportOptions(onnx_registry=onnx_registry) -onnx_program = torch.onnx.dynamo_export( - custom_addandround_model, input_addandround_x, export_options=export_options - ) -onnx_program.save("./custom_addandround_model.onnx") +onnx_program = torch.onnx.export( + AddAndRoundModel().eval(), + (x,), + dynamo=True, + custom_translation_table={ + torch.ops.mylibrary.add_and_round_op.default: onnx_add_and_round, + }, +) +# Optimize the ONNX graph to remove redundant nodes +onnx_program.optimize() +print(onnx_program) ###################################################################### -# The ``onnx_program`` exposes the exported model as protobuf through ``onnx_program.model_proto``. -# The graph has one graph nodes for ``custom_addandround``, and inside ``custom_addandround``, -# there are two function nodes, one for each operator. +# The translation is using our custom implementation to translate the ``torch.ops.mylibrary.add_and_round_op.default`` +# operator in the :class:`torch.export.ExportedProgram`` to the ONNX operator ``Add`` and ``Round``. # -assert onnx_program.model_proto.graph.node[0].domain == "test.customop" -assert onnx_program.model_proto.graph.node[0].op_type == "CustomOpOne" -assert onnx_program.model_proto.graph.node[1].domain == "test.customop" -assert onnx_program.model_proto.graph.node[1].op_type == "CustomOpTwo" +###################################################################### +# Finally we verify the results. +result = onnx_program(x)[0] +torch.testing.assert_close(result, add_and_round_op(x)) ###################################################################### -# This is how ``custom_addandround_model`` ONNX graph looks using Netron. -# We can see the two custom operators we used in the function (``CustomOpOne``, and ``CustomOpTwo``), -# and they are from module ``test.customop``: -# -# .. image:: /_static/img/onnx/custom_addandround.png -# -# Custom Ops Registration in ONNX Runtime -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -# -# To link your custom op library to ONNX Runtime, you need to -# compile your C++ code into a shared library and link it to ONNX Runtime. -# Follow the instructions below: -# -# 1. Implement your custom op in C++ by following -# `ONNX Runtime instructions <`https://github.com/microsoft/onnxruntime/blob/gh-pages/docs/reference/operators/add-custom-op.md>`__. -# 2. Download ONNX Runtime source distribution from -# `ONNX Runtime releases `__. -# 3. Compile and link your custom op library to ONNX Runtime, for example: -# -# .. code-block:: bash -# -# $ gcc -shared -o libcustom_op_library.so custom_op_library.cc -L /path/to/downloaded/ort/lib/ -lonnxruntime -fPIC -# -# 4. Run the model with ONNX Runtime Python API and compare the results with PyTorch. -# -# .. code-block:: python -# -# ort_session_options = onnxruntime.SessionOptions() -# -# # NOTE: Link the custom op library to ONNX Runtime and replace the path -# # with the path to your custom op library -# ort_session_options.register_custom_ops_library( -# "/path/to/libcustom_op_library.so" -# ) -# ort_session = onnxruntime.InferenceSession( -# "./custom_addandround_model.onnx", providers=['CPUExecutionProvider'], sess_options=ort_session_options) -# -# def to_numpy(tensor): -# return tensor.detach().cpu().numpy() if tensor.requires_grad else tensor.cpu().numpy() -# -# onnx_input = onnx_program.adapt_torch_inputs_to_onnx(input_addandround_x) -# onnxruntime_input = {k.name: to_numpy(v) for k, v in zip(ort_session.get_inputs(), onnx_input)} -# onnxruntime_outputs = ort_session.run(None, onnxruntime_input) -# -# torch_outputs = custom_addandround_model(input_addandround_x) -# torch_outputs = onnx_program.adapt_torch_outputs_to_onnx(torch_outputs) -# -# assert len(torch_outputs) == len(onnxruntime_outputs) -# for torch_output, onnxruntime_output in zip(torch_outputs, onnxruntime_outputs): -# torch.testing.assert_close(torch_output, torch.tensor(onnxruntime_output)) -# # Conclusion # ---------- # -# Congratulations! In this tutorial, we explored the :class:`ONNXRegistry` API and -# discovered how to create custom implementations for unsupported or existing ATen operators +# Congratulations! In this tutorial, we explored the ``custom_translation_table`` option and +# discovered how to create custom implementations for unsupported or existing PyTorch operators # using ONNX Script. +# # Finally, we leveraged ONNX Runtime to execute the model and compare the results with PyTorch, # providing us with a comprehensive understanding of handling unsupported # operators in the ONNX ecosystem. diff --git a/beginner_source/onnx/onnx_toc.txt b/beginner_source/onnx/onnx_toc.txt index 674f7752c5d..ac293fbedd7 100644 --- a/beginner_source/onnx/onnx_toc.txt +++ b/beginner_source/onnx/onnx_toc.txt @@ -1,2 +1,3 @@ | 1. `Exporting a PyTorch model to ONNX `_ -| 2. `Extending the ONNX registry `_ +| 2. `Extending the ONNX exporter operator support `_ +| 3. `Export a model with control flow to ONNX `_ \ No newline at end of file From 780d5cbf72c4fb738caa3e180e3ad45ee4174f4a Mon Sep 17 00:00:00 2001 From: Ti-Tai Wang Date: Fri, 7 Mar 2025 15:21:08 -0800 Subject: [PATCH 058/347] [ONNX] Add and fix custom cards for ONNX (#3287) * add and fix custom cards for ONNX --- index.rst | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/index.rst b/index.rst index 55c7ce63048..155b63a006d 100644 --- a/index.rst +++ b/index.rst @@ -265,10 +265,17 @@ Welcome to PyTorch Tutorials :tags: Production,ONNX,Backends .. customcarditem:: - :header: Introduction to ONNX Registry - :card_description: Demonstrate end-to-end how to address unsupported operators by using ONNX Registry. + :header: Extending the ONNX exporter operator support + :card_description: Demonstrate end-to-end how to address unsupported operators in ONNX. :image: _static/img/thumbnails/cropped/Exporting-PyTorch-Models-to-ONNX-Graphs.png - :link: advanced/onnx_registry_tutorial.html + :link: beginner/onnx/onnx_registry_tutorial.html + :tags: Production,ONNX,Backends + +.. customcarditem:: + :header: Exporting a model with control flow to ONNX + :card_description: Demonstrate how to handle control flow logic while exporting a PyTorch model to ONNX. + :image: _static/img/thumbnails/cropped/Exporting-PyTorch-Models-to-ONNX-Graphs.png + :link: beginner/onnx/export_control_flow_model_to_onnx_tutorial.html :tags: Production,ONNX,Backends .. Reinforcement Learning From dfeb1aebb3151e8dc04a4be2fbcc941355891012 Mon Sep 17 00:00:00 2001 From: "Thomas J. Fan" Date: Fri, 7 Mar 2025 18:25:42 -0500 Subject: [PATCH 059/347] Fixes backward definition in triton tutorial (#3282) Co-authored-by: Svetlana Karslioglu --- .../torch_compile_user_defined_triton_kernel_tutorial.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/recipes_source/torch_compile_user_defined_triton_kernel_tutorial.py b/recipes_source/torch_compile_user_defined_triton_kernel_tutorial.py index 7f3e6fbf6f8..eb4eb01d972 100644 --- a/recipes_source/torch_compile_user_defined_triton_kernel_tutorial.py +++ b/recipes_source/torch_compile_user_defined_triton_kernel_tutorial.py @@ -257,9 +257,9 @@ def sin_triton(x): # Prefer this to using ``torch.autograd.Function`` (which has various composability footguns # with ``torch.compile``). -def backward(ctx, grad_output): +def backward(ctx, grad): x, = ctx.saved_tensors - return grad_input * x.cos() + return grad * x.cos() def setup_context(ctx, inputs, output): x, = inputs @@ -293,9 +293,9 @@ def mycos(x: torch.Tensor) -> torch.Tensor: wrap_triton(cos_kernel)[(n_elements,)](x, out, n_elements, BLOCK_SIZE=4) return out -def backward(ctx, grad_output): +def backward(ctx, grad): x, = ctx.saved_tensors - return grad_input * mycos(x) + return grad * mycos(x) def setup_context(ctx, inputs, output): x, = inputs From 94024ab2f50c24c26b8264492e0d87d93e0d82b1 Mon Sep 17 00:00:00 2001 From: Jeremy Watt Date: Tue, 11 Mar 2025 12:15:13 -0700 Subject: [PATCH 060/347] Update data_loading_tutorial.py (#3283) RandomSizedCrop depreciated in favor of RandomResizedCrop in 0.9 https://pytorch.org/vision/0.9/transforms.html Co-authored-by: Svetlana Karslioglu --- beginner_source/data_loading_tutorial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beginner_source/data_loading_tutorial.py b/beginner_source/data_loading_tutorial.py index ab9de0d7d73..8f21bb7bff6 100644 --- a/beginner_source/data_loading_tutorial.py +++ b/beginner_source/data_loading_tutorial.py @@ -445,7 +445,7 @@ def show_landmarks_batch(sample_batched): # from torchvision import transforms, datasets # # data_transform = transforms.Compose([ -# transforms.RandomSizedCrop(224), +# transforms.RandomResizedCrop(224), # transforms.RandomHorizontalFlip(), # transforms.ToTensor(), # transforms.Normalize(mean=[0.485, 0.456, 0.406], From 6493011464b7a602d9407d3e9e0eea233dab4196 Mon Sep 17 00:00:00 2001 From: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> Date: Thu, 13 Mar 2025 09:50:15 -0400 Subject: [PATCH 061/347] Monthly Link Check (#3280) * Create MonthlyLinkCheck.yml * Update MonthlyLinkCheck.yml * Update MonthlyLinkCheck.yml * Update MonthlyLinkCheck.yml --- .github/workflows/MonthlyLinkCheck.yml | 44 ++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .github/workflows/MonthlyLinkCheck.yml diff --git a/.github/workflows/MonthlyLinkCheck.yml b/.github/workflows/MonthlyLinkCheck.yml new file mode 100644 index 00000000000..4c8a841c57b --- /dev/null +++ b/.github/workflows/MonthlyLinkCheck.yml @@ -0,0 +1,44 @@ +#Runs once a month and checks links in the repo to ensure they are valid +#If action fails, it creates an issue with the failing links and an "incorrect link" label +#If link is valid but failing, it can be added to the .lycheeignore file +#Action can also be run manually as needed. + + +name: Monthly Link Check +on: + schedule: + - cron: '0 0 1 * *' # Runs at midnight on the first day of every month + workflow_dispatch: # Allows manual triggering of the workflow +jobs: + linkChecker: + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 1 + - name: Check Links + id: lychee + uses: lycheeverse/lychee-action@v2 + with: + args: --accept=200,403,429 --base . --verbose --no-progress './**/*.md' './**/*.html' './**/*.rst' + token: ${{ secrets.CUSTOM_TOKEN }} + fail: true + + - name: Create Issue From File + if: failure() && (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') + uses: peter-evans/create-issue-from-file@v5 + with: + title: Broken links detected in docs 🔗 + content-filepath: ./lychee/out.md + labels: 'incorrect link' + #token: ${{ secrets.CUSTOM_TOKEN }} + + + - name: Suggestions + if: failure() + run: | + echo -e "\nPlease review the links reported in the Check links step above." + echo -e "If a link is valid but fails due to a CAPTCHA challenge, IP blocking, login requirements, etc., consider adding such links to .lycheeignore file to bypass future checks.\n" + exit 1 From ce291f4d8a3ef3c5eec0761f9552a5a6ad95c196 Mon Sep 17 00:00:00 2001 From: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> Date: Thu, 13 Mar 2025 16:30:31 -0400 Subject: [PATCH 062/347] Add Docs Survey (#3289) Add Docs Survey to Tutorials Site. Will be disabled on April 14, 2025 --- _templates/layout.html | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/_templates/layout.html b/_templates/layout.html index e3827929f00..d6946eab087 100644 --- a/_templates/layout.html +++ b/_templates/layout.html @@ -24,7 +24,7 @@ { var div = '

Note

This tutorial describes a prototype feature. Prototype features are typically not available as part of binary distributions like PyPI or Conda, except sometimes behind run-time flags, and are at an early stage for feedback and testing.

' document.getElementById("pytorch-article").insertAdjacentHTML('afterBegin', div) - } + } {%- endblock %} @@ -102,9 +102,9 @@ if (googleSearchboxText) { googleSearchboxText.placeholder = placeholderText; googleSearchboxText.style.fontFamily = 'FreightSans'; - googleSearchboxText.style.fontSize = "1.2rem"; + googleSearchboxText.style.fontSize = "1.2rem"; googleSearchboxText.style.color = '#262626'; - } + } }; }); @@ -114,7 +114,7 @@ {{ super() }} + +//temporarily add a link to survey + + {% endblock %} From 057341fa62c5fd4dda893bd7ab1b424cdf6ff957 Mon Sep 17 00:00:00 2001 From: rice-e <111106282+rice-e@users.noreply.github.com> Date: Fri, 14 Mar 2025 11:47:52 -0500 Subject: [PATCH 063/347] Add OOV token handling to character-level RNN tutorial (#3284) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improves char-rnn tutorial code quality ## Description This PR adds proper handling for Out-Of-Vocabulary (OOV) characters in the character-level RNN tutorial. Problem: - The current implementation doesn't properly handle characters not in the allowed set - Using string.find() returns -1 for unknown characters, causing them to be treated as apostrophes (the last character in the allowed_characters string) - This creates ambiguity between actual apostrophes in names (like O'Brien) and unknown characters Solution: - Added an underscore character "_" as a dedicated OOV token - Modified letterToIndex() to explicitly handle unknown characters - Added comments explaining the purpose of OOV handling - Updated the comment about input nodes (57 → 58) to reflect the added character This change follows best practices for NLP systems by explicitly handling unknown characters, improving both the model's accuracy and the tutorial's educational value. --- .../char_rnn_classification_tutorial.py | 109 +++++++++--------- 1 file changed, 57 insertions(+), 52 deletions(-) diff --git a/intermediate_source/char_rnn_classification_tutorial.py b/intermediate_source/char_rnn_classification_tutorial.py index 67c3f04cbe3..6d56029f5d5 100644 --- a/intermediate_source/char_rnn_classification_tutorial.py +++ b/intermediate_source/char_rnn_classification_tutorial.py @@ -25,7 +25,7 @@ Specifically, we'll train on a few thousand surnames from 18 languages of origin, and predict which language a name is from based on the -spelling. +spelling. Recommended Preparation ======================= @@ -50,13 +50,13 @@ general """ ###################################################################### -# Preparing Torch +# Preparing Torch # ========================== # -# Set up torch to default to the right device use GPU acceleration depending on your hardware (CPU or CUDA). +# Set up torch to default to the right device use GPU acceleration depending on your hardware (CPU or CUDA). # -import torch +import torch # Check if CUDA is available device = torch.device('cpu') @@ -70,7 +70,7 @@ # Preparing the Data # ================== # -# Download the data from `here `__ +# Download the data from `here `__ # and extract it to the current directory. # # Included in the ``data/names`` directory are 18 text files named as @@ -78,16 +78,17 @@ # line, mostly romanized (but we still need to convert from Unicode to # ASCII). # -# The first step is to define and clean our data. Initially, we need to convert Unicode to plain ASCII to -# limit the RNN input layers. This is accomplished by converting Unicode strings to ASCII and allowing only a small set of allowed characters. +# The first step is to define and clean our data. Initially, we need to convert Unicode to plain ASCII to +# limit the RNN input layers. This is accomplished by converting Unicode strings to ASCII and allowing only a small set of allowed characters. -import string +import string import unicodedata -allowed_characters = string.ascii_letters + " .,;'" -n_letters = len(allowed_characters) +# We can use "_" to represent an out-of-vocabulary character, that is, any character we are not handling in our model +allowed_characters = string.ascii_letters + " .,;'" + "_" +n_letters = len(allowed_characters) -# Turn a Unicode string to plain ASCII, thanks to https://stackoverflow.com/a/518232/2809427 +# Turn a Unicode string to plain ASCII, thanks to https://stackoverflow.com/a/518232/2809427 def unicodeToAscii(s): return ''.join( c for c in unicodedata.normalize('NFD', s) @@ -120,7 +121,11 @@ def unicodeToAscii(s): # Find letter index from all_letters, e.g. "a" = 0 def letterToIndex(letter): - return allowed_characters.find(letter) + # return our out-of-vocabulary character if we encounter a letter unknown to our model + if letter not in allowed_characters: + return allowed_characters.find("_") + else: + return allowed_characters.find(letter) # Turn a line into a , # or an array of one-hot letter vectors @@ -137,16 +142,16 @@ def lineToTensor(line): print (f"The name 'Ahn' becomes {lineToTensor('Ahn')}") #notice 'A' sets the 27th index to 1 ######################### -# Congratulations, you have built the foundational tensor objects for this learning task! You can use a similar approach +# Congratulations, you have built the foundational tensor objects for this learning task! You can use a similar approach # for other RNN tasks with text. # -# Next, we need to combine all our examples into a dataset so we can train, test and validate our models. For this, -# we will use the `Dataset and DataLoader `__ classes +# Next, we need to combine all our examples into a dataset so we can train, test and validate our models. For this, +# we will use the `Dataset and DataLoader `__ classes # to hold our dataset. Each Dataset needs to implement three functions: ``__init__``, ``__len__``, and ``__getitem__``. from io import open import glob import os -import time +import time import torch from torch.utils.data import Dataset @@ -155,26 +160,26 @@ class NamesDataset(Dataset): def __init__(self, data_dir): self.data_dir = data_dir #for provenance of the dataset - self.load_time = time.localtime #for provenance of the dataset + self.load_time = time.localtime #for provenance of the dataset labels_set = set() #set of all classes self.data = [] self.data_tensors = [] - self.labels = [] - self.labels_tensors = [] + self.labels = [] + self.labels_tensors = [] #read all the ``.txt`` files in the specified directory - text_files = glob.glob(os.path.join(data_dir, '*.txt')) + text_files = glob.glob(os.path.join(data_dir, '*.txt')) for filename in text_files: label = os.path.splitext(os.path.basename(filename))[0] labels_set.add(label) lines = open(filename, encoding='utf-8').read().strip().split('\n') - for name in lines: + for name in lines: self.data.append(name) self.data_tensors.append(lineToTensor(name)) self.labels.append(label) - #Cache the tensor representation of the labels + #Cache the tensor representation of the labels self.labels_uniq = list(labels_set) for idx in range(len(self.labels)): temp_tensor = torch.tensor([self.labels_uniq.index(self.labels[idx])], dtype=torch.long) @@ -187,7 +192,7 @@ def __getitem__(self, idx): data_item = self.data[idx] data_label = self.labels[idx] data_tensor = self.data_tensors[idx] - label_tensor = self.labels_tensors[idx] + label_tensor = self.labels_tensors[idx] return label_tensor, data_tensor, data_label, data_item @@ -200,17 +205,17 @@ def __getitem__(self, idx): print(f"example = {alldata[0]}") ######################### -#Using the dataset object allows us to easily split the data into train and test sets. Here we create a 80/20 -# split but the ``torch.utils.data`` has more useful utilities. Here we specify a generator since we need to use the -#same device as PyTorch defaults to above. +#Using the dataset object allows us to easily split the data into train and test sets. Here we create a 80/20 +# split but the ``torch.utils.data`` has more useful utilities. Here we specify a generator since we need to use the +#same device as PyTorch defaults to above. train_set, test_set = torch.utils.data.random_split(alldata, [.85, .15], generator=torch.Generator(device=device).manual_seed(2024)) print(f"train examples = {len(train_set)}, validation examples = {len(test_set)}") ######################### -# Now we have a basic dataset containing **20074** examples where each example is a pairing of label and name. We have also -#split the dataset into training and testing so we can validate the model that we build. +# Now we have a basic dataset containing **20074** examples where each example is a pairing of label and name. We have also +#split the dataset into training and testing so we can validate the model that we build. ###################################################################### @@ -222,11 +227,11 @@ def __getitem__(self, idx): # held hidden state and gradients which are now entirely handled by the # graph itself. This means you can implement a RNN in a very "pure" way, # as regular feed-forward layers. -# -# This CharRNN class implements an RNN with three components. +# +# This CharRNN class implements an RNN with three components. # First, we use the `nn.RNN implementation `__. # Next, we define a layer that maps the RNN hidden layers to our output. And finally, we apply a ``softmax`` function. Using ``nn.RNN`` -# leads to a significant improvement in performance, such as cuDNN-accelerated kernels, versus implementing +# leads to a significant improvement in performance, such as cuDNN-accelerated kernels, versus implementing # each layer as a ``nn.Linear``. It also simplifies the implementation in ``forward()``. # @@ -240,7 +245,7 @@ def __init__(self, input_size, hidden_size, output_size): self.rnn = nn.RNN(input_size, hidden_size) self.h2o = nn.Linear(hidden_size, output_size) self.softmax = nn.LogSoftmax(dim=1) - + def forward(self, line_tensor): rnn_out, hidden = self.rnn(line_tensor) output = self.h2o(hidden[0]) @@ -250,14 +255,14 @@ def forward(self, line_tensor): ########################### -# We can then create an RNN with 57 input nodes, 128 hidden nodes, and 18 outputs: +# We can then create an RNN with 58 input nodes, 128 hidden nodes, and 18 outputs: n_hidden = 128 rnn = CharRNN(n_letters, n_hidden, len(alldata.labels_uniq)) -print(rnn) +print(rnn) ###################################################################### -# After that we can pass our Tensor to the RNN to obtain a predicted output. Subsequently, +# After that we can pass our Tensor to the RNN to obtain a predicted output. Subsequently, # we use a helper function, ``label_from_output``, to derive a text label for the class. def label_from_output(output, output_labels): @@ -267,7 +272,7 @@ def label_from_output(output, output_labels): input = lineToTensor('Albert') output = rnn(input) #this is equivalent to ``output = rnn.forward(input)`` -print(output) +print(output) print(label_from_output(output, alldata.labels_uniq)) ###################################################################### @@ -283,13 +288,13 @@ def label_from_output(output, output_labels): # Now all it takes to train this network is show it a bunch of examples, # have it make guesses, and tell it if it's wrong. # -# We do this by defining a ``train()`` function which trains the model on a given dataset using minibatches. RNNs +# We do this by defining a ``train()`` function which trains the model on a given dataset using minibatches. RNNs # RNNs are trained similarly to other networks; therefore, for completeness, we include a batched training method here. -# The loop (``for i in batch``) computes the losses for each of the items in the batch before adjusting the -# weights. This operation is repeated until the number of epochs is reached. +# The loop (``for i in batch``) computes the losses for each of the items in the batch before adjusting the +# weights. This operation is repeated until the number of epochs is reached. -import random -import numpy as np +import random +import numpy as np def train(rnn, training_data, n_epoch = 10, n_batch_size = 64, report_every = 50, learning_rate = 0.2, criterion = nn.NLLLoss()): """ @@ -298,14 +303,14 @@ def train(rnn, training_data, n_epoch = 10, n_batch_size = 64, report_every = 50 # Keep track of losses for plotting current_loss = 0 all_losses = [] - rnn.train() + rnn.train() optimizer = torch.optim.SGD(rnn.parameters(), lr=learning_rate) start = time.time() print(f"training on data set with n = {len(training_data)}") - for iter in range(1, n_epoch + 1): - rnn.zero_grad() # clear the gradients + for iter in range(1, n_epoch + 1): + rnn.zero_grad() # clear the gradients # create some minibatches # we cannot use dataloaders because each of our names is a different length @@ -313,7 +318,7 @@ def train(rnn, training_data, n_epoch = 10, n_batch_size = 64, report_every = 50 random.shuffle(batches) batches = np.array_split(batches, len(batches) //n_batch_size ) - for idx, batch in enumerate(batches): + for idx, batch in enumerate(batches): batch_loss = 0 for i in batch: #for each example in this batch (label_tensor, text_tensor, label, text) = training_data[i] @@ -328,16 +333,16 @@ def train(rnn, training_data, n_epoch = 10, n_batch_size = 64, report_every = 50 optimizer.zero_grad() current_loss += batch_loss.item() / len(batch) - + all_losses.append(current_loss / len(batches) ) if iter % report_every == 0: print(f"{iter} ({iter / n_epoch:.0%}): \t average batch loss = {all_losses[-1]}") current_loss = 0 - + return all_losses ########################################################################## -# We can now train a dataset with minibatches for a specified number of epochs. The number of epochs for this +# We can now train a dataset with minibatches for a specified number of epochs. The number of epochs for this # example is reduced to speed up the build. You can get better results with different parameters. start = time.time() @@ -373,12 +378,12 @@ def train(rnn, training_data, n_epoch = 10, n_batch_size = 64, report_every = 50 def evaluate(rnn, testing_data, classes): confusion = torch.zeros(len(classes), len(classes)) - + rnn.eval() #set to eval mode with torch.no_grad(): # do not record the gradients during eval phase for i in range(len(testing_data)): (label_tensor, text_tensor, label, text) = testing_data[i] - output = rnn(text_tensor) + output = rnn(text_tensor) guess, guess_i = label_from_output(output, classes) label_i = classes.index(label) confusion[label_i][guess_i] += 1 @@ -409,7 +414,7 @@ def evaluate(rnn, testing_data, classes): evaluate(rnn, test_set, classes=alldata.labels_uniq) - + ###################################################################### # You can pick out bright spots off the main axis that show which @@ -429,7 +434,7 @@ def evaluate(rnn, testing_data, classes): # - Try the ``nn.LSTM`` and ``nn.GRU`` layers # - Modify the size of the layers, such as increasing or decreasing the number of hidden nodes or adding additional linear layers # - Combine multiple of these RNNs as a higher level network -# +# # - Try with a different dataset of line -> label, for example: # # - Any word -> language From 622d8bc6411cc57834689499e733c7ae9360c5cf Mon Sep 17 00:00:00 2001 From: Thanh Ha Date: Sat, 15 Mar 2025 00:40:14 -0400 Subject: [PATCH 064/347] Tags in tj-actions/changed-files are compromised (#3290) The tags in tj-actions/changed-files action are compromised and are leaking GitHub secrets in repos using the compromised repo. This pins the action to a known good hash. https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised Signed-off-by: Thanh Ha --- .github/workflows/link_checkPR.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/link_checkPR.yml b/.github/workflows/link_checkPR.yml index 1fde2371590..134542f085a 100644 --- a/.github/workflows/link_checkPR.yml +++ b/.github/workflows/link_checkPR.yml @@ -18,7 +18,7 @@ jobs: - name: Get Changed Files id: changed-files - uses: tj-actions/changed-files@v41 + uses: tj-actions/changed-files@d6e91a2266cdb9d62096cebf1e8546899c6aa18f # v45.0.6 - name: Check for Skip Label id: skip-label From b090df76d804008e5a31f2a396cb11fe9327bac3 Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Mon, 17 Mar 2025 08:15:47 -0700 Subject: [PATCH 065/347] Fix broken links in introyt_index.html (#3291) --- beginner_source/introyt/introyt_index.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/beginner_source/introyt/introyt_index.py b/beginner_source/introyt/introyt_index.py index 63b9c053a3c..9ef60574dd9 100644 --- a/beginner_source/introyt/introyt_index.py +++ b/beginner_source/introyt/introyt_index.py @@ -1,11 +1,11 @@ """ -`Introduction `_ || -`Tensors `_ || -`Autograd `_ || -`Building Models `_ || -`TensorBoard Support `_ || -`Training Models `_ || -`Model Understanding `_ +`Introduction `_ || +`Tensors `_ || +`Autograd `_ || +`Building Models `_ || +`TensorBoard Support `_ || +`Training Models `_ || +`Model Understanding `_ Introduction to PyTorch - YouTube Series ======================================== From 0c7bb1a9b2ec6049c33f3b7754dff957d8878aa6 Mon Sep 17 00:00:00 2001 From: GdoongMathew Date: Tue, 18 Mar 2025 23:03:50 +0800 Subject: [PATCH 066/347] docs/fix pageable memory description in `Other copy directions`. (#3292) Co-authored-by: Vincent Moens --- intermediate_source/pinmem_nonblock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intermediate_source/pinmem_nonblock.py b/intermediate_source/pinmem_nonblock.py index 14d178ab882..4d82a06a989 100644 --- a/intermediate_source/pinmem_nonblock.py +++ b/intermediate_source/pinmem_nonblock.py @@ -547,7 +547,7 @@ def pin_copy_to_device_nonblocking(*tensors): i = -1 for i in range(100): - # Create a tensor in pin-memory + # Create a tensor in pageable memory cpu_tensor = torch.ones(1024, 1024) torch.cuda.synchronize() # Send the tensor to CUDA From 270db76d51e7d931079c378955ef2d490c2e1862 Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Tue, 18 Mar 2025 13:28:34 -0700 Subject: [PATCH 067/347] Fix issue with the post-processing script (#3295) Attempt to use a better regex. --- .jenkins/post_process_notebooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.jenkins/post_process_notebooks.py b/.jenkins/post_process_notebooks.py index 81f51766c3e..1dbd3422a9c 100644 --- a/.jenkins/post_process_notebooks.py +++ b/.jenkins/post_process_notebooks.py @@ -12,7 +12,7 @@ # Pattern to search ``` {.python .jupyter-code-cell} -pattern = re.compile(r'(.*?)``` {.python .jupyter-code-cell}\n\n(from IPython.display import display, HTML\nhtml_code = """\n.*?\n"""\ndisplay\(HTML\(html_code\)\))\n```(.*)', re.DOTALL) +pattern = re.compile(r'(.*?)``` {\.python \.jupyter-code-cell}\n(.*?from IPython\.display import display, HTML.*?display\(HTML\(html_code\)\))\n```(.*)', re.DOTALL) def process_video_cell(notebook_path): From 08a9bcdbc5e23dce147b336ae08877c02e7ef829 Mon Sep 17 00:00:00 2001 From: Richard Zou Date: Wed, 19 Mar 2025 11:08:32 -0400 Subject: [PATCH 068/347] Fix python custom ops tutorial (#3299) We tightned invariants in PyTorch 2.7 that exposed a bug with the python custom ops tutorial. This PR fixes said bug. Test Plan: - tested locally --- advanced_source/python_custom_ops.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/advanced_source/python_custom_ops.py b/advanced_source/python_custom_ops.py index 5ace0b40897..1f20125f785 100644 --- a/advanced_source/python_custom_ops.py +++ b/advanced_source/python_custom_ops.py @@ -112,7 +112,10 @@ def crop(pic: torch.Tensor, box: Sequence[int]) -> torch.Tensor: def _(pic, box): channels = pic.shape[0] x0, y0, x1, y1 = box - return pic.new_empty(channels, y1 - y0, x1 - x0) + result = pic.new_empty(y1 - y0, x1 - x0, channels).permute(2, 0, 1) + # The result should have the same metadata (shape/strides/``dtype``/device) + # as running the ``crop`` function above. + return result ###################################################################### # After this, ``crop`` now works without graph breaks: From 6053b2a50ada2df305e358429d005d869cfce999 Mon Sep 17 00:00:00 2001 From: "Nichols A. Romero" <165712832+naromero77amd@users.noreply.github.com> Date: Wed, 19 Mar 2025 13:09:28 -0500 Subject: [PATCH 069/347] [ROCm] Update C++ Extension Tutorial for AMD GPU (#3197) * Update C++ Extension tutorial with note about AMD GPU. --------- Co-authored-by: Svetlana Karslioglu --- advanced_source/cpp_extension.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/advanced_source/cpp_extension.rst b/advanced_source/cpp_extension.rst index 96cbb9f5cc7..9784cf910ba 100644 --- a/advanced_source/cpp_extension.rst +++ b/advanced_source/cpp_extension.rst @@ -1207,3 +1207,5 @@ examples displayed in this note `here `_. If you have questions, please use `the forums `_. Also be sure to check our `FAQ `_ in case you run into any issues. +A blog on writing extensions for AMD ROCm can be found `here +`_. From 02d251989ecddb264f105fe2fcd1d08855e72096 Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Thu, 20 Mar 2025 16:27:56 -0700 Subject: [PATCH 070/347] Bump torchrl and tensordict to 0.7.2 (#3298) * Bump torchrl and torchdict to 0.7.2 * Add devices * fix semi_structured_sparse.py with default device * Disable semi sparse tutorial --------- Co-authored-by: Vincent Moens --- .ci/docker/requirements.txt | 4 ++-- .jenkins/validate_tutorials_built.py | 1 + advanced_source/coding_ddpg.py | 2 +- advanced_source/semi_structured_sparse.py | 2 ++ intermediate_source/reinforcement_ppo.py | 2 +- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.ci/docker/requirements.txt b/.ci/docker/requirements.txt index c0328356336..89dd788ae70 100644 --- a/.ci/docker/requirements.txt +++ b/.ci/docker/requirements.txt @@ -28,8 +28,8 @@ tensorboard jinja2==3.1.3 pytorch-lightning torchx -torchrl==0.6.0 -tensordict==0.6.0 +torchrl==0.7.2 +tensordict==0.7.2 ax-platform>=0.4.0 nbformat>=5.9.2 datasets diff --git a/.jenkins/validate_tutorials_built.py b/.jenkins/validate_tutorials_built.py index ed790a18872..f78ec11e1aa 100644 --- a/.jenkins/validate_tutorials_built.py +++ b/.jenkins/validate_tutorials_built.py @@ -50,6 +50,7 @@ "intermediate_source/flask_rest_api_tutorial", "intermediate_source/text_to_speech_with_torchaudio", "intermediate_source/tensorboard_profiler_tutorial", # reenable after 2.0 release. + "advanced_source/semi_structured_sparse" # reenable after 3303 is fixed. ] def tutorial_source_dirs() -> List[Path]: diff --git a/advanced_source/coding_ddpg.py b/advanced_source/coding_ddpg.py index 3ee4ddc39bd..90ea4565dab 100644 --- a/advanced_source/coding_ddpg.py +++ b/advanced_source/coding_ddpg.py @@ -1040,7 +1040,7 @@ def ceil_div(x, y): ############################################################################### # let's use the TD(lambda) estimator! -loss_module.make_value_estimator(ValueEstimators.TDLambda, gamma=gamma, lmbda=lmbda) +loss_module.make_value_estimator(ValueEstimators.TDLambda, gamma=gamma, lmbda=lmbda, device=device) ############################################################################### # .. note:: diff --git a/advanced_source/semi_structured_sparse.py b/advanced_source/semi_structured_sparse.py index 38c2c6878b3..1bf3586045f 100644 --- a/advanced_source/semi_structured_sparse.py +++ b/advanced_source/semi_structured_sparse.py @@ -210,6 +210,8 @@ SparseSemiStructuredTensor._FORCE_CUTLASS = True torch.manual_seed(100) +# Set default device to "cuda:0" +torch.set_default_device(torch.device("cuda:0" if torch.cuda.is_available() else "cpu")) ###################################################################### # We’ll also need to define some helper functions that are specific to the diff --git a/intermediate_source/reinforcement_ppo.py b/intermediate_source/reinforcement_ppo.py index b25a6f8c8ac..44d5c05a8a4 100644 --- a/intermediate_source/reinforcement_ppo.py +++ b/intermediate_source/reinforcement_ppo.py @@ -551,7 +551,7 @@ # advantage_module = GAE( - gamma=gamma, lmbda=lmbda, value_network=value_module, average_gae=True + gamma=gamma, lmbda=lmbda, value_network=value_module, average_gae=True, device=device, ) loss_module = ClipPPOLoss( From 9b8204b521a2ea343074abce0cd4feb17ace90df Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Fri, 21 Mar 2025 09:57:09 -0700 Subject: [PATCH 071/347] Update en-wordlist.txt --- en-wordlist.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/en-wordlist.txt b/en-wordlist.txt index 9f318cb99f2..6a794e7786f 100644 --- a/en-wordlist.txt +++ b/en-wordlist.txt @@ -407,6 +407,7 @@ fuser geomean globals grayscale +html handoff hardcode helpdesk @@ -492,6 +493,7 @@ numericalize numpy nvFuser nvFuser's +ok oneDNN opset optimizable @@ -540,6 +542,7 @@ randint randn readably recomputation +reenable regressor reimplement reimplementing From 4c18d79adce483ef541116a74ff1e401cc8ca537 Mon Sep 17 00:00:00 2001 From: clee2000 <44682903+clee2000@users.noreply.github.com> Date: Wed, 26 Mar 2025 14:05:13 -0700 Subject: [PATCH 072/347] Fix typo in README (#3307) Fix typos --------- Co-authored-by: Svetlana Karslioglu --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f17f7f5e3d9..0ef22c19256 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ All the tutorials are now presented as sphinx style documentation at: # Asking a question -If you hve a qestion about a tutorial, post in https://dev-discuss.pytorch.org/ rather than creating an issue in this repo. Your question will be answered much faster on the dev-discuss forum. +If you have a question about a tutorial, post in https://dev-discuss.pytorch.org/ rather than creating an issue in this repo. Your question will be answered much faster on the dev-discuss forum. # Submitting an issue From 63295e89be282b9dba9df708e6316a41c38e0aa0 Mon Sep 17 00:00:00 2001 From: William Wen Date: Tue, 1 Apr 2025 10:32:16 -0700 Subject: [PATCH 073/347] Add system requirements to compile tutorial (#3311) * add system requirements to compile tutorial * Apply suggestions from code review Co-authored-by: Svetlana Karslioglu --------- Co-authored-by: Svetlana Karslioglu --- intermediate_source/torch_compile_tutorial.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/intermediate_source/torch_compile_tutorial.py b/intermediate_source/torch_compile_tutorial.py index 67b055d9ff2..a5c1b345e9c 100644 --- a/intermediate_source/torch_compile_tutorial.py +++ b/intermediate_source/torch_compile_tutorial.py @@ -30,6 +30,10 @@ # - ``numpy`` # - ``scipy`` # - ``tabulate`` +# +# **System Requirements** +# - A C++ compiler, such as ``g++`` +# - Python development package (``python-devel``/``python-dev``) ###################################################################### # NOTE: a modern NVIDIA GPU (H100, A100, or V100) is recommended for this tutorial in From 548ea1c8927cf1d75cba0e9006a90ae78fc5f822 Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Wed, 9 Apr 2025 14:37:01 -0700 Subject: [PATCH 074/347] Update layout.html (#3316) --- _templates/layout.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_templates/layout.html b/_templates/layout.html index d6946eab087..17d95152d3b 100644 --- a/_templates/layout.html +++ b/_templates/layout.html @@ -211,8 +211,8 @@ -//temporarily add a link to survey {% endblock %} From 71695c7da5651cfeb750c6af41b30589f0a09840 Mon Sep 17 00:00:00 2001 From: ZhaoqiongZ <106125927+ZhaoqiongZ@users.noreply.github.com> Date: Wed, 23 Apr 2025 05:02:28 +0800 Subject: [PATCH 084/347] Fix code snippet format issue in inductor_windows (#3339) Fix code snippet format issue in inductor_windows --------- Co-authored-by: Svetlana Karslioglu --- prototype_source/inductor_windows.rst | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/prototype_source/inductor_windows.rst b/prototype_source/inductor_windows.rst index ae1b454865e..1844e6d2735 100644 --- a/prototype_source/inductor_windows.rst +++ b/prototype_source/inductor_windows.rst @@ -44,18 +44,21 @@ Next, let's configure our environment. "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvars64.bat" #. Create and activate a virtual environment: :: + #. Install `PyTorch 2.5 `_ or later for CPU Usage. Install PyTorch 2.7 or later refer to `Getting Started on Intel GPU `_ for XPU usage. + #. Here is an example of how to use TorchInductor on Windows: -.. code-block:: python - - import torch - device="cpu" # or "xpu" for XPU - def foo(x, y): - a = torch.sin(x) - b = torch.cos(x) - return a + b - opt_foo1 = torch.compile(foo) - print(opt_foo1(torch.randn(10, 10).to(device), torch.randn(10, 10).to(device))) + + .. code-block:: python + + import torch + device="cpu" # or "xpu" for XPU + def foo(x, y): + a = torch.sin(x) + b = torch.cos(x) + return a + b + opt_foo1 = torch.compile(foo) + print(opt_foo1(torch.randn(10, 10).to(device), torch.randn(10, 10).to(device))) #. Below is the output of the above example:: From 35c68eac7b7e411ae54ffc3fc9e3863942531d84 Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Tue, 22 Apr 2025 16:03:59 -0500 Subject: [PATCH 085/347] Add a note that foreach feature is a prototype (#3341) * Add a note that foreach feature is a prototype --- recipes_source/foreach_map.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/recipes_source/foreach_map.py b/recipes_source/foreach_map.py index b8bf0aa2836..0225a77e279 100644 --- a/recipes_source/foreach_map.py +++ b/recipes_source/foreach_map.py @@ -1,6 +1,6 @@ """ -(beta) Explicit horizontal fusion with foreach_map and torch.compile -============================================================ +Explicit horizontal fusion with foreach_map and torch.compile +=============================================================== **Author:** `Michael Lazos `_ """ @@ -13,11 +13,17 @@ # allows conversion of any pointwise op in ``torch`` to a horiztonally fused foreach # variant. In this tutorial, we will demonstrate how to implement the Adam optimizer # with ``foreach_map`` to generate a fully fused kernel. -# # # .. note:: # -# This tutorial requires PyTorch 2.7.0 or later. +# This recipe describes a prototype feature. Prototype features are typically +# at an early stage for feedback and testing and are subject to change. +# +# Prerequisites +# ------------- +# +# * PyTorch v2.7.0 or later +# ##################################################################### # Model Setup From a5632da518f96f4c5c7779d2fa0d5cd6e83eb4e1 Mon Sep 17 00:00:00 2001 From: Alanna Burke Date: Wed, 23 Apr 2025 12:19:59 -0400 Subject: [PATCH 086/347] Updating tutorials for 2.7. (#3338) Update the What's New section. --------- Co-authored-by: Svetlana Karslioglu --- index.rst | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/index.rst b/index.rst index 155b63a006d..e4ab3c1d81e 100644 --- a/index.rst +++ b/index.rst @@ -3,13 +3,11 @@ Welcome to PyTorch Tutorials **What's new in PyTorch tutorials?** -* `Dynamic Compilation Control with torch.compiler.set_stance `__ -* `Accelerating PyTorch Transformers by replacing nn.Transformer with Nested Tensors and torch.compile() `__ -* `Understanding the torch.export Flow and Solutions to Common Challenges `__ -* Updated `torch.export Tutorial `__ with automatic dynamic shapes ``Dim.AUTO`` -* Updated `torch.export AOTInductor Tutorial for Python runtime `__ -* Updated `Using User-Defined Triton Kernels with torch.compile `__ with new ``torch.library.triton_op`` -* Updated `Compile Time Caching in torch.compile `__ with new ``Mega-Cache`` +* `Utilizing Torch Function modes with torch.compile `__ +* `Context Parallel Tutorial `__ +* `PyTorch 2 Export Quantization with Intel GPU Backend through Inductor `__ +* `(beta) Explicit horizontal fusion with foreach_map and torch.compile `__ +* Updated `Inductor Windows CPU Tutorial `__ .. raw:: html From 7fc877b9f634a4993e9479d347f500e5d7e9b59a Mon Sep 17 00:00:00 2001 From: Maxime Guerreiro Date: Mon, 28 Apr 2025 18:26:45 +0100 Subject: [PATCH 087/347] Adjust torch.compile() best practices (#3336) * Adjust torch.compile() best practices 1. Add best practice to prefer `mod.compile` over `torch.compile(mod)`, which avoids `_orig_` naming problems. Repro steps: - opt_mod = torch.compile(mod) - train opt_mod - save checkpoint In another script, potentially on a machine that does NOT support `torch.compile`: load checkpoint. This fails with an error, because the checkpoint on `opt_mod` got its params renamed by `torch.compile`: ``` RuntimeError: Error(s) in loading state_dict for VQVAE: Missing key(s) in state_dict: "embedding.weight", "encoder.encoder.net.0.weight", "encoder.encoder.net.0.bias", ... Unexpected key(s) in state_dict: "_orig_mod.embedding.weight", "_orig_mod.encoder.encoder.net.0.weight", "_orig_mod.encoder.encoder.net.0.bias", ... ``` - Add best practice to use, or at least try, `fullgraph=True`. This doesn't always work, but we should encourage it. --------- Co-authored-by: Svetlana Karslioglu --- intermediate_source/torch_compile_tutorial.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/intermediate_source/torch_compile_tutorial.py b/intermediate_source/torch_compile_tutorial.py index a5c1b345e9c..de31af04dc1 100644 --- a/intermediate_source/torch_compile_tutorial.py +++ b/intermediate_source/torch_compile_tutorial.py @@ -101,8 +101,11 @@ def forward(self, x): return torch.nn.functional.relu(self.lin(x)) mod = MyModule() -opt_mod = torch.compile(mod) -print(opt_mod(t)) +mod.compile() +print(mod(t)) +## or: +# opt_mod = torch.compile(mod) +# print(opt_mod(t)) ###################################################################### # torch.compile and Nested Calls @@ -135,8 +138,8 @@ def forward(self, x): return torch.nn.functional.relu(self.outer_lin(x)) outer_mod = OuterModule() -opt_outer_mod = torch.compile(outer_mod) -print(opt_outer_mod(t)) +outer_mod.compile() +print(outer_mod(t)) ###################################################################### # We can also disable some functions from being compiled by using @@ -197,6 +200,12 @@ def outer_function(): # 4. **Compile Leaf Functions First:** In complex models with multiple nested # functions and modules, start by compiling the leaf functions or modules first. # For more information see `TorchDynamo APIs for fine-grained tracing `__. +# +# 5. **Prefer ``mod.compile()`` over ``torch.compile(mod)``:** Avoids ``_orig_`` prefix issues in ``state_dict``. +# +# 6. **Use ``fullgraph=True`` to catch graph breaks:** Helps ensure end-to-end compilation, maximizing speedup +# and compatibility with ``torch.export``. + ###################################################################### # Demonstrating Speedups From bdeca268c0c31c5fc14e2db789ae733a1999c7a5 Mon Sep 17 00:00:00 2001 From: ZhaoqiongZ <106125927+ZhaoqiongZ@users.noreply.github.com> Date: Tue, 29 Apr 2025 01:27:23 +0800 Subject: [PATCH 088/347] fix index format (#3343) Co-authored-by: Svetlana Karslioglu --- prototype_source/inductor_windows.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/prototype_source/inductor_windows.rst b/prototype_source/inductor_windows.rst index 1844e6d2735..871cc48a33e 100644 --- a/prototype_source/inductor_windows.rst +++ b/prototype_source/inductor_windows.rst @@ -22,10 +22,9 @@ Install a Compiler C++ compiler is required for TorchInductor optimization, let's take Microsoft Visual C++ (MSVC) as an example. -1. Download and install `MSVC `_. +#. Download and install `MSVC `_. -1. During Installation, select **Workloads** and then **Desktop & Mobile**. -1. Select a checkmark on **Desktop Development with C++** and install. +#. During Installation, select **Workloads** and then **Desktop & Mobile**. Select a checkmark on **Desktop Development with C++** and install. .. image:: ../_static/img/install_msvc.png From 1988e26824a85f9448ddacef19770d39be740239 Mon Sep 17 00:00:00 2001 From: partev Date: Mon, 28 Apr 2025 13:28:02 -0400 Subject: [PATCH 089/347] fix a typo in optimization_tutorial.py (#3333) Co-authored-by: Svetlana Karslioglu --- beginner_source/basics/optimization_tutorial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beginner_source/basics/optimization_tutorial.py b/beginner_source/basics/optimization_tutorial.py index c6c327f8511..82bfaa8f07c 100644 --- a/beginner_source/basics/optimization_tutorial.py +++ b/beginner_source/basics/optimization_tutorial.py @@ -76,7 +76,7 @@ def forward(self, x): # (`read more `__ about hyperparameter tuning) # # We define the following hyperparameters for training: -# - **Number of Epochs** - the number times to iterate over the dataset +# - **Number of Epochs** - the number of times to iterate over the dataset # - **Batch Size** - the number of data samples propagated through the network before the parameters are updated # - **Learning Rate** - how much to update models parameters at each batch/epoch. Smaller values yield slow learning speed, while large values may result in unpredictable behavior during training. # From 70d21540673864d5fbda0c1a4ec773490b550003 Mon Sep 17 00:00:00 2001 From: partev Date: Mon, 28 Apr 2025 13:28:40 -0400 Subject: [PATCH 090/347] fix a typo in zeroing_out_gradients.py (#3337) Co-authored-by: Svetlana Karslioglu --- recipes_source/recipes/zeroing_out_gradients.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recipes_source/recipes/zeroing_out_gradients.py b/recipes_source/recipes/zeroing_out_gradients.py index 0914edbf558..a4f80354961 100644 --- a/recipes_source/recipes/zeroing_out_gradients.py +++ b/recipes_source/recipes/zeroing_out_gradients.py @@ -182,7 +182,7 @@ def forward(self, x): # ``optimizer.zero_grad()`` as long as all your model parameters are in # that optimizer. Use your best judgment to decide which one to use. # -# Congratulations! You have successfully zeroed out gradients PyTorch. +# Congratulations! You have successfully zeroed out gradients in PyTorch. # # Learn More # ---------- From 5c60fe07d67a98dda60383c9a55e4389f7457c4f Mon Sep 17 00:00:00 2001 From: Daniil Lyakhov Date: Mon, 28 Apr 2025 12:45:04 -0700 Subject: [PATCH 091/347] Add PyTorch 2 Export Quantization for OpenVINO torch.compile backend (#3321) Add PyTorch 2 Export Quantization for OpenVINO torch.compile backend ------ Co-authored-by: Alexander Suslov Co-authored-by: Yamini Nimmagadda Co-authored-by: Svetlana Karslioglu --- en-wordlist.txt | 11 ++ prototype_source/openvino_quantizer.rst | 250 ++++++++++++++++++++++++ prototype_source/prototype_index.rst | 7 + 3 files changed, 268 insertions(+) create mode 100644 prototype_source/openvino_quantizer.rst diff --git a/en-wordlist.txt b/en-wordlist.txt index 6a794e7786f..baf75d75ac0 100644 --- a/en-wordlist.txt +++ b/en-wordlist.txt @@ -698,3 +698,14 @@ TorchServe Inductor’s onwards recompilations +BiasCorrection +ELU +GELU +NNCF +OpenVINO +OpenVINOQuantizer +PReLU +Quantizer +SmoothQuant +quantizer +quantizers \ No newline at end of file diff --git a/prototype_source/openvino_quantizer.rst b/prototype_source/openvino_quantizer.rst new file mode 100644 index 00000000000..9412c772204 --- /dev/null +++ b/prototype_source/openvino_quantizer.rst @@ -0,0 +1,250 @@ +PyTorch 2 Export Quantization for OpenVINO torch.compile Backend +=========================================================================== + +**Authors**: `Daniil Lyakhov `_, `Aamir Nazir `_, `Alexander Suslov `_, `Yamini Nimmagadda `_, `Alexander Kozlov `_ + +Prerequisites +-------------- +- `PyTorch 2 Export Post Training Quantization `_ +- `How to Write a Quantizer for PyTorch 2 Export Quantization `_ + +Introduction +-------------- + +.. note:: + + This is an experimental feature, the quantization API is subject to change. + +This tutorial demonstrates how to use ``OpenVINOQuantizer`` from `Neural Network Compression Framework (NNCF) `_ in PyTorch 2 Export Quantization flow to generate a quantized model customized for the `OpenVINO torch.compile backend `_ and explains how to lower the quantized model into the `OpenVINO `_ representation. +``OpenVINOQuantizer`` unlocks the full potential of low-precision OpenVINO kernels due to the placement of quantizers designed specifically for the OpenVINO. + +The PyTorch 2 export quantization flow uses ``torch.export`` to capture the model into a graph and performs quantization transformations on top of the ATen graph. +This approach is expected to have significantly higher model coverage, improved flexibility, and a simplified UX. +OpenVINO backend compiles the FX Graph generated by TorchDynamo into an optimized OpenVINO model. + +The quantization flow mainly includes four steps: + +- Step 1: Capture the FX Graph from the eager Model based on the `torch export mechanism `_. +- Step 2: Apply the PyTorch 2 Export Quantization flow with OpenVINOQuantizer based on the captured FX Graph. +- Step 3: Lower the quantized model into OpenVINO representation with the `torch.compile `_ API. +- Optional step 4: : Improve quantized model metrics via `quantize_pt2e `_ method. + +The high-level architecture of this flow could look like this: + +:: + + float_model(Python) Example Input + \ / + \ / + —-------------------------------------------------------- + | export | + —-------------------------------------------------------- + | + FX Graph in ATen + | + | OpenVINOQuantizer + | / + —-------------------------------------------------------- + | prepare_pt2e | + | | | + | Calibrate + | | | + | convert_pt2e | + —-------------------------------------------------------- + | + Quantized Model + | + —-------------------------------------------------------- + | Lower into Inductor | + —-------------------------------------------------------- + | + OpenVINO model + +Post Training Quantization +---------------------------- + +Now, we will walk you through a step-by-step tutorial for how to use it with `torchvision resnet18 model `_ +for post training quantization. + +Prerequisite: OpenVINO and NNCF installation +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +OpenVINO and NNCF could be easily installed via `pip distribution `_: + +.. code-block:: bash + + pip install -U pip + pip install openvino, nncf + + +1. Capture FX Graph +^^^^^^^^^^^^^^^^^^^^^ + +We will start by performing the necessary imports, capturing the FX Graph from the eager module. + +.. code-block:: python + + import copy + import openvino.torch + import torch + import torchvision.models as models + from torch.ao.quantization.quantize_pt2e import convert_pt2e + from torch.ao.quantization.quantize_pt2e import prepare_pt2e + + import nncf.torch + + # Create the Eager Model + model_name = "resnet18" + model = models.__dict__[model_name](pretrained=True) + + # Set the model to eval mode + model = model.eval() + + # Create the data, using the dummy data here as an example + traced_bs = 50 + x = torch.randn(traced_bs, 3, 224, 224) + example_inputs = (x,) + + # Capture the FX Graph to be quantized + with torch.no_grad(), nncf.torch.disable_patching(): + exported_model = torch.export.export(model, example_inputs).module() + + + +2. Apply Quantization +^^^^^^^^^^^^^^^^^^^^^^^ + +After we capture the FX Module to be quantized, we will import the OpenVINOQuantizer. + + +.. code-block:: python + + from nncf.experimental.torch.fx import OpenVINOQuantizer + + quantizer = OpenVINOQuantizer() + +``OpenVINOQuantizer`` has several optional parameters that allow tuning the quantization process to get a more accurate model. +Below is the list of essential parameters and their description: + + +* ``preset`` - defines quantization scheme for the model. Two types of presets are available: + + * ``PERFORMANCE`` (default) - defines symmetric quantization of weights and activations + + * ``MIXED`` - weights are quantized with symmetric quantization and the activations are quantized with asymmetric quantization. This preset is recommended for models with non-ReLU and asymmetric activation functions, e.g. ELU, PReLU, GELU, etc. + + .. code-block:: python + + OpenVINOQuantizer(preset=nncf.QuantizationPreset.MIXED) + +* ``model_type`` - used to specify quantization scheme required for specific type of the model. Transformer is the only supported special quantization scheme to preserve accuracy after quantization of Transformer models (BERT, Llama, etc.). None is default, i.e. no specific scheme is defined. + + .. code-block:: python + + OpenVINOQuantizer(model_type=nncf.ModelType.Transformer) + +* ``ignored_scope`` - this parameter can be used to exclude some layers from the quantization process to preserve the model accuracy. For example, when you want to exclude the last layer of the model from quantization. Below are some examples of how to use this parameter: + + .. code-block:: python + + #Exclude by layer name: + names = ['layer_1', 'layer_2', 'layer_3'] + OpenVINOQuantizer(ignored_scope=nncf.IgnoredScope(names=names)) + + #Exclude by layer type: + types = ['Conv2d', 'Linear'] + OpenVINOQuantizer(ignored_scope=nncf.IgnoredScope(types=types)) + + #Exclude by regular expression: + regex = '.*layer_.*' + OpenVINOQuantizer(ignored_scope=nncf.IgnoredScope(patterns=regex)) + + #Exclude by subgraphs: + # In this case, all nodes along all simple paths in the graph + # from input to output nodes will be excluded from the quantization process. + subgraph = nncf.Subgraph(inputs=['layer_1', 'layer_2'], outputs=['layer_3']) + OpenVINOQuantizer(ignored_scope=nncf.IgnoredScope(subgraphs=[subgraph])) + + +* ``target_device`` - defines the target device, the specificity of which will be taken into account during optimization. The following values are supported: ``ANY`` (default), ``CPU``, ``CPU_SPR``, ``GPU``, and ``NPU``. + + .. code-block:: python + + OpenVINOQuantizer(target_device=nncf.TargetDevice.CPU) + +For further details on `OpenVINOQuantizer` please see the `documentation `_. + +After we import the backend-specific Quantizer, we will prepare the model for post-training quantization. +``prepare_pt2e`` folds BatchNorm operators into preceding Conv2d operators, and inserts observers in appropriate places in the model. + +.. code-block:: python + + prepared_model = prepare_pt2e(exported_model, quantizer) + +Now, we will calibrate the ``prepared_model`` after the observers are inserted in the model. + +.. code-block:: python + + # We use the dummy data as an example here + prepared_model(*example_inputs) + +Finally, we will convert the calibrated Model to a quantized Model. ``convert_pt2e`` takes a calibrated model and produces a quantized model. + +.. code-block:: python + + quantized_model = convert_pt2e(prepared_model, fold_quantize=False) + +After these steps, we finished running the quantization flow, and we will get the quantized model. + + +3. Lower into OpenVINO representation +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +After that the FX Graph can utilize OpenVINO optimizations using `torch.compile(…, backend=”openvino”) `_ functionality. + +.. code-block:: python + + with torch.no_grad(), nncf.torch.disable_patching(): + optimized_model = torch.compile(quantized_model, backend="openvino") + + # Running some benchmark + optimized_model(*example_inputs) + + + +The optimized model is using low-level kernels designed specifically for Intel CPU. +This should significantly speed up inference time in comparison with the eager model. + +4. Optional: Improve quantized model metrics +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +NNCF implements advanced quantization algorithms like `SmoothQuant `_ and `BiasCorrection `_, which help +to improve the quantized model metrics while minimizing the output discrepancies between the original and compressed models. +These advanced NNCF algorithms can be accessed via the NNCF `quantize_pt2e` API: + +.. code-block:: python + + from nncf.experimental.torch.fx import quantize_pt2e + + calibration_loader = torch.utils.data.DataLoader(...) + + + def transform_fn(data_item): + images, _ = data_item + return images + + + calibration_dataset = nncf.Dataset(calibration_loader, transform_fn) + quantized_model = quantize_pt2e( + exported_model, quantizer, calibration_dataset, smooth_quant=True, fast_bias_correction=False + ) + + +For further details, please see the `documentation `_ +and a complete `example on Resnet18 quantization `_. + +Conclusion +------------ + +This tutorial introduces how to use torch.compile with the OpenVINO backend and the OpenVINO quantizer. +For more details on NNCF and the NNCF Quantization Flow for PyTorch models, refer to the `NNCF Quantization Guide `_. +For additional information, check out the `OpenVINO Deployment via torch.compile Documentation `_. diff --git a/prototype_source/prototype_index.rst b/prototype_source/prototype_index.rst index a0f7706c61d..5d6a1b5ea9f 100644 --- a/prototype_source/prototype_index.rst +++ b/prototype_source/prototype_index.rst @@ -96,6 +96,13 @@ Prototype features are not available as part of binary distributions like PyPI o :link: ../prototype/pt2e_quant_x86_inductor.html :tags: Quantization +.. customcarditem:: + :header: PyTorch 2 Export Quantization for OpenVINO torch.compile Backend + :card_description: Learn how to use PT2 Export Quantization with OpenVINO torch.compile Backend. + :image: ../_static/img/thumbnails/cropped/generic-pytorch-logo.png + :link: ../prototype/openvino_quantizer.html + :tags: Quantization + .. customcarditem:: :header: PyTorch 2 Export Quantization with Intel GPU Backend through Inductor :card_description: Learn how to use PT2 Export Quantization with Intel GPU Backend through Inductor. From 960ef1df338471eae035157ba8dcc0b78e3d782c Mon Sep 17 00:00:00 2001 From: Alex Mendes da Costa Date: Tue, 29 Apr 2025 09:27:26 -0700 Subject: [PATCH 092/347] Fix typos in colab.rst (#3344) --- beginner_source/colab.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beginner_source/colab.rst b/beginner_source/colab.rst index 329f4884666..e5106a2c81a 100644 --- a/beginner_source/colab.rst +++ b/beginner_source/colab.rst @@ -11,7 +11,7 @@ PyTorch Version in Google Colab ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Wen you are running a tutorial that requires a version of PyTorch that has -jst been released, that version might not be yet available in Google Colab. +just been released, that version might not be yet available in Google Colab. To check that you have the required ``torch`` and compatible domain libraries installed, run ``!pip list``. @@ -27,7 +27,7 @@ Using Tutorial Data from Google Drive in Colab ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We've added a new feature to tutorials that allows users to open the -ntebook associated with a tutorial in Google Colab. You may need to +notebook associated with a tutorial in Google Colab. You may need to copy data to your Google drive account to get the more complex tutorials to work. From 222f291d0fe1d3caef8395bd358362c69de16c7f Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Thu, 1 May 2025 18:09:35 -0500 Subject: [PATCH 093/347] Use 2.7 as stable (#3345) * Use 2.7 as stable * Disable memory_format tutorial as it causes issue with the torch_logs.py tutorial. --- .ci/docker/requirements.txt | 6 +++--- .jenkins/build.sh | 7 ++----- .jenkins/validate_tutorials_built.py | 10 ++-------- conf.py | 6 ++++++ 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/.ci/docker/requirements.txt b/.ci/docker/requirements.txt index 0e95c62c6b2..e6802cb045e 100644 --- a/.ci/docker/requirements.txt +++ b/.ci/docker/requirements.txt @@ -14,7 +14,7 @@ tqdm==4.66.1 numpy==1.24.4 matplotlib librosa -torch==2.6 +torch==2.7 torchvision torchdata networkx @@ -67,7 +67,7 @@ iopath pygame==2.6.0 pycocotools semilearn==0.3.2 -torchao==0.5.0 +torchao==0.10.0 segment_anything==1.0 torchrec==1.1.0; platform_system == "Linux" -fbgemm-gpu==1.1.0; platform_system == "Linux" +fbgemm-gpu==1.2.0; platform_system == "Linux" diff --git a/.jenkins/build.sh b/.jenkins/build.sh index 8786859d7d7..58483c168b5 100755 --- a/.jenkins/build.sh +++ b/.jenkins/build.sh @@ -22,13 +22,10 @@ sudo apt-get install -y pandoc #Install PyTorch Nightly for test. # Nightly - pip install --pre torch torchvision torchaudio -f https://download.pytorch.org/whl/nightly/cu102/torch_nightly.html # Install 2.5 to merge all 2.4 PRs - uncomment to install nightly binaries (update the version as needed). -# sudo pip uninstall -y torch torchvision torchaudio torchtext torchdata -# sudo pip3 install torch==2.6.0 torchvision --no-cache-dir --index-url https://download.pytorch.org/whl/test/cu124 # sudo pip uninstall -y fbgemm-gpu torchrec +# sudo pip uninstall -y torch torchvision torchaudio torchtext torchdata torchrl tensordict # sudo pip3 install fbgemm-gpu==1.1.0 torchrec==1.0.0 --no-cache-dir --index-url https://download.pytorch.org/whl/test/cu124 -sudo pip uninstall -y torch torchvision torchaudio torchtext torchdata torchrl tensordict -pip3 install torch==2.7.0 torchvision torchaudio --no-cache-dir --index-url https://download.pytorch.org/whl/test/cu126 -#sudo pip uninstall -y fbgemm-gpu +# pip3 install torch==2.7.0 torchvision torchaudio --no-cache-dir --index-url https://download.pytorch.org/whl/test/cu126 # Install two language tokenizers for Translation with TorchText tutorial python -m spacy download en_core_web_sm python -m spacy download de_core_news_sm diff --git a/.jenkins/validate_tutorials_built.py b/.jenkins/validate_tutorials_built.py index 3ed1e0c0280..f4586c19b3e 100644 --- a/.jenkins/validate_tutorials_built.py +++ b/.jenkins/validate_tutorials_built.py @@ -51,14 +51,8 @@ "intermediate_source/text_to_speech_with_torchaudio", "intermediate_source/tensorboard_profiler_tutorial", # reenable after 2.0 release. "advanced_source/semi_structured_sparse", # reenable after 3303 is fixed. - "intermediate_source/mario_rl_tutorial", # reenable after 3302 is fixed - "intermediate_source/reinforcement_ppo", # reenable after 3302 is fixed - "intermediate_source/pinmem_nonblock", # reenable after 3302 is fixed - "intermediate_source/dqn_with_rnn_tutorial", # reenable after 3302 is fixed - "advanced_source/pendulum", # reenable after 3302 is fixed - "advanced_source/coding_ddpg", # reenable after 3302 is fixed - "intermediate_source/torchrec_intro_tutorial", # reenable after 3302 is fixed - "recipes_source/recipes/reasoning_about_shapes" # reenable after 3326 is fixed + "intermediate_source/torchrec_intro_tutorial", # reenable after 3302 is fixe + "intermediate_source/memory_format_tutorial", # causes other tutorials like torch_logs fail. "state" issue, reseting dynamo didn't help ] def tutorial_source_dirs() -> List[Path]: diff --git a/conf.py b/conf.py index a12a05d21c2..05cfa11ca1b 100644 --- a/conf.py +++ b/conf.py @@ -99,10 +99,16 @@ def reset_seeds(gallery_conf, fname): torch.cuda.empty_cache() + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False + torch._dynamo.reset() + torch._inductor.config.force_disable_caches = True torch.manual_seed(42) torch.set_default_device(None) random.seed(10) numpy.random.seed(10) + torch.set_grad_enabled(True) + gc.collect() sphinx_gallery_conf = { From fd981a50453c8f46f24b969ba2e05eb12072c022 Mon Sep 17 00:00:00 2001 From: mikaylagawarecki Date: Fri, 2 May 2025 12:01:40 -0400 Subject: [PATCH 094/347] Add GPUDirect Storage prototype tutorial (#3317) * Add GPUDirect Storage prototype tutorial --------- Co-authored-by: Svetlana Karslioglu --- .jenkins/validate_tutorials_built.py | 1 + prototype_source/gpu_direct_storage.py | 132 +++++++++++++++++++++++++ prototype_source/prototype_index.rst | 8 ++ 3 files changed, 141 insertions(+) create mode 100644 prototype_source/gpu_direct_storage.py diff --git a/.jenkins/validate_tutorials_built.py b/.jenkins/validate_tutorials_built.py index f4586c19b3e..f5cd187dbc6 100644 --- a/.jenkins/validate_tutorials_built.py +++ b/.jenkins/validate_tutorials_built.py @@ -31,6 +31,7 @@ "prototype_source/vmap_recipe", "prototype_source/torchscript_freezing", "prototype_source/nestedtensor", + "prototype_source/gpu_direct_storage", # requires specific filesystem + GPUDirect Storage to be set up "recipes_source/recipes/saving_and_loading_models_for_inference", "recipes_source/recipes/saving_multiple_models_in_one_file", "recipes_source/recipes/tensorboard_with_pytorch", diff --git a/prototype_source/gpu_direct_storage.py b/prototype_source/gpu_direct_storage.py new file mode 100644 index 00000000000..2b06c53bc7f --- /dev/null +++ b/prototype_source/gpu_direct_storage.py @@ -0,0 +1,132 @@ +""" +(prototype) Accelerating ``torch.save`` and ``torch.load`` with GPUDirect Storage +================================================================================= + +GPUDirect Storage enables a direct data path for direct memory access transfers +between GPU memory and storage, avoiding a bounce buffer through the CPU. + +In version **2.7**, we introduced new prototype APIs to ``torch.cuda.gds`` that serve as thin wrappers around +the `cuFile APIs `_ +that can be used with ``torch.Tensor`` to achieve improved I/O performance. + +In this tutorial, we will demonstrate how to use the ``torch.cuda.gds`` APIs in conjunction with +checkpoints generated by ``torch.save`` and ``torch.load`` on local filesystem. + +.. grid:: 2 + + .. grid-item-card:: :octicon:`mortar-board;1em;` What you will learn + :class-card: card-prerequisites + + * Understand how to use the ``torch.cuda.gds`` APIs in conjunction with + checkpoints generated by ``torch.save`` and ``torch.load`` on local filesystem + + .. grid-item-card:: :octicon:`list-unordered;1em;` Prerequisites + :class-card: card-prerequisites + + * PyTorch v.2.7.0 or later + * GPUDirect Storage must be installed per + `the documentation `_ + * Ensure that the filesystem that you are saving/loading to supports GPUDirect Storage. +""" + +################################################################################ +# Using GPUDirect Storage with ``torch.save`` and ``torch.load`` +# ------------------------------------------------------------------------------------ +# GPUDirect Storage requires a storage alignment of 4KB. You can toggle this by using +# ``torch.utils.serialization.config.save.storage_alignment``: + +import torch +from torch.utils.serialization import config as serialization_config + +serialization_config.save.storage_alignment = 4096 + +################################################################################ +# The steps involved in the process are as follows: +# * Write the checkpoint file without any actual data. This reserves the space on disk. +# * Read the offsets for the storage associated with each tensor in the checkpoint using ``FakeTensor``. +# * Use ``GDSFile`` to write the appropriate data at these offsets. +# +# Given a state dictionary of tensors that are on the GPU, one can use the ``torch.serialization.skip_data`` context +# manager to save a checkpoint that contains all relevant metadata except the storage bytes. For each ``torch.Storage`` +# in the state dictionary, space will be reserved within the checkpoint for the storage bytes. + +import torch.nn as nn + +m = nn.Linear(5, 10, device='cuda') +sd = m.state_dict() + +with torch.serialization.skip_data(): + torch.save(sd, "checkpoint.pt") + +################################################################################ +# We can get the offsets that each storage should be written to within the checkpoint by loading under +# a ``FakeTensorMode``. A FakeTensor is a tensor that has metadata (such as sizes, strides, dtype, device) +# information about the tensor but does not have any storage bytes. The following snippet will not materialize +# any data but will tag each ``FakeTensor`` with the offset within the checkpoint that +# corresponds to the tensor. +# +# If you are continuously saving the same state dictionary during training, you +# would only need to obtain the offsets once and the same offsets can be re-used. Similarly if tensor is going to +# be saved or loaded to repeatedly you can use the ``torch.cuda.gds.gds_register_buffer`` which wraps +# ``cuFileBufRegister`` to register the storages as GDS buffers. +# +# Note that ``torch.cuda.gds.GdsFile.save_storage`` binds to the synchronous ``cuFileWrite`` API, +# so no synchronization is needed afterwards. + + +import os +from torch._subclasses.fake_tensor import FakeTensorMode + +with FakeTensorMode() as mode: + fake_sd = torch.load("checkpoint.pt") + +for k, v in fake_sd.items(): + print(f"key={k}, offset={v.untyped_storage()._checkpoint_offset}") + +f = torch.cuda.gds.GdsFile("checkpoint.pt", os.O_RDWR) + +for k, v in sd.items(): + offset = fake_sd[k].untyped_storage()._checkpoint_offset + # save_storage is a wrapper around `cuFileWrite` + f.save_storage(v.untyped_storage(), offset) + + +################################################################################ +# We verify correctness of the saved checkpoint by ``torch.load`` and comparing. + +sd_loaded = torch.load("checkpoint.pt") +for k, v in sd_loaded.items(): + assert torch.equal(v, sd[k]) + +################################################################################ +# The loading flow is the inverse: you can use ``torch.load`` with the ``torch.serialization.skip_data`` context +# manager to load everything except the storage bytes. This means that any tensors in the checkpoint will be +# created but their storages will be empty (as if the tensors were created via ``torch.empty``). + +with torch.serialization.skip_data(): + sd_loaded = torch.load("checkpoint.pt") + +################################################################################ +# We once again use the ``FakeTensorMode`` to get the checkpoint offsets and +# ascertain that the loaded checkpoint is the same as the saved checkpoint. +# +# Similar to ``torch.cuda.gds.GdsFile.save_storage``, ``torch.cuda.gds.GdsFile.load_storage`` +# binds to the synchronous ``cuFileRead`` API, so no synchronization is needed afterwards. + +for k, v in sd_loaded.items(): + assert not torch.equal(v, sd[k]) + offset = fake_sd[k].untyped_storage()._checkpoint_offset + # load_storage is a wrapper around `cuFileRead` + f.load_storage(v.untyped_storage(), offset) + +for k, v in sd_loaded.items(): + assert torch.equal(v, sd[k]) + +del f +########################################################## +# Conclusion +# ========== +# +# In this tutorial we have demonstrated how to use the prototype ``torch.cuda.gds`` APIs +# in conjunction with ``torch.save`` and ``torch.load`` on local filesystem. Please +# file an issue in the PyTorch GitHub repo if you have any feedback. diff --git a/prototype_source/prototype_index.rst b/prototype_source/prototype_index.rst index 5d6a1b5ea9f..c4986681dd6 100644 --- a/prototype_source/prototype_index.rst +++ b/prototype_source/prototype_index.rst @@ -268,6 +268,14 @@ Prototype features are not available as part of binary distributions like PyPI o :link: ../prototype/python_extension_autoload.html :tags: Extending-PyTorch, Frontend-APIs +.. GPUDirect Storage +.. customcarditem:: + :header: (prototype) Using GPUDirect Storage + :card_description: Learn how to use GPUDirect Storage in PyTorch. + :image: ../_static/img/thumbnails/cropped/generic-pytorch-logo.png + :link: ../prototype/gpudirect_storage.html + :tags: GPUDirect-Storage + .. End of tutorial card section .. raw:: html From b6b5c2115180d0a8eae1e0ddcfe62441c6763c8f Mon Sep 17 00:00:00 2001 From: "Thomas J. Fan" Date: Fri, 2 May 2025 14:21:40 -0400 Subject: [PATCH 095/347] Remove unused sin_triton in triton tutorial (#3281) Co-authored-by: Svetlana Karslioglu --- .../torch_compile_user_defined_triton_kernel_tutorial.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/recipes_source/torch_compile_user_defined_triton_kernel_tutorial.py b/recipes_source/torch_compile_user_defined_triton_kernel_tutorial.py index eb4eb01d972..10ecd74ce91 100644 --- a/recipes_source/torch_compile_user_defined_triton_kernel_tutorial.py +++ b/recipes_source/torch_compile_user_defined_triton_kernel_tutorial.py @@ -227,12 +227,6 @@ def sin_kernel( output = tl.sin(x) tl.store(out_ptr + offsets, output, mask=mask) -def sin_triton(x): - out = torch.empty_like(x) - n_elements = x.numel() - sin_kernel[(n_elements,)](x, out, n_elements, BLOCK_SIZE=4) - return out - ###################################################################### # You can invoke the ``triton_op`` in one of the following two ways. From 67667efbc6acdf5f82a9c8be5000801e406edc50 Mon Sep 17 00:00:00 2001 From: Shoufa Chen Date: Thu, 8 May 2025 10:30:46 -0700 Subject: [PATCH 096/347] Update distributed_checkpoint_recipe.rst (#3356) --- recipes_source/distributed_checkpoint_recipe.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recipes_source/distributed_checkpoint_recipe.rst b/recipes_source/distributed_checkpoint_recipe.rst index 950839966c5..8a81d63bb65 100644 --- a/recipes_source/distributed_checkpoint_recipe.rst +++ b/recipes_source/distributed_checkpoint_recipe.rst @@ -352,7 +352,7 @@ Alternatively, methods are also provided for users who may wish to convert check dcp_to_torch_save(CHECKPOINT_DIR, TORCH_SAVE_CHECKPOINT_DIR) # converts the torch.save model back to DCP - dcp_to_torch_save(TORCH_SAVE_CHECKPOINT_DIR, f"{CHECKPOINT_DIR}_new") + torch_save_to_dcp(TORCH_SAVE_CHECKPOINT_DIR, f"{CHECKPOINT_DIR}_new") From 85739f5fa917306583caa7cb917027de5bf6c0f8 Mon Sep 17 00:00:00 2001 From: Chris Ohk Date: Tue, 13 May 2025 01:19:41 +0900 Subject: [PATCH 097/347] fix: Correct filename in README.txt (#3357) --- beginner_source/basics/README.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beginner_source/basics/README.txt b/beginner_source/basics/README.txt index 23d6282fd45..d247c7253ea 100644 --- a/beginner_source/basics/README.txt +++ b/beginner_source/basics/README.txt @@ -13,7 +13,7 @@ Learn the Basics Tensors https://pytorch.org/tutorials/beginner/basics/tensor_tutorial.html -4. dataquickstart_tutorial.py +4. data_tutorial.py Datasets & DataLoaders https://pytorch.org/tutorials/beginner/basics/data_tutorial.html From 6ca0b804c2918b055a105662b7d29890970c781b Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Thu, 15 May 2025 10:26:52 -0500 Subject: [PATCH 098/347] Improve discoverability of the transformer_building_blocks tutorial (#3359) * Improve discoverability of the transformer_building_blocks tutorial --- intermediate_source/transformer_building_blocks.py | 3 +++ prototype_source/nestedtensor.py | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/intermediate_source/transformer_building_blocks.py b/intermediate_source/transformer_building_blocks.py index 36b2019f19f..67860b85b79 100644 --- a/intermediate_source/transformer_building_blocks.py +++ b/intermediate_source/transformer_building_blocks.py @@ -1,4 +1,7 @@ """ +.. meta:: + :description: Learn how to optimize transformer models by replacing nn.Transformer with Nested Tensors and torch.compile() for significant performance gains in PyTorch. + Accelerating PyTorch Transformers by replacing ``nn.Transformer`` with Nested Tensors and ``torch.compile()`` ============================================================================================================= **Author:** `Mikayla Gawarecki `_ diff --git a/prototype_source/nestedtensor.py b/prototype_source/nestedtensor.py index ecf099c1e02..6578cf73454 100644 --- a/prototype_source/nestedtensor.py +++ b/prototype_source/nestedtensor.py @@ -369,3 +369,8 @@ def benchmark(func, *args, **kwargs): # how implement multi-head attention for transformers in a way that avoids computation on padding. # For more information, check out the docs for the # `torch.nested `__ namespace. +# +# See Also +# -------- +# +# * `Accelerating PyTorch Transformers by replacing nn.Transformer with Nested Tensors and torch.compile() Date: Thu, 15 May 2025 12:04:27 -0400 Subject: [PATCH 099/347] Fix broken link in prototype_index (#3360) --- prototype_source/prototype_index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prototype_source/prototype_index.rst b/prototype_source/prototype_index.rst index c4986681dd6..d8839020633 100644 --- a/prototype_source/prototype_index.rst +++ b/prototype_source/prototype_index.rst @@ -273,7 +273,7 @@ Prototype features are not available as part of binary distributions like PyPI o :header: (prototype) Using GPUDirect Storage :card_description: Learn how to use GPUDirect Storage in PyTorch. :image: ../_static/img/thumbnails/cropped/generic-pytorch-logo.png - :link: ../prototype/gpudirect_storage.html + :link: ../prototype/gpu_direct_storage.html :tags: GPUDirect-Storage .. End of tutorial card section From 78933b112bcc014b9f0b6c49ed1a2133e83cc74e Mon Sep 17 00:00:00 2001 From: clee2000 <44682903+clee2000@users.noreply.github.com> Date: Thu, 15 May 2025 13:20:18 -0700 Subject: [PATCH 100/347] [build] Monkeypatch gen_rst to call inside subprocess (#3355) Replace sphinx gallery's generate_file_rst call with a wrapper that calls that function but in a subprocess, and remove some things that we added in order to handle files changing global state I tried using multiprocess.pool and apply async but ran into pickling problems. I don't understand why this current implementation doesn't have that issue parallelism got added in some version of sphinx gallery, but it uses joblib, which seems to result in errors (conflicts with functions in subprocess library? im not sure). Additionally, it has no effect if you set parallel = 1 (it will not put each file run into its own process and run singly) so you need parallel >= 2, but I'm not sure if there are any tutorials that require being run on their own (ex for memory profiling) Afaict (although I am not familiar with joblib) the way I do it here is similar to how sphinx gallery does it with job lib but they can wrap the actual call instead of needing to replace the function https://github.com/sphinx-gallery/sphinx-gallery/blob/dd092a09513ea1d0616ac9e59b8d76d5a8217e4a/sphinx_gallery/gen_rst.py#L594-L608 I also tried to call sphinx once per file in https://github.com/pytorch/tutorials/pull/3351 but there's an over head of ~5 min per file due to sphinx doing extra stuff (generating the html for all the other files?) resulting in taking 2x longer --- .jenkins/validate_tutorials_built.py | 1 - conf.py | 57 +++++++++++++++++++--------- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/.jenkins/validate_tutorials_built.py b/.jenkins/validate_tutorials_built.py index f5cd187dbc6..5c9e60e90bd 100644 --- a/.jenkins/validate_tutorials_built.py +++ b/.jenkins/validate_tutorials_built.py @@ -53,7 +53,6 @@ "intermediate_source/tensorboard_profiler_tutorial", # reenable after 2.0 release. "advanced_source/semi_structured_sparse", # reenable after 3303 is fixed. "intermediate_source/torchrec_intro_tutorial", # reenable after 3302 is fixe - "intermediate_source/memory_format_tutorial", # causes other tutorials like torch_logs fail. "state" issue, reseting dynamo didn't help ] def tutorial_source_dirs() -> List[Path]: diff --git a/conf.py b/conf.py index 05cfa11ca1b..e189f9449fc 100644 --- a/conf.py +++ b/conf.py @@ -33,8 +33,6 @@ sys.path.insert(0, os.path.abspath('./.jenkins')) import pytorch_sphinx_theme import torch -import numpy -import gc import glob import random import shutil @@ -49,6 +47,46 @@ pio.renderers.default = 'sphinx_gallery' +import sphinx_gallery.gen_rst +import multiprocessing + +# Monkey patch sphinx gallery to run each example in an isolated process so that +# we don't need to worry about examples changing global state. +# +# Alt option 1: Parallelism was added to sphinx gallery (a later version that we +# are not using yet) using joblib, but it seems to result in errors for us, and +# it has no effect if you set parallel = 1 (it will not put each file run into +# its own process and run singly) so you need parallel >= 2, and there may be +# tutorials that cannot be run in parallel. +# +# Alt option 2: Run sphinx gallery once per file (similar to how we shard in CI +# but with shard sizes of 1), but running sphinx gallery for each file has a +# ~5min overhead, resulting in the entire suite taking ~2x time +def call_fn(func, args, kwargs, result_queue): + try: + result = func(*args, **kwargs) + result_queue.put((True, result)) + except Exception as e: + result_queue.put((False, str(e))) + +def call_in_subprocess(func): + def wrapper(*args, **kwargs): + result_queue = multiprocessing.Queue() + p = multiprocessing.Process( + target=call_fn, + args=(func, args, kwargs, result_queue) + ) + p.start() + p.join() + success, result = result_queue.get() + if success: + return result + else: + raise RuntimeError(f"Error in subprocess: {result}") + return wrapper + +sphinx_gallery.gen_rst.generate_file_rst = call_in_subprocess(sphinx_gallery.gen_rst.generate_file_rst) + try: import torchvision except ImportError: @@ -97,20 +135,6 @@ # -- Sphinx-gallery configuration -------------------------------------------- -def reset_seeds(gallery_conf, fname): - torch.cuda.empty_cache() - torch.backends.cudnn.deterministic = True - torch.backends.cudnn.benchmark = False - torch._dynamo.reset() - torch._inductor.config.force_disable_caches = True - torch.manual_seed(42) - torch.set_default_device(None) - random.seed(10) - numpy.random.seed(10) - torch.set_grad_enabled(True) - - gc.collect() - sphinx_gallery_conf = { 'examples_dirs': ['beginner_source', 'intermediate_source', 'advanced_source', 'recipes_source', 'prototype_source'], @@ -121,7 +145,6 @@ def reset_seeds(gallery_conf, fname): 'first_notebook_cell': ("# For tips on running notebooks in Google Colab, see\n" "# https://pytorch.org/tutorials/beginner/colab\n" "%matplotlib inline"), - 'reset_modules': (reset_seeds), 'ignore_pattern': r'_torch_export_nightly_tutorial.py', 'pypandoc': {'extra_args': ['--mathjax', '--toc'], 'filters': ['.jenkins/custom_pandoc_filter.py'], From bb88078eae38151edab2d44e55c3c7f36c6a31fa Mon Sep 17 00:00:00 2001 From: "Wei (Will) Feng" <134637289+weifengpy@users.noreply.github.com> Date: Fri, 16 May 2025 13:20:31 -0700 Subject: [PATCH 101/347] FSDP2 tutorial (#3358) * FSDP2 tutorial * FSDP1 deprecation msg * fix typos --- _static/img/distributed/fsdp_implicit.png | Bin 0 -> 41760 bytes index.rst | 6 +- intermediate_source/FSDP1_tutorial.rst | 448 +++++++++++++++ intermediate_source/FSDP_tutorial.rst | 633 ++++++++++------------ 4 files changed, 749 insertions(+), 338 deletions(-) create mode 100644 _static/img/distributed/fsdp_implicit.png create mode 100644 intermediate_source/FSDP1_tutorial.rst diff --git a/_static/img/distributed/fsdp_implicit.png b/_static/img/distributed/fsdp_implicit.png new file mode 100644 index 0000000000000000000000000000000000000000..85b19b7e72e87ddc4b8f6ef076b3b313ab4d4156 GIT binary patch literal 41760 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 literal 0 HcmV?d00001 diff --git a/index.rst b/index.rst index e4ab3c1d81e..da64acca181 100644 --- a/index.rst +++ b/index.rst @@ -766,14 +766,14 @@ Welcome to PyTorch Tutorials :tags: Parallel-and-Distributed-Training .. customcarditem:: - :header: Getting Started with Fully Sharded Data Parallel(FSDP) - :card_description: Learn how to train models with Fully Sharded Data Parallel package. + :header: Getting Started with Fully Sharded Data Parallel (FSDP2) + :card_description: Learn how to train models with Fully Sharded Data Parallel (fully_shard) package. :image: _static/img/thumbnails/cropped/Getting-Started-with-FSDP.png :link: intermediate/FSDP_tutorial.html :tags: Parallel-and-Distributed-Training .. customcarditem:: - :header: Advanced Model Training with Fully Sharded Data Parallel (FSDP) + :header: Advanced Model Training with Fully Sharded Data Parallel (FSDP1) :card_description: Explore advanced model training with Fully Sharded Data Parallel package. :image: _static/img/thumbnails/cropped/Getting-Started-with-FSDP.png :link: intermediate/FSDP_advanced_tutorial.html diff --git a/intermediate_source/FSDP1_tutorial.rst b/intermediate_source/FSDP1_tutorial.rst new file mode 100644 index 00000000000..b983879a449 --- /dev/null +++ b/intermediate_source/FSDP1_tutorial.rst @@ -0,0 +1,448 @@ +Getting Started with Fully Sharded Data Parallel(FSDP) +====================================================== + +**Author**: `Hamid Shojanazeri `__, `Yanli Zhao `__, `Shen Li `__ + +.. note:: + FSDP1 is deprecated. Please check out `FSDP2 tutorial `_. + +Training AI models at a large scale is a challenging task that requires a lot of compute power and resources. +It also comes with considerable engineering complexity to handle the training of these very large models. +`PyTorch FSDP `__, released in PyTorch 1.11 makes this easier. + +In this tutorial, we show how to use `FSDP APIs `__, for simple MNIST models that can be extended to other larger models such as `HuggingFace BERT models `__, +`GPT 3 models up to 1T parameters `__ . The sample DDP MNIST code courtesy of `Patrick Hu `_. + + +How FSDP works +-------------- +In `DistributedDataParallel `__, (DDP) training, each process/ worker owns a replica of the model and processes a batch of data, finally it uses all-reduce to sum up gradients over different workers. In DDP the model weights and optimizer states are replicated across all workers. FSDP is a type of data parallelism that shards model parameters, optimizer states and gradients across DDP ranks. + +When training with FSDP, the GPU memory footprint is smaller than when training with DDP across all workers. This makes the training of some very large models feasible by allowing larger models or batch sizes to fit on device. This comes with the cost of increased communication volume. The communication overhead is reduced by internal optimizations like overlapping communication and computation. + +.. figure:: /_static/img/distributed/fsdp_workflow.png + :width: 100% + :align: center + :alt: FSDP workflow + + FSDP Workflow + +At a high level FSDP works as follow: + +*In constructor* + +* Shard model parameters and each rank only keeps its own shard + +*In forward path* + +* Run all_gather to collect all shards from all ranks to recover the full parameter in this FSDP unit +* Run forward computation +* Discard parameter shards it has just collected + +*In backward path* + +* Run all_gather to collect all shards from all ranks to recover the full parameter in this FSDP unit +* Run backward computation +* Run reduce_scatter to sync gradients +* Discard parameters. + +One way to view FSDP's sharding is to decompose the DDP gradient all-reduce into reduce-scatter and all-gather. Specifically, during the backward pass, FSDP reduces and scatters gradients, ensuring that each rank possesses a shard of the gradients. Then it updates the corresponding shard of the parameters in the optimizer step. Finally, in the subsequent forward pass, it performs an all-gather operation to collect and combine the updated parameter shards. + +.. figure:: /_static/img/distributed/fsdp_sharding.png + :width: 100% + :align: center + :alt: FSDP allreduce + + FSDP Allreduce + +How to use FSDP +--------------- +Here we use a toy model to run training on the MNIST dataset for demonstration purposes. The APIs and logic can be applied to training larger models as well. + +*Setup* + +1.1 Install PyTorch along with Torchvision + +See the `Get Started guide `__ for information on installation. + +We add the following code snippets to a python script “FSDP_mnist.py”. + +1.2 Import necessary packages + +.. note:: + This tutorial is intended for PyTorch versions 1.12 and later. If you are using an earlier version, replace all instances of `size_based_auto_wrap_policy` with `default_auto_wrap_policy` and `fsdp_auto_wrap_policy` with `auto_wrap_policy`. + +.. code-block:: python + + # Based on: https://github.com/pytorch/examples/blob/master/mnist/main.py + import os + import argparse + import functools + import torch + import torch.nn as nn + import torch.nn.functional as F + import torch.optim as optim + from torchvision import datasets, transforms + + + from torch.optim.lr_scheduler import StepLR + + import torch.distributed as dist + import torch.multiprocessing as mp + from torch.nn.parallel import DistributedDataParallel as DDP + from torch.utils.data.distributed import DistributedSampler + from torch.distributed.fsdp import FullyShardedDataParallel as FSDP + from torch.distributed.fsdp.fully_sharded_data_parallel import ( + CPUOffload, + BackwardPrefetch, + ) + from torch.distributed.fsdp.wrap import ( + size_based_auto_wrap_policy, + enable_wrap, + wrap, + ) + +1.3 Distributed training setup. As we mentioned FSDP is a type of data parallelism which requires a distributed training environment, so here we use two helper functions to initialize the processes for distributed training and clean up. + +.. code-block:: python + + def setup(rank, world_size): + os.environ['MASTER_ADDR'] = 'localhost' + os.environ['MASTER_PORT'] = '12355' + + # initialize the process group + dist.init_process_group("nccl", rank=rank, world_size=world_size) + + def cleanup(): + dist.destroy_process_group() + +2.1 Define our toy model for handwritten digit classification. + +.. code-block:: python + + 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.Dropout(0.25) + self.dropout2 = nn.Dropout(0.5) + self.fc1 = nn.Linear(9216, 128) + self.fc2 = nn.Linear(128, 10) + + def forward(self, x): + + x = self.conv1(x) + x = F.relu(x) + x = self.conv2(x) + x = F.relu(x) + x = F.max_pool2d(x, 2) + x = self.dropout1(x) + x = torch.flatten(x, 1) + x = self.fc1(x) + x = F.relu(x) + x = self.dropout2(x) + x = self.fc2(x) + output = F.log_softmax(x, dim=1) + return output + +2.2 Define a train function + +.. code-block:: python + + def train(args, model, rank, world_size, train_loader, optimizer, epoch, sampler=None): + model.train() + ddp_loss = torch.zeros(2).to(rank) + if sampler: + sampler.set_epoch(epoch) + for batch_idx, (data, target) in enumerate(train_loader): + data, target = data.to(rank), target.to(rank) + optimizer.zero_grad() + output = model(data) + loss = F.nll_loss(output, target, reduction='sum') + loss.backward() + optimizer.step() + ddp_loss[0] += loss.item() + ddp_loss[1] += len(data) + + dist.all_reduce(ddp_loss, op=dist.ReduceOp.SUM) + if rank == 0: + print('Train Epoch: {} \tLoss: {:.6f}'.format(epoch, ddp_loss[0] / ddp_loss[1])) + +2.3 Define a validation function + +.. code-block:: python + + def test(model, rank, world_size, test_loader): + model.eval() + correct = 0 + ddp_loss = torch.zeros(3).to(rank) + with torch.no_grad(): + for data, target in test_loader: + data, target = data.to(rank), target.to(rank) + output = model(data) + ddp_loss[0] += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss + pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability + ddp_loss[1] += pred.eq(target.view_as(pred)).sum().item() + ddp_loss[2] += len(data) + + dist.all_reduce(ddp_loss, op=dist.ReduceOp.SUM) + + if rank == 0: + test_loss = ddp_loss[0] / ddp_loss[2] + print('Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.2f}%)\n'.format( + test_loss, int(ddp_loss[1]), int(ddp_loss[2]), + 100. * ddp_loss[1] / ddp_loss[2])) + +2.4 Define a distributed train function that wraps the model in FSDP + +**Note: to save the FSDP model, we need to call the state_dict on each rank then on Rank 0 save the overall states.** + +.. code-block:: python + + def fsdp_main(rank, world_size, args): + setup(rank, world_size) + + transform=transforms.Compose([ + transforms.ToTensor(), + transforms.Normalize((0.1307,), (0.3081,)) + ]) + + dataset1 = datasets.MNIST('../data', train=True, download=True, + transform=transform) + dataset2 = datasets.MNIST('../data', train=False, + transform=transform) + + sampler1 = DistributedSampler(dataset1, rank=rank, num_replicas=world_size, shuffle=True) + sampler2 = DistributedSampler(dataset2, rank=rank, num_replicas=world_size) + + train_kwargs = {'batch_size': args.batch_size, 'sampler': sampler1} + test_kwargs = {'batch_size': args.test_batch_size, 'sampler': sampler2} + cuda_kwargs = {'num_workers': 2, + 'pin_memory': True, + 'shuffle': False} + train_kwargs.update(cuda_kwargs) + test_kwargs.update(cuda_kwargs) + + train_loader = torch.utils.data.DataLoader(dataset1,**train_kwargs) + test_loader = torch.utils.data.DataLoader(dataset2, **test_kwargs) + my_auto_wrap_policy = functools.partial( + size_based_auto_wrap_policy, min_num_params=100 + ) + torch.cuda.set_device(rank) + + + init_start_event = torch.cuda.Event(enable_timing=True) + init_end_event = torch.cuda.Event(enable_timing=True) + + model = Net().to(rank) + + model = FSDP(model) + + optimizer = optim.Adadelta(model.parameters(), lr=args.lr) + + scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma) + init_start_event.record() + for epoch in range(1, args.epochs + 1): + train(args, model, rank, world_size, train_loader, optimizer, epoch, sampler=sampler1) + test(model, rank, world_size, test_loader) + scheduler.step() + + init_end_event.record() + + if rank == 0: + init_end_event.synchronize() + print(f"CUDA event elapsed time: {init_start_event.elapsed_time(init_end_event) / 1000}sec") + print(f"{model}") + + if args.save_model: + # use a barrier to make sure training is done on all ranks + dist.barrier() + states = model.state_dict() + if rank == 0: + torch.save(states, "mnist_cnn.pt") + + cleanup() + + + +2.5 Finally, parse the arguments and set the main function + +.. code-block:: python + + if __name__ == '__main__': + # Training settings + parser = argparse.ArgumentParser(description='PyTorch MNIST Example') + parser.add_argument('--batch-size', type=int, default=64, metavar='N', + help='input batch size for training (default: 64)') + parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N', + help='input batch size for testing (default: 1000)') + parser.add_argument('--epochs', type=int, default=10, metavar='N', + help='number of epochs to train (default: 14)') + parser.add_argument('--lr', type=float, default=1.0, metavar='LR', + help='learning rate (default: 1.0)') + parser.add_argument('--gamma', type=float, default=0.7, metavar='M', + help='Learning rate step gamma (default: 0.7)') + parser.add_argument('--no-cuda', action='store_true', default=False, + help='disables CUDA training') + parser.add_argument('--seed', type=int, default=1, metavar='S', + help='random seed (default: 1)') + parser.add_argument('--save-model', action='store_true', default=False, + help='For Saving the current Model') + args = parser.parse_args() + + torch.manual_seed(args.seed) + + WORLD_SIZE = torch.cuda.device_count() + mp.spawn(fsdp_main, + args=(WORLD_SIZE, args), + nprocs=WORLD_SIZE, + join=True) + + +We have recorded cuda events to measure the time of FSDP model specifics. The CUDA event time was 110.85 seconds. + +.. code-block:: bash + + python FSDP_mnist.py + + CUDA event elapsed time on training loop 40.67462890625sec + +Wrapping the model with FSDP, the model will look as follows, we can see the model has been wrapped in one FSDP unit. +Alternatively, we will look at adding the auto_wrap_policy next and will discuss the differences. + +.. code-block:: bash + + FullyShardedDataParallel( + (_fsdp_wrapped_module): FlattenParamsWrapper( + (_fpw_module): Net( + (conv1): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1)) + (conv2): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1)) + (dropout1): Dropout(p=0.25, inplace=False) + (dropout2): Dropout(p=0.5, inplace=False) + (fc1): Linear(in_features=9216, out_features=128, bias=True) + (fc2): Linear(in_features=128, out_features=10, bias=True) + ) + ) + ) + +The following is the peak memory usage from FSDP MNIST training on g4dn.12.xlarge AWS EC2 instance with 4 GPUs captured from PyTorch Profiler. + + +.. figure:: /_static/img/distributed/FSDP_memory.gif + :width: 100% + :align: center + :alt: FSDP peak memory + + FSDP Peak Memory Usage + +Applying *auto_wrap_policy* in FSDP otherwise, FSDP will put the entire model in one FSDP unit, which will reduce computation efficiency and memory efficiency. +The way it works is that, suppose your model contains 100 Linear layers. If you do FSDP(model), there will only be one FSDP unit which wraps the entire model. +In that case, the allgather would collect the full parameters for all 100 linear layers, and hence won't save CUDA memory for parameter sharding. +Also, there is only one blocking allgather call for the all 100 linear layers, there will not be communication and computation overlapping between layers. + +To avoid that, you can pass in an auto_wrap_policy, which will seal the current FSDP unit and start a new one automatically when the specified condition is met (e.g., size limit). +In that way you will have multiple FSDP units, and only one FSDP unit needs to collect full parameters at a time. E.g., suppose you have 5 FSDP units, and each wraps 20 linear layers. +Then, in the forward, the 1st FSDP unit will allgather parameters for the first 20 linear layers, do computation, discard the parameters and then move on to the next 20 linear layers. So, at any point in time, each rank only materializes parameters/grads for 20 linear layers instead of 100. + + +To do so in 2.4 we define the auto_wrap_policy and pass it to FSDP wrapper, in the following example, my_auto_wrap_policy defines that a layer could be wrapped or sharded by FSDP if the number of parameters in this layer is larger than 100. +If the number of parameters in this layer is smaller than 100, it will be wrapped with other small layers together by FSDP. +Finding an optimal auto wrap policy is challenging, PyTorch will add auto tuning for this config in the future. Without an auto tuning tool, it is good to profile your workflow using different auto wrap policies experimentally and find the optimal one. + +.. code-block:: python + + my_auto_wrap_policy = functools.partial( + size_based_auto_wrap_policy, min_num_params=20000 + ) + torch.cuda.set_device(rank) + model = Net().to(rank) + + model = FSDP(model, + auto_wrap_policy=my_auto_wrap_policy) + +Applying the auto_wrap_policy, the model would be as follows: + +.. code-block:: bash + + FullyShardedDataParallel( + (_fsdp_wrapped_module): FlattenParamsWrapper( + (_fpw_module): Net( + (conv1): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1)) + (conv2): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1)) + (dropout1): Dropout(p=0.25, inplace=False) + (dropout2): Dropout(p=0.5, inplace=False) + (fc1): FullyShardedDataParallel( + (_fsdp_wrapped_module): FlattenParamsWrapper( + (_fpw_module): Linear(in_features=9216, out_features=128, bias=True) + ) + ) + (fc2): Linear(in_features=128, out_features=10, bias=True) + ) + ) + + +.. code-block:: bash + + python FSDP_mnist.py + + CUDA event elapsed time on training loop 41.89130859375sec + +The following is the peak memory usage from FSDP with auto_wrap policy of MNIST training on a g4dn.12.xlarge AWS EC2 instance with 4 GPUs captured from PyTorch Profiler. +It can be observed that the peak memory usage on each device is smaller compared to FSDP without auto wrap policy applied, from ~75 MB to 66 MB. + +.. figure:: /_static/img/distributed/FSDP_autowrap.gif + :width: 100% + :align: center + :alt: FSDP peak memory + + FSDP Peak Memory Usage using Auto_wrap policy + +*CPU Off-loading*: In case the model is very large that even with FSDP wouldn't fit into GPUs, then CPU offload can be helpful here. + +Currently, only parameter and gradient CPU offload is supported. It can be enabled via passing in cpu_offload=CPUOffload(offload_params=True). + +Note that this currently implicitly enables gradient offloading to CPU in order for params and grads to be on the same device to work with the optimizer. This API is subject to change. The default is None in which case there will be no offloading. + +Using this feature may slow down the training considerably, due to frequent copying of tensors from host to device, but it could help improve memory efficiency and train larger scale models. + +In 2.4 we just add it to the FSDP wrapper + + +.. code-block:: python + + model = FSDP(model, + auto_wrap_policy=my_auto_wrap_policy, + cpu_offload=CPUOffload(offload_params=True)) + + +Compare it with DDP, if in 2.4 we just normally wrap the model in DPP, saving the changes in “DDP_mnist.py”. + +.. code-block:: python + + model = Net().to(rank) + model = DDP(model) + + +.. code-block:: bash + + python DDP_mnist.py + + CUDA event elapsed time on training loop 39.77766015625sec + +The following is the peak memory usage from DDP MNIST training on g4dn.12.xlarge AWS EC2 instance with 4 GPUs captured from PyTorch profiler. + +.. figure:: /_static/img/distributed/DDP_memory.gif + :width: 100% + :align: center + :alt: FSDP peak memory + + DDP Peak Memory Usage using Auto_wrap policy + + +Considering the toy example and tiny MNIST model we defined here, we can observe the difference between peak memory usage of DDP and FSDP. +In DDP each process holds a replica of the model, so the memory footprint is higher compared to FSDP which shards the model parameters, optimizer states and gradients over DDP ranks. +The peak memory usage using FSDP with auto_wrap policy is the lowest followed by FSDP and DDP. + +Also, looking at timings, considering the small model and running the training on a single machine, FSDP with and without auto_wrap policy performed almost as fast as DDP. +This example does not represent most of the real applications, for detailed analysis and comparison between DDP and FSDP please refer to this `blog post `__ . diff --git a/intermediate_source/FSDP_tutorial.rst b/intermediate_source/FSDP_tutorial.rst index 8e5217c64a8..6e1ff030ef2 100644 --- a/intermediate_source/FSDP_tutorial.rst +++ b/intermediate_source/FSDP_tutorial.rst @@ -1,448 +1,411 @@ -Getting Started with Fully Sharded Data Parallel(FSDP) +Getting Started with Fully Sharded Data Parallel (FSDP2) ====================================================== -**Author**: `Hamid Shojanazeri `__, `Yanli Zhao `__, `Shen Li `__ +**Author**: `Wei Feng `__, `Will Constable `__, `Yifan Mao `__ .. note:: - |edit| View and edit this tutorial in `github `__. + |edit| Check out the code in this tutorial from `pytorch/examples `_. FSDP1 will be deprecated. The old tutorial can be found `here `_. -Training AI models at a large scale is a challenging task that requires a lot of compute power and resources. -It also comes with considerable engineering complexity to handle the training of these very large models. -`PyTorch FSDP `__, released in PyTorch 1.11 makes this easier. - -In this tutorial, we show how to use `FSDP APIs `__, for simple MNIST models that can be extended to other larger models such as `HuggingFace BERT models `__, -`GPT 3 models up to 1T parameters `__ . The sample DDP MNIST code courtesy of `Patrick Hu `_. - - -How FSDP works +How FSDP2 works -------------- -In `DistributedDataParallel `__, (DDP) training, each process/ worker owns a replica of the model and processes a batch of data, finally it uses all-reduce to sum up gradients over different workers. In DDP the model weights and optimizer states are replicated across all workers. FSDP is a type of data parallelism that shards model parameters, optimizer states and gradients across DDP ranks. +In `DistributedDataParallel `__ (DDP) training, each rank owns a model replica and processes a batch of data, finally it uses all-reduce to sync gradients across ranks. -When training with FSDP, the GPU memory footprint is smaller than when training with DDP across all workers. This makes the training of some very large models feasible by allowing larger models or batch sizes to fit on device. This comes with the cost of increased communication volume. The communication overhead is reduced by internal optimizations like overlapping communication and computation. +Comparing with DDP, FSDP reduces GPU memory footprint by sharding model parameters, gradients, and optimizer states. It makes it feasible to train models that cannot fit on a single GPU. As shown below in the picture, + +* Outside of forward and backward computation, parameters are fully sharded +* Before forward and backward, sharded parameters are all-gathered into unsharded parameters +* Inside backward, local unsharded gradients are reduce-scatterred into sharded gradients +* Optimizer updates sharded parameters with sharded gradients, resulting in sharded optimizer states .. figure:: /_static/img/distributed/fsdp_workflow.png :width: 100% :align: center :alt: FSDP workflow - FSDP Workflow - -At a high level FSDP works as follow: -*In constructor* +FSDP can be considered a decomposition of DDP's all-reduce into reduce-scatter and all-gather operations -* Shard model parameters and each rank only keeps its own shard +.. figure:: /_static/img/distributed/fsdp_sharding.png + :width: 100% + :align: center + :alt: FSDP all-gather and reduce-scatter -*In forward path* -* Run all_gather to collect all shards from all ranks to recover the full parameter in this FSDP unit -* Run forward computation -* Discard parameter shards it has just collected +Comparing with `FSDP1 +`__, FSDP2 has following advantages: -*In backward path* +* Representing sharded parameters as `DTensor `_ sharded on dim-i, allowing for easy manipulation of individual parameters, communication-free sharded state dicts, and a simpler meta-device initialization flow. +* Improving memory management system that achieves lower and deterministic GPU memory by avoiding ``recordStream`` (`doc `_) and does so without any CPU synchronization. +* Offering a tensor subclass extension point to customize the all-gather, e.g. for float8 all-gather for float8 linears (`doc `_), and NF4 for QLoRA (`doc `_) +* Mixing frozen and non-frozen parameters can in the same communication group without using extra memory. -* Run all_gather to collect all shards from all ranks to recover the full parameter in this FSDP unit -* Run backward computation -* Run reduce_scatter to sync gradients -* Discard parameters. +How to use FSDP2 +--------------- -One way to view FSDP's sharding is to decompose the DDP gradient all-reduce into reduce-scatter and all-gather. Specifically, during the backward pass, FSDP reduces and scatters gradients, ensuring that each rank possesses a shard of the gradients. Then it updates the corresponding shard of the parameters in the optimizer step. Finally, in the subsequent forward pass, it performs an all-gather operation to collect and combine the updated parameter shards. +Model Initialization +~~~~~~~~~~~~~~~ -.. figure:: /_static/img/distributed/fsdp_sharding.png - :width: 100% - :align: center - :alt: FSDP allreduce +**Applying fully_shard on submodules**: Different from DDP, we should apply `fully_shard `_ on submodules as well as the root model. In the transformer example below, we applied ``fully_shard`` on each layer first, then the root model - FSDP Allreduce +* During forward computation of ``layers[i]``, the rest of the layers are sharded to reduce memory footprint +* Inside ``fully_shard(model)``, FSDP2 excludes parameters from ``model.layers`` and classify remaining parameters into a parameter group for performant all-gather and reduce-scatter +* ``fully_shard`` moves sharded model to actual training device (eg ``cuda``) -How to use FSDP ---------------- -Here we use a toy model to run training on the MNIST dataset for demonstration purposes. The APIs and logic can be applied to training larger models as well. -*Setup* +**Command**: ``torchrun --nproc_per_node 2 train.py`` -1.1 Install PyTorch along with Torchvision +.. code-block:: python -See the `Get Started guide `__ for information on installation. + from torch.distributed.fsdp import fully_shard, FSDPModule + model = Transformer() + for layer in model.layers: + fully_shard(layer) + fully_shard(model) -We add the following code snippets to a python script “FSDP_mnist.py”. + assert isinstance(model, Transformer) + assert isinstance(model, FSDPModule) + print(model) + # FSDPTransformer( + # (tok_embeddings): Embedding(...) + # ... + # (layers): 3 x FSDPTransformerBlock(...) + # (output): Linear(...) + # ) -1.2 Import necessary packages +We can inspect the nested wrapping with ``print(model)``. ``FSDPTransformer`` is a joint class of `Transformer `_ and `FSDPModule +<​https://docs.pytorch.org/docs/main/distributed.fsdp.fully_shard.html#torch.distributed.fsdp.FSDPModule>`_. The same thing happens to `FSDPTransformerBlock `_. All FSDP2 public APIs are exposed through ``FSDPModule``. For example, users can call ``model.unshard()`` to manually control all-gather schedules. See "explicit prefetching" below for details. -.. note:: - This tutorial is intended for PyTorch versions 1.12 and later. If you are using an earlier version, replace all instances of `size_based_auto_wrap_policy` with `default_auto_wrap_policy` and `fsdp_auto_wrap_policy` with `auto_wrap_policy`. +**model.parameters() as DTensor**: ``fully_shard`` shards parameters across ranks, and convert ``model.parameters()`` from plain ``torch.Tensor`` to DTensor to represent sharded parameters. FSDP2 shards on dim-0 by default so DTensor placements are `Shard(dim=0)`. Say we have N ranks and a parameter with N rows before sharding. After sharding, each rank will have 1 row of the parameter. We can inspect sharded parameters using ``param.to_local()``. .. code-block:: python - # Based on: https://github.com/pytorch/examples/blob/master/mnist/main.py - import os - import argparse - import functools - import torch - import torch.nn as nn - import torch.nn.functional as F - import torch.optim as optim - from torchvision import datasets, transforms + from torch.distributed.tensor import DTensor + for param in model.parameters(): + assert isinstance(param, DTensor) + assert param.placements == (Shard(0),) + # inspect sharded parameters with param.to_local() + optim = torch.optim.Adam(model.parameters(), lr=1e-2) - from torch.optim.lr_scheduler import StepLR +Note the optimizer is constructed after applying ``fully_shard``. Both model and optimizer state dicts are represented in DTensor. - import torch.distributed as dist - import torch.multiprocessing as mp - from torch.nn.parallel import DistributedDataParallel as DDP - from torch.utils.data.distributed import DistributedSampler - from torch.distributed.fsdp import FullyShardedDataParallel as FSDP - from torch.distributed.fsdp.fully_sharded_data_parallel import ( - CPUOffload, - BackwardPrefetch, - ) - from torch.distributed.fsdp.wrap import ( - size_based_auto_wrap_policy, - enable_wrap, - wrap, - ) +DTensor facilitates optimizer, gradient clipping and checkpointing -1.3 Distributed training setup. As we mentioned FSDP is a type of data parallelism which requires a distributed training environment, so here we use two helper functions to initialize the processes for distributed training and clean up. +* ``torch.optim.Adam`` and ``torch.nn.utils.clip_grad_norm_`` works out of the box for DTensor parameters. It makes the code consistent between single-device and distributed training +* we can use DTensor and DCP APIs to manipulate parameters to get full state dict, see "state dict" section below for details. For distributed state dicts, we can save/load checkpoints (`doc `_) without extra communication + + +Forward/Backward with Prefetching +~~~~~~~~~~~~~~~ + +**command**: ``torchrun --nproc_per_node 2 train.py`` .. code-block:: python - def setup(rank, world_size): - os.environ['MASTER_ADDR'] = 'localhost' - os.environ['MASTER_PORT'] = '12355' + for _ in range(epochs): + x = torch.randint(0, vocab_size, (batch_size, seq_len), device=device) + loss = model(x).sum() + loss.backward() + optim.step() + optim.zero_grad() - # initialize the process group - dist.init_process_group("nccl", rank=rank, world_size=world_size) +``fully_shard`` registers forward/backward hooks to all-gather parameters before computation, and reshards parameters after computation. To overlap all-gathers with computation, FSDP2 offers **implicit prefetching** that works out of the box with the training loop above and **explicit prefetching** for advanced users to control all-gather schedules manually. - def cleanup(): - dist.destroy_process_group() +**Implicit Prefetching**: CPU thread issues all-gather i before layer i. All-gathers are queued into its own cuda stream while layer i computation happens in the default stream. For non-cpu-bound workload (eg Transformer with big batch size), all-gather i+1 can overlap with computation for layer i. Implicit prefetching works similarly in the backward, except all-gathers are issued in the reverse of post-forward order. -2.1 Define our toy model for handwritten digit classification. +.. figure:: /_static/img/distributed/fsdp_implicit.png + :width: 100% + :align: center + :alt: FSDP Implicit -.. code-block:: python +We recommend users to start with implicit prefetching to understand the performance out of the box. - 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.Dropout(0.25) - self.dropout2 = nn.Dropout(0.5) - self.fc1 = nn.Linear(9216, 128) - self.fc2 = nn.Linear(128, 10) - - def forward(self, x): - - x = self.conv1(x) - x = F.relu(x) - x = self.conv2(x) - x = F.relu(x) - x = F.max_pool2d(x, 2) - x = self.dropout1(x) - x = torch.flatten(x, 1) - x = self.fc1(x) - x = F.relu(x) - x = self.dropout2(x) - x = self.fc2(x) - output = F.log_softmax(x, dim=1) - return output - -2.2 Define a train function +**Explicit Prefetching**: Users can specify forward ordering with `set_modules_to_forward_prefetch `_, and backward ordering with `set_modules_to_backward_prefetch `_. As shown in the code below, CPU thread issue all-gather i + 1 and i + 2 at layer i -.. code-block:: python +Explicit prefetching works well in following situation: + +**CPU-bound workload**: If using implicit prefetching, CPU thread will be too slow to issue all-gather for layer i+1 when kernels from layer i get executed. We have to explicitly issue all-gather i+1 before running forward for layer i + +**Prefetching for 2+ layers**: Implicit prefetching only all-gathers next one layer at a time to keep memory footprint minimum. With explicit prefetching can all-gather multiple layers at a time to possibly for better perf with increased memory. See ``layers_to_prefetch`` in the code - def train(args, model, rank, world_size, train_loader, optimizer, epoch, sampler=None): - model.train() - ddp_loss = torch.zeros(2).to(rank) - if sampler: - sampler.set_epoch(epoch) - for batch_idx, (data, target) in enumerate(train_loader): - data, target = data.to(rank), target.to(rank) - optimizer.zero_grad() - output = model(data) - loss = F.nll_loss(output, target, reduction='sum') - loss.backward() - optimizer.step() - ddp_loss[0] += loss.item() - ddp_loss[1] += len(data) - - dist.all_reduce(ddp_loss, op=dist.ReduceOp.SUM) - if rank == 0: - print('Train Epoch: {} \tLoss: {:.6f}'.format(epoch, ddp_loss[0] / ddp_loss[1])) - -2.3 Define a validation function +**Issuing 1st all-gather earlier**: Implicit prefetching happens at the time of calling ``model(x)``. The 1st all-gather gets exposed. We can call `model.unshard() `_ explicitly earlier to issue 1st all-gather earlier + +**command**: ``torchrun --nproc_per_node 2 train.py --explicit-prefetching`` .. code-block:: python - def test(model, rank, world_size, test_loader): - model.eval() - correct = 0 - ddp_loss = torch.zeros(3).to(rank) - with torch.no_grad(): - for data, target in test_loader: - data, target = data.to(rank), target.to(rank) - output = model(data) - ddp_loss[0] += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss - pred = output.argmax(dim=1, keepdim=True) # get the index of the max log-probability - ddp_loss[1] += pred.eq(target.view_as(pred)).sum().item() - ddp_loss[2] += len(data) + num_to_forward_prefetch = 2 + for i, layer in enumerate(model.layers): + if i >= len(model.layers) - num_to_forward_prefetch: + break + layers_to_prefetch = [ + model.layers[i + j] for j in range(1, num_to_forward_prefetch + 1) + ] + layer.set_modules_to_forward_prefetch(layers_to_prefetch) + + num_to_backward_prefetch = 2 + for i, layer in enumerate(model.layers): + if i < num_to_backward_prefetch: + continue + layers_to_prefetch = [ + model.layers[i - j] for j in range(1, num_to_backward_prefetch + 1) + ] + layer.set_modules_to_backward_prefetch(layers_to_prefetch) + + for _ in range(epochs): + # trigger 1st all-gather earlier + # this overlaps all-gather with any computation before model(x) + model.unshard() + x = torch.randint(0, vocab_size, (batch_size, seq_len), device=device) + loss = model(x).sum() + loss.backward() + optim.step() + optim.zero_grad() + + +Enabling Mixed Precision +~~~~~~~~~~~~~~~ + +FSDP2 offers a flexible `mixed precision policy `_ to speed up training. One typical use case is + +* Casting float32 parameters to bfloat16 for forward/backward computation, see ``param_dtype=torch.bfloat16`` +* Upcasting gradients to float32 for reduce-scatter to preserve accuracy, see ``reduce_dtype=torch.float32`` + +Comparing with `torch.amp `_, FSDP2 mixed precision has following advantages - dist.all_reduce(ddp_loss, op=dist.ReduceOp.SUM) +* **Performant and flexible parameter casting**: All the parameters inside a ``FSDPModule`` are cast together at the module boundary (before and after before/backward). We can set different mixed precision policies for each layer. For example, the first few layers can be in float32 while remaining layers can be in bfloat16. - if rank == 0: - test_loss = ddp_loss[0] / ddp_loss[2] - print('Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.2f}%)\n'.format( - test_loss, int(ddp_loss[1]), int(ddp_loss[2]), - 100. * ddp_loss[1] / ddp_loss[2])) +* **float32 gradient reduction (reduce-scatter)**: Gradients might vary a lot from rank to rank. Reducing gradients in float32 can be critical for numerics. -2.4 Define a distributed train function that wraps the model in FSDP -**Note: to save the FSDP model, we need to call the state_dict on each rank then on Rank 0 save the overall states.** + +**command**: ``torchrun --nproc_per_node 2 train.py --mixed-precision`` .. code-block:: python - def fsdp_main(rank, world_size, args): - setup(rank, world_size) - - transform=transforms.Compose([ - transforms.ToTensor(), - transforms.Normalize((0.1307,), (0.3081,)) - ]) - - dataset1 = datasets.MNIST('../data', train=True, download=True, - transform=transform) - dataset2 = datasets.MNIST('../data', train=False, - transform=transform) - - sampler1 = DistributedSampler(dataset1, rank=rank, num_replicas=world_size, shuffle=True) - sampler2 = DistributedSampler(dataset2, rank=rank, num_replicas=world_size) - - train_kwargs = {'batch_size': args.batch_size, 'sampler': sampler1} - test_kwargs = {'batch_size': args.test_batch_size, 'sampler': sampler2} - cuda_kwargs = {'num_workers': 2, - 'pin_memory': True, - 'shuffle': False} - train_kwargs.update(cuda_kwargs) - test_kwargs.update(cuda_kwargs) - - train_loader = torch.utils.data.DataLoader(dataset1,**train_kwargs) - test_loader = torch.utils.data.DataLoader(dataset2, **test_kwargs) - my_auto_wrap_policy = functools.partial( - size_based_auto_wrap_policy, min_num_params=100 + model = Transformer(model_args) + fsdp_kwargs = { + "mp_policy": MixedPrecisionPolicy( + param_dtype=torch.bfloat16, + reduce_dtype=torch.float32, ) - torch.cuda.set_device(rank) - - - init_start_event = torch.cuda.Event(enable_timing=True) - init_end_event = torch.cuda.Event(enable_timing=True) + } + for layer in model.layers: + fully_shard(layer, **fsdp_kwargs) + fully_shard(model, **fsdp_kwargs) + + # sharded parameters are float32 + for param in model.parameters(): + assert param.dtype == torch.float32 + + # unsharded parameters are bfloat16 + model.unshard() + for param in model.parameters(recurse=False): + assert param.dtype == torch.bfloat16 + model.reshard() - model = Net().to(rank) + # optimizer states are in float32 + optim = torch.optim.Adam(model.parameters(), lr=1e-2) - model = FSDP(model) + # training loop + # ... - optimizer = optim.Adadelta(model.parameters(), lr=args.lr) - scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma) - init_start_event.record() - for epoch in range(1, args.epochs + 1): - train(args, model, rank, world_size, train_loader, optimizer, epoch, sampler=sampler1) - test(model, rank, world_size, test_loader) - scheduler.step() - init_end_event.record() +Gradient Clipping and Optimizer with DTensor +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +**command**: ``torchrun --nproc_per_node 2 train.py`` + +.. code-block:: python - if rank == 0: - init_end_event.synchronize() - print(f"CUDA event elapsed time: {init_start_event.elapsed_time(init_end_event) / 1000}sec") - print(f"{model}") + # optim is constructed base on DTensor model parameters + optim = torch.optim.Adam(model.parameters(), lr=1e-2) + for _ in range(epochs): + x = torch.randint(0, vocab_size, (batch_size, seq_len), device=device) + loss = model(x).sum() + loss.backward() + torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=max_norm) + optim.step() + optim.zero_grad() - if args.save_model: - # use a barrier to make sure training is done on all ranks - dist.barrier() - states = model.state_dict() - if rank == 0: - torch.save(states, "mnist_cnn.pt") - - cleanup() +Optimizer is initialized after applying ``fully_shard`` on the model, and holds reference to DTensor ``model.parameters()``. For gradient clipping, ``torch.nn.utils.clip_grad_norm_`` works for DTensor parameters. Tensor ops will be dispatched correctly inside DTensor to communicate partial tensors across ranks to preserve the single device semantic. +State Dicts with DTensor APIs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +We showcase how to convert a full state dict into a DTensor state dict for loading, and how to convert it back to full state dict for saving. -2.5 Finally, parse the arguments and set the main function +**command**: ``torchrun --nproc_per_node 2 train.py`` + +* For the 1st time, it creates checkpoints for the model and optimizer +* For the 2nd time, it loads from the previous checkpoint to resume training + +**Loading state dicts**: We initialize the model under meta device and call ``fully_shard`` to convert ``model.parameters()`` from plain ``torch.Tensor`` to DTensor. After reading the full state dict from torch.load, we can call `distributed_tensor `_ to convert plain ``torch.Tensor`` into DTensor, using the same placements and device mesh from ``model.state_dict()``. Finally we can call `model.load_state_dict `_ to load DTensor state dicts into the model. .. code-block:: python - if __name__ == '__main__': - # Training settings - parser = argparse.ArgumentParser(description='PyTorch MNIST Example') - parser.add_argument('--batch-size', type=int, default=64, metavar='N', - help='input batch size for training (default: 64)') - parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N', - help='input batch size for testing (default: 1000)') - parser.add_argument('--epochs', type=int, default=10, metavar='N', - help='number of epochs to train (default: 14)') - parser.add_argument('--lr', type=float, default=1.0, metavar='LR', - help='learning rate (default: 1.0)') - parser.add_argument('--gamma', type=float, default=0.7, metavar='M', - help='Learning rate step gamma (default: 0.7)') - parser.add_argument('--no-cuda', action='store_true', default=False, - help='disables CUDA training') - parser.add_argument('--seed', type=int, default=1, metavar='S', - help='random seed (default: 1)') - parser.add_argument('--save-model', action='store_true', default=False, - help='For Saving the current Model') - args = parser.parse_args() - - torch.manual_seed(args.seed) - - WORLD_SIZE = torch.cuda.device_count() - mp.spawn(fsdp_main, - args=(WORLD_SIZE, args), - nprocs=WORLD_SIZE, - join=True) - - -We have recorded cuda events to measure the time of FSDP model specifics. The CUDA event time was 110.85 seconds. - -.. code-block:: bash - - python FSDP_mnist.py - - CUDA event elapsed time on training loop 40.67462890625sec - -Wrapping the model with FSDP, the model will look as follows, we can see the model has been wrapped in one FSDP unit. -Alternatively, we will look at adding the auto_wrap_policy next and will discuss the differences. - -.. code-block:: bash - - FullyShardedDataParallel( - (_fsdp_wrapped_module): FlattenParamsWrapper( - (_fpw_module): Net( - (conv1): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1)) - (conv2): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1)) - (dropout1): Dropout(p=0.25, inplace=False) - (dropout2): Dropout(p=0.5, inplace=False) - (fc1): Linear(in_features=9216, out_features=128, bias=True) - (fc2): Linear(in_features=128, out_features=10, bias=True) - ) + from torch.distributed.tensor import distribute_tensor + + # mmap=True reduces CPU memory usage + full_sd = torch.load( + "checkpoints/model_state_dict.pt", + mmap=True, + weights_only=True, + map_location='cpu', ) - ) + meta_sharded_sd = model.state_dict() + sharded_sd = {} + for param_name, full_tensor in full_sd.items(): + sharded_meta_param = meta_sharded_sd.get(param_name) + sharded_tensor = distribute_tensor( + full_tensor, + sharded_meta_param.device_mesh, + sharded_meta_param.placements, + ) + sharded_sd[param_name] = nn.Parameter(sharded_tensor) + # `assign=True` since we cannot call `copy_` on meta tensor + model.load_state_dict(sharded_sd, assign=True) -The following is the peak memory usage from FSDP MNIST training on g4dn.12.xlarge AWS EC2 instance with 4 GPUs captured from PyTorch Profiler. +**Saving state dicts**: ``model.state_dict()`` returns a DTensor state dict. We can convert a DTensor into a plain ``torch.Tensor`` by calling `full_tensor() `_. Internally it issues an all-gather across ranks to get unsharded parameters in plain torch.Tensor. For rank 0, ``full_param.cpu()`` offloads the tensor to cpu one by one to avoid peaking GPU memory with unsharded parameters. +.. code-block:: python + + sharded_sd = model.state_dict() + cpu_state_dict = {} + for param_name, sharded_param in sharded_sd.items(): + full_param = sharded_param.full_tensor() + if torch.distributed.get_rank() == 0: + cpu_state_dict[param_name] = full_param.cpu() + else: + del full_param + torch.save(cpu_state_dict, "checkpoints/model_state_dict.pt") -.. figure:: /_static/img/distributed/FSDP_memory.gif - :width: 100% - :align: center - :alt: FSDP peak memory - FSDP Peak Memory Usage +Optimizer state dict works similarly (`code `_). Users can customize the above DTensor scripts to work with 3rd party checkpoints. -Applying *auto_wrap_policy* in FSDP otherwise, FSDP will put the entire model in one FSDP unit, which will reduce computation efficiency and memory efficiency. -The way it works is that, suppose your model contains 100 Linear layers. If you do FSDP(model), there will only be one FSDP unit which wraps the entire model. -In that case, the allgather would collect the full parameters for all 100 linear layers, and hence won't save CUDA memory for parameter sharding. -Also, there is only one blocking allgather call for the all 100 linear layers, there will not be communication and computation overlapping between layers. +If there is no need for customization, we can use `DCP APIs `_ directly to support both single-node and multi-node training. -To avoid that, you can pass in an auto_wrap_policy, which will seal the current FSDP unit and start a new one automatically when the specified condition is met (e.g., size limit). -In that way you will have multiple FSDP units, and only one FSDP unit needs to collect full parameters at a time. E.g., suppose you have 5 FSDP units, and each wraps 20 linear layers. -Then, in the forward, the 1st FSDP unit will allgather parameters for the first 20 linear layers, do computation, discard the parameters and then move on to the next 20 linear layers. So, at any point in time, each rank only materializes parameters/grads for 20 linear layers instead of 100. +State Dict with DCP APIs +~~~~~~~~~~~~~~~~~~~~~~~~ -To do so in 2.4 we define the auto_wrap_policy and pass it to FSDP wrapper, in the following example, my_auto_wrap_policy defines that a layer could be wrapped or sharded by FSDP if the number of parameters in this layer is larger than 100. -If the number of parameters in this layer is smaller than 100, it will be wrapped with other small layers together by FSDP. -Finding an optimal auto wrap policy is challenging, PyTorch will add auto tuning for this config in the future. Without an auto tuning tool, it is good to profile your workflow using different auto wrap policies experimentally and find the optimal one. +**command**: ``torchrun --nproc_per_node 2 train.py --dcp-api`` + +* For the 1st time, it creates checkpoints for the model and optimizer +* For the 2nd time, it loads from the previous checkpoint to resume training + +**Loading state dicts**: We can load a full state dict into a FSDP2 model with `set_model_state_dict `_. With ``broadcast_from_rank0=True``, we can load the full state dict only on rank 0 to avoid peaking CPU memory. DCP will shard tensors and broadcast them to other ranks. .. code-block:: python - my_auto_wrap_policy = functools.partial( - size_based_auto_wrap_policy, min_num_params=20000 - ) - torch.cuda.set_device(rank) - model = Net().to(rank) - - model = FSDP(model, - auto_wrap_policy=my_auto_wrap_policy) - -Applying the auto_wrap_policy, the model would be as follows: - -.. code-block:: bash - - FullyShardedDataParallel( - (_fsdp_wrapped_module): FlattenParamsWrapper( - (_fpw_module): Net( - (conv1): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1)) - (conv2): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1)) - (dropout1): Dropout(p=0.25, inplace=False) - (dropout2): Dropout(p=0.5, inplace=False) - (fc1): FullyShardedDataParallel( - (_fsdp_wrapped_module): FlattenParamsWrapper( - (_fpw_module): Linear(in_features=9216, out_features=128, bias=True) + from torch.distributed.checkpoint.state_dict import set_model_state_dict + set_model_state_dict( + model=model, + model_state_dict=full_sd, + options=StateDictOptions( + full_state_dict=True, + broadcast_from_rank0=True, + ), + ) + +**Saving state dicts**: `get_model_state_dict `_ with ``full_state_dict=True`` and ``cpu_offload=True`` all-gathers tensors and offload them to CPU. It works similarly to DTensor APIs. + +.. code-block:: python + + from torch.distributed.checkpoint.state_dict import get_model_state_dict + model_state_dict = get_model_state_dict( + model=model, + options=StateDictOptions( + full_state_dict=True, + cpu_offload=True, ) - ) - (fc2): Linear(in_features=128, out_features=10, bias=True) ) - ) + torch.save(model_state_dict, "model_state_dict.pt") -.. code-block:: bash +Refer to `pytorch/examples `__ for loading and saving optimizer state dicts with `set_optimizer_state_dict `_ and `get_optimizer_state_dict `_. - python FSDP_mnist.py - CUDA event elapsed time on training loop 41.89130859375sec +FSDP1-to-FSDP2 migration guide +--------------- -The following is the peak memory usage from FSDP with auto_wrap policy of MNIST training on a g4dn.12.xlarge AWS EC2 instance with 4 GPUs captured from PyTorch Profiler. -It can be observed that the peak memory usage on each device is smaller compared to FSDP without auto wrap policy applied, from ~75 MB to 66 MB. +Let’s look at an example of an `FSDP `_ usage and an equivalent `fully_shard `_ usage. We’ll highlight the key differences and suggest steps for migration. -.. figure:: /_static/img/distributed/FSDP_autowrap.gif - :width: 100% - :align: center - :alt: FSDP peak memory +Original FSDP() usage - FSDP Peak Memory Usage using Auto_wrap policy +.. code-block:: python -*CPU Off-loading*: In case the model is very large that even with FSDP wouldn't fit into GPUs, then CPU offload can be helpful here. + from torch.distributed.fsdp import FullyShardedDataParallel as FSDP + with torch.device("meta"): + model = Transformer() + policy = ModuleWrapPolicy({TransformerBlock}) + model = FSDP(model, auto_wrap_policy=policy) + def param_init_fn(module: nn.Module) -> None: ... + model = FSDP(model, auto_wrap_policy=policy, param_init_fn=param_init_fn) -Currently, only parameter and gradient CPU offload is supported. It can be enabled via passing in cpu_offload=CPUOffload(offload_params=True). +New fully_shard() usage -Note that this currently implicitly enables gradient offloading to CPU in order for params and grads to be on the same device to work with the optimizer. This API is subject to change. The default is None in which case there will be no offloading. +.. code-block:: python -Using this feature may slow down the training considerably, due to frequent copying of tensors from host to device, but it could help improve memory efficiency and train larger scale models. + with torch.device("meta"): + model = Transformer() + for module in model.modules(): + if isinstance(module, TransformerBlock): + fully_shard(module) + fully_shard(model) + for tensor in itertools.chain(model.parameters(), model.buffers()): + assert tensor.device == torch.device("meta") -In 2.4 we just add it to the FSDP wrapper + # Initialize the model after sharding + model.to_empty(device="cuda") + model.reset_parameters() -.. code-block:: python +Migration Steps - model = FSDP(model, - auto_wrap_policy=my_auto_wrap_policy, - cpu_offload=CPUOffload(offload_params=True)) +* Replace the imports +* Implement your ‘policy’ directly (apply ``fully_shard`` to the desired sublayers) +* Wrap your root model with ``fully_shard`` instead of ``FSDP`` +* Get rid of ``param_init_fn`` and manually call ``model.reset_parameters()`` +* Replace other FSDP1 kwargs (see below) -Compare it with DDP, if in 2.4 we just normally wrap the model in DPP, saving the changes in “DDP_mnist.py”. +sharding_strategy -.. code-block:: python +* FULL_SHARD: ``reshard_after_forward=True`` +* SHARD_GRAD_OP: ``reshard_after_forward=False`` +* HYBRID_SHARD: ``reshard_after_forward=True`` with a 2D device mesh +* _HYBRID_SHARD_ZERO2: ``reshard_after_forward=False`` with a 2D device mesh - model = Net().to(rank) - model = DDP(model) +cpu_offload +* CPUOffload.offload_params=False: ``offload_policy=None`` +* CPUOffload.offload_params = True: ``offload_policy=CPUOffloadPolicy()`` -.. code-block:: bash +backward_prefetch - python DDP_mnist.py +* BACKWARD_PRE: always used +* BACKWARD_POST: not supported - CUDA event elapsed time on training loop 39.77766015625sec +mixed_precision -The following is the peak memory usage from DDP MNIST training on g4dn.12.xlarge AWS EC2 instance with 4 GPUs captured from PyTorch profiler. +* ``buffer_dtype`` is omitted because fully_shard does not shard buffers +* fully_shard’s ``cast_forward_inputs`` maps to both ``cast_forward_inputs`` and ``cast_root_forward_inputs`` in FSDP1 +* ``output_dtype`` is a new config for fully_shard -.. figure:: /_static/img/distributed/DDP_memory.gif - :width: 100% - :align: center - :alt: FSDP peak memory +device_id: Inferred from device_mesh’s device + +sync_module_states=True/False: Moved to DCP. User can broadcast state dicts from rank0 using `set_model_state_dict `_ with ``broadcast_from_rank0=True`` + +forward_prefetch: Manual control over prefetching is possible with + +* Manually call `fsdp_module.unshard() `_ +* Use these APIs to control automatic prefetching, `set_modules_to_forward_prefetch `_ and `set_modules_to_backward_prefetch `_ - DDP Peak Memory Usage using Auto_wrap policy +limit_all_gathers: No longer needed, because ``fully_shard`` removed cpu synchronization +use_orig_params: Original params are always used (no more flat parameter) -Considering the toy example and tiny MNIST model we defined here, we can observe the difference between peak memory usage of DDP and FSDP. -In DDP each process holds a replica of the model, so the memory footprint is higher compared to FSDP which shards the model parameters, optimizer states and gradients over DDP ranks. -The peak memory usage using FSDP with auto_wrap policy is the lowest followed by FSDP and DDP. +no_sync(): `set_requires_gradient_sync `_ -Also, looking at timings, considering the small model and running the training on a single machine, FSDP with and without auto_wrap policy performed almost as fast as DDP. -This example does not represent most of the real applications, for detailed analysis and comparison between DDP and FSDP please refer to this `blog post `__ . +ignored_params and ignored_states: `ignored_params `_ From 001e1a53d4eb599e07ed87823e5bab8022db59b9 Mon Sep 17 00:00:00 2001 From: partev Date: Mon, 19 May 2025 16:32:56 -0400 Subject: [PATCH 102/347] remove obsolete encoding header (#3361) --- beginner_source/examples_autograd/polynomial_autograd.py | 1 - beginner_source/examples_autograd/polynomial_custom_function.py | 1 - 2 files changed, 2 deletions(-) diff --git a/beginner_source/examples_autograd/polynomial_autograd.py b/beginner_source/examples_autograd/polynomial_autograd.py index 5e2fdc4101d..525d0c33ce9 100755 --- a/beginner_source/examples_autograd/polynomial_autograd.py +++ b/beginner_source/examples_autograd/polynomial_autograd.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ PyTorch: Tensors and autograd ------------------------------- diff --git a/beginner_source/examples_autograd/polynomial_custom_function.py b/beginner_source/examples_autograd/polynomial_custom_function.py index 130775ea985..eea1c4ca9ee 100755 --- a/beginner_source/examples_autograd/polynomial_custom_function.py +++ b/beginner_source/examples_autograd/polynomial_custom_function.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ PyTorch: Defining New autograd Functions ---------------------------------------- From 155f386a1fc656f607f1ef83d019077c85d7c03b Mon Sep 17 00:00:00 2001 From: Ivan Barbosa Filho <185492043+IvanBarbosaFilho@users.noreply.github.com> Date: Wed, 21 May 2025 19:55:11 -0300 Subject: [PATCH 103/347] Fix typos in README and tutorial_submission_policy.md (#3314) Corrected 'Ceate' to 'Create' in README. Removed duplicated phrase 'best practices' in tutorial_submission_policy.md. --- README.md | 2 +- tutorial_submission_policy.md | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0ef22c19256..af84d9ebe79 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ You can submit the following types of issues: We use sphinx-gallery's [notebook styled examples](https://sphinx-gallery.github.io/stable/tutorials/index.html) to create the tutorials. Syntax is very simple. In essence, you write a slightly well formatted Python file and it shows up as an HTML page. In addition, a Jupyter notebook is autogenerated and available to run in Google Colab. -Here is how you can ceate a new tutorial (for a detailed description, see [CONTRIBUTING.md](./CONTRIBUTING.md)): +Here is how you can create a new tutorial (for a detailed description, see [CONTRIBUTING.md](./CONTRIBUTING.md)): NOTE: Before submitting a new tutorial, read [PyTorch Tutorial Submission Policy](./tutorial_submission_policy.md). diff --git a/tutorial_submission_policy.md b/tutorial_submission_policy.md index c5c3a800876..f83bf4d26dd 100644 --- a/tutorial_submission_policy.md +++ b/tutorial_submission_policy.md @@ -91,8 +91,7 @@ Please note the following: ## Deleting Stale Tutorials A tutorial might be considered stale when it no longer aligns with -the latest PyTorch updates, features, or best practices or best -practices: +the latest PyTorch updates, features, or best practices: * The tutorial is no longer functional due to changes in PyTorch or its dependencies From ddd40ec93a06a8a7883b39c4f9c3d41c89e0d1ab Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Fri, 23 May 2025 14:28:07 -0500 Subject: [PATCH 104/347] Fix link format (#3365) * Fix link format * Update nestedtensor.py --- prototype_source/nestedtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prototype_source/nestedtensor.py b/prototype_source/nestedtensor.py index 6578cf73454..77f8a4cebe1 100644 --- a/prototype_source/nestedtensor.py +++ b/prototype_source/nestedtensor.py @@ -373,4 +373,4 @@ def benchmark(func, *args, **kwargs): # See Also # -------- # -# * `Accelerating PyTorch Transformers by replacing nn.Transformer with Nested Tensors and torch.compile() `__ From 20bf27e027d35a455d24469098f6d685547ff11d Mon Sep 17 00:00:00 2001 From: Oussama Chaib <88498674+oussamachaib@users.noreply.github.com> Date: Mon, 2 Jun 2025 16:12:58 +0100 Subject: [PATCH 105/347] Replacing obsolete function in data tutorials (#3371) Replacing obsolete function read_image with its up-to-date equivalent decode_image in data tutorials --- beginner_source/basics/data_tutorial.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/beginner_source/basics/data_tutorial.py b/beginner_source/basics/data_tutorial.py index 561e9723fde..fe649e9e009 100644 --- a/beginner_source/basics/data_tutorial.py +++ b/beginner_source/basics/data_tutorial.py @@ -120,7 +120,7 @@ import os import pandas as pd -from torchvision.io import read_image +from torchvision.io import decode_image class CustomImageDataset(Dataset): def __init__(self, annotations_file, img_dir, transform=None, target_transform=None): @@ -134,7 +134,7 @@ def __len__(self): def __getitem__(self, idx): img_path = os.path.join(self.img_dir, self.img_labels.iloc[idx, 0]) - image = read_image(img_path) + image = decode_image(img_path) label = self.img_labels.iloc[idx, 1] if self.transform: image = self.transform(image) @@ -184,7 +184,7 @@ def __len__(self): # ^^^^^^^^^^^^^^^^^^^^ # # The __getitem__ function loads and returns a sample from the dataset at the given index ``idx``. -# Based on the index, it identifies the image's location on disk, converts that to a tensor using ``read_image``, retrieves the +# Based on the index, it identifies the image's location on disk, converts that to a tensor using ``decode_image``, retrieves the # corresponding label from the csv data in ``self.img_labels``, calls the transform functions on them (if applicable), and returns the # tensor image and corresponding label in a tuple. From 1e86b827fa341261e69789af726ec159918ed303 Mon Sep 17 00:00:00 2001 From: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> Date: Tue, 3 Jun 2025 11:20:40 -0400 Subject: [PATCH 106/347] Update docathon-label-sync.py (#3374) Update the label to 2025 --- .github/scripts/docathon-label-sync.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/scripts/docathon-label-sync.py b/.github/scripts/docathon-label-sync.py index a8e512a3204..7eeca6f7659 100644 --- a/.github/scripts/docathon-label-sync.py +++ b/.github/scripts/docathon-label-sync.py @@ -25,11 +25,11 @@ def main(): issue_number = int(re.findall(r'#(\d{1,5})', pull_request_body)[0]) issue = repo.get_issue(issue_number) issue_labels = issue.labels - docathon_label_present = any(label.name == 'docathon-h1-2024' for label in issue_labels) + docathon_label_present = any(label.name == 'docathon-h1-2025' for label in issue_labels) # if the issue has a docathon label, add all labels from the issue to the PR. if not docathon_label_present: - print("The 'docathon-h1-2024' label is not present in the issue.") + print("The 'docathon-h1-2025' label is not present in the issue.") return pull_request_labels = pull_request.get_labels() issue_label_names = [label.name for label in issue_labels] From d627981b16f9929c91b4f03144b6a186f6b9ee8e Mon Sep 17 00:00:00 2001 From: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> Date: Tue, 3 Jun 2025 11:22:41 -0400 Subject: [PATCH 107/347] Update docathon-assign.yml (#3375) Assign to me gh update Co-authored-by: Svetlana Karslioglu --- .github/workflows/docathon-assign.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docathon-assign.yml b/.github/workflows/docathon-assign.yml index 31fa28289b0..8eef2b2fc88 100644 --- a/.github/workflows/docathon-assign.yml +++ b/.github/workflows/docathon-assign.yml @@ -28,14 +28,14 @@ jobs: repo: context.repo.repo, issue_number: issueNumber }); - const hasLabel = issue.labels.some(label => label.name === 'docathon-h1-2024'); + const hasLabel = issue.labels.some(label => label.name === 'docathon-h1-2025'); if (hasLabel) { if (issue.assignee !== null) { await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issueNumber, - body: "The issue is already assigned. Please pick an opened and unnasigned issue with the [docathon-h1-2024 label](https://github.com/pytorch/pytorch/issues?q=is%3Aopen+is%3Aissue+label%3Adocathon-h1-2024)." + body: "The issue is already assigned. Please pick an opened and unnasigned issue with the [docathon-h1-2025 label](https://github.com/pytorch/pytorch/issues?q=is%3Aopen+is%3Aissue+label%3Adocathon-h1-2025)." }); } else { await github.rest.issues.addAssignees({ @@ -46,7 +46,7 @@ jobs: }); } } else { - const commmentMessage = "This issue does not have the correct label. Please pick an opened and unnasigned issue with the [docathon-h1-2024 label](https://github.com/pytorch/pytorch/issues?q=is%3Aopen+is%3Aissue+label%3Adocathon-h1-2024)." + const commmentMessage = "This issue does not have the correct label. Please pick an opened and unnasigned issue with the [docathon-h1-2025 label](https://github.com/pytorch/pytorch/issues?q=is%3Aopen+is%3Aissue+label%3Adocathon-h1-2025)." await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, From 0353137a033eae026f0c252a973f9680cf540d81 Mon Sep 17 00:00:00 2001 From: nirajkamalk Date: Wed, 4 Jun 2025 11:32:19 -0400 Subject: [PATCH 108/347] Update broken links tia index 3372 (#3376) * Fix broken links in intermediate_source/tiatoolbox_tutorial.rst * Update broken links in index.rst * Fix index.rst ios executorch demo link --- index.rst | 12 ++++++------ intermediate_source/tiatoolbox_tutorial.rst | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/index.rst b/index.rst index da64acca181..54bd8b37466 100644 --- a/index.rst +++ b/index.rst @@ -806,21 +806,21 @@ Welcome to PyTorch Tutorials :header: Using the ExecuTorch SDK to Profile a Model :card_description: Explore how to use the ExecuTorch SDK to profile, debug, and visualize ExecuTorch models :image: _static/img/ExecuTorch-Logo-cropped.svg - :link: https://pytorch.org/executorch/stable/tutorials/sdk-integration-tutorial.html + :link: https://docs.pytorch.org/executorch/main/tutorials/devtools-integration-tutorial.html :tags: Edge .. customcarditem:: :header: Building an ExecuTorch iOS Demo App :card_description: Explore how to set up the ExecuTorch iOS Demo App, which uses the MobileNet v3 model to process live camera images leveraging three different backends: XNNPACK, Core ML, and Metal Performance Shaders (MPS). :image: _static/img/ExecuTorch-Logo-cropped.svg - :link: https://pytorch.org/executorch/stable/demo-apps-ios.html + :link: https://github.com/pytorch-labs/executorch-examples/tree/main/mv3/apple/ExecuTorchDemo :tags: Edge .. customcarditem:: :header: Building an ExecuTorch Android Demo App :card_description: Learn how to set up the ExecuTorch Android Demo App for image segmentation tasks using the DeepLab v3 model and XNNPACK FP32 backend. :image: _static/img/ExecuTorch-Logo-cropped.svg - :link: https://pytorch.org/executorch/stable/demo-apps-android.html + :link: https://github.com/pytorch-labs/executorch-examples/tree/main/dl3/android/DeepLabV3Demo#executorch-android-demo-app :tags: Edge .. customcarditem:: @@ -1123,9 +1123,9 @@ Additional Resources Exporting to ExecuTorch Tutorial Running an ExecuTorch Model in C++ Tutorial < https://pytorch.org/executorch/stable/running-a-model-cpp-tutorial.html> - Using the ExecuTorch SDK to Profile a Model - Building an ExecuTorch iOS Demo App - Building an ExecuTorch Android Demo App + Using the ExecuTorch SDK to Profile a Model + Building an ExecuTorch iOS Demo App + Building an ExecuTorch Android Demo App Lowering a Model as a Delegate .. toctree:: diff --git a/intermediate_source/tiatoolbox_tutorial.rst b/intermediate_source/tiatoolbox_tutorial.rst index de9b3031330..8528ddf0095 100644 --- a/intermediate_source/tiatoolbox_tutorial.rst +++ b/intermediate_source/tiatoolbox_tutorial.rst @@ -348,7 +348,7 @@ The PatchPredictor class runs a CNN-based classifier written in PyTorch. - Alternatively, you can pass ``pretrained_model`` as a string argument. This specifies the CNN model that performs the prediction, and it must be one of the models listed - `here `__. + `here `__. The command will look like this: ``predictor = PatchPredictor(pretrained_model='resnet18-kather100k', pretrained_weights=weights_path, batch_size=32)``. - ``pretrained_weights``: When using a ``pretrained_model``, the @@ -621,7 +621,7 @@ results. Here are the arguments and their descriptions: which is equivalent to level 0. In general, this is the level of greatest resolution. In this particular case, the image has only one level. More information can be found in the - `documentation `__. + `documentation `__. - ``masks``: A list of paths corresponding to the masks of WSIs in the ``imgs`` list. These masks specify the regions in the original WSIs from which we want to extract patches. If the mask of a particular From 7584f2f7527c9d2697c09fd3b6b5367226e97866 Mon Sep 17 00:00:00 2001 From: Ashish Soni Date: Wed, 4 Jun 2025 23:05:04 +0200 Subject: [PATCH 109/347] Add download instructions for pretrained model in dynamic quantization tutorial (#3379) * Add download instructions for pretrained model in dynamic quantization tutorial Fixes #3254 - Added wget command to download word_language_model_quantize.pth to improve readability --------- Co-authored-by: Svetlana Karslioglu Co-authored-by: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> --- advanced_source/dynamic_quantization_tutorial.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/advanced_source/dynamic_quantization_tutorial.py b/advanced_source/dynamic_quantization_tutorial.py index c8d94789d5d..c5b7d70a046 100644 --- a/advanced_source/dynamic_quantization_tutorial.py +++ b/advanced_source/dynamic_quantization_tutorial.py @@ -134,10 +134,18 @@ def tokenize(self, path): # ----------------------------- # # This is a tutorial on dynamic quantization, a quantization technique -# that is applied after a model has been trained. Therefore, we'll simply load some -# pretrained weights into this model architecture; these weights were obtained -# by training for five epochs using the default settings in the word language model -# example. +# that is applied after a model has been trained. Therefore, we'll simply +# load some pretrained weights into this model architecture; these +# weights were obtained by training for five epochs using the default +# settings in the word language model example. +# +# Before running this tutorial, download the required pre-trained model: +# +# .. code-block:: bash +# +# wget https://s3.amazonaws.com/pytorch-tutorial-assets/word_language_model_quantize.pth +# +# Place the downloaded file in the data directory or update the model_data_filepath accordingly. ntokens = len(corpus.dictionary) From 0f312dfa3b9f0a5a6570d48bbf25632096977881 Mon Sep 17 00:00:00 2001 From: Harshal Janjani <75426551+harshaljanjani@users.noreply.github.com> Date: Thu, 5 Jun 2025 01:11:20 +0400 Subject: [PATCH 110/347] docs: Fix incorrect usage description of ctx.save_for_backward (#3377) * docs: Fix incorrect usage description of ctx.save_for_backward --------- Co-authored-by: Svetlana Karslioglu Co-authored-by: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> --- .../examples_autograd/polynomial_custom_function.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/beginner_source/examples_autograd/polynomial_custom_function.py b/beginner_source/examples_autograd/polynomial_custom_function.py index eea1c4ca9ee..39057c8fd7a 100755 --- a/beginner_source/examples_autograd/polynomial_custom_function.py +++ b/beginner_source/examples_autograd/polynomial_custom_function.py @@ -33,8 +33,11 @@ def forward(ctx, input): """ In the forward pass we receive a Tensor containing the input and return a Tensor containing the output. ctx is a context object that can be used - to stash information for backward computation. You can cache arbitrary - objects for use in the backward pass using the ctx.save_for_backward method. + to stash information for backward computation. You can cache tensors for + use in the backward pass using the ``ctx.save_for_backward`` method. Other + objects can be stored directly as attributes on the ctx object, such as + ``ctx.my_object = my_object``. Check out `Extending torch.autograd `_ + for further details. """ ctx.save_for_backward(input) return 0.5 * (5 * input ** 3 - 3 * input) From 50ba81fd7d18a45b2400ff2c95a4cc30533d897b Mon Sep 17 00:00:00 2001 From: fortfanop <65244260+FORTFANOP@users.noreply.github.com> Date: Fri, 6 Jun 2025 00:51:49 +0530 Subject: [PATCH 111/347] Add import os to example code block (#3378) Co-authored-by: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> --- intermediate_source/ddp_tutorial.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/intermediate_source/ddp_tutorial.rst b/intermediate_source/ddp_tutorial.rst index c91c195d6f0..1f7221680b1 100644 --- a/intermediate_source/ddp_tutorial.rst +++ b/intermediate_source/ddp_tutorial.rst @@ -311,6 +311,7 @@ Let's still use the Toymodel example and create a file named ``elastic_ddp.py``. .. code:: python + import os import torch import torch.distributed as dist import torch.nn as nn From 640fd89dd36388fa1b1012b40903cc1742143a5d Mon Sep 17 00:00:00 2001 From: "Runtian (Rachel) Li" <115634891+Rachel0619@users.noreply.github.com> Date: Thu, 5 Jun 2025 12:23:42 -0700 Subject: [PATCH 112/347] Remove Markdown backticks, use RST syntax for code block (#3381) * Fix #2814: Remove Markdown backticks, use RST syntax for code block --------- Co-authored-by: Svetlana Karslioglu --- beginner_source/introyt/introyt1_tutorial.py | 25 ++++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/beginner_source/introyt/introyt1_tutorial.py b/beginner_source/introyt/introyt1_tutorial.py index 74675070708..c01befb40cc 100644 --- a/beginner_source/introyt/introyt1_tutorial.py +++ b/beginner_source/introyt/introyt1_tutorial.py @@ -303,22 +303,21 @@ def num_flat_features(self, x): # The values passed to the transform are the means (first tuple) and the # standard deviations (second tuple) of the rgb values of the images in # the dataset. You can calculate these values yourself by running these -# few lines of code: -# ``` -# from torch.utils.data import ConcatDataset -# transform = transforms.Compose([transforms.ToTensor()]) -# trainset = torchvision.datasets.CIFAR10(root='./data', train=True, +# few lines of code:: +# +# from torch.utils.data import ConcatDataset +# transform = transforms.Compose([transforms.ToTensor()]) +# trainset = torchvision.datasets.CIFAR10(root='./data', train=True, # download=True, transform=transform) # -# #stack all train images together into a tensor of shape -# #(50000, 3, 32, 32) -# x = torch.stack([sample[0] for sample in ConcatDataset([trainset])]) +# # stack all train images together into a tensor of shape +# # (50000, 3, 32, 32) +# x = torch.stack([sample[0] for sample in ConcatDataset([trainset])]) # -# #get the mean of each channel -# mean = torch.mean(x, dim=(0,2,3)) #tensor([0.4914, 0.4822, 0.4465]) -# std = torch.std(x, dim=(0,2,3)) #tensor([0.2470, 0.2435, 0.2616]) -# -# ``` +# # get the mean of each channel +# mean = torch.mean(x, dim=(0,2,3)) # tensor([0.4914, 0.4822, 0.4465]) +# std = torch.std(x, dim=(0,2,3)) # tensor([0.2470, 0.2435, 0.2616]) +# # # There are many more transforms available, including cropping, centering, # rotation, and reflection. From 06f9c4b866ef0ab190ee4f562cec1a0e38e87f50 Mon Sep 17 00:00:00 2001 From: "Runtian (Rachel) Li" <115634891+Rachel0619@users.noreply.github.com> Date: Thu, 5 Jun 2025 14:52:28 -0700 Subject: [PATCH 113/347] Fix broken deployment link in Flask REST API tutorial (#3385) * Fixes #3044: Fix broken deployment link in Flask REST API tutorial --------- Co-authored-by: Svetlana Karslioglu --- intermediate_source/flask_rest_api_tutorial.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/intermediate_source/flask_rest_api_tutorial.py b/intermediate_source/flask_rest_api_tutorial.py index 8b0162a9e84..b6b46aed91d 100644 --- a/intermediate_source/flask_rest_api_tutorial.py +++ b/intermediate_source/flask_rest_api_tutorial.py @@ -321,9 +321,7 @@ def get_prediction(image_bytes): # for deploying a Flask server in production. # # - You can also add a UI by creating a page with a form which takes the image and -# displays the prediction. Check out the `demo `_ -# of a similar project and its `source code `_. -# +# displays the prediction. # - In this tutorial, we only showed how to build a service that could return predictions for # a single image at a time. We could modify our service to be able to return predictions for # multiple images at once. In addition, the `service-streamer `_ From 33a7401a4f99bd311fe09fd09a8aa524cbf30056 Mon Sep 17 00:00:00 2001 From: Krishna Kalyan Date: Fri, 13 Jun 2025 16:24:12 +0200 Subject: [PATCH 114/347] Update CONTRIBUTING.md (#3387) * update readme * update with feedback --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index af84d9ebe79..6c0f1859e49 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,16 @@ GALLERY_PATTERN="neural_style_transfer_tutorial.py" sphinx-build . _build The `GALLERY_PATTERN` variable respects regular expressions. +## Spell Check +You can run pyspelling to check for spelling errors in the tutorials. To check only Python files, run pyspelling -n python. To check only .rst files, use pyspelling -n reST. Currently, .rst spell checking is limited to the beginner/ directory. Contributions to enable spell checking in other directories are welcome! + + +``` +pyspelling # full check (~3 mins) +pyspelling -n python # Python files only +pyspelling -n reST # reST files (only beginner/ dir currently included) +``` + ## About contributing to PyTorch Documentation and Tutorials * You can find information about contributing to PyTorch documentation in the From 2c4c99d7454ec1d2c95c2812c6ef8ee13264b7b6 Mon Sep 17 00:00:00 2001 From: Frost Mitchell Date: Sun, 15 Jun 2025 17:20:58 -0400 Subject: [PATCH 115/347] Fix XPU and CUDA tables in profiler recipe. (#3393) * Fix XPU and CUDA tables in profiler recipe. --- recipes_source/recipes/profiler_recipe.py | 72 +++++++++++------------ 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/recipes_source/recipes/profiler_recipe.py b/recipes_source/recipes/profiler_recipe.py index 4d43726e71f..6402c74e770 100644 --- a/recipes_source/recipes/profiler_recipe.py +++ b/recipes_source/recipes/profiler_recipe.py @@ -105,22 +105,24 @@ ###################################################################### # The output will look like (omitting some columns): - -# --------------------------------- ------------ ------------ ------------ ------------ -# Name Self CPU CPU total CPU time avg # of Calls -# --------------------------------- ------------ ------------ ------------ ------------ -# model_inference 5.509ms 57.503ms 57.503ms 1 -# aten::conv2d 231.000us 31.931ms 1.597ms 20 -# aten::convolution 250.000us 31.700ms 1.585ms 20 -# aten::_convolution 336.000us 31.450ms 1.573ms 20 -# aten::mkldnn_convolution 30.838ms 31.114ms 1.556ms 20 -# aten::batch_norm 211.000us 14.693ms 734.650us 20 -# aten::_batch_norm_impl_index 319.000us 14.482ms 724.100us 20 -# aten::native_batch_norm 9.229ms 14.109ms 705.450us 20 -# aten::mean 332.000us 2.631ms 125.286us 21 -# aten::select 1.668ms 2.292ms 8.988us 255 -# --------------------------------- ------------ ------------ ------------ ------------ -# Self CPU time total: 57.549m +# +# .. code-block:: sh +# +# --------------------------------- ------------ ------------ ------------ ------------ +# Name Self CPU CPU total CPU time avg # of Calls +# --------------------------------- ------------ ------------ ------------ ------------ +# model_inference 5.509ms 57.503ms 57.503ms 1 +# aten::conv2d 231.000us 31.931ms 1.597ms 20 +# aten::convolution 250.000us 31.700ms 1.585ms 20 +# aten::_convolution 336.000us 31.450ms 1.573ms 20 +# aten::mkldnn_convolution 30.838ms 31.114ms 1.556ms 20 +# aten::batch_norm 211.000us 14.693ms 734.650us 20 +# aten::_batch_norm_impl_index 319.000us 14.482ms 724.100us 20 +# aten::native_batch_norm 9.229ms 14.109ms 705.450us 20 +# aten::mean 332.000us 2.631ms 125.286us 21 +# aten::select 1.668ms 2.292ms 8.988us 255 +# --------------------------------- ------------ ------------ ------------ ------------ +# Self CPU time total: 57.549m # ###################################################################### @@ -209,8 +211,6 @@ # Self CPU time total: 23.015ms # Self CUDA time total: 11.666ms # -###################################################################### - ###################################################################### # (Note: the first use of XPU profiling may bring an extra overhead.) @@ -220,28 +220,26 @@ # # .. code-block:: sh # -#------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ -# Name Self XPU Self XPU % XPU total XPU time avg # of Calls -# ------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ -# model_inference 0.000us 0.00% 2.567ms 2.567ms 1 -# aten::conv2d 0.000us 0.00% 1.871ms 93.560us 20 -# aten::convolution 0.000us 0.00% 1.871ms 93.560us 20 -# aten::_convolution 0.000us 0.00% 1.871ms 93.560us 20 -# aten::convolution_overrideable 1.871ms 72.89% 1.871ms 93.560us 20 -# gen_conv 1.484ms 57.82% 1.484ms 74.216us 20 -# aten::batch_norm 0.000us 0.00% 432.640us 21.632us 20 -# aten::_batch_norm_impl_index 0.000us 0.00% 432.640us 21.632us 20 -# aten::native_batch_norm 432.640us 16.85% 432.640us 21.632us 20 -# conv_reorder 386.880us 15.07% 386.880us 6.448us 60 -# ------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ -# Self CPU time total: 712.486ms -# Self XPU time total: 2.567ms - +# ------------------------------ ------------ ------------ ------------ ------------ ------------ +# Name Self XPU Self XPU % XPU total XPU time avg # of Calls +# ------------------------------ ------------ ------------ ------------ ------------ ------------ +# model_inference 0.000us 0.00% 2.567ms 2.567ms 1 +# aten::conv2d 0.000us 0.00% 1.871ms 93.560us 20 +# aten::convolution 0.000us 0.00% 1.871ms 93.560us 20 +# aten::_convolution 0.000us 0.00% 1.871ms 93.560us 20 +# aten::convolution_overrideable 1.871ms 72.89% 1.871ms 93.560us 20 +# gen_conv 1.484ms 57.82% 1.484ms 74.216us 20 +# aten::batch_norm 0.000us 0.00% 432.640us 21.632us 20 +# aten::_batch_norm_impl_index 0.000us 0.00% 432.640us 21.632us 20 +# aten::native_batch_norm 432.640us 16.85% 432.640us 21.632us 20 +# conv_reorder 386.880us 15.07% 386.880us 6.448us 60 +# ------------------------------ ------------ ------------ ------------ ------------ ------------ +# Self CPU time total: 712.486ms +# Self XPU time total: 2.567ms # - ###################################################################### -# Note the occurrence of on-device kernels in the output (e.g. ``sgemm_32x32x32_NN``). +# Note the occurrence of on-device kernels in the output (e.g. ``sgemm_32x32x32_NN`` for CUDA or ``gen_conv`` for XPU). ###################################################################### # 4. Using profiler to analyze memory consumption From ab2aafd489bdf4ac6974f99a78593fb1affcdc72 Mon Sep 17 00:00:00 2001 From: Suryaprakash Senthil Kumar Date: Mon, 16 Jun 2025 11:08:44 -0700 Subject: [PATCH 116/347] Update hyper params and set seeds (#3384) * Update hyper params and set seeds * Updated hyper params * Added commented paragraph --------- Co-authored-by: sekyondaMeta <127536312+sekyondaMeta@users.noreply.github.com> Co-authored-by: Svetlana Karslioglu --- .../reinforcement_q_learning.py | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/intermediate_source/reinforcement_q_learning.py b/intermediate_source/reinforcement_q_learning.py index 0ae3ea9a90c..1e50fcb3673 100644 --- a/intermediate_source/reinforcement_q_learning.py +++ b/intermediate_source/reinforcement_q_learning.py @@ -92,6 +92,24 @@ ) +# To ensure reproducibility during training, you can fix the random seeds +# by uncommenting the lines below. This makes the results consistent across +# runs, which is helpful for debugging or comparing different approaches. +# +# That said, allowing randomness can be beneficial in practice, as it lets +# the model explore different training trajectories. + + +# seed = 42 +# random.seed(seed) +# torch.manual_seed(seed) +# env.reset(seed=seed) +# env.action_space.seed(seed) +# env.observation_space.seed(seed) +# if torch.cuda.is_available(): +# torch.cuda.manual_seed(seed) + + ###################################################################### # Replay Memory # ------------- @@ -253,13 +271,15 @@ def forward(self, x): # EPS_DECAY controls the rate of exponential decay of epsilon, higher means a slower decay # TAU is the update rate of the target network # LR is the learning rate of the ``AdamW`` optimizer + BATCH_SIZE = 128 GAMMA = 0.99 EPS_START = 0.9 -EPS_END = 0.05 -EPS_DECAY = 1000 +EPS_END = 0.01 +EPS_DECAY = 2500 TAU = 0.005 -LR = 1e-4 +LR = 3e-4 + # Get number of actions from gym action space n_actions = env.action_space.n From cb9d99dbbcfa4902cbb01cbebb0c3315d52900b3 Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Wed, 18 Jun 2025 14:46:40 -0700 Subject: [PATCH 117/347] Update docathon-leaderboard.md --- docathon-leaderboard.md | 47 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/docathon-leaderboard.md b/docathon-leaderboard.md index 49912c2abfb..f969e7dce3f 100644 --- a/docathon-leaderboard.md +++ b/docathon-leaderboard.md @@ -1,3 +1,50 @@ +# 🎉 PyTorch Docathon Leaderboard 2025 🎉 + +This is the list of the docathon contributors that have participated and contributed to the PyTorch H1 2024 docathon. A big shout out to everyone who have participated! +We have awarded points for each merged PR as follows: + +* easy - 2 points +* medium - 5 points +* advanced - 10 points + +We have granted half points (1, 2, and 5 respectively) for all additional PRs merged against the same issue. +In some cases, we have awarded credit for the PRs that were not merged or issues that have been closed without a merged PR. + +| Rank | Author | Points | PRs | +|:---:|:------------|------:|:----| +| 🥇 | [j-silv](https://github.com/j-silv) | 31 | [#155753](https://github.com/pytorch/pytorch/pull/155753), [#155659](https://github.com/pytorch/pytorch/pull/155659), [#155567](https://github.com/pytorch/pytorch/pull/155567), [#155540](https://github.com/pytorch/pytorch/pull/155540), [#155528](https://github.com/pytorch/pytorch/pull/155528), [#155198](https://github.com/pytorch/pytorch/pull/155198), [#155093](https://github.com/pytorch/pytorch/pull/155093), [#3389](https://github.com/pytorch/tutorials/pull/3389) | +| 🥇 | [windsonsea](https://github.com/windsonsea) | 19 | [#155789](https://github.com/pytorch/pytorch/pull/155789), [#155520](https://github.com/pytorch/pytorch/pull/155520), [#156039](https://github.com/pytorch/pytorch/pull/156039), [#156009](https://github.com/pytorch/pytorch/pull/156009), [#155653](https://github.com/pytorch/pytorch/pull/155653) | +| 🥇 | [kiszk](https://github.com/kiszk) | 16 | [#155762](https://github.com/pytorch/pytorch/pull/155762), [#155514](https://github.com/pytorch/pytorch/pull/155514), [#155351](https://github.com/pytorch/pytorch/pull/155351), [#155348](https://github.com/pytorch/pytorch/pull/155348), [#155347](https://github.com/pytorch/pytorch/pull/155347) | +| 🥈 | [Rachel0619](https://github.com/Rachel0619) | 14 | [#155764](https://github.com/pytorch/pytorch/pull/155764), [#155482](https://github.com/pytorch/pytorch/pull/155482), [#3385](https://github.com/pytorch/tutorials/pull/3385), [#3381](https://github.com/pytorch/tutorials/pull/3381) | +| 🥈 | [jafraustro](https://github.com/jafraustro) | 14 | [#155523](https://github.com/pytorch/pytorch/pull/155523), [#155369](https://github.com/pytorch/pytorch/pull/155369), [#133563](https://github.com/pytorch/pytorch/issues/133563), [#129446](https://github.com/pytorch/pytorch/issues/129446) | +| 🥈 | [Dhia-naouali](https://github.com/Dhia-naouali) | 12 | [#155911](https://github.com/pytorch/pytorch/pull/155911), [#155840](https://github.com/pytorch/pytorch/pull/155840), [#155505](https://github.com/pytorch/pytorch/pull/155505) | +| 🥈 | [loganthomas](https://github.com/loganthomas) | 12 | [#155702](https://github.com/pytorch/pytorch/pull/155702), [#155088](https://github.com/pytorch/pytorch/pull/155088), [#155649](https://github.com/pytorch/pytorch/pull/155649) | +| 🥈 | [nirajkamal](https://github.com/nirajkamal) | 12 | [#155430](https://github.com/pytorch/pytorch/pull/155430), [#155228](https://github.com/pytorch/pytorch/pull/155228), [#3376](https://github.com/pytorch/tutorials/pull/3376) | +| 🥉 | [Juliandlb](https://github.com/Juliandlb) | 10 | [#155987](https://github.com/pytorch/pytorch/pull/155987), [#155618](https://github.com/pytorch/pytorch/pull/155618) | +| 🥉 | [ggsmith842](https://github.com/ggsmith842) | 7 | [#155767](https://github.com/pytorch/pytorch/pull/155767), [#155297](https://github.com/pytorch/pytorch/pull/155297) | +| 🥉 | [ParagEkbote](https://github.com/ParagEkbote) | 7 | [#155683](https://github.com/pytorch/pytorch/pull/155683), [#155341](https://github.com/pytorch/pytorch/pull/155341) | +| ⭐ | [GdoongMathew](https://github.com/GdoongMathew) | 5 | [#155813](https://github.com/pytorch/pytorch/pull/155813) | +| ⭐ | [eromomon](https://github.com/eromomon) | 5 | [#155696](https://github.com/pytorch/pytorch/pull/155696) | +| ⭐ | [dggaytan](https://github.com/dggaytan) | 5 | [#155377](https://github.com/pytorch/pytorch/pull/155377) | +| ⭐ | [spzala](https://github.com/spzala) | 5 | [#155335](https://github.com/pytorch/pytorch/pull/155335) | +| ⭐ | [framoncg](https://github.com/framoncg) | 5 | [#155298](https://github.com/pytorch/pytorch/pull/155298) | +| ⭐ | [abhinav-TB](https://github.com/abhinav-TB) | 5 | [#155252](https://github.com/pytorch/pytorch/pull/155252) | +| ⭐ | [aagalleg](https://github.com/aagalleg) | 5 | [#155137](https://github.com/pytorch/pytorch/pull/155137) | +| ⭐ | [kiersten-stokes](https://github.com/kiersten-stokes) | 5 | [#155067](https://github.com/pytorch/pytorch/pull/155067) | +| ⭐ | [krishnakalyan3](https://github.com/krishnakalyan3) | 5 | [#3387](https://github.com/pytorch/tutorials/pull/3387) | +| ⭐ | [splion-360](https://github.com/splion-360) | 5 | [#3384](https://github.com/pytorch/tutorials/pull/3384) | +| ⭐ | [harshaljanjani](https://github.com/harshaljanjani) | 5 | [#3377](https://github.com/pytorch/tutorials/pull/3377) | +| ⭐ | [b-koopman](https://github.com/b-koopman) | 4 | [#155100](https://github.com/pytorch/pytorch/pull/155100), [#155889](https://github.com/pytorch/pytorch/pull/155889) | +| ⭐ | [thatgeeman](https://github.com/thatgeeman) | 4 | [#155404](https://github.com/pytorch/pytorch/pull/155404), [#156094](https://github.com/pytorch/pytorch/pull/156094) | +| ⭐ | [frost-intel](https://github.com/frost-intel) | 2 | [#3393](https://github.com/pytorch/tutorials/pull/3393) | +| ⭐ | [ANotFox](https://github.com/ANotFox) | 2 | [#155148](https://github.com/pytorch/pytorch/pull/155148) | +| ⭐ | [QasimKhan5x](https://github.com/QasimKhan5x) | 2 | [#155074](https://github.com/pytorch/pytorch/pull/155074) | +| ⭐ | [Ashish-Soni08](https://github.com/Ashish-Soni08) | 2 | [#3379](https://github.com/pytorch/tutorials/pull/3379) | +| ⭐ | [FORTFANOP](https://github.com/FORTFANOP) | 2 | [#3378](https://github.com/pytorch/tutorials/pull/3378) | +| ⭐ | [newtdms ](https://github.com/newtdms ) | 2 | [#155497](https://github.com/pytorch/pytorch/pull/155497) | +| ⭐ | [srini047](https://github.com/srini047) | 2 | [#155554](https://github.com/pytorch/pytorch/pull/155554) | + + # 🎉 Docathon H1 2024 Leaderboard 🎉 This is the list of the docathon contributors that have participated and contributed to the PyTorch H1 2024 docathon. From 5f17335a36fe402e50c0c19a1606ed0732ed5a97 Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Wed, 18 Jun 2025 15:17:49 -0700 Subject: [PATCH 118/347] Update docathon-leaderboard.md --- docathon-leaderboard.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docathon-leaderboard.md b/docathon-leaderboard.md index f969e7dce3f..3c8f4bf3994 100644 --- a/docathon-leaderboard.md +++ b/docathon-leaderboard.md @@ -1,6 +1,6 @@ # 🎉 PyTorch Docathon Leaderboard 2025 🎉 -This is the list of the docathon contributors that have participated and contributed to the PyTorch H1 2024 docathon. A big shout out to everyone who have participated! +This is the list of the docathon contributors that have participated and contributed to the PyTorch H1 2025 docathon. A big shout out to everyone who have participated! We have awarded points for each merged PR as follows: * easy - 2 points From cccce6b28d5d510cbf51b9f5500cb40f15346e03 Mon Sep 17 00:00:00 2001 From: Jing Xu Date: Tue, 24 Jun 2025 01:09:52 +0900 Subject: [PATCH 119/347] remove unnecessary term XPUs from profiler (#3394) * remove unnecessary term XPUs from profiler * fine tune ProfilerActivity usage --------- Co-authored-by: Svetlana Karslioglu --- recipes_source/recipes/profiler_recipe.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/recipes_source/recipes/profiler_recipe.py b/recipes_source/recipes/profiler_recipe.py index 6402c74e770..602ab1a7a80 100644 --- a/recipes_source/recipes/profiler_recipe.py +++ b/recipes_source/recipes/profiler_recipe.py @@ -163,18 +163,20 @@ # Note the occurrence of ``aten::convolution`` twice with different input shapes. ###################################################################### -# Profiler can also be used to analyze performance of models executed on GPUs and XPUs: +# Profiler can also be used to analyze performance of models executed on GPUs: # Users could switch between cpu, cuda and xpu +activities = [ProfilerActivity.CPU] if torch.cuda.is_available(): device = 'cuda' + activities += [ProfilerActivity.CUDA] elif torch.xpu.is_available(): device = 'xpu' + activities += [ProfilerActivity.XPU] else: print('Neither CUDA nor XPU devices are available to demonstrate profiling on acceleration devices') import sys sys.exit(0) -activities = [ProfilerActivity.CPU, ProfilerActivity.CUDA, ProfilerActivity.XPU] sort_by_keyword = device + "_time_total" model = models.resnet18().to(device) @@ -308,9 +310,17 @@ # Profiling results can be outputted as a ``.json`` trace file: # Tracing CUDA or XPU kernels # Users could switch between cpu, cuda and xpu -device = 'cuda' - -activities = [ProfilerActivity.CPU, ProfilerActivity.CUDA, ProfilerActivity.XPU] +activities = [ProfilerActivity.CPU] +if torch.cuda.is_available(): + device = 'cuda' + activities += [ProfilerActivity.CUDA] +elif torch.xpu.is_available(): + device = 'xpu' + activities += [ProfilerActivity.XPU] +else: + print('Neither CUDA nor XPU devices are available to demonstrate profiling on acceleration devices') + import sys + sys.exit(0) model = models.resnet18().to(device) inputs = torch.randn(5, 3, 224, 224).to(device) From f96996bac3d336e78f831dacaf43f389b2716cef Mon Sep 17 00:00:00 2001 From: Mark Saroufim Date: Mon, 23 Jun 2025 13:06:03 -0700 Subject: [PATCH 120/347] Remove PyTorch Cheat Sheet (#3402) * Remove PyTorch Cheat Sheet * Update link_checkPR.yml --- .github/workflows/link_checkPR.yml | 7 +- beginner_source/PyTorch Cheat.md | 191 -------------------- beginner_source/ptcheat.rst | 275 ----------------------------- 3 files changed, 6 insertions(+), 467 deletions(-) delete mode 100644 beginner_source/PyTorch Cheat.md delete mode 100644 beginner_source/ptcheat.rst diff --git a/.github/workflows/link_checkPR.yml b/.github/workflows/link_checkPR.yml index 134542f085a..e91b31f5a31 100644 --- a/.github/workflows/link_checkPR.yml +++ b/.github/workflows/link_checkPR.yml @@ -43,7 +43,12 @@ jobs: - name: Skip Message if: steps.skip-label.outputs.result == 'true' run: echo "Link check was skipped due to the presence of the 'skip-link-check' label." - + + # Per tj-actions, a delete file is not a changed file so this ensures lint checking does not occur on deleted files + - name: No Files to Check + if: steps.skip-label.outputs.result == 'false' && steps.changed-files.outputs.any_changed == 'true' + run: echo "No relevant files were changed in this PR that require link checking." + - name: Suggestions if: failure() run: | diff --git a/beginner_source/PyTorch Cheat.md b/beginner_source/PyTorch Cheat.md deleted file mode 100644 index 4f7af63038c..00000000000 --- a/beginner_source/PyTorch Cheat.md +++ /dev/null @@ -1,191 +0,0 @@ ---- -Title: PyTorch Cheat Sheet -PyTorch version: 1.0Pre -Date updated: 7/30/18 - ---- - -# Imports ---------------- -### General - -``` -import torch # root package -from torch.utils.data import Dataset, DataLoader # dataset representation and loading -``` - -### Neural Network API - -``` -import torch.autograd as autograd # computation graph -from torch.autograd import Variable # variable node in computation graph -import torch.nn as nn # neural networks -import torch.nn.functional as F # layers, activations and more -import torch.optim as optim # optimizers e.g. gradient descent, ADAM, etc. -from torch.jit import script, trace # hybrid frontend decorator and tracing jit -``` -See [autograd](https://pytorch.org/docs/stable/autograd.html), [nn](https://pytorch.org/docs/stable/nn.html), [functional](https://pytorch.org/docs/stable/nn.html#torch-nn-functional) and [optim](https://pytorch.org/docs/stable/optim.html) - -### Torchscript and JIT - -``` -torch.jit.trace() # takes your module or function and an example data input, and traces the computational steps that the data encounters as it progresses through the model -@script # decorator used to indicate data-dependent control flow within the code being traced -``` -See [Torchscript](https://pytorch.org/docs/stable/jit.html) - -### ONNX - -``` -torch.onnx.export(model, dummy data, xxxx.proto) # exports an ONNX formatted model using a trained model, dummy data and the desired file name -model = onnx.load("alexnet.proto") # load an ONNX model -onnx.checker.check_model(model) # check that the model IR is well formed -onnx.helper.printable_graph(model.graph) # print a human readable representation of the graph -``` -See [onnx](https://pytorch.org/docs/stable/onnx.html) - -### Vision - -``` -from torchvision import datasets, models, transforms # vision datasets, architectures & transforms -import torchvision.transforms as transforms # composable transforms -``` -See [torchvision](https://pytorch.org/vision/stable/index.html) - -### Distributed Training - -``` -import torch.distributed as dist # distributed communication -from multiprocessing import Process # memory sharing processes -``` -See [distributed](https://pytorch.org/docs/stable/distributed.html) and [multiprocessing](https://pytorch.org/docs/stable/multiprocessing.html) - - -# Tensors --------------------- - -### Creation - -``` -torch.randn(*size) # tensor with independent N(0,1) entries -torch.[ones|zeros](*size) # tensor with all 1's [or 0's] -torch.Tensor(L) # create tensor from [nested] list or ndarray L -x.clone() # clone of x -with torch.no_grad(): # code wrap that stops autograd from tracking tensor history -requires_grad=True # arg, when set to True, tracks computation history for future derivative calculations -``` -See [tensor](https://pytorch.org/docs/stable/tensors.html) - -### Dimensionality - -``` -x.size() # return tuple-like object of dimensions -torch.cat(tensor_seq, dim=0) # concatenates tensors along dim -x.view(a,b,...) # reshapes x into size (a,b,...) -x.view(-1,a) # reshapes x into size (b,a) for some b -x.transpose(a,b) # swaps dimensions a and b -x.permute(*dims) # permutes dimensions -x.unsqueeze(dim) # tensor with added axis -x.unsqueeze(dim=2) # (a,b,c) tensor -> (a,b,1,c) tensor -``` -See [tensor](https://pytorch.org/docs/stable/tensors.html) - -### Algebra - -``` -A.mm(B) # matrix multiplication -A.mv(x) # matrix-vector multiplication -x.t() # matrix transpose -``` -See [math operations](https://pytorch.org/docs/stable/torch.html?highlight=mm#math-operations) - -### GPU Usage - -``` -torch.cuda.is_available() # check for cuda -x.cuda() # move x's data from CPU to GPU and return new object -x.cpu() # move x's data from GPU to CPU and return new object - -if not args.disable_cuda and torch.cuda.is_available(): # device agnostic code and modularity - args.device = torch.device('cuda') # -else: # - args.device = torch.device('cpu') # - -net.to(device) # recursively convert their parameters and buffers to device specific tensors -mytensor.to(device) # copy your tensors to a device (gpu, cpu) -``` -See [cuda](https://pytorch.org/docs/stable/cuda.html) - - -# Deep Learning -``` -nn.Linear(m,n) # fully connected layer from m to n units -nn.ConvXd(m,n,s) # X dimensional conv layer from m to n channels where X⍷{1,2,3} and the kernel size is s -nn.MaxPoolXd(s) # X dimension pooling layer (notation as above) -nn.BatchNorm # batch norm layer -nn.RNN/LSTM/GRU # recurrent layers -nn.Dropout(p=0.5, inplace=False) # dropout layer for any dimensional input -nn.Dropout2d(p=0.5, inplace=False) # 2-dimensional channel-wise dropout -nn.Embedding(num_embeddings, embedding_dim) # (tensor-wise) mapping from indices to embedding vectors -``` -See [nn](https://pytorch.org/docs/stable/nn.html) - -### Loss Functions - -``` -nn.X # where X is BCELoss, CrossEntropyLoss, L1Loss, MSELoss, NLLLoss, SoftMarginLoss, MultiLabelSoftMarginLoss, CosineEmbeddingLoss, KLDivLoss, MarginRankingLoss, HingeEmbeddingLoss or CosineEmbeddingLoss -``` -See [loss functions](https://pytorch.org/docs/stable/nn.html#loss-functions) - -### Activation Functions - -``` -nn.X # where X is ReLU, ReLU6, ELU, SELU, PReLU, LeakyReLU, Threshold, HardTanh, Sigmoid, Tanh, LogSigmoid, Softplus, SoftShrink, Softsign, TanhShrink, Softmin, Softmax, Softmax2d or LogSoftmax -``` -See [activation functions](https://pytorch.org/docs/stable/nn.html#non-linear-activations-weighted-sum-nonlinearity) - -### Optimizers - -``` -opt = optim.x(model.parameters(), ...) # create optimizer -opt.step() # update weights -optim.X # where X is SGD, Adadelta, Adagrad, Adam, SparseAdam, Adamax, ASGD, LBFGS, RMSProp or Rprop -``` -See [optimizers](https://pytorch.org/docs/stable/optim.html) - -### Learning rate scheduling - -``` -scheduler = optim.X(optimizer,...) # create lr scheduler -scheduler.step() # update lr at start of epoch -optim.lr_scheduler.X # where X is LambdaLR, StepLR, MultiStepLR, ExponentialLR or ReduceLROnPLateau -``` -See [learning rate scheduler](https://pytorch.org/docs/stable/optim.html#how-to-adjust-learning-rate) - - -# Data Utilities - -### Datasets - -``` -Dataset # abstract class representing dataset -TensorDataset # labelled dataset in the form of tensors -ConcatDataset # concatenation of Datasets -``` -See [datasets](https://pytorch.org/docs/stable/data.html?highlight=dataset#torch.utils.data.Dataset) - -### Dataloaders and DataSamplers - -``` -DataLoader(dataset, batch_size=1, ...) # loads data batches agnostic of structure of individual data points -sampler.Sampler(dataset,...) # abstract class dealing with ways to sample from dataset -sampler.XSampler # where X is Sequential, Random, Subset, WeightedRandom or Distributed -``` -See [dataloader](https://pytorch.org/docs/stable/data.html?highlight=dataloader#torch.utils.data.DataLoader) - - -## Also see - -* [Deep Learning with PyTorch: A 60 Minute Blitz](https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html) _(pytorch.org)_ -* [PyTorch Forums](https://discuss.pytorch.org/) _(discuss.pytorch.org)_ -* [PyTorch for Numpy users](https://github.com/wkentaro/pytorch-for-numpy-users) _(github.com/wkentaro/pytorch-for-numpy-users)_ diff --git a/beginner_source/ptcheat.rst b/beginner_source/ptcheat.rst deleted file mode 100644 index 49f9c9f5951..00000000000 --- a/beginner_source/ptcheat.rst +++ /dev/null @@ -1,275 +0,0 @@ -PyTorch Cheat Sheet -****************************** - -Imports -========= - -General -------- - -.. code-block:: python - - import torch # root package - from torch.utils.data import Dataset, DataLoader # dataset representation and loading - -Neural Network API ------------------- - -.. code-block:: python - - import torch.autograd as autograd # computation graph - from torch import Tensor # tensor node in the computation graph - import torch.nn as nn # neural networks - import torch.nn.functional as F # layers, activations and more - import torch.optim as optim # optimizers e.g. gradient descent, ADAM, etc. - -See `autograd `__, -`nn `__, -`functional `__ -and `optim `__ - -ONNX ----- - -.. code-block:: python - - torch.onnx.export(model, dummy data, xxxx.proto) # exports an ONNX formatted - # model using a trained model, dummy - # data and the desired file name - - model = onnx.load("alexnet.proto") # load an ONNX model - onnx.checker.check_model(model) # check that the model - # IR is well formed - - onnx.helper.printable_graph(model.graph) # print a human readable - # representation of the graph - -See `onnx `__ - -Vision ------- - -.. code-block:: python - - from torchvision import datasets, models, transforms # vision datasets, - # architectures & - # transforms - - import torchvision.transforms as transforms # composable transforms - -See -`torchvision `__ - -Distributed Training --------------------- - -.. code-block:: python - - import torch.distributed as dist # distributed communication - from torch.multiprocessing import Process # memory sharing processes - -See `distributed `__ -and -`multiprocessing `__ - -Tensors -========= - -Creation --------- - -.. code-block:: python - - x = torch.randn(*size) # tensor with independent N(0,1) entries - x = torch.[ones|zeros](*size) # tensor with all 1's [or 0's] - x = torch.tensor(L) # create tensor from [nested] list or ndarray L - y = x.clone() # clone of x - with torch.no_grad(): # code wrap that stops autograd from tracking tensor history - requires_grad=True # arg, when set to True, tracks computation - # history for future derivative calculations - -See `tensor `__ - -Dimensionality --------------- - -.. code-block:: python - - x.size() # return tuple-like object of dimensions - x = torch.cat(tensor_seq, dim=0) # concatenates tensors along dim - y = x.view(a,b,...) # reshapes x into size (a,b,...) - y = x.view(-1,a) # reshapes x into size (b,a) for some b - y = x.transpose(a,b) # swaps dimensions a and b - y = x.permute(*dims) # permutes dimensions - y = x.unsqueeze(dim) # tensor with added axis - y = x.unsqueeze(dim=2) # (a,b,c) tensor -> (a,b,1,c) tensor - y = x.squeeze() # removes all dimensions of size 1 (a,1,b,1) -> (a,b) - y = x.squeeze(dim=1) # removes specified dimension of size 1 (a,1,b,1) -> (a,b,1) - -See `tensor `__ - -Algebra -------- - - -.. code-block:: python - - ret = A.mm(B) # matrix multiplication - ret = A.mv(x) # matrix-vector multiplication - x = x.t() # matrix transpose - -See `math -operations `__ - -GPU Usage ---------- - -.. code-block:: python - - torch.cuda.is_available # check for cuda - x = x.cuda() # move x's data from - # CPU to GPU and return new object - - x = x.cpu() # move x's data from GPU to CPU - # and return new object - - if not args.disable_cuda and torch.cuda.is_available(): # device agnostic code - args.device = torch.device('cuda') # and modularity - else: # - args.device = torch.device('cpu') # - - net.to(device) # recursively convert their - # parameters and buffers to - # device specific tensors - - x = x.to(device) # copy your tensors to a device - # (gpu, cpu) - -See `cuda `__ - -Deep Learning -============= - -.. code-block:: python - - nn.Linear(m,n) # fully connected layer from - # m to n units - - nn.ConvXd(m,n,s) # X dimensional conv layer from - # m to n channels where X⍷{1,2,3} - # and the kernel size is s - - nn.MaxPoolXd(s) # X dimension pooling layer - # (notation as above) - - nn.BatchNormXd # batch norm layer - nn.RNN/LSTM/GRU # recurrent layers - nn.Dropout(p=0.5, inplace=False) # dropout layer for any dimensional input - nn.Dropout2d(p=0.5, inplace=False) # 2-dimensional channel-wise dropout - nn.Embedding(num_embeddings, embedding_dim) # (tensor-wise) mapping from - # indices to embedding vectors - -See `nn `__ - -Loss Functions --------------- - -.. code-block:: python - - nn.X # where X is L1Loss, MSELoss, CrossEntropyLoss - # CTCLoss, NLLLoss, PoissonNLLLoss, - # KLDivLoss, BCELoss, BCEWithLogitsLoss, - # MarginRankingLoss, HingeEmbeddingLoss, - # MultiLabelMarginLoss, SmoothL1Loss, - # SoftMarginLoss, MultiLabelSoftMarginLoss, - # CosineEmbeddingLoss, MultiMarginLoss, - # or TripletMarginLoss - - -See `loss -functions `__ - -Activation Functions --------------------- - -.. code-block:: python - - nn.X # where X is ReLU, ReLU6, ELU, SELU, PReLU, LeakyReLU, - # RReLu, CELU, GELU, Threshold, Hardshrink, HardTanh, - # Sigmoid, LogSigmoid, Softplus, SoftShrink, - # Softsign, Tanh, TanhShrink, Softmin, Softmax, - # Softmax2d, LogSoftmax or AdaptiveSoftmaxWithLoss - -See `activation -functions `__ - -Optimizers ----------- - -.. code-block:: python - - opt = optim.x(model.parameters(), ...) # create optimizer - opt.step() # update weights - opt.zero_grad() # clear the gradients - optim.X # where X is SGD, AdamW, Adam, - # Adafactor, NAdam, RAdam, Adadelta, - # Adagrad, SparseAdam, Adamax, ASGD, - # LBFGS, RMSprop or Rprop - -See `optimizers `__ - -Learning rate scheduling ------------------------- - -.. code-block:: python - - scheduler = optim.X(optimizer,...) # create lr scheduler - scheduler.step() # update lr after optimizer updates weights - optim.lr_scheduler.X # where X is LambdaLR, MultiplicativeLR, - # StepLR, MultiStepLR, ExponentialLR, - # CosineAnnealingLR, ReduceLROnPlateau, CyclicLR, - # OneCycleLR, CosineAnnealingWarmRestarts, - -See `learning rate -scheduler `__ - -Data Utilities -============== - -Datasets --------- - -.. code-block:: python - - Dataset # abstract class representing dataset - TensorDataset # labelled dataset in the form of tensors - Concat Dataset # concatenation of Datasets - -See -`datasets `__ - -Dataloaders and ``DataSamplers`` --------------------------------- - -.. code-block:: python - - DataLoader(dataset, batch_size=1, ...) # loads data batches agnostic - # of structure of individual data points - - sampler.Sampler(dataset,...) # abstract class dealing with - # ways to sample from dataset - - sampler.XSampler where ... # Sequential, Random, SubsetRandom, - # WeightedRandom, Batch, Distributed - -See -`dataloader `__ - -Also see --------- - -- `Deep Learning with PyTorch: A 60 Minute - Blitz `__ -- `PyTorch Forums `__ -- `PyTorch for Numpy - users `__ From 555d2310e98b80825533f52c90cdd092b98b9468 Mon Sep 17 00:00:00 2001 From: Mark Saroufim Date: Mon, 23 Jun 2025 14:14:30 -0700 Subject: [PATCH 121/347] delete seq2seq ts tutorial (#3404) --- ...deploy_seq2seq_hybrid_frontend_tutorial.py | 877 ------------------ 1 file changed, 877 deletions(-) delete mode 100644 beginner_source/deploy_seq2seq_hybrid_frontend_tutorial.py diff --git a/beginner_source/deploy_seq2seq_hybrid_frontend_tutorial.py b/beginner_source/deploy_seq2seq_hybrid_frontend_tutorial.py deleted file mode 100644 index 1fb0f4d24b4..00000000000 --- a/beginner_source/deploy_seq2seq_hybrid_frontend_tutorial.py +++ /dev/null @@ -1,877 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Deploying a Seq2Seq Model with TorchScript -================================================== -**Author:** `Matthew Inkawhich `_ - -.. warning:: TorchScript is no longer in active development. -""" - - -###################################################################### -# This tutorial will walk through the process of transitioning a -# sequence-to-sequence model to TorchScript using the TorchScript -# API. The model that we will convert is the chatbot model from the -# `Chatbot tutorial `__. -# You can either treat this tutorial as a “Part 2” to the Chatbot tutorial -# and deploy your own pretrained model, or you can start with this -# document and use a pretrained model that we host. In the latter case, -# you can reference the original Chatbot tutorial for details -# regarding data preprocessing, model theory and definition, and model -# training. -# -# What is TorchScript? -# ---------------------------- -# -# During the research and development phase of a deep learning-based -# project, it is advantageous to interact with an **eager**, imperative -# interface like PyTorch’s. This gives users the ability to write -# familiar, idiomatic Python, allowing for the use of Python data -# structures, control flow operations, print statements, and debugging -# utilities. Although the eager interface is a beneficial tool for -# research and experimentation applications, when it comes time to deploy -# the model in a production environment, having a **graph**-based model -# representation is very beneficial. A deferred graph representation -# allows for optimizations such as out-of-order execution, and the ability -# to target highly optimized hardware architectures. Also, a graph-based -# representation enables framework-agnostic model exportation. PyTorch -# provides mechanisms for incrementally converting eager-mode code into -# TorchScript, a statically analyzable and optimizable subset of Python -# that Torch uses to represent deep learning programs independently from -# the Python runtime. -# -# The API for converting eager-mode PyTorch programs into TorchScript is -# found in the ``torch.jit`` module. This module has two core modalities for -# converting an eager-mode model to a TorchScript graph representation: -# **tracing** and **scripting**. The ``torch.jit.trace`` function takes a -# module or function and a set of example inputs. It then runs the example -# input through the function or module while tracing the computational -# steps that are encountered, and outputs a graph-based function that -# performs the traced operations. **Tracing** is great for straightforward -# modules and functions that do not involve data-dependent control flow, -# such as standard convolutional neural networks. However, if a function -# with data-dependent if statements and loops is traced, only the -# operations called along the execution route taken by the example input -# will be recorded. In other words, the control flow itself is not -# captured. To convert modules and functions containing data-dependent -# control flow, a **scripting** mechanism is provided. The -# ``torch.jit.script`` function/decorator takes a module or function and -# does not requires example inputs. Scripting then explicitly converts -# the module or function code to TorchScript, including all control flows. -# One caveat with using scripting is that it only supports a subset of -# Python, so you might need to rewrite the code to make it compatible -# with the TorchScript syntax. -# -# For all details relating to the supported features, see the `TorchScript -# language reference `__. -# To provide the maximum flexibility, you can also mix tracing and scripting -# modes together to represent your whole program, and these techniques can -# be applied incrementally. -# -# .. figure:: /_static/img/chatbot/pytorch_workflow.png -# :align: center -# :alt: workflow -# - - - -###################################################################### -# Acknowledgments -# ---------------- -# -# This tutorial was inspired by the following sources: -# -# 1) Yuan-Kuei Wu's pytorch-chatbot implementation: -# https://github.com/ywk991112/pytorch-chatbot -# -# 2) Sean Robertson's practical-pytorch seq2seq-translation example: -# https://github.com/spro/practical-pytorch/tree/master/seq2seq-translation -# -# 3) FloydHub's Cornell Movie Corpus preprocessing code: -# https://github.com/floydhub/textutil-preprocess-cornell-movie-corpus -# - - -###################################################################### -# Prepare Environment -# ------------------- -# -# First, we will import the required modules and set some constants. If -# you are planning on using your own model, be sure that the -# ``MAX_LENGTH`` constant is set correctly. As a reminder, this constant -# defines the maximum allowed sentence length during training and the -# maximum length output that the model is capable of producing. -# - -import torch -import torch.nn as nn -import torch.nn.functional as F -import re -import os -import unicodedata -import numpy as np - -device = torch.device("cpu") - - -MAX_LENGTH = 10 # Maximum sentence length - -# Default word tokens -PAD_token = 0 # Used for padding short sentences -SOS_token = 1 # Start-of-sentence token -EOS_token = 2 # End-of-sentence token - - -###################################################################### -# Model Overview -# -------------- -# -# As mentioned, the model that we are using is a -# `sequence-to-sequence `__ (seq2seq) -# model. This type of model is used in cases when our input is a -# variable-length sequence, and our output is also a variable length -# sequence that is not necessarily a one-to-one mapping of the input. A -# seq2seq model is comprised of two recurrent neural networks (RNNs) that -# work cooperatively: an **encoder** and a **decoder**. -# -# .. figure:: /_static/img/chatbot/seq2seq_ts.png -# :align: center -# :alt: model -# -# -# Image source: -# https://jeddy92.github.io/JEddy92.github.io/ts_seq2seq_intro/ -# -# Encoder -# ~~~~~~~ -# -# The encoder RNN iterates through the input sentence one token -# (e.g. word) at a time, at each time step outputting an “output” vector -# and a “hidden state” vector. The hidden state vector is then passed to -# the next time step, while the output vector is recorded. The encoder -# transforms the context it saw at each point in the sequence into a set -# of points in a high-dimensional space, which the decoder will use to -# generate a meaningful output for the given task. -# -# Decoder -# ~~~~~~~ -# -# The decoder RNN generates the response sentence in a token-by-token -# fashion. It uses the encoder’s context vectors, and internal hidden -# states to generate the next word in the sequence. It continues -# generating words until it outputs an *EOS_token*, representing the end -# of the sentence. We use an `attention -# mechanism `__ in our decoder to help it -# to “pay attention” to certain parts of the input when generating the -# output. For our model, we implement `Luong et -# al. `__\ ’s “Global attention” module, -# and use it as a submodule in our decode model. -# - - -###################################################################### -# Data Handling -# ------------- -# -# Although our models conceptually deal with sequences of tokens, in -# reality, they deal with numbers like all machine learning models do. In -# this case, every word in the model’s vocabulary, which was established -# before training, is mapped to an integer index. We use a ``Voc`` object -# to contain the mappings from word to index, as well as the total number -# of words in the vocabulary. We will load the object later before we run -# the model. -# -# Also, in order for us to be able to run evaluations, we must provide a -# tool for processing our string inputs. The ``normalizeString`` function -# converts all characters in a string to lowercase and removes all -# non-letter characters. The ``indexesFromSentence`` function takes a -# sentence of words and returns the corresponding sequence of word -# indexes. -# - -class Voc: - def __init__(self, name): - self.name = name - self.trimmed = False - self.word2index = {} - self.word2count = {} - self.index2word = {PAD_token: "PAD", SOS_token: "SOS", EOS_token: "EOS"} - self.num_words = 3 # Count SOS, EOS, PAD - - def addSentence(self, sentence): - for word in sentence.split(' '): - self.addWord(word) - - def addWord(self, word): - if word not in self.word2index: - self.word2index[word] = self.num_words - self.word2count[word] = 1 - self.index2word[self.num_words] = word - self.num_words += 1 - else: - self.word2count[word] += 1 - - # Remove words below a certain count threshold - def trim(self, min_count): - if self.trimmed: - return - self.trimmed = True - keep_words = [] - for k, v in self.word2count.items(): - if v >= min_count: - keep_words.append(k) - - print('keep_words {} / {} = {:.4f}'.format( - len(keep_words), len(self.word2index), len(keep_words) / len(self.word2index) - )) - # Reinitialize dictionaries - self.word2index = {} - self.word2count = {} - self.index2word = {PAD_token: "PAD", SOS_token: "SOS", EOS_token: "EOS"} - self.num_words = 3 # Count default tokens - for word in keep_words: - self.addWord(word) - - -# Lowercase and remove non-letter characters -def normalizeString(s): - s = s.lower() - s = re.sub(r"([.!?])", r" \1", s) - s = re.sub(r"[^a-zA-Z.!?]+", r" ", s) - return s - - -# Takes string sentence, returns sentence of word indexes -def indexesFromSentence(voc, sentence): - return [voc.word2index[word] for word in sentence.split(' ')] + [EOS_token] - - -###################################################################### -# Define Encoder -# -------------- -# -# We implement our encoder’s RNN with the ``torch.nn.GRU`` module which we -# feed a batch of sentences (vectors of word embeddings) and it internally -# iterates through the sentences one token at a time calculating the -# hidden states. We initialize this module to be bidirectional, meaning -# that we have two independent GRUs: one that iterates through the -# sequences in chronological order, and another that iterates in reverse -# order. We ultimately return the sum of these two GRUs’ outputs. Since -# our model was trained using batching, our ``EncoderRNN`` model’s -# ``forward`` function expects a padded input batch. To batch -# variable-length sentences, we allow a maximum of *MAX_LENGTH* tokens in -# a sentence, and all sentences in the batch that have less than -# *MAX_LENGTH* tokens are padded at the end with our dedicated *PAD_token* -# tokens. To use padded batches with a PyTorch RNN module, we must wrap -# the forward pass call with ``torch.nn.utils.rnn.pack_padded_sequence`` -# and ``torch.nn.utils.rnn.pad_packed_sequence`` data transformations. -# Note that the ``forward`` function also takes an ``input_lengths`` list, -# which contains the length of each sentence in the batch. This input is -# used by the ``torch.nn.utils.rnn.pack_padded_sequence`` function when -# padding. -# -# TorchScript Notes: -# ~~~~~~~~~~~~~~~~~~~~~~ -# -# Since the encoder’s ``forward`` function does not contain any -# data-dependent control flow, we will use **tracing** to convert it to -# script mode. When tracing a module, we can leave the module definition -# as-is. We will initialize all models towards the end of this document -# before we run evaluations. -# - -class EncoderRNN(nn.Module): - def __init__(self, hidden_size, embedding, n_layers=1, dropout=0): - super(EncoderRNN, self).__init__() - self.n_layers = n_layers - self.hidden_size = hidden_size - self.embedding = embedding - - # Initialize GRU; the ``input_size`` and ``hidden_size`` parameters are both set to 'hidden_size' - # because our input size is a word embedding with number of features == hidden_size - self.gru = nn.GRU(hidden_size, hidden_size, n_layers, - dropout=(0 if n_layers == 1 else dropout), bidirectional=True) - - def forward(self, input_seq, input_lengths, hidden=None): - # type: (Tensor, Tensor, Optional[Tensor]) -> Tuple[Tensor, Tensor] - # Convert word indexes to embeddings - embedded = self.embedding(input_seq) - # Pack padded batch of sequences for RNN module - packed = torch.nn.utils.rnn.pack_padded_sequence(embedded, input_lengths) - # Forward pass through GRU - outputs, hidden = self.gru(packed, hidden) - # Unpack padding - outputs, _ = torch.nn.utils.rnn.pad_packed_sequence(outputs) - # Sum bidirectional GRU outputs - outputs = outputs[:, :, :self.hidden_size] + outputs[:, : ,self.hidden_size:] - # Return output and final hidden state - return outputs, hidden - - -###################################################################### -# Define Decoder’s Attention Module -# --------------------------------- -# -# Next, we’ll define our attention module (``Attn``). Note that this -# module will be used as a submodule in our decoder model. Luong et -# al. consider various “score functions”, which take the current decoder -# RNN output and the entire encoder output, and return attention -# “energies”. This attention energies tensor is the same size as the -# encoder output, and the two are ultimately multiplied, resulting in a -# weighted tensor whose largest values represent the most important parts -# of the query sentence at a particular time-step of decoding. -# - -# Luong attention layer -class Attn(nn.Module): - def __init__(self, method, hidden_size): - super(Attn, self).__init__() - self.method = method - if self.method not in ['dot', 'general', 'concat']: - raise ValueError(self.method, "is not an appropriate attention method.") - self.hidden_size = hidden_size - if self.method == 'general': - self.attn = nn.Linear(self.hidden_size, hidden_size) - elif self.method == 'concat': - self.attn = nn.Linear(self.hidden_size * 2, hidden_size) - self.v = nn.Parameter(torch.FloatTensor(hidden_size)) - - def dot_score(self, hidden, encoder_output): - return torch.sum(hidden * encoder_output, dim=2) - - def general_score(self, hidden, encoder_output): - energy = self.attn(encoder_output) - return torch.sum(hidden * energy, dim=2) - - def concat_score(self, hidden, encoder_output): - energy = self.attn(torch.cat((hidden.expand(encoder_output.size(0), -1, -1), encoder_output), 2)).tanh() - return torch.sum(self.v * energy, dim=2) - - def forward(self, hidden, encoder_outputs): - # Calculate the attention weights (energies) based on the given method - if self.method == 'general': - attn_energies = self.general_score(hidden, encoder_outputs) - elif self.method == 'concat': - attn_energies = self.concat_score(hidden, encoder_outputs) - elif self.method == 'dot': - attn_energies = self.dot_score(hidden, encoder_outputs) - - # Transpose max_length and batch_size dimensions - attn_energies = attn_energies.t() - - # Return the softmax normalized probability scores (with added dimension) - return F.softmax(attn_energies, dim=1).unsqueeze(1) - - -###################################################################### -# Define Decoder -# -------------- -# -# Similarly to the ``EncoderRNN``, we use the ``torch.nn.GRU`` module for -# our decoder’s RNN. This time, however, we use a unidirectional GRU. It -# is important to note that unlike the encoder, we will feed the decoder -# RNN one word at a time. We start by getting the embedding of the current -# word and applying a -# `dropout `__. -# Next, we forward the embedding and the last hidden state to the GRU and -# obtain a current GRU output and hidden state. We then use our ``Attn`` -# module as a layer to obtain the attention weights, which we multiply by -# the encoder’s output to obtain our attended encoder output. We use this -# attended encoder output as our ``context`` tensor, which represents a -# weighted sum indicating what parts of the encoder’s output to pay -# attention to. From here, we use a linear layer and softmax normalization -# to select the next word in the output sequence. - -# TorchScript Notes: -# ~~~~~~~~~~~~~~~~~~~~~~ -# -# Similarly to the ``EncoderRNN``, this module does not contain any -# data-dependent control flow. Therefore, we can once again use -# **tracing** to convert this model to TorchScript after it -# is initialized and its parameters are loaded. -# - -class LuongAttnDecoderRNN(nn.Module): - def __init__(self, attn_model, embedding, hidden_size, output_size, n_layers=1, dropout=0.1): - super(LuongAttnDecoderRNN, self).__init__() - - # Keep for reference - self.attn_model = attn_model - self.hidden_size = hidden_size - self.output_size = output_size - self.n_layers = n_layers - self.dropout = dropout - - # Define layers - self.embedding = embedding - self.embedding_dropout = nn.Dropout(dropout) - self.gru = nn.GRU(hidden_size, hidden_size, n_layers, dropout=(0 if n_layers == 1 else dropout)) - self.concat = nn.Linear(hidden_size * 2, hidden_size) - self.out = nn.Linear(hidden_size, output_size) - - self.attn = Attn(attn_model, hidden_size) - - def forward(self, input_step, last_hidden, encoder_outputs): - # Note: we run this one step (word) at a time - # Get embedding of current input word - embedded = self.embedding(input_step) - embedded = self.embedding_dropout(embedded) - # Forward through unidirectional GRU - rnn_output, hidden = self.gru(embedded, last_hidden) - # Calculate attention weights from the current GRU output - attn_weights = self.attn(rnn_output, encoder_outputs) - # Multiply attention weights to encoder outputs to get new "weighted sum" context vector - context = attn_weights.bmm(encoder_outputs.transpose(0, 1)) - # Concatenate weighted context vector and GRU output using Luong eq. 5 - rnn_output = rnn_output.squeeze(0) - context = context.squeeze(1) - concat_input = torch.cat((rnn_output, context), 1) - concat_output = torch.tanh(self.concat(concat_input)) - # Predict next word using Luong eq. 6 - output = self.out(concat_output) - output = F.softmax(output, dim=1) - # Return output and final hidden state - return output, hidden - - -###################################################################### -# Define Evaluation -# ----------------- -# -# Greedy Search Decoder -# ~~~~~~~~~~~~~~~~~~~~~ -# -# As in the chatbot tutorial, we use a ``GreedySearchDecoder`` module to -# facilitate the actual decoding process. This module has the trained -# encoder and decoder models as attributes, and drives the process of -# encoding an input sentence (a vector of word indexes), and iteratively -# decoding an output response sequence one word (word index) at a time. -# -# Encoding the input sequence is straightforward: simply forward the -# entire sequence tensor and its corresponding lengths vector to the -# ``encoder``. It is important to note that this module only deals with -# one input sequence at a time, **NOT** batches of sequences. Therefore, -# when the constant **1** is used for declaring tensor sizes, this -# corresponds to a batch size of 1. To decode a given decoder output, we -# must iteratively run forward passes through our decoder model, which -# outputs softmax scores corresponding to the probability of each word -# being the correct next word in the decoded sequence. We initialize the -# ``decoder_input`` to a tensor containing an *SOS_token*. After each pass -# through the ``decoder``, we *greedily* append the word with the highest -# softmax probability to the ``decoded_words`` list. We also use this word -# as the ``decoder_input`` for the next iteration. The decoding process -# terminates either if the ``decoded_words`` list has reached a length of -# *MAX_LENGTH* or if the predicted word is the *EOS_token*. -# -# TorchScript Notes: -# ~~~~~~~~~~~~~~~~~~~~~~ -# -# The ``forward`` method of this module involves iterating over the range -# of :math:`[0, max\_length)` when decoding an output sequence one word at -# a time. Because of this, we should use **scripting** to convert this -# module to TorchScript. Unlike with our encoder and decoder models, -# which we can trace, we must make some necessary changes to the -# ``GreedySearchDecoder`` module in order to initialize an object without -# error. In other words, we must ensure that our module adheres to the -# rules of the TorchScript mechanism, and does not utilize any language -# features outside of the subset of Python that TorchScript includes. -# -# To get an idea of some manipulations that may be required, we will go -# over the diffs between the ``GreedySearchDecoder`` implementation from -# the chatbot tutorial and the implementation that we use in the cell -# below. Note that the lines highlighted in red are lines removed from the -# original implementation and the lines highlighted in green are new. -# -# .. figure:: /_static/img/chatbot/diff.png -# :align: center -# :alt: diff -# -# Changes: -# ^^^^^^^^ -# -# - Added ``decoder_n_layers`` to the constructor arguments -# -# - This change stems from the fact that the encoder and decoder -# models that we pass to this module will be a child of -# ``TracedModule`` (not ``Module``). Therefore, we cannot access the -# decoder’s number of layers with ``decoder.n_layers``. Instead, we -# plan for this, and pass this value in during module construction. -# -# -# - Store away new attributes as constants -# -# - In the original implementation, we were free to use variables from -# the surrounding (global) scope in our ``GreedySearchDecoder``\ ’s -# ``forward`` method. However, now that we are using scripting, we -# do not have this freedom, as the assumption with scripting is that -# we cannot necessarily hold on to Python objects, especially when -# exporting. An easy solution to this is to store these values from -# the global scope as attributes to the module in the constructor, -# and add them to a special list called ``__constants__`` so that -# they can be used as literal values when constructing the graph in -# the ``forward`` method. An example of this usage is on NEW line -# 19, where instead of using the ``device`` and ``SOS_token`` global -# values, we use our constant attributes ``self._device`` and -# ``self._SOS_token``. -# -# -# - Enforce types of ``forward`` method arguments -# -# - By default, all parameters to a TorchScript function are assumed -# to be Tensor. If we need to pass an argument of a different type, -# we can use function type annotations as introduced in `PEP -# 3107 `__. In addition, -# it is possible to declare arguments of different types using -# Mypy-style type annotations (see -# `doc `__). -# -# -# - Change initialization of ``decoder_input`` -# -# - In the original implementation, we initialized our -# ``decoder_input`` tensor with ``torch.LongTensor([[SOS_token]])``. -# When scripting, we are not allowed to initialize tensors in a -# literal fashion like this. Instead, we can initialize our tensor -# with an explicit torch function such as ``torch.ones``. In this -# case, we can easily replicate the scalar ``decoder_input`` tensor -# by multiplying 1 by our SOS_token value stored in the constant -# ``self._SOS_token``. -# - -class GreedySearchDecoder(nn.Module): - def __init__(self, encoder, decoder, decoder_n_layers): - super(GreedySearchDecoder, self).__init__() - self.encoder = encoder - self.decoder = decoder - self._device = device - self._SOS_token = SOS_token - self._decoder_n_layers = decoder_n_layers - - __constants__ = ['_device', '_SOS_token', '_decoder_n_layers'] - - def forward(self, input_seq : torch.Tensor, input_length : torch.Tensor, max_length : int): - # Forward input through encoder model - encoder_outputs, encoder_hidden = self.encoder(input_seq, input_length) - # Prepare encoder's final hidden layer to be first hidden input to the decoder - decoder_hidden = encoder_hidden[:self._decoder_n_layers] - # Initialize decoder input with SOS_token - decoder_input = torch.ones(1, 1, device=self._device, dtype=torch.long) * self._SOS_token - # Initialize tensors to append decoded words to - all_tokens = torch.zeros([0], device=self._device, dtype=torch.long) - all_scores = torch.zeros([0], device=self._device) - # Iteratively decode one word token at a time - for _ in range(max_length): - # Forward pass through decoder - decoder_output, decoder_hidden = self.decoder(decoder_input, decoder_hidden, encoder_outputs) - # Obtain most likely word token and its softmax score - decoder_scores, decoder_input = torch.max(decoder_output, dim=1) - # Record token and score - all_tokens = torch.cat((all_tokens, decoder_input), dim=0) - all_scores = torch.cat((all_scores, decoder_scores), dim=0) - # Prepare current token to be next decoder input (add a dimension) - decoder_input = torch.unsqueeze(decoder_input, 0) - # Return collections of word tokens and scores - return all_tokens, all_scores - - - -###################################################################### -# Evaluating an Input -# ~~~~~~~~~~~~~~~~~~~ -# -# Next, we define some functions for evaluating an input. The ``evaluate`` -# function takes a normalized string sentence, processes it to a tensor of -# its corresponding word indexes (with batch size of 1), and passes this -# tensor to a ``GreedySearchDecoder`` instance called ``searcher`` to -# handle the encoding/decoding process. The searcher returns the output -# word index vector and a scores tensor corresponding to the softmax -# scores for each decoded word token. The final step is to convert each -# word index back to its string representation using ``voc.index2word``. -# -# We also define two functions for evaluating an input sentence. The -# ``evaluateInput`` function prompts a user for an input, and evaluates -# it. It will continue to ask for another input until the user enters ‘q’ -# or ‘quit’. -# -# The ``evaluateExample`` function simply takes a string input sentence as -# an argument, normalizes it, evaluates it, and prints the response. -# - -def evaluate(searcher, voc, sentence, max_length=MAX_LENGTH): - ### Format input sentence as a batch - # words -> indexes - indexes_batch = [indexesFromSentence(voc, sentence)] - # Create lengths tensor - lengths = torch.tensor([len(indexes) for indexes in indexes_batch]) - # Transpose dimensions of batch to match models' expectations - input_batch = torch.LongTensor(indexes_batch).transpose(0, 1) - # Use appropriate device - input_batch = input_batch.to(device) - lengths = lengths.to(device) - # Decode sentence with searcher - tokens, scores = searcher(input_batch, lengths, max_length) - # indexes -> words - decoded_words = [voc.index2word[token.item()] for token in tokens] - return decoded_words - - -# Evaluate inputs from user input (``stdin``) -def evaluateInput(searcher, voc): - input_sentence = '' - while(1): - try: - # Get input sentence - input_sentence = input('> ') - # Check if it is quit case - if input_sentence == 'q' or input_sentence == 'quit': break - # Normalize sentence - input_sentence = normalizeString(input_sentence) - # Evaluate sentence - output_words = evaluate(searcher, voc, input_sentence) - # Format and print response sentence - output_words[:] = [x for x in output_words if not (x == 'EOS' or x == 'PAD')] - print('Bot:', ' '.join(output_words)) - - except KeyError: - print("Error: Encountered unknown word.") - -# Normalize input sentence and call ``evaluate()`` -def evaluateExample(sentence, searcher, voc): - print("> " + sentence) - # Normalize sentence - input_sentence = normalizeString(sentence) - # Evaluate sentence - output_words = evaluate(searcher, voc, input_sentence) - output_words[:] = [x for x in output_words if not (x == 'EOS' or x == 'PAD')] - print('Bot:', ' '.join(output_words)) - - -###################################################################### -# Load Pretrained Parameters -# -------------------------- -# -# No, let's load our model! -# -# Use hosted model -# ~~~~~~~~~~~~~~~~ -# -# To load the hosted model: -# -# 1) Download the model `here `__. -# -# 2) Set the ``loadFilename`` variable to the path to the downloaded -# checkpoint file. -# -# 3) Leave the ``checkpoint = torch.load(loadFilename)`` line uncommented, -# as the hosted model was trained on CPU. -# -# Use your own model -# ~~~~~~~~~~~~~~~~~~ -# -# To load your own pretrained model: -# -# 1) Set the ``loadFilename`` variable to the path to the checkpoint file -# that you wish to load. Note that if you followed the convention for -# saving the model from the chatbot tutorial, this may involve changing -# the ``model_name``, ``encoder_n_layers``, ``decoder_n_layers``, -# ``hidden_size``, and ``checkpoint_iter`` (as these values are used in -# the model path). -# -# 2) If you trained the model on a CPU, make sure that you are opening the -# checkpoint with the ``checkpoint = torch.load(loadFilename)`` line. -# If you trained the model on a GPU and are running this tutorial on a -# CPU, uncomment the -# ``checkpoint = torch.load(loadFilename, map_location=torch.device('cpu'))`` -# line. -# -# TorchScript Notes: -# ~~~~~~~~~~~~~~~~~~~~~~ -# -# Notice that we initialize and load parameters into our encoder and -# decoder models as usual. If you are using tracing mode(``torch.jit.trace``) -# for some part of your models, you must call ``.to(device)`` to set the device -# options of the models and ``.eval()`` to set the dropout layers to test mode -# **before** tracing the models. `TracedModule` objects do not inherit the -# ``to`` or ``eval`` methods. Since in this tutorial we are only using -# scripting instead of tracing, we only need to do this before we do -# evaluation (which is the same as we normally do in eager mode). -# - -save_dir = os.path.join("data", "save") -corpus_name = "cornell movie-dialogs corpus" - -# Configure models -model_name = 'cb_model' -attn_model = 'dot' -#attn_model = 'general'`` -#attn_model = 'concat' -hidden_size = 500 -encoder_n_layers = 2 -decoder_n_layers = 2 -dropout = 0.1 -batch_size = 64 - -# If you're loading your own model -# Set checkpoint to load from -checkpoint_iter = 4000 - -############################################################# -# Sample code to load from a checkpoint: -# -# .. code-block:: python -# -# loadFilename = os.path.join(save_dir, model_name, corpus_name, -# '{}-{}_{}'.format(encoder_n_layers, decoder_n_layers, hidden_size), -# '{}_checkpoint.tar'.format(checkpoint_iter)) - -# If you're loading the hosted model -loadFilename = 'data/4000_checkpoint.tar' - -# Load model -# Force CPU device options (to match tensors in this tutorial) -checkpoint = torch.load(loadFilename, map_location=torch.device('cpu')) -encoder_sd = checkpoint['en'] -decoder_sd = checkpoint['de'] -encoder_optimizer_sd = checkpoint['en_opt'] -decoder_optimizer_sd = checkpoint['de_opt'] -embedding_sd = checkpoint['embedding'] -voc = Voc(corpus_name) -voc.__dict__ = checkpoint['voc_dict'] - - -print('Building encoder and decoder ...') -# Initialize word embeddings -embedding = nn.Embedding(voc.num_words, hidden_size) -embedding.load_state_dict(embedding_sd) -# Initialize encoder & decoder models -encoder = EncoderRNN(hidden_size, embedding, encoder_n_layers, dropout) -decoder = LuongAttnDecoderRNN(attn_model, embedding, hidden_size, voc.num_words, decoder_n_layers, dropout) -# Load trained model parameters -encoder.load_state_dict(encoder_sd) -decoder.load_state_dict(decoder_sd) -# Use appropriate device -encoder = encoder.to(device) -decoder = decoder.to(device) -# Set dropout layers to ``eval`` mode -encoder.eval() -decoder.eval() -print('Models built and ready to go!') - - -###################################################################### -# Convert Model to TorchScript -# ----------------------------- -# -# Encoder -# ~~~~~~~ -# -# As previously mentioned, to convert the encoder model to TorchScript, -# we use **scripting**. The encoder model takes an input sequence and -# a corresponding lengths tensor. Therefore, we create an example input -# sequence tensor ``test_seq``, which is of appropriate size (MAX_LENGTH, -# 1), contains numbers in the appropriate range -# :math:`[0, voc.num\_words)`, and is of the appropriate type (int64). We -# also create a ``test_seq_length`` scalar which realistically contains -# the value corresponding to how many words are in the ``test_seq``. The -# next step is to use the ``torch.jit.trace`` function to trace the model. -# Notice that the first argument we pass is the module that we want to -# trace, and the second is a tuple of arguments to the module’s -# ``forward`` method. -# -# Decoder -# ~~~~~~~ -# -# We perform the same process for tracing the decoder as we did for the -# encoder. Notice that we call forward on a set of random inputs to the -# traced_encoder to get the output that we need for the decoder. This is -# not required, as we could also simply manufacture a tensor of the -# correct shape, type, and value range. This method is possible because in -# our case we do not have any constraints on the values of the tensors -# because we do not have any operations that could fault on out-of-range -# inputs. -# -# GreedySearchDecoder -# ~~~~~~~~~~~~~~~~~~~ -# -# Recall that we scripted our searcher module due to the presence of -# data-dependent control flow. In the case of scripting, we do necessary -# language changes to make sure the implementation complies with -# TorchScript. We initialize the scripted searcher the same way that we -# would initialize an unscripted variant. -# - -### Compile the whole greedy search model to TorchScript model -# Create artificial inputs -test_seq = torch.LongTensor(MAX_LENGTH, 1).random_(0, voc.num_words).to(device) -test_seq_length = torch.LongTensor([test_seq.size()[0]]).to(device) -# Trace the model -traced_encoder = torch.jit.trace(encoder, (test_seq, test_seq_length)) - -### Convert decoder model -# Create and generate artificial inputs -test_encoder_outputs, test_encoder_hidden = traced_encoder(test_seq, test_seq_length) -test_decoder_hidden = test_encoder_hidden[:decoder.n_layers] -test_decoder_input = torch.LongTensor(1, 1).random_(0, voc.num_words) -# Trace the model -traced_decoder = torch.jit.trace(decoder, (test_decoder_input, test_decoder_hidden, test_encoder_outputs)) - -### Initialize searcher module by wrapping ``torch.jit.script`` call -scripted_searcher = torch.jit.script(GreedySearchDecoder(traced_encoder, traced_decoder, decoder.n_layers)) - - - - -###################################################################### -# Print Graphs -# ------------ -# -# Now that our models are in TorchScript form, we can print the graphs of -# each to ensure that we captured the computational graph appropriately. -# Since TorchScript allow us to recursively compile the whole model -# hierarchy and inline the ``encoder`` and ``decoder`` graph into a single -# graph, we just need to print the `scripted_searcher` graph - -print('scripted_searcher graph:\n', scripted_searcher.graph) - - -###################################################################### -# Run Evaluation -# -------------- -# -# Finally, we will run evaluation of the chatbot model using the TorchScript -# models. If converted correctly, the models will behave exactly as they -# would in their eager-mode representation. -# -# By default, we evaluate a few common query sentences. If you want to -# chat with the bot yourself, uncomment the ``evaluateInput`` line and -# give it a spin. -# - - -# Use appropriate device -scripted_searcher.to(device) -# Set dropout layers to ``eval`` mode -scripted_searcher.eval() - -# Evaluate examples -sentences = ["hello", "what's up?", "who are you?", "where am I?", "where are you from?"] -for s in sentences: - evaluateExample(s, scripted_searcher, voc) - -# Evaluate your input by running -# ``evaluateInput(traced_encoder, traced_decoder, scripted_searcher, voc)`` - - -###################################################################### -# Save Model -# ---------- -# -# Now that we have successfully converted our model to TorchScript, we -# will serialize it for use in a non-Python deployment environment. To do -# this, we can simply save our ``scripted_searcher`` module, as this is -# the user-facing interface for running inference against the chatbot -# model. When saving a Script module, use script_module.save(PATH) instead -# of torch.save(model, PATH). -# - -scripted_searcher.save("scripted_chatbot.pth") From c654b2c22f67bde71a504b59410aabcfa5384366 Mon Sep 17 00:00:00 2001 From: Mark Saroufim Date: Mon, 23 Jun 2025 14:16:58 -0700 Subject: [PATCH 122/347] get rid of flask tutorials (#3403) * get rid of flask tutorials * revert changes to link PR checker --- .jenkins/validate_tutorials_built.py | 1 - .lycheeignore | 3 - index.rst | 9 - intermediate_source/README.txt | 6 +- .../flask_rest_api_tutorial.py | 333 ------------------ recipes_source/deployment_with_flask.rst | 284 --------------- recipes_source/recipes_index.rst | 8 - 7 files changed, 1 insertion(+), 643 deletions(-) delete mode 100644 intermediate_source/flask_rest_api_tutorial.py delete mode 100644 recipes_source/deployment_with_flask.rst diff --git a/.jenkins/validate_tutorials_built.py b/.jenkins/validate_tutorials_built.py index 5c9e60e90bd..e02ea6028f7 100644 --- a/.jenkins/validate_tutorials_built.py +++ b/.jenkins/validate_tutorials_built.py @@ -48,7 +48,6 @@ "recipes_source/recipes/timer_quick_start", "recipes_source/recipes/amp_recipe", "recipes_source/recipes/Captum_Recipe", - "intermediate_source/flask_rest_api_tutorial", "intermediate_source/text_to_speech_with_torchaudio", "intermediate_source/tensorboard_profiler_tutorial", # reenable after 2.0 release. "advanced_source/semi_structured_sparse", # reenable after 3303 is fixed. diff --git a/.lycheeignore b/.lycheeignore index 3d86ae872de..e3113ca5152 100644 --- a/.lycheeignore +++ b/.lycheeignore @@ -13,8 +13,5 @@ https://pytorch.org/tutorials/beginner/colab/n # Ignore local host link from intermediate_source/tensorboard_tutorial.rst http://localhost:6006 -# Ignore local host link from recipes_source/deployment_with_flask.rst -http://localhost:5000/predict - # Ignore local host link from advanced_source/cpp_frontend.rst https://www.uber.com/blog/deep-neuroevolution/ diff --git a/index.rst b/index.rst index 54bd8b37466..2d202e0fe57 100644 --- a/index.rst +++ b/index.rst @@ -322,14 +322,6 @@ Welcome to PyTorch Tutorials .. Deploying PyTorch Models in Production - -.. customcarditem:: - :header: Deploying PyTorch in Python via a REST API with Flask - :card_description: Deploy a PyTorch model using Flask and expose a REST API for model inference using the example of a pretrained DenseNet 121 model which detects the image. - :image: _static/img/thumbnails/cropped/Deploying-PyTorch-in-Python-via-a-REST-API-with-Flask.png - :link: intermediate/flask_rest_api_tutorial.html - :tags: Production - .. customcarditem:: :header: Introduction to TorchScript :card_description: Introduction to TorchScript, an intermediate representation of a PyTorch model (subclass of nn.Module) that can then be run in a high-performance environment such as C++. @@ -1005,7 +997,6 @@ Additional Resources :caption: Deploying PyTorch Models in Production beginner/onnx/intro_onnx - intermediate/flask_rest_api_tutorial beginner/Intro_to_TorchScript_tutorial advanced/cpp_export advanced/super_resolution_with_onnxruntime diff --git a/intermediate_source/README.txt b/intermediate_source/README.txt index 0307e89a1a9..ecc8eb74af4 100644 --- a/intermediate_source/README.txt +++ b/intermediate_source/README.txt @@ -29,10 +29,6 @@ Intermediate tutorials Spatial Transformer Networks Tutorial https://pytorch.org/tutorials/intermediate/spatial_transformer_tutorial.html -8. flask_rest_api_tutorial.py - Deploying PyTorch and Building a REST API using Flask - https://pytorch.org/tutorials/intermediate/flask_rest_api_tutorial.html - -9. nvfuser_intro_tutorial.py +8. nvfuser_intro_tutorial.py Introduction to nvFuser https://pytorch.org/tutorials/intermediate/nvfuser_intro_tutorial.html diff --git a/intermediate_source/flask_rest_api_tutorial.py b/intermediate_source/flask_rest_api_tutorial.py deleted file mode 100644 index b6b46aed91d..00000000000 --- a/intermediate_source/flask_rest_api_tutorial.py +++ /dev/null @@ -1,333 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Deploying PyTorch in Python via a REST API with Flask -======================================================== -**Author**: `Avinash Sajjanshetty `_ - -In this tutorial, we will deploy a PyTorch model using Flask and expose a -REST API for model inference. In particular, we will deploy a pretrained -DenseNet 121 model which detects the image. - -.. tip:: All the code used here is released under MIT license and is available on `Github `_. - -This represents the first in a series of tutorials on deploying PyTorch models -in production. Using Flask in this way is by far the easiest way to start -serving your PyTorch models, but it will not work for a use case -with high performance requirements. For that: - - - If you're already familiar with TorchScript, you can jump straight into our - `Loading a TorchScript Model in C++ `_ tutorial. - - - If you first need a refresher on TorchScript, check out our - `Intro a TorchScript `_ tutorial. -""" - - -###################################################################### -# API Definition -# -------------- -# -# We will first define our API endpoints, the request and response types. Our -# API endpoint will be at ``/predict`` which takes HTTP POST requests with a -# ``file`` parameter which contains the image. The response will be of JSON -# response containing the prediction: -# -# .. code-block:: sh -# -# {"class_id": "n02124075", "class_name": "Egyptian_cat"} -# -# - -###################################################################### -# Dependencies -# ------------ -# -# Install the required dependencies by running the following command: -# -# .. code-block:: sh -# -# pip install Flask==2.0.1 torchvision==0.10.0 - - -###################################################################### -# Simple Web Server -# ----------------- -# -# Following is a simple web server, taken from Flask's documentation - - -from flask import Flask -app = Flask(__name__) - - -@app.route('/') -def hello(): - return 'Hello World!' - -############################################################################### -# We will also change the response type, so that it returns a JSON response -# containing ImageNet class id and name. The updated ``app.py`` file will -# be now: - -from flask import Flask, jsonify -app = Flask(__name__) - -@app.route('/predict', methods=['POST']) -def predict(): - return jsonify({'class_id': 'IMAGE_NET_XXX', 'class_name': 'Cat'}) - - -###################################################################### -# Inference -# ----------------- -# -# In the next sections we will focus on writing the inference code. This will -# involve two parts, one where we prepare the image so that it can be fed -# to DenseNet and next, we will write the code to get the actual prediction -# from the model. -# -# Preparing the image -# ~~~~~~~~~~~~~~~~~~~ -# -# DenseNet model requires the image to be of 3 channel RGB image of size -# 224 x 224. We will also normalize the image tensor with the required mean -# and standard deviation values. You can read more about it -# `here `_. -# -# We will use ``transforms`` from ``torchvision`` library and build a -# transform pipeline, which transforms our images as required. You -# can read more about transforms `here `_. - -import io - -import torchvision.transforms as transforms -from PIL import Image - -def transform_image(image_bytes): - my_transforms = transforms.Compose([transforms.Resize(255), - transforms.CenterCrop(224), - transforms.ToTensor(), - transforms.Normalize( - [0.485, 0.456, 0.406], - [0.229, 0.224, 0.225])]) - image = Image.open(io.BytesIO(image_bytes)) - return my_transforms(image).unsqueeze(0) - -###################################################################### -# The above method takes image data in bytes, applies the series of transforms -# and returns a tensor. To test the above method, read an image file in -# bytes mode (first replacing `../_static/img/sample_file.jpeg` with the actual -# path to the file on your computer) and see if you get a tensor back: - -with open("../_static/img/sample_file.jpeg", 'rb') as f: - image_bytes = f.read() - tensor = transform_image(image_bytes=image_bytes) - print(tensor) - -###################################################################### -# Prediction -# ~~~~~~~~~~~~~~~~~~~ -# -# Now will use a pretrained DenseNet 121 model to predict the image class. We -# will use one from ``torchvision`` library, load the model and get an -# inference. While we'll be using a pretrained model in this example, you can -# use this same approach for your own models. See more about loading your -# models in this :doc:`tutorial `. - -from torchvision import models - -# Make sure to set `weights` as `'IMAGENET1K_V1'` to use the pretrained weights: -model = models.densenet121(weights='IMAGENET1K_V1') -# Since we are using our model only for inference, switch to `eval` mode: -model.eval() - - -def get_prediction(image_bytes): - tensor = transform_image(image_bytes=image_bytes) - outputs = model.forward(tensor) - _, y_hat = outputs.max(1) - return y_hat - -###################################################################### -# The tensor ``y_hat`` will contain the index of the predicted class id. -# However, we need a human readable class name. For that we need a class id -# to name mapping. Download -# `this file `_ -# as ``imagenet_class_index.json`` and remember where you saved it (or, if you -# are following the exact steps in this tutorial, save it in -# `tutorials/_static`). This file contains the mapping of ImageNet class id to -# ImageNet class name. We will load this JSON file and get the class name of -# the predicted index. - -import json - -imagenet_class_index = json.load(open('../_static/imagenet_class_index.json')) - -def get_prediction(image_bytes): - tensor = transform_image(image_bytes=image_bytes) - outputs = model.forward(tensor) - _, y_hat = outputs.max(1) - predicted_idx = str(y_hat.item()) - return imagenet_class_index[predicted_idx] - - -###################################################################### -# Before using ``imagenet_class_index`` dictionary, first we will convert -# tensor value to a string value, since the keys in the -# ``imagenet_class_index`` dictionary are strings. -# We will test our above method: - - -with open("../_static/img/sample_file.jpeg", 'rb') as f: - image_bytes = f.read() - print(get_prediction(image_bytes=image_bytes)) - -###################################################################### -# You should get a response like this: - -['n02124075', 'Egyptian_cat'] - -###################################################################### -# The first item in array is ImageNet class id and second item is the human -# readable name. -# - -###################################################################### -# Integrating the model in our API Server -# --------------------------------------- -# -# In this final part we will add our model to our Flask API server. Since -# our API server is supposed to take an image file, we will update our ``predict`` -# method to read files from the requests: -# -# .. code-block:: python -# -# from flask import request -# -# @app.route('/predict', methods=['POST']) -# def predict(): -# if request.method == 'POST': -# # we will get the file from the request -# file = request.files['file'] -# # convert that to bytes -# img_bytes = file.read() -# class_id, class_name = get_prediction(image_bytes=img_bytes) -# return jsonify({'class_id': class_id, 'class_name': class_name}) -# -# -###################################################################### -# The ``app.py`` file is now complete. Following is the full version; replace -# the paths with the paths where you saved your files and it should run: -# -# .. code-block:: python -# -# import io -# import json -# -# from torchvision import models -# import torchvision.transforms as transforms -# from PIL import Image -# from flask import Flask, jsonify, request -# -# -# app = Flask(__name__) -# imagenet_class_index = json.load(open('/imagenet_class_index.json')) -# model = models.densenet121(weights='IMAGENET1K_V1') -# model.eval() -# -# -# def transform_image(image_bytes): -# my_transforms = transforms.Compose([transforms.Resize(255), -# transforms.CenterCrop(224), -# transforms.ToTensor(), -# transforms.Normalize( -# [0.485, 0.456, 0.406], -# [0.229, 0.224, 0.225])]) -# image = Image.open(io.BytesIO(image_bytes)) -# return my_transforms(image).unsqueeze(0) -# -# -# def get_prediction(image_bytes): -# tensor = transform_image(image_bytes=image_bytes) -# outputs = model.forward(tensor) -# _, y_hat = outputs.max(1) -# predicted_idx = str(y_hat.item()) -# return imagenet_class_index[predicted_idx] -# -# -# @app.route('/predict', methods=['POST']) -# def predict(): -# if request.method == 'POST': -# file = request.files['file'] -# img_bytes = file.read() -# class_id, class_name = get_prediction(image_bytes=img_bytes) -# return jsonify({'class_id': class_id, 'class_name': class_name}) -# -# -# if __name__ == '__main__': -# app.run() -# -# -###################################################################### -# Let's test our web server! Run: -# -# .. code-block:: sh -# -# FLASK_ENV=development FLASK_APP=app.py flask run -# -####################################################################### -# We can use the -# `requests `_ -# library to send a POST request to our app: -# -# .. code-block:: python -# -# import requests -# -# resp = requests.post("http://localhost:5000/predict", -# files={"file": open('/cat.jpg','rb')}) -# - -####################################################################### -# Printing `resp.json()` will now show the following: -# -# .. code-block:: sh -# -# {"class_id": "n02124075", "class_name": "Egyptian_cat"} -# -###################################################################### -# Next steps -# -------------- -# -# The server we wrote is quite trivial and may not do everything -# you need for your production application. So, here are some things you -# can do to make it better: -# -# - The endpoint ``/predict`` assumes that always there will be a image file -# in the request. This may not hold true for all requests. Our user may -# send image with a different parameter or send no images at all. -# -# - The user may send non-image type files too. Since we are not handling -# errors, this will break our server. Adding an explicit error handing -# path that will throw an exception would allow us to better handle -# the bad inputs -# -# - Even though the model can recognize a large number of classes of images, -# it may not be able to recognize all images. Enhance the implementation -# to handle cases when the model does not recognize anything in the image. -# -# - We run the Flask server in the development mode, which is not suitable for -# deploying in production. You can check out `this tutorial `_ -# for deploying a Flask server in production. -# -# - You can also add a UI by creating a page with a form which takes the image and -# displays the prediction. -# - In this tutorial, we only showed how to build a service that could return predictions for -# a single image at a time. We could modify our service to be able to return predictions for -# multiple images at once. In addition, the `service-streamer `_ -# library automatically queues requests to your service and samples them into mini-batches -# that can be fed into your model. You can check out `this tutorial `_. -# -# - Finally, we encourage you to check out our other tutorials on deploying PyTorch models -# linked-to at the top of the page. -# diff --git a/recipes_source/deployment_with_flask.rst b/recipes_source/deployment_with_flask.rst deleted file mode 100644 index 213a326429c..00000000000 --- a/recipes_source/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 - - - - 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/vision/stable/models.html -.. _the Flask site: https://flask.palletsprojects.com/en/1.1.x/installation/ diff --git a/recipes_source/recipes_index.rst b/recipes_source/recipes_index.rst index ce76e1931c2..bdafd705264 100644 --- a/recipes_source/recipes_index.rst +++ b/recipes_source/recipes_index.rst @@ -206,13 +206,6 @@ Recipes are bite-sized, actionable examples of how to use specific PyTorch featu :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 (Android and iOS). @@ -505,7 +498,6 @@ Recipes are bite-sized, actionable examples of how to use specific PyTorch featu /recipes/compiling_optimizer /recipes/torch_compile_backend_ipex /recipes/torchscript_inference - /recipes/deployment_with_flask /recipes/distributed_rpc_profiling /recipes/zero_redundancy_optimizer /recipes/cuda_rpc From d5ec6bf2d61875e64cae52ac2f8b0d3bbcfd1bb1 Mon Sep 17 00:00:00 2001 From: Alanna Burke Date: Mon, 23 Jun 2025 20:53:15 -0400 Subject: [PATCH 123/347] Removing TorchServe reference per TT228127036. (#3405) --- intermediate_source/rpc_async_execution.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/intermediate_source/rpc_async_execution.rst b/intermediate_source/rpc_async_execution.rst index 562d93bbbf8..4c7739104cc 100644 --- a/intermediate_source/rpc_async_execution.rst +++ b/intermediate_source/rpc_async_execution.rst @@ -15,8 +15,7 @@ Prerequisites: 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 TorchServe `__. +RPC threads and consolidating CUDA operations on the callee. .. note:: This tutorial requires PyTorch v1.6.0 or above. From d3ac156f11e338f0ab82746917a3f2e3bbe30e47 Mon Sep 17 00:00:00 2001 From: partev Date: Tue, 24 Jun 2025 00:16:44 -0400 Subject: [PATCH 124/347] fix broken URL (#3369) --- beginner_source/hyperparameter_tuning_tutorial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beginner_source/hyperparameter_tuning_tutorial.py b/beginner_source/hyperparameter_tuning_tutorial.py index aa84069f7be..dd3fe65699e 100644 --- a/beginner_source/hyperparameter_tuning_tutorial.py +++ b/beginner_source/hyperparameter_tuning_tutorial.py @@ -184,7 +184,7 @@ def forward(self, x): # inputs, labels = inputs.to(device), labels.to(device) # # The code now supports training on CPUs, on a single GPU, and on multiple GPUs. Notably, Ray -# also supports `fractional GPUs `_ +# also supports `fractional GPUs `_ # so we can share GPUs among trials, as long as the model still fits on the GPU memory. We'll come back # to that later. # From d781eeb46f0a80848b95cf16e422b6dccbbf0959 Mon Sep 17 00:00:00 2001 From: Alanna Burke Date: Tue, 24 Jun 2025 13:02:40 -0400 Subject: [PATCH 125/347] Removing Torchserve references per T228126881. (#3409) Co-authored-by: Svetlana Karslioglu --- index.rst | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/index.rst b/index.rst index 2d202e0fe57..ebcc774214e 100644 --- a/index.rst +++ b/index.rst @@ -612,20 +612,6 @@ Welcome to PyTorch Tutorials :link: advanced/static_quantization_tutorial.html :tags: Quantization -.. customcarditem:: - :header: Grokking PyTorch Intel CPU Performance from First Principles - :card_description: A case study on the TorchServe inference framework optimized with Intel® Extension for PyTorch. - :image: _static/img/thumbnails/cropped/generic-pytorch-logo.png - :link: intermediate/torchserve_with_ipex - :tags: Model-Optimization,Production - -.. customcarditem:: - :header: Grokking PyTorch Intel CPU Performance from First Principles (Part 2) - :card_description: A case study on the TorchServe inference framework optimized with Intel® Extension for PyTorch (Part 2). - :image: _static/img/thumbnails/cropped/generic-pytorch-logo.png - :link: intermediate/torchserve_with_ipex_2 - :tags: Model-Optimization,Production - .. customcarditem:: :header: Multi-Objective Neural Architecture Search with Ax :card_description: Learn how to use Ax to search over architectures find optimal tradeoffs between accuracy and latency. @@ -1071,8 +1057,6 @@ Additional Resources intermediate/dynamic_quantization_bert_tutorial intermediate/quantized_transfer_learning_tutorial advanced/static_quantization_tutorial - intermediate/torchserve_with_ipex - intermediate/torchserve_with_ipex_2 intermediate/nvfuser_intro_tutorial intermediate/ax_multiobjective_nas_tutorial intermediate/torch_compile_tutorial From aef25109a77ce40f5dc9a4556496cda11d04fc5c Mon Sep 17 00:00:00 2001 From: Alanna Burke Date: Tue, 24 Jun 2025 13:47:58 -0400 Subject: [PATCH 126/347] Removing torchserve card from recipes list, per T228127035. (#3407) Co-authored-by: Svetlana Karslioglu --- recipes_source/recipes_index.rst | 9 --------- 1 file changed, 9 deletions(-) diff --git a/recipes_source/recipes_index.rst b/recipes_source/recipes_index.rst index bdafd705264..da1f571982b 100644 --- a/recipes_source/recipes_index.rst +++ b/recipes_source/recipes_index.rst @@ -450,15 +450,6 @@ Recipes are bite-sized, actionable examples of how to use specific PyTorch featu :link: ../recipes/distributed_comm_debug_mode.html :tags: Distributed-Training -.. TorchServe - -.. customcarditem:: - :header: Deploying a PyTorch Stable Diffusion model as a Vertex AI Endpoint - :card_description: Learn how to deploy model in Vertex AI with TorchServe - :image: ../_static/img/thumbnails/cropped/generic-pytorch-logo.png - :link: ../recipes/torchserve_vertexai_tutorial.html - :tags: Production - .. End of tutorial card section .. raw:: html From 8a5c6f1ca69c57b65811ddd843859809cf8c541a Mon Sep 17 00:00:00 2001 From: Richard Zou Date: Tue, 24 Jun 2025 18:31:25 -0400 Subject: [PATCH 127/347] Add explaination of AutotuneCache to torch compile cache tutorial (#3411) * Add explaination of AutotuneCache to torch compile cache tutorial It was mentioned but never explained --------- Co-authored-by: Svetlana Karslioglu --- recipes_source/torch_compile_caching_tutorial.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/recipes_source/torch_compile_caching_tutorial.rst b/recipes_source/torch_compile_caching_tutorial.rst index 12fcad4163f..e846817cbc0 100644 --- a/recipes_source/torch_compile_caching_tutorial.rst +++ b/recipes_source/torch_compile_caching_tutorial.rst @@ -88,6 +88,9 @@ The aforementioned ``Mega-Cache`` is composed of individual components that can * ``InductorCache``: A bundle of ``FXGraphCache`` and ``Triton`` cache. * ``AOTAutogradCache``: A cache of joint graph artifacts. * ``PGO-cache``: A cache of dynamic shape decisions to reduce number of recompilations. +* `AutotuningCache `__: + * ``Inductor`` generates ``Triton`` kernels and benchmarks them to select the fastest kernels. + * ``torch.compile``'s built-in ``AutotuningCache`` caches these results. All these cache artifacts are written to ``TORCHINDUCTOR_CACHE_DIR`` which by default will look like ``/tmp/torchinductor_myusername``. From e2a51c81522124d885a8397b612d17fbf8f4c8ca Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Wed, 25 Jun 2025 09:10:54 -0700 Subject: [PATCH 128/347] Add sphinx-redirect extension (#3406) * Added `sphinx_redirects` to the project dependencies * Created `redirects.py` with URL mapping for redirected pages * Updated `conf.py` to import `redirect.py` and use the sphinx_redirects extension * Added redirects for recently deleted tutorials: `intermediate/flask_rest_api_tutorial`, `beginner/ptcheat.html`, `beginner/deploy_seq2seq_hybrid_frontend_tutorial.html` to redirect to ../index.html. --- .ci/docker/requirements.txt | 5 +++-- conf.py | 5 +++-- redirects.py | 5 +++++ 3 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 redirects.py diff --git a/.ci/docker/requirements.txt b/.ci/docker/requirements.txt index e6802cb045e..23481ebe4a5 100644 --- a/.ci/docker/requirements.txt +++ b/.ci/docker/requirements.txt @@ -1,9 +1,10 @@ # --extra-index-url https://download.pytorch.org/whl/cu117/index.html # Use this to run/publish tutorials against the latest binaries during the RC stage. Comment out after the release. Each release verify the correct cuda version. # Refer to ./jenkins/build.sh for tutorial build instructions -sphinx==5.0.0 +sphinx==5.3.0 sphinx-gallery==0.11.1 -sphinx_design +sphinx-reredirects==0.1.4 +sphinx-design==0.4.0 docutils==0.16 sphinx-copybutton sphinx_sitemap==2.6.0 diff --git a/conf.py b/conf.py index e189f9449fc..7401bdcea2a 100644 --- a/conf.py +++ b/conf.py @@ -45,7 +45,7 @@ import plotly.io as pio from pathlib import Path pio.renderers.default = 'sphinx_gallery' - +from redirects import redirects import sphinx_gallery.gen_rst import multiprocessing @@ -121,7 +121,8 @@ def wrapper(*args, **kwargs): 'sphinx_copybutton', 'sphinx_gallery.gen_gallery', 'sphinx_design', - 'sphinx_sitemap' + 'sphinx_sitemap', + 'sphinx_reredirects' ] intersphinx_mapping = { diff --git a/redirects.py b/redirects.py new file mode 100644 index 00000000000..f0a13d5dfae --- /dev/null +++ b/redirects.py @@ -0,0 +1,5 @@ +redirects = { + "intermediate/flask_rest_api_tutorial": "../index.html", + "beginner/ptcheat.html": "../index.html", + "beginner/deploy_seq2seq_hybrid_frontend_tutorial.html": "../index.html", +} From efd3430b170c0a10163c08ed10b829d5d7c29822 Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Wed, 25 Jun 2025 13:19:30 -0700 Subject: [PATCH 129/347] Cleanup directories before pushing to gh-pages (#3408) * Cleanup directories before pushing to gh-pages --------- Co-authored-by: Nikita Shulga <2453524+malfet@users.noreply.github.com> --- .jenkins/build.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.jenkins/build.sh b/.jenkins/build.sh index 58483c168b5..9162152ff78 100755 --- a/.jenkins/build.sh +++ b/.jenkins/build.sh @@ -151,6 +151,12 @@ elif [[ "${JOB_TYPE}" == "manager" ]]; then # Step 7: push new HTML files and static files to gh-pages if [[ "$COMMIT_SOURCE" == "refs/heads/master" || "$COMMIT_SOURCE" == "refs/heads/main" ]]; then git clone https://github.com/pytorch/tutorials.git -b gh-pages gh-pages + # Clean up directories that contain tutorials + + for dir in beginner intermediate prototype recipes advanced distributed vision text audio; do + rm -rf "gh-pages/$dir" + done + cp -r docs/* gh-pages/ pushd gh-pages # DANGER! DO NOT REMOVE THE `set +x` SETTING HERE! From 3ea5abca65bd92f7802a152662a50dcb1f2a6651 Mon Sep 17 00:00:00 2001 From: "Cui, Yifeng" Date: Thu, 26 Jun 2025 08:30:13 -0700 Subject: [PATCH 130/347] Set strict export explicitly for API change (#3410) Co-authored-by: Svetlana Karslioglu --- prototype_source/pt2e_quant_xpu_inductor.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/prototype_source/pt2e_quant_xpu_inductor.rst b/prototype_source/pt2e_quant_xpu_inductor.rst index 84f9cc60ffc..38be4cc73cc 100644 --- a/prototype_source/pt2e_quant_xpu_inductor.rst +++ b/prototype_source/pt2e_quant_xpu_inductor.rst @@ -85,6 +85,7 @@ We will start by performing the necessary imports, capturing the FX Graph from t exported_model = export_for_training( model, example_inputs, + strict=True ).module() From b0d6cb2dab7455d8502886c904d4c804ab2efe64 Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Thu, 26 Jun 2025 13:50:09 -0700 Subject: [PATCH 131/347] Remove Bundled Inputs tutorial (#3418) --- recipes_source/bundled_inputs.rst | 204 ------------------------------ redirects.py | 1 + 2 files changed, 1 insertion(+), 204 deletions(-) delete mode 100644 recipes_source/bundled_inputs.rst diff --git a/recipes_source/bundled_inputs.rst b/recipes_source/bundled_inputs.rst deleted file mode 100644 index 1bdf5c7b7d2..00000000000 --- a/recipes_source/bundled_inputs.rst +++ /dev/null @@ -1,204 +0,0 @@ -(beta) Bundling inputs to PyTorch Models -================================================================== - -**Author**: `Jacob Szwejbka `_ - -Introduction ------------- - -This tutorial introduces the steps to use PyTorch's utility to bundle example or trivial inputs directly into your TorchScript Module. - -The interface of the model remains unchanged (other than adding a few methods), so it can still be safely deployed to production. The advantage of this standardized interface is that tools that run models can use it instead of having some sort of external file (or worse, document) that tells you how to run the model properly. - -Common case -------------------- - -One of the common cases—bundling an input to a model that only uses 'forward' for inference. - -1. **Prepare model**: Convert your model to TorchScript through either tracing or scripting - -.. code:: python - - import torch - import torch.jit - import torch.utils - import torch.utils.bundled_inputs - - class Net(nn.Module): - def __init__(self): - super(Net, self).__init__() - self.lin = nn.Linear(10, 1) - - def forward(self, x): - return self.lin(x) - - model = Net() - scripted_module = torch.jit.script(model) - -2. **Create example input and attach to model** - -.. code:: python - - # For each method create a list of inputs and each input is a tuple of arguments - sample_input = [(torch.zeros(1,10),)] - - # Create model with bundled inputs, if type(input) is list then the input is bundled to 'forward' - bundled_model = bundle_inputs(scripted_module, sample_input) - - -3. **Run model with input as arguments** - -.. code:: python - - sample_inputs = bundled_model.get_all_bundled_inputs() - - print(bundled_model(*sample_inputs[0])) - - -Uncommon case --------------- - -An uncommon case would be bundling and retrieving inputs for functions beyond 'forward'. - -1. **Prepare model**: Convert your model to TorchScript through either tracing or scripting - -.. code:: python - - import torch - import torch.jit - import torch.utils - import torch.utils.bundled_inputs - from typing import Dict - - class Net(nn.Module): - def __init__(self): - super(Net, self).__init__() - self.lin = nn.Linear(10, 1) - - def forward(self, x): - return self.lin(x) - - @torch.jit.export - def foo(self, x: Dict[String, int]): - return x['a'] + x['b'] - - - model = Net() - scripted_module = torch.jit.script(model) - -2. **Create example input and attach to model** - -.. code:: python - - # For each method create a list of inputs and each input is a tuple of arguments - example_dict = {'a' : 1, 'b' : 2} - sample_input = { - scripted_module.forward : [(torch.zeros(1,10),)], - scripted_module.foo : [(example_dict,)] - } - - # Create model with bundled inputs, if type(sample_input) is Dict then each callable key is mapped to its corresponding bundled input - bundled_model = bundle_inputs(scripted_module, sample_input) - - -3. **Retrieve inputs and run model on them** - -.. code:: python - - all_info = bundled_model.get_bundled_inputs_functions_and_info() - - # The return type for get_bundled_inputs_functions_and_info is complex, but essentially we are retrieving the name - # of a function we can use to get the bundled input for our models method - for func_name in all_info.keys(): - input_func_name = all_info[func_name]['get_inputs_function_name'][0] - func_to_run = getattr(bundled_model, input_func_name) - # retrieve input - sample_input = func_to_run() - model_function = getattr(bundled_model, func_name) - for i in range(len(sample_input)): - print(model_function(*sample_input[i])) - -Inflatable args -------------------- -Attaching inputs to models can result in nontrivial size increases. Inflatable args are a way to compress and decompress inputs to minimize this impact. - -.. note:: Any automatic compression, or parsing of inflatable args only happens to top level arguments in the input tuple. - - - ie if your model takes in a List type of inputs you would need to create an inflatable arg that returned a list not create a list of inflatable args. - -1. **Existing Inflatable args** - -The following input types are compressed automatically without requiring an explicit inflatable arg: - - Small contiguous tensors are cloned to have small storage. - - Inputs from torch.zeros, torch.ones, or torch.full are moved to their compact representations. - -.. code:: python - - # bundle_randn will generate a random tensor when the model is asked for bundled inputs - sample_inputs = [(torch.utils.bundled_inputs.bundle_randn((1,10)),)] - bundled_model = bundle_inputs(scripted_module, sample_inputs) - print(bundled_model.get_all_bundled_inputs()) - -2. **Creating your own** - -Inflatable args are composed of 2 parts, the deflated (compressed) argument, and an expression or function definition to inflate them. - -.. code:: python - - def create_example(*size, dtype=None): - """Generate a tuple of 2 random tensors both of the specified size""" - - deflated_input = (torch.zeros(1, dtype=dtype).expand(*size), torch.zeros(1, dtype=dtype).expand(*size)) - - # {0} is how you access your deflated value in the inflation expression - return torch.utils.bundled_inputs.InflatableArg( - value=stub, - fmt="(torch.randn_like({0}[0]), torch.randn_like({0}[1]))", - ) - -3. **Using a function instead** - If you need to create a more complicated input providing a function is an easy alternative - -.. code:: python - - sample = dict( - a=torch.zeros([10, 20]), - b=torch.zeros([1, 1]), - c=torch.zeros([10, 20]), - ) - - def condensed(t): - ret = torch.empty_like(t).flatten()[0].clone().expand(t.shape) - assert ret.storage().size() == 1 - return ret - - # An example of how to create an inflatable arg for a complex model input like Optional[Dict[str, Tensor]] - # here we take in a normal input, deflate it, and define an inflater function that converts the mapped tensors to random values - def bundle_optional_dict_of_randn(template: Optional[Dict[str, Tensor]]): - return torch.utils.bundled_inputs.InflatableArg( - value=( - None - if template is None - else {k: condensed(v) for (k, v) in template.items()} - ), - fmt="{}", - fmt_fn=""" - def {}(self, value: Optional[Dict[str, Tensor]]): - if value is not None: - output = {{}} - for k, v in value.items(): - output[k] = torch.randn_like(v) - return output - else: - return None - """, - ) - - sample_inputs = ( - bundle_optional_dict_of_randn(sample), - ) - - -Learn More ----------- -- To learn more about PyTorch Mobile, please refer to `PyTorch Mobile Home Page `_ diff --git a/redirects.py b/redirects.py index f0a13d5dfae..6aff4e2909d 100644 --- a/redirects.py +++ b/redirects.py @@ -2,4 +2,5 @@ "intermediate/flask_rest_api_tutorial": "../index.html", "beginner/ptcheat.html": "../index.html", "beginner/deploy_seq2seq_hybrid_frontend_tutorial.html": "../index.html", + "recipes/bundled_inputs.html": "../index.html", } From a47d520eb40328b78fe9bf28952db90a976ca60d Mon Sep 17 00:00:00 2001 From: Svetlana Karslioglu Date: Thu, 26 Jun 2025 14:13:47 -0700 Subject: [PATCH 132/347] Remove TorchScript part from the Saving and Loading tutorial (#3419) Remove TorchScript part from the Saving and Loading tutorial --- beginner_source/saving_loading_models.py | 49 +++++++++--------------- 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/beginner_source/saving_loading_models.py b/beginner_source/saving_loading_models.py index 13bca8ca3de..d09f9ca4491 100644 --- a/beginner_source/saving_loading_models.py +++ b/beginner_source/saving_loading_models.py @@ -227,43 +227,30 @@ # normalization layers to evaluation mode before running inference. # Failing to do this will yield inconsistent inference results. # -# Export/Load Model in TorchScript Format -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# Saving an Exported Program +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # -# One common way to do inference with a trained model is to use -# `TorchScript `__, an intermediate -# representation of a PyTorch model that can be run in Python as well as in a -# high performance environment like C++. TorchScript is actually the recommended model format -# for scaled inference and deployment. +# If you are using ``torch.export``, you can save and load your ``ExportedProgram`` using the +# ``torch.export.save()`` and ``torch.export.load()`` APIs. with the ``.pt2`` file extension: # -# .. note:: -# Using the TorchScript format, you will be able to load the exported model and -# run inference without defining the model class. -# -# **Export:** -# -# .. code:: python -# -# model_scripted = torch.jit.script(model) # Export to TorchScript -# model_scripted.save('model_scripted.pt') # Save -# -# **Load:** +# .. code-block:: python +# +# class SimpleModel(torch.nn.Module): +# def forward(self, x): +# return x + 10 # -# .. code:: python +# # Create a sample input +# sample_input = torch.randn(5) +# +# # Export the model +# exported_program = torch.export.export(SimpleModel(), sample_input) # -# model = torch.jit.load('model_scripted.pt') -# model.eval() +# # Save the exported program +# torch.export.save(exported_program, 'exported_program.pt2') # -# Remember 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. +# # Load the exported program +# saved_exported_program = torch.export.load('exported_program.pt2') # -# For more information on TorchScript, feel free to visit the dedicated -# `tutorials `__. -# You will get familiar with the tracing conversion and learn how to -# run a TorchScript module in a `C++ environment `__. - - ###################################################################### # Saving & Loading a General Checkpoint for Inference and/or Resuming Training From 65bd9777f4e914189b80618bcffc41630c7fb583 Mon Sep 17 00:00:00 2001 From: Nicolas Hug Date: Mon, 30 Jun 2025 21:12:09 +0100 Subject: [PATCH 133/347] Remove vt_tutorial.py (#3415) Co-authored-by: Svetlana Karslioglu --- beginner_source/vt_tutorial.py | 293 --------------------------------- index.rst | 9 - 2 files changed, 302 deletions(-) delete mode 100644 beginner_source/vt_tutorial.py diff --git a/beginner_source/vt_tutorial.py b/beginner_source/vt_tutorial.py deleted file mode 100644 index 777098be946..00000000000 --- a/beginner_source/vt_tutorial.py +++ /dev/null @@ -1,293 +0,0 @@ -""" -Optimizing Vision Transformer Model for Deployment -================================================== - -`Jeff Tang `_, -`Geeta Chauhan `_ - -Vision Transformer models apply the cutting-edge attention-based -transformer models, introduced in Natural Language Processing to achieve -all kinds of the state of the art (SOTA) results, to Computer Vision -tasks. Facebook Data-efficient Image Transformers `DeiT `_ -is a Vision Transformer model trained on ImageNet for image -classification. - -In this tutorial, we will first cover what DeiT is and how to use it, -then go through the complete steps of scripting, quantizing, optimizing, -and using the model in iOS and Android apps. We will also compare the -performance of quantized, optimized and non-quantized, non-optimized -models, and show the benefits of applying quantization and optimization -to the model along the steps. - -""" - - - -############################################################################### -# What is DeiT -# --------------------- -# -# Convolutional Neural Networks (CNNs) have been the main models for image -# classification since deep learning took off in 2012, but CNNs typically -# require hundreds of millions of images for training to achieve the -# SOTA results. DeiT is a vision transformer model that requires a lot less -# data and computing resources for training to compete with the leading -# CNNs in performing image classification, which is made possible by two -# key components of of DeiT: -# -# - Data augmentation that simulates training on a much larger dataset; -# - Native distillation that allows the transformer network to learn from -# a CNN’s output. -# -# DeiT shows that Transformers can be successfully applied to computer -# vision tasks, with limited access to data and resources. For more -# details on DeiT, see the `repo `_ -# and `paper `_. -# - - -###################################################################### -# Classifying Images with DeiT -# ------------------------------- -# -# Follow the ``README.md`` at the DeiT repository for detailed information on how to -# classify images using DeiT, or for a quick test, first install the -# required packages: -# -# .. code-block:: python -# -# pip install torch torchvision timm pandas requests - -####################################################### -# To run in Google Colab, install dependencies by running the following command: -# -# .. code-block:: python -# -# !pip install timm pandas requests - -############################# -# then run the script below: - -from PIL import Image -import torch -import timm -import requests -import torchvision.transforms as transforms -from timm.data.constants import IMAGENET_DEFAULT_MEAN, IMAGENET_DEFAULT_STD - -print(torch.__version__) -# should be 1.8.0 - - -model = torch.hub.load('facebookresearch/deit:main', 'deit_base_patch16_224', pretrained=True) -model.eval() - -transform = transforms.Compose([ - transforms.Resize(256, interpolation=3), - transforms.CenterCrop(224), - transforms.ToTensor(), - transforms.Normalize(IMAGENET_DEFAULT_MEAN, IMAGENET_DEFAULT_STD), -]) - -img = Image.open(requests.get("https://raw.githubusercontent.com/pytorch/ios-demo-app/master/HelloWorld/HelloWorld/HelloWorld/image.png", stream=True).raw) -img = transform(img)[None,] -out = model(img) -clsidx = torch.argmax(out) -print(clsidx.item()) - - -###################################################################### -# The output should be 269, which, according to the ImageNet list of class -# index to `labels file `_, maps to ``timber -# wolf, grey wolf, gray wolf, Canis lupus``. -# -# Now that we have verified that we can use the DeiT model to classify -# images, let’s see how to modify the model so it can run on iOS and -# Android apps. -# - - -###################################################################### -# Scripting DeiT -# ---------------------- -# To use the model on mobile, we first need to script the -# model. See the `Script and Optimize recipe `_ for a -# quick overview. Run the code below to convert the DeiT model used in the -# previous step to the TorchScript format that can run on mobile. -# - - -model = torch.hub.load('facebookresearch/deit:main', 'deit_base_patch16_224', pretrained=True) -model.eval() -scripted_model = torch.jit.script(model) -scripted_model.save("fbdeit_scripted.pt") - - -###################################################################### -# The scripted model file ``fbdeit_scripted.pt`` of size about 346MB is -# generated. -# - - -###################################################################### -# Quantizing DeiT -# --------------------- -# To reduce the trained model size significantly while -# keeping the inference accuracy about the same, quantization can be -# applied to the model. Thanks to the transformer model used in DeiT, we -# can easily apply dynamic-quantization to the model, because dynamic -# quantization works best for LSTM and transformer models (see `here `_ -# for more details). -# -# Now run the code below: -# - -# Use 'x86' for server inference (the old 'fbgemm' is still available but 'x86' is the recommended default) and ``qnnpack`` for mobile inference. -backend = "x86" # replaced with ``qnnpack`` causing much worse inference speed for quantized model on this notebook -model.qconfig = torch.quantization.get_default_qconfig(backend) -torch.backends.quantized.engine = backend - -quantized_model = torch.quantization.quantize_dynamic(model, qconfig_spec={torch.nn.Linear}, dtype=torch.qint8) -scripted_quantized_model = torch.jit.script(quantized_model) -scripted_quantized_model.save("fbdeit_scripted_quantized.pt") - - -###################################################################### -# This generates the scripted and quantized version of the model -# ``fbdeit_quantized_scripted.pt``, with size about 89MB, a 74% reduction of -# the non-quantized model size of 346MB! -# - -###################################################################### -# You can use the ``scripted_quantized_model`` to generate the same -# inference result: -# - -out = scripted_quantized_model(img) -clsidx = torch.argmax(out) -print(clsidx.item()) -# The same output 269 should be printed - -###################################################################### -# Optimizing DeiT -# --------------------- -# The final step before using the quantized and scripted -# model on mobile is to optimize it: -# - -from torch.utils.mobile_optimizer import optimize_for_mobile -optimized_scripted_quantized_model = optimize_for_mobile(scripted_quantized_model) -optimized_scripted_quantized_model.save("fbdeit_optimized_scripted_quantized.pt") - - -###################################################################### -# The generated ``fbdeit_optimized_scripted_quantized.pt`` file has about the -# same size as the quantized, scripted, but non-optimized model. The -# inference result remains the same. -# - - - -out = optimized_scripted_quantized_model(img) -clsidx = torch.argmax(out) -print(clsidx.item()) -# Again, the same output 269 should be printed - - -###################################################################### -# Using Lite Interpreter -# ------------------------ -# -# To see how much model size reduction and inference speed up the Lite -# Interpreter can result in, let’s create the lite version of the model. -# - -optimized_scripted_quantized_model._save_for_lite_interpreter("fbdeit_optimized_scripted_quantized_lite.ptl") -ptl = torch.jit.load("fbdeit_optimized_scripted_quantized_lite.ptl") - - -###################################################################### -# Although the lite model size is comparable to the non-lite version, when -# running the lite version on mobile, the inference speed up is expected. -# - - -###################################################################### -# Comparing Inference Speed -# --------------------------- -# -# To see how the inference speed differs for the four models - the -# original model, the scripted model, the quantized-and-scripted model, -# the optimized-quantized-and-scripted model - run the code below: -# - -with torch.autograd.profiler.profile(use_cuda=False) as prof1: - out = model(img) -with torch.autograd.profiler.profile(use_cuda=False) as prof2: - out = scripted_model(img) -with torch.autograd.profiler.profile(use_cuda=False) as prof3: - out = scripted_quantized_model(img) -with torch.autograd.profiler.profile(use_cuda=False) as prof4: - out = optimized_scripted_quantized_model(img) -with torch.autograd.profiler.profile(use_cuda=False) as prof5: - out = ptl(img) - -print("original model: {:.2f}ms".format(prof1.self_cpu_time_total/1000)) -print("scripted model: {:.2f}ms".format(prof2.self_cpu_time_total/1000)) -print("scripted & quantized model: {:.2f}ms".format(prof3.self_cpu_time_total/1000)) -print("scripted & quantized & optimized model: {:.2f}ms".format(prof4.self_cpu_time_total/1000)) -print("lite model: {:.2f}ms".format(prof5.self_cpu_time_total/1000)) - -###################################################################### -# The results running on a Google Colab are: -# -# .. code-block:: sh -# -# original model: 1236.69ms -# scripted model: 1226.72ms -# scripted & quantized model: 593.19ms -# scripted & quantized & optimized model: 598.01ms -# lite model: 600.72ms -# - - -###################################################################### -# The following results summarize the inference time taken by each model -# and the percentage reduction of each model relative to the original -# model. -# - -import pandas as pd -import numpy as np - -df = pd.DataFrame({'Model': ['original model','scripted model', 'scripted & quantized model', 'scripted & quantized & optimized model', 'lite model']}) -df = pd.concat([df, pd.DataFrame([ - ["{:.2f}ms".format(prof1.self_cpu_time_total/1000), "0%"], - ["{:.2f}ms".format(prof2.self_cpu_time_total/1000), - "{:.2f}%".format((prof1.self_cpu_time_total-prof2.self_cpu_time_total)/prof1.self_cpu_time_total*100)], - ["{:.2f}ms".format(prof3.self_cpu_time_total/1000), - "{:.2f}%".format((prof1.self_cpu_time_total-prof3.self_cpu_time_total)/prof1.self_cpu_time_total*100)], - ["{:.2f}ms".format(prof4.self_cpu_time_total/1000), - "{:.2f}%".format((prof1.self_cpu_time_total-prof4.self_cpu_time_total)/prof1.self_cpu_time_total*100)], - ["{:.2f}ms".format(prof5.self_cpu_time_total/1000), - "{:.2f}%".format((prof1.self_cpu_time_total-prof5.self_cpu_time_total)/prof1.self_cpu_time_total*100)]], - columns=['Inference Time', 'Reduction'])], axis=1) - -print(df) - -""" - Model Inference Time Reduction -0 original model 1236.69ms 0% -1 scripted model 1226.72ms 0.81% -2 scripted & quantized model 593.19ms 52.03% -3 scripted & quantized & optimized model 598.01ms 51.64% -4 lite model 600.72ms 51.43% -""" - -###################################################################### -# Learn More -# ~~~~~~~~~~~~~~~~~ -# -# - `Facebook Data-efficient Image Transformers `__ -# - `Vision Transformer with ImageNet and MNIST on iOS `__ -# - `Vision Transformer with ImageNet and MNIST on Android `__ diff --git a/index.rst b/index.rst index ebcc774214e..71cddc0bda9 100644 --- a/index.rst +++ b/index.rst @@ -116,13 +116,6 @@ Welcome to PyTorch Tutorials :link: beginner/transfer_learning_tutorial.html :tags: Image/Video -.. customcarditem:: - :header: Optimizing Vision Transformer Model - :card_description: Apply cutting-edge, attention-based transformer models to computer vision tasks. - :image: _static/img/thumbnails/cropped/60-min-blitz.png - :link: beginner/vt_tutorial.html - :tags: Image/Video - .. customcarditem:: :header: Adversarial Example Generation :card_description: Train a convolutional neural network for image classification using transfer learning. @@ -937,7 +930,6 @@ Additional Resources beginner/fgsm_tutorial beginner/dcgan_faces_tutorial intermediate/spatial_transformer_tutorial - beginner/vt_tutorial intermediate/tiatoolbox_tutorial .. toctree:: @@ -1050,7 +1042,6 @@ Additional Resources beginner/profiler intermediate/tensorboard_profiler_tutorial beginner/hyperparameter_tuning_tutorial - beginner/vt_tutorial intermediate/parametrizations intermediate/pruning_tutorial advanced/dynamic_quantization_tutorial From 47516f577f41f3fbaff75c8c5ab359cae3f39841 Mon Sep 17 00:00:00 2001 From: Alanna Burke Date: Tue, 1 Jul 2025 12:46:55 -0400 Subject: [PATCH 134/347] Removing Torchserve tutorial per Tutorial audit T228122175 (#3414) * Removing torchserve_vertexai_tutorial per tutorial audit. * Removing references to torchserve_vertexai_tutorial per tutorial audit. * Adding removed page to redirects file. --- .../torchserve_vertexai_tutorial.rst | 144 ------------------ redirects.py | 1 + 2 files changed, 1 insertion(+), 144 deletions(-) delete mode 100644 recipes_source/torchserve_vertexai_tutorial.rst diff --git a/recipes_source/torchserve_vertexai_tutorial.rst b/recipes_source/torchserve_vertexai_tutorial.rst deleted file mode 100644 index 9c748e7b8c1..00000000000 --- a/recipes_source/torchserve_vertexai_tutorial.rst +++ /dev/null @@ -1,144 +0,0 @@ -Deploying a PyTorch Stable Diffusion model as a Vertex AI Endpoint -================================================================== - -Deploying large models, like Stable Diffusion, can be challenging and time-consuming. - -In this recipe, we will show how you can streamline the deployment of a PyTorch Stable Diffusion -model by leveraging Vertex AI. - -PyTorch is the framework used by Stability AI on Stable -Diffusion v1.5. Vertex AI is a fully-managed machine learning platform with tools and -infrastructure designed to help ML practitioners accelerate and scale ML in production with -the benefit of open-source frameworks like PyTorch. - -In four steps you can deploy a PyTorch Stable Diffusion model (v1.5). - -Deploying your Stable Diffusion model on a Vertex AI Endpoint can be done in four steps: - -* Create a custom TorchServe handler. - -* Upload model artifacts to Google Cloud Storage (GCS). - -* Create a Vertex AI model with the model artifacts and a prebuilt PyTorch container image. - -* Deploy the Vertex AI model onto an endpoint. - -Let’s have a look at each step in more detail. You can follow and implement the steps using the -`Notebook example `__. - -NOTE: Please keep in mind that this recipe requires a billable Vertex AI as explained in more details in the notebook example. - -Create a custom TorchServe handler ----------------------------------- - -TorchServe is an easy and flexible tool for serving PyTorch models. The model deployed to Vertex AI -uses TorchServe to handle requests and return responses from the model. -You must create a custom TorchServe handler to include in the model artifacts uploaded to Vertex AI. Include the handler file in the -directory with the other model artifacts, like this: `model_artifacts/handler.py`. - -After creating the handler file, you must package the handler as a model archiver (MAR) file. -The output file must be named `model.mar`. - - -.. code:: shell - - !torch-model-archiver \ - -f \ - --model-name \ - --version 1.0 \ - --handler model_artifacts/handler.py \ - --export-path model_artifacts - -Upload model artifacts to Google Cloud Storage (GCS) ----------------------------------------------------- - -In this step we are uploading -`model artifacts `__ -to GCS, like the model file or handler. The advantage of storing your artifacts on GCS is that you can -track the artifacts in a central bucket. - - -.. code:: shell - - BUCKET_NAME = "your-bucket-name-unique" # @param {type:"string"} - BUCKET_URI = f"gs://{BUCKET_NAME}/" - - # Will copy the artifacts into the bucket - !gsutil cp -r model_artifacts $BUCKET_URI - -Create a Vertex AI model with the model artifacts and a prebuilt PyTorch container image ----------------------------------------------------------------------------------------- - -Once you've uploaded the model artifacts into a GCS bucket, you can upload your PyTorch model to -`Vertex AI Model Registry `__. -From the Vertex AI Model Registry, you have an overview of your models -so you can better organize, track, and train new versions. For this you can use the -`Vertex AI SDK `__ -and this -`pre-built PyTorch container `__. - - -.. code:: shell - - from google.cloud import aiplatform as vertexai - PYTORCH_PREDICTION_IMAGE_URI = ( - "us-docker.pkg.dev/vertex-ai/prediction/pytorch-gpu.1-12:latest" - ) - MODEL_DISPLAY_NAME = "stable_diffusion_1_5-unique" - MODEL_DESCRIPTION = "stable_diffusion_1_5 container" - - vertexai.init(project='your_project', location='us-central1', staging_bucket=BUCKET_NAME) - - model = aiplatform.Model.upload( - display_name=MODEL_DISPLAY_NAME, - description=MODEL_DESCRIPTION, - serving_container_image_uri=PYTORCH_PREDICTION_IMAGE_URI, - artifact_uri=BUCKET_URI, - ) - -Deploy the Vertex AI model onto an endpoint -------------------------------------------- - -Once the model has been uploaded to Vertex AI Model Registry you can then take it and deploy -it to an Vertex AI Endpoint. For this you can use the Console or the Vertex AI SDK. In this -example you will deploy the model on a NVIDIA Tesla P100 GPU and n1-standard-8 machine. You can -specify your machine type. - - -.. code:: shell - - endpoint = aiplatform.Endpoint.create(display_name=ENDPOINT_DISPLAY_NAME) - - model.deploy( - endpoint=endpoint, - deployed_model_display_name=MODEL_DISPLAY_NAME, - machine_type="n1-standard-8", - accelerator_type="NVIDIA_TESLA_P100", - accelerator_count=1, - traffic_percentage=100, - deploy_request_timeout=1200, - sync=True, - ) - -If you follow this -`notebook `__ -you can also get online predictions using the Vertex AI SDK as shown in the following snippet. - - -.. code:: shell - - instances = [{"prompt": "An examplePup dog with a baseball jersey."}] - response = endpoint.predict(instances=instances) - - with open("img.jpg", "wb") as g: - g.write(base64.b64decode(response.predictions[0])) - - display.Image("img.jpg") - -Create a Vertex AI model with the model artifacts and a prebuilt PyTorch container image - -More resources --------------- - -This tutorial was created using the vendor documentation. To refer to the original documentation on the vendor site, please see -`torchserve example `__. diff --git a/redirects.py b/redirects.py index 6aff4e2909d..90574926ec6 100644 --- a/redirects.py +++ b/redirects.py @@ -3,4 +3,5 @@ "beginner/ptcheat.html": "../index.html", "beginner/deploy_seq2seq_hybrid_frontend_tutorial.html": "../index.html", "recipes/bundled_inputs.html": "../index.html", + "recipes/torchserve_vertexai_tutorial": "../index.html", } From a2e226c75621d88942996d937a821b21febb3f70 Mon Sep 17 00:00:00 2001 From: Alanna Burke Date: Tue, 1 Jul 2025 12:48:46 -0400 Subject: [PATCH 135/347] Removing Torchserve tutorial per Tutorial audit t228126880 (#3412) * Removing Torchserve tutorial per tutorial audit. * Adding removed page to redirects file. --------- Co-authored-by: Svetlana Karslioglu --- .../torchserve_with_ipex_2.rst | 447 ------------------ recipes_source/xeon_run_cpu.rst | 1 - redirects.py | 1 + 3 files changed, 1 insertion(+), 448 deletions(-) delete mode 100644 intermediate_source/torchserve_with_ipex_2.rst diff --git a/intermediate_source/torchserve_with_ipex_2.rst b/intermediate_source/torchserve_with_ipex_2.rst deleted file mode 100644 index 64f3db6b27c..00000000000 --- a/intermediate_source/torchserve_with_ipex_2.rst +++ /dev/null @@ -1,447 +0,0 @@ -Grokking PyTorch Intel CPU performance from first principles (Part 2) -===================================================================== - -Authors: `Min Jean Cho `_, `Jing Xu `_, `Mark Saroufim `_ - -In the `Grokking PyTorch Intel CPU Performance From First Principles `_ tutorial -, we have introduced how to tune CPU runtime configurations, how to profile them, and how to integrate them into `TorchServe `_ for optimized CPU performance. - -In this tutorial, we will demonstrate boosting performance with memory allocator via the `Intel® Extension for PyTorch* Launcher `_ -, and optimized kernels on CPU via `Intel® Extension for PyTorch* `_ -, and apply them to TorchServe showcasing 7.71x throughput speedup for ResNet50 and 2.20x throughput speedup for BERT. - -.. figure:: /_static/img/torchserve-ipex-images-2/1.png - :width: 100% - :align: center - -Prerequisites -------------- -Throughout this tutorial, we will use `Top-down Microarchitecture Analysis (TMA) `_ to profile and show that the Back End Bound (Memory Bound, Core Bound) is often the primary bottleneck for under-optimized or under-tuned deep learning workloads, and demonstrate optimization techniques via Intel® Extension for PyTorch* for improving Back End Bound. We will use `toplev `_, a tool part of `pmu-tools `_ built on top of `Linux perf `_, for TMA. - -We will also use `Intel® VTune™ Profiler's Instrumentation and Tracing Technology (ITT) `__ to profile at finer granularity. - -Top-down Microarchitecture Analysis Method (TMA) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When tuning CPU for optimal performance, it's useful to know where the bottleneck is. Most CPU cores have on-chip Performance Monitoring Units (PMUs). PMUs are dedicated pieces of logic within a CPU core that count specific hardware events as they occur on the system. Examples of these events may be Cache Misses or Branch Mispredictions. PMUs are used for Top-down Microarchitecture Analysis (TMA) to identify the bottlenecks. TMA consists of hierarchical levels as shown: - -.. figure:: /_static/img/torchserve-ipex-images-2/2.png - :width: 100% - :align: center - -The top level, level-1, metrics collect *Retiring*, *Bad Speculation*, *Front End Bound*, *Back End Bound*. The pipeline of CPU can conceptually be simplified and divided into two: the frontend and the backend. The *frontend* is responsible for fetching the program code and decoding them into low-level hardware operations called micro-ops (uOps). The uOps are then fed to the *backend* in a process called allocation. Once allocated, the backend is responsible for executing the uOp in an available execution unit. A completion of uOp's execution is called *retirement*. In contrast, a *bad speculation* is when speculatively fetched uOps are canceled before retiring such as in the case of mispredicted branches. Each of these metrics can further be broken down in the subsequent levels to pinpoint the bottleneck. - -Tune for the Back End Bound -+++++++++++++++++++++++++++ -The majority of untuned deep learning workloads will be Back End Bound. Resolving Back End bound is often resolving sources of latency causing retirement to take longer than necessary. As shown above, Back End Bound has two sub-metrics – Core Bound and Memory Bound. - -Memory Bound stalls have causes related to the memory subsystem. For example, last-level cache (LLC or L3 cache) miss causing access to DRAM. Scaling deep learning models often requires significant compute. And high compute utilization requires that data is available when the execution units need it to execute the uOps. This requires prefetching the data and reusing the data in cache instead of fetching that same data multiple times from main memory which causes execution units to be starved while data is being returned. Throughout this tutorial, we wll show that a more efficient memory allocator, operator fusion, memory layout format optimization reduce overhead on Memory Bound with better cache locality. - -Core Bound stalls indicate sub-optimal use of available execution units while there are no uncompleted memory accesses. For example, several general matrix-matrix multiplication (GEMM) instructions in a row competing for fused-multiply-add (FMA) or dot-product (DP) execution units could cause Core Bound stalls. Key deep learning kernels, including the DP kernels, have been well optimized by `oneDNN library `_ (oneAPI Deep Neural Network Library), reducing overhead on Core Bound. - -Operations like GEMM, convolution, deconvolution are compute-intensive. While operations like pooling, batch normalization, activation functions like ReLU are memory-bound. - -Intel® VTune™ Profiler's Instrumentation and Tracing Technology (ITT) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The ITT APIs of Intel® VTune Profiler is a useful tool to annotate a region of your workload for tracing to profile and visualize at a finer granularity of your annotation – OP/function/sub-function granularity. By annotating at the granularity of your PyTorch model's OPs, Intel® VTune Profiler's ITT enables op-level profiling. Intel® VTune Profiler's ITT has been integrated into `PyTorch Autograd Profiler `_. :superscript:`1` - -1. The feature has to be explicitly enabled by *with torch.autograd.profiler.emit_itt()*. - -TorchServe with Intel® Extension for PyTorch* ---------------------------------------------- -`Intel® Extension for PyTorch* `__ is a Python package to extend PyTorch with optimizations for extra performance boost on Intel hardware. - -Intel® Extension for PyTorch* has already been integrated into TorchServe to improve the performance out-of-box. :superscript:`2` For custom handler scripts, we recommend adding the *intel_extension_for_pytorch* package in. - -2. The feature has to be explicitly enabled by setting *ipex_enable=true* in *config.properties*. - -Throughout this section, we will show that Back End Bound is often the primary bottleneck for under-optimized or under-tuned deep learning workloads, and demonstrate optimization techniques via Intel® Extension for PyTorch* for improving Back End Bound, which has two submetrics - Memory Bound, and Core Bound. A more efficient memory allocator, operator fusion, memory layout format optimization improve Memory Bound. Ideally, Memory Bound can be improved to Core Bound by optimized operators and better cache locality. And key deep learning primitives, such as convolution, matrix multiplication, dot-product, have been well optimized by Intel® Extension for PyTorch* and oneDNN library, improving Core Bound. - -Leveraging Advanced Launcher Configuration: Memory Allocator -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Memory allocator plays an important role from performance perspective. A more efficient memory usage reduces overhead on unnecessary memory allocations or destructions, and thus faster execution. For deep learning workloads in practice, especially those running on large multi-core systems or servers like TorchServe, TCMalloc, or JeMalloc can generally get better memory usage than the default PyTorch memory allocator, PTMalloc. - -TCMalloc, JeMalloc, PTMalloc -++++++++++++++++++++++++++++ -Both TCMalloc and JeMalloc use thread-local caches to reduce overhead on thread synchronization, and lock contention by using spinlocks and per-thread arenas respectively. TCMalloc and JeMalloc reduce overhead on unnecessary memory allocation and deallocation. Both allocators categorize memory allocations by sizes to reduce overhead on memory fragmentation. - -With the launcher, users can easily experiment with different memory allocators by choosing one of the three launcher knobs *--enable_tcmalloc* (TCMalloc), *--enable_jemalloc* (JeMalloc), *--use_default_allocator* (PTMalloc). - -Exercise -^^^^^^^^ -Let's profile PTMalloc vs. JeMalloc. - -We will use the launcher to designate the memory allocator, and to bind the workload to physical cores of the first socket to avoid any NUMA complication – to profile the effect of memory allocator only. - -The following example measures the average inference time of ResNet50: - -.. code:: python - - import torch - import torchvision.models as models - import time - - model = models.resnet50(pretrained=False) - model.eval() - batch_size = 32 - data = torch.rand(batch_size, 3, 224, 224) - - # warm up - for _ in range(100): - model(data) - - # measure - # Intel® VTune Profiler's ITT context manager - with torch.autograd.profiler.emit_itt(): - start = time.time() - for i in range(100): - # Intel® VTune Profiler's ITT to annotate each step - torch.profiler.itt.range_push('step_{}'.format(i)) - model(data) - torch.profiler.itt.range_pop() - end = time.time() - - print('Inference took {:.2f} ms in average'.format((end-start)/100*1000)) - -Let's collect level-1 TMA metrics. - -.. figure:: /_static/img/torchserve-ipex-images-2/3.png - :width: 100% - :align: center - -Level-1 TMA shows that both PTMalloc and JeMalloc are bounded by the backend. More than half of the execution time was stalled by the backend. Let's go one level deeper. - -.. figure:: /_static/img/torchserve-ipex-images-2/4.png - :width: 100% - :align: center - -Level-2 TMA shows that the Back End Bound was caused by Memory Bound. Let's go one level deeper. - -.. figure:: /_static/img/torchserve-ipex-images-2/5.png - :width: 100% - :align: center - -Most of the metrics under the Memory Bound identify which level of the memory hierarchy from the L1 cache to main memory is the bottleneck. A hotspot bounded at a given level indicates that most of the data was being retrieved from that cache or memory-level. Optimizations should focus on moving data closer to the core. Level-3 TMA shows that PTMalloc was bottlenecked by DRAM Bound. On the other hand, JeMalloc was bottlenecked by L1 Bound – JeMalloc moved data closer to the core, and thus faster execution. - -Let's look at Intel® VTune Profiler ITT trace. In the example script, we have annotated each *step_x* of the inference loop. - -.. figure:: /_static/img/torchserve-ipex-images-2/6.png - :width: 100% - :align: center - -Each step is traced in the timeline graph. The duration of model inference on the last step (step_99) decreased from 304.308 ms to 261.843 ms. - -Exercise with TorchServe -^^^^^^^^^^^^^^^^^^^^^^^^ -Let's profile PTMalloc vs. JeMalloc with TorchServe. - -We will use `TorchServe apache-bench benchmarking `_ with ResNet50 FP32, batch size 32, concurrency 32, requests 8960. All other parameters are the same as the `default parameters `_. - -As in the previous exercise, we will use the launcher to designate the memory allocator, and to bind the workload to physical cores of the first socket. To do so, user simply needs to add a few lines in `config.properties `__: - -PTMalloc - -.. code:: python - - cpu_launcher_enable=true - cpu_launcher_args=--node_id 0 --use_default_allocator - -JeMalloc - -.. code:: python - - cpu_launcher_enable=true - cpu_launcher_args=--node_id 0 --enable_jemalloc - -Let's collect level-1 TMA metrics. - -.. figure:: /_static/img/torchserve-ipex-images-2/7.png - :width: 100% - :align: center - -Let's go one level deeper. - -.. figure:: /_static/img/torchserve-ipex-images-2/8.png - :width: 100% - :align: center - -Let's use Intel® VTune Profiler ITT to annotate `TorchServe inference scope `_ to profile at inference-level granularity. As `TorchServe Architecture `_ consists of several sub-components, including the Java frontend for handling request/response, and the Python backend for running the actual inference on the models, it is helpful to use Intel® VTune Profiler ITT to limit the collection of trace data at inference-level. - -.. figure:: /_static/img/torchserve-ipex-images-2/9.png - :width: 100% - :align: center - -Each inference call is traced in the timeline graph. The duration of the last model inference decreased from 561.688 ms to 251.287 ms - 2.2x speedup. - -.. figure:: /_static/img/torchserve-ipex-images-2/10.png - :width: 100% - :align: center - -The timeline graph can be expanded to see op-level profiling results. The duration of *aten::conv2d* decreased from 16.401 ms to 6.392 ms - 2.6x speedup. - -In this section, we have demonstrated that JeMalloc can give better performance than the default PyTorch memory allocator, PTMalloc, with efficient thread-local caches improving Back-End-Bound. - -Intel® Extension for PyTorch* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The three major `Intel® Extension for PyTorch* `__ optimization techniques, Operator, Graph, Runtime, are as shown: - -+------------------------------------------------------------------------------------------------------------------------+ -| Intel® Extension for PyTorch* Optimization Techniques | -+======================================================+=======================================+=========================+ -| Operator | Graph | Runtime | -+------------------------------------------------------+---------------------------------------+-------------------------+ -| - Vectorization and Multi-threading | - Constant folding to reduce compute | - Thread affinitization | -| - Low-precision BF16/INT8 compute | - Op fusion for better cache locality | - Memory buffer pooling | -| - Data layout optimization for better cache locality | | - GPU runtime | -| | | - Launcher | -+------------------------------------------------------+---------------------------------------+-------------------------+ - -Operator Optimization -+++++++++++++++++++++ -Optimized operators and kernels are registered through PyTorch dispatching mechanism. These operators and kernels are accelerated from native vectorization feature and matrix calculation feature of Intel hardware. During execution, Intel® Extension for PyTorch* intercepts invocation of ATen operators, and replaces the original ones with these optimized ones. Popular operators like Convolution, Linear have been optimized in Intel® Extension for PyTorch*. - -Exercise -^^^^^^^^ -Let's profile optimized operator with Intel® Extension for PyTorch*. We will compare with and without the lines in code changes. - -As in the previous exercises, we will bind the workload to physical cores of the first socket. - -.. code:: python - - import torch - - class Model(torch.nn.Module): - def __init__(self): - super(Model, self).__init__() - self.conv = torch.nn.Conv2d(16, 33, 3, stride=2) - self.relu = torch.nn.ReLU() - - def forward(self, x): - x = self.conv(x) - x = self.relu(x) - return x - - model = Model() - model.eval() - data = torch.rand(20, 16, 50, 100) - - #################### code changes #################### - import intel_extension_for_pytorch as ipex - model = ipex.optimize(model) - ###################################################### - - print(model) - -The model consists of two operations—Conv2d and ReLU. By printing the model object, we get the following output. - -.. figure:: /_static/img/torchserve-ipex-images-2/11.png - :width: 60% - :align: center - -Let's collect level-1 TMA metrics. - -.. figure:: /_static/img/torchserve-ipex-images-2/12.png - :width: 100% - :align: center - -Notice the Back End Bound reduced from 68.9 to 38.5 – 1.8x speedup. - -Additionally, let's profile with PyTorch Profiler. - -.. figure:: /_static/img/torchserve-ipex-images-2/13.png - :width: 100% - :align: center - -Notice the CPU time reduced from 851 us to 310 us – 2.7X speedup. - -Graph Optimization -++++++++++++++++++ -It is highly recommended for users to take advantage of Intel® Extension for PyTorch* with `TorchScript `_ for further graph optimizations. To optimize performance further with TorchScript, Intel® Extension for PyTorch* supports oneDNN fusion of frequently used FP32/BF16 operator patterns, like Conv2D+ReLU, Linear+ReLU, and more to reduce operator/kernel invocation overheads, and for better cache locality. Some operator fusions allow to maintain temporary calculations, data type conversions, data layouts for better cache locality. As well as for INT8, Intel® Extension for PyTorch* has built-in quantization recipes to deliver good statistical accuracy for popular DL workloads including CNN, NLP and recommendation models. The quantized model is then optimized with oneDNN fusion support. - -Exercise -^^^^^^^^ -Let's profile FP32 graph optimization with TorchScript. - -As in the previous exercises, we will bind the workload to physical cores of the first socket. - -.. code:: python - - import torch - - class Model(torch.nn.Module): - def __init__(self): - super(Model, self).__init__() - self.conv = torch.nn.Conv2d(16, 33, 3, stride=2) - self.relu = torch.nn.ReLU() - - def forward(self, x): - x = self.conv(x) - x = self.relu(x) - return x - - model = Model() - model.eval() - data = torch.rand(20, 16, 50, 100) - - #################### code changes #################### - import intel_extension_for_pytorch as ipex - model = ipex.optimize(model) - ###################################################### - - # torchscript - with torch.no_grad(): - model = torch.jit.trace(model, data) - model = torch.jit.freeze(model) - -Let's collect level-1 TMA metrics. - -.. figure:: /_static/img/torchserve-ipex-images-2/14.png - :width: 100% - :align: center - -Notice the Back End Bound reduced from 67.1 to 37.5 – 1.8x speedup. - -Additionally, let's profile with PyTorch Profiler. - -.. figure:: /_static/img/torchserve-ipex-images-2/15.png - :width: 100% - :align: center - -Notice that with Intel® Extension for PyTorch* Conv + ReLU operators are fused, and the CPU time reduced from 803 us to 248 us – 3.2X speedup. The oneDNN eltwise post-op enables fusing a primitive with an elementwise primitive. This is one of the most popular kinds of fusion: an eltwise (typically an activation function such as ReLU) with preceding convolution or inner product. Have a look at the oneDNN verbose log shown in the next section. - -Channels Last Memory Format -+++++++++++++++++++++++++++ -When invoking *ipex.optimize* on model, Intel® Extension for PyTorch* automatically converts the model to optimized memory format, channels last. Channels last is a memory format that is more friendly to Intel Architecture. Compared to PyTorch default channels first NCHW (batch, channels, height, width) memory format, channels last NHWC (batch, height, width, channels) memory format generally accelerates convolutional neural networks with better cache locality. - -One thing to note is that it is expensive to convert memory format. So it's better to convert the memory format prior to deployment once, and keep the memory format conversion minimum during deployment. As the data propagates through model's layers the channels last memory format is preserved through consecutive channels last supported layers (for example, Conv2d -> ReLU -> Conv2d) and conversions are only made in between channels last unsupported layers. See `Memory Format Propagation `_ for more details. - -Exercise -^^^^^^^^ -Let's demonstrate channels last optimization. - -.. code:: python - - import torch - - class Model(torch.nn.Module): - def __init__(self): - super(Model, self).__init__() - self.conv = torch.nn.Conv2d(16, 33, 3, stride=2) - self.relu = torch.nn.ReLU() - - def forward(self, x): - x = self.conv(x) - x = self.relu(x) - return x - - model = Model() - model.eval() - data = torch.rand(20, 16, 50, 100) - - import intel_extension_for_pytorch as ipex - ############################### code changes ############################### - ipex.disable_auto_channels_last() # omit this line for channels_last (default) - ############################################################################ - model = ipex.optimize(model) - - with torch.no_grad(): - model = torch.jit.trace(model, data) - model = torch.jit.freeze(model) - -We will use `oneDNN verbose mode `_, a tool to help collect information at oneDNN graph level such as operator fusions, kernel execution time spent on executing oneDNN primitives. For more information, refer to the `oneDNN Documentation `_. - -.. figure:: /_static/img/torchserve-ipex-images-2/16.png - :width: 15% - :align: center - -.. figure:: /_static/img/torchserve-ipex-images-2/17.png - :width: 100% - :align: center - -Above is oneDNN verbose from channels first. We can verify that there are reorders from weight and data, then do computation, and finally reorder output back. - -.. figure:: /_static/img/torchserve-ipex-images-2/18.png - :width: 80% - :align: center - -Above is oneDNN verbose from channels last. We can verify that channels last memory format avoids unnecessary reorders. - -Performance Boost with Intel® Extension for PyTorch* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Below summarizes performance boost of TorchServe with Intel® Extension for PyTorch* for ResNet50 and BERT-base-uncased. - -.. figure:: /_static/img/torchserve-ipex-images-2/19.png - :width: 100% - :align: center - -Exercise with TorchServe -~~~~~~~~~~~~~~~~~~~~~~~~ -Let's profile Intel® Extension for PyTorch* optimizations with TorchServe. - -We will use `TorchServe apache-bench benchmarking `_ with ResNet50 FP32 TorchScript, batch size 32, concurrency 32, requests 8960. All other parameters are the same as the `default parameters `_. - -As in the previous exercise, we will use the launcher to bind the workload to physical cores of the first socket. To do so, user simply needs to add a few lines in `config.properties `__: - -.. code:: python - - cpu_launcher_enable=true - cpu_launcher_args=--node_id 0 - -Let's collect level-1 TMA metrics. - -.. figure:: /_static/img/torchserve-ipex-images-2/20.png - :width: 100% - :align: center - -Level-1 TMA shows that both are bounded by the backend. As discussed earlier, the majority of untuned deep learning workloads will be Back End Bound. Notice the Back End Bound reduced from 70.0 to 54.1. Let's go one level deeper. - -.. figure:: /_static/img/torchserve-ipex-images-2/21.png - :width: 100% - :align: center - -As discussed earlier, Back End Bound has two submetrics – Memory Bound and Core Bound. Memory Bound indicates the workload is under-optimized or under-utilized, and ideally memory-bound operations can be improved to core-bound by optimizing the OPs and improving cache locality. Level-2 TMA shows that the Back End Bound improved from Memory Bound to Core Bound. Let's go one level deeper. - -.. figure:: /_static/img/torchserve-ipex-images-2/22.png - :width: 100% - :align: center - -Scaling deep learning models for production on a model serving framework like TorchServe requires high compute utilization. This requires that data is available through prefetching and reusing the data in cache when the execution units need it to execute the uOps. Level-3 TMA shows that the Back End Memory Bound improved from DRAM Bound to Core Bound. - -As in the previous exercise with TorchServe, let's use Intel® VTune Profiler ITT to annotate `TorchServe inference scope `_ to profile at inference-level granularity. - -.. figure:: /_static/img/torchserve-ipex-images-2/23.png - :width: 100% - :align: center - -Each inference call is traced in the timeline graph. The duration of the last inference call decreased from 215.731 ms to 95.634 ms - 2.3x speedup. - -.. figure:: /_static/img/torchserve-ipex-images-2/24.png - :width: 100% - :align: center - -The timeline graph can be expanded to see op-level profiling results. Notice that Conv + ReLU has been fused, and the duration decreased from 6.393 ms + 1.731 ms to 3.408 ms - 2.4x speedup. - -Conclusion ------------ -In this tutorial, we have used Top-down Microarchitecture Analysis (TMA) and Intel® VTune™ Profiler's Instrumentation and Tracing Technology (ITT) to demonstrate that - -- Often the primary bottleneck of under-optimized or under-tuned deep learning workloads are Back End Bound, which has two submetrics, Memory Bound and Core Bound. - -- A more efficient memory allocator, operator fusion, memory layout format optimization by Intel® Extension for PyTorch* improve Memory Bound. - -- Key deep learning primitives, such as convolution, matrix multiplication, dot-product, etc have been well optimized by Intel® Extension for PyTorch* and oneDNN library, improving Core Bound. - -- Intel® Extension for PyTorch* has been integrated into TorchServe with an ease-of-use API. - -- TorchServe with Intel® Extension for PyTorch* shows 7.71x throughput speedup for ResNet50, and 2.20x throughput speedup for BERT. - -Related Readings ----------------- -`Top-down Microarchitecture Analysis Method `_ - -`Top-Down performance analysis methodology `_ - -`Accelerating PyTorch with Intel® Extension for PyTorch* `_ - -Acknowledgement ---------------- -We would like to thank Ashok Emani (Intel) and Jiong Gong (Intel) for their immense guidance and support, and thorough feedback and reviews throughout many steps of this tutorial. We would also like to thank Hamid Shojanazeri (Meta) and Li Ning (AWS) for their helpful feedback in code review and the tutorial. diff --git a/recipes_source/xeon_run_cpu.rst b/recipes_source/xeon_run_cpu.rst index 6426bc57819..9ff14be08e3 100644 --- a/recipes_source/xeon_run_cpu.rst +++ b/recipes_source/xeon_run_cpu.rst @@ -361,4 +361,3 @@ See also: * `PyTorch Performance Tuning Guide `__ * `PyTorch Multiprocessing Best Practices `__ -* Grokking PyTorch Intel CPU performance: `Part 1 `__ `Part 2 `__ diff --git a/redirects.py b/redirects.py index 90574926ec6..772eafe7770 100644 --- a/redirects.py +++ b/redirects.py @@ -3,5 +3,6 @@ "beginner/ptcheat.html": "../index.html", "beginner/deploy_seq2seq_hybrid_frontend_tutorial.html": "../index.html", "recipes/bundled_inputs.html": "../index.html", + "intermediate/torchserve_with_ipex_2": "../index.html", "recipes/torchserve_vertexai_tutorial": "../index.html", } From d1fc665761c33939e83cc9ecac13f9221939f9fd Mon Sep 17 00:00:00 2001 From: Alanna Burke Date: Tue, 1 Jul 2025 13:05:26 -0400 Subject: [PATCH 136/347] Removing Torchserve tutorial per Tutorial audit T228334528 (#3413) * Removing torchserve_with_ipex.rst tutorial and references per tutorial audit. * Update redirects.py --------- Co-authored-by: Svetlana Karslioglu --- intermediate_source/torchserve_with_ipex.rst | 394 ------------------- redirects.py | 5 +- 2 files changed, 3 insertions(+), 396 deletions(-) delete mode 100644 intermediate_source/torchserve_with_ipex.rst diff --git a/intermediate_source/torchserve_with_ipex.rst b/intermediate_source/torchserve_with_ipex.rst deleted file mode 100644 index 23d91f50cb6..00000000000 --- a/intermediate_source/torchserve_with_ipex.rst +++ /dev/null @@ -1,394 +0,0 @@ -Grokking PyTorch Intel CPU performance from first principles -============================================================ - -A case study on the TorchServe inference framework optimized with `Intel® Extension for PyTorch* `_. - -Authors: Min Jean Cho, Mark Saroufim - -Reviewers: Ashok Emani, Jiong Gong - -Getting a strong out-of-box performance for deep learning on CPUs can be tricky but it’s much easier if you’re aware of the main problems that affect performance, how to measure them and how to solve them. - -TL;DR - -+-----------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| Problem | How to measure it | Solution | -+-----------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| Bottlenecked GEMM execution units | - `Imbalance or Serial Spinning `_ | Avoid using logical cores by setting thread affinity to physical cores via core pinning | -| | - `Front-End Bound `_ | | -| | - `Core Bound `_ | | -+-----------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| Non Uniform Memory Access (NUMA) | - Local vs. remote memory access | Avoid cross-socket computation by setting thread affinity to a specific socket via core pinning | -| | - `UPI Utilization `_ | | -| | - Latency in memory accesses | | -| | - Thread migration | | -+-----------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------+ - -*GEMM (General Matrix Multiply)* run on fused-multiply-add (FMA) or dot-product (DP) execution units which will be bottlenecked and cause delays in thread waiting/*spinning at synchronization* barrier when *hyperthreading* is enabled - because using logical cores causes insufficient concurrency for all working threads as each logical thread *contends for the same core resources*. Instead, if we use 1 thread per physical core, we avoid this contention. So we generally recommend *avoiding logical cores* by setting CPU *thread affinity* to physical cores via *core pinning*. - -Multi-socket systems have *Non-Uniform Memory Access (NUMA)* which is a shared memory architecture that describes the placement of main memory modules with respect to processors. But if a process is not NUMA-aware, slow *remote memory* is frequently accessed when *threads migrate* cross socket via *Intel Ultra Path Interconnect (UPI)* during run time. We address this problem by setting CPU *thread affinity* to a specific socket via *core pinning*. - -Knowing these principles in mind, proper CPU runtime configuration can significantly boost out-of-box performance. - -In this blog, we'll walk you through the important runtime configurations you should be aware of from `CPU Performance Tuning Guide `_, explain how they work, how to profile them and how to integrate them within a model serving framework like `TorchServe `_ via an easy to use `launch script `_ which we’ve `integrated `_ :superscript:`1` natively. - -We’ll explain all of these ideas :strong:`visually` from :strong:`first principles` with lots of :strong:`profiles` and show you how we applied our learnings to make out of the box CPU performance on TorchServe better. - -1. The feature has to be explicitly enabled by setting *cpu_launcher_enable=true* in *config.properties*. - -Avoid logical cores for deep learning -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Avoiding logical cores for deep learning workloads generally improves performance. To understand this, let us take a step back to GEMM. - -:strong:`Optimizing GEMM optimizes deep learning` - -The majority of time in deep learning training or inference is spent on millions of repeated operations of GEMM which is at the core of fully connected layers. Fully connected layers have been used for decades since multi-layer perceptrons (MLP) `proved to be a universal approximator of any continuous function `_. Any MLP can be entirely represented as GEMM. And even a convolution can be represented as a GEMM by using a `Toepliz matrix `_. - -Returning to the original topic, most GEMM operators benefit from using non-hyperthreading, because the majority of time in deep learning training or inference is spent on millions of repeated operations of GEMM running on fused-multiply-add (FMA) or dot-product (DP) execution units shared by hyperthreading cores. With hyperthreading enabled, OpenMP threads will contend for the same GEMM execution units. - -.. figure:: /_static/img/torchserve-ipex-images/1_.png - :width: 70% - :align: center - -And if 2 logical threads run GEMM at the same time, they will be sharing the same core resources causing front end bound, such that the overhead from this front end bound is greater than the gain from running both logical threads at the same time. - -Therefore we generally recommend avoiding using logical cores for deep learning workloads to achieve good performance. The launch script by default uses physical cores only; however, users can easily experiment with logical vs. physical cores by simply toggling the ``--use_logical_core`` launch script knob. - -:strong:`Exercise` - -We'll use the following example of feeding ResNet50 dummy tensor: - -.. code:: python - - import torch - import torchvision.models as models - import time - - model = models.resnet50(pretrained=False) - model.eval() - data = torch.rand(1, 3, 224, 224) - - # warm up - for _ in range(100): - model(data) - - start = time.time() - for _ in range(100): - model(data) - end = time.time() - print('Inference took {:.2f} ms in average'.format((end-start)/100*1000)) - -Throughout the blog, we'll use `Intel® VTune™ Profiler `_ to profile and verify optimizations. And we'll run all exercises on a machine with two Intel(R) Xeon(R) Platinum 8180M CPUs. The CPU information is shown in Figure 2.1. - -Environment variable ``OMP_NUM_THREADS`` is used to set the number of threads for parallel region. We'll compare ``OMP_NUM_THREADS=2`` with (1) use of logical cores and (2) use of physical cores only. - -(1) Both OpenMP threads trying to utilize the same GEMM execution units shared by hyperthreading cores (0, 56) - -We can visualize this by running ``htop`` command on Linux as shown below. - -.. figure:: /_static/img/torchserve-ipex-images/2.png - :width: 100% - :align: center - - -.. figure:: /_static/img/torchserve-ipex-images/3.png - :width: 100% - :align: center - -We notice that the Spin Time is flagged, and Imbalance or Serial Spinning contributed to the majority of it - 4.980 seconds out of the 8.982 seconds total. The Imbalance or Serial Spinning when using logical cores is due to insufficient concurrency of working threads as each logical thread contends for the same core resources. - -The Top Hotspots section of the execution summary indicates that ``__kmp_fork_barrier`` took 4.589 seconds of CPU time - during 9.33% of the CPU execution time, threads were just spinning at this barrier due to thread synchronization. - -(2) Each OpenMP thread utilizing GEMM execution units in respective physical cores (0,1) - - -.. figure:: /_static/img/torchserve-ipex-images/4.png - :width: 80% - :align: center - - -.. figure:: /_static/img/torchserve-ipex-images/5.png - :width: 80% - :align: center - -We first note that the execution time dropped from 32 seconds to 23 seconds by avoiding logical cores. While there's still some non-negligible Imbalance or Serial Spinning, we note relative improvement from 4.980 seconds to 3.887 seconds. - -By not using logical threads (instead, using 1 thread per physical core), we avoid logical threads contending for the same core resources. The Top Hotspots section also indicates relative improvement of ``__kmp_fork_barrier`` time from 4.589 seconds to 3.530 seconds. - -Local memory access is always faster than remote memory access -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -We generally recommend binding a process to a local socket such that the process does not migrate across sockets. Generally the goal of doing so is to utilize high speed cache on local memory and to avoid remote memory access which can be ~2x slower. - - -.. figure:: /_static/img/torchserve-ipex-images/6.png - :width: 80% - :align: center -Figure 1. Two-socket configuration - -Figure 1. shows a typical two-socket configuration. Notice that each socket has its own local memory. Sockets are connected to each other via Intel Ultra Path Interconnect (UPI) which allows each socket to access the local memory of another socket called remote memory. Local memory access is always faster than remote memory access. - -.. figure:: /_static/img/torchserve-ipex-images/7.png - :width: 50% - :align: center -Figure 2.1. CPU information - -Users can get their CPU information by running ``lscpu`` command on their Linux machine. Figure 2.1. shows an example of ``lscpu`` execution on a machine with two Intel(R) Xeon(R) Platinum 8180M CPUs. Notice that there are 28 cores per socket, and 2 threads per core (i.e., hyperthreading is enabled). In other words, there are 28 logical cores in addition to 28 physical cores, giving a total of 56 cores per socket. And there are 2 sockets, giving a total of 112 cores (``Thread(s) per core`` x ``Core(s) per socket`` x ``Socket(s)``). - -.. figure:: /_static/img/torchserve-ipex-images/8.png - :width: 100% - :align: center -Figure 2.2. CPU information - -The 2 sockets are mapped to 2 NUMA nodes (NUMA node 0, NUMA node 1) respectively. Physical cores are indexed prior to logical cores. As shown in Figure 2.2., the first 28 physical cores (0-27) and the first 28 logical cores (56-83) on the first socket are on NUMA node 0. And the second 28 physical cores (28-55) and the second 28 logical cores (84-111) on the second socket are on NUMA node 1. Cores on the same socket share local memory and last level cache (LLC) which is much faster than cross-socket communication via Intel UPI. - -Now that we understand NUMA, cross-socket (UPI) traffic, local vs. remote memory access in multi-processor systems, let's profile and verify our understanding. - -:strong:`Exercise` - -We'll reuse the ResNet50 example above. - -As we did not pin threads to processor cores of a specific socket, the operating system periodically schedules threads on processor cores located in different sockets. - -.. figure:: /_static/img/torchserve-ipex-images/9.gif - :width: 100% - :align: center - -Figure 3. CPU usage of non NUMA-aware application. 1 main worker thread was launched, then it launched a physical core number (56) of threads on all cores, including logical cores. - -(Aside: If the number of threads is not set by `torch.set_num_threads `_, the default number of threads is the number of physical cores in a hyperthreading enabled system. This can be verified by `torch.get_num_threads `_. Hence we see above about half of the cores busy running the example script.) - -.. figure:: /_static/img/torchserve-ipex-images/10.png - :width: 100% - :align: center -Figure 4. Non-Uniform Memory Access Analysis graph - - -Figure 4. compares local vs. remote memory access over time. We verify usage of remote memory which could result in sub-optimal performance. - -:strong:`Set thread affinity to reduce remote memory access and cross-socket (UPI) traffic` - -Pinning threads to cores on the same socket helps maintain locality of memory access. In this example, we'll pin to the physical cores on the first NUMA node (0-27). With the launch script, users can easily experiment with NUMA nodes configuration by simply toggling the ``--node_id`` launch script knob. - -Let's visualize the CPU usage now. - -.. figure:: /_static/img/torchserve-ipex-images/11.gif - :width: 100% - :align: center -Figure 5. CPU usage of NUMA-aware application - -1 main worker thread was launched, then it launched threads on all physical cores on the first numa node. - -.. figure:: /_static/img/torchserve-ipex-images/12.png - :width: 100% - :align: center -Figure 6. Non-Uniform Memory Access Analysis graph - -As shown in Figure 6., now almost all memory accesses are local accesses. - -Efficient CPU usage with core pinning for multi-worker inference -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When running multi-worker inference, cores are overlapped (or shared) between workers causing inefficient CPU usage. To address this problem, the launch script equally divides the number of available cores by the number of workers such that each worker is pinned to assigned cores during runtime. - -:strong:`Exercise with TorchServe` - -For this exercise, let's apply the CPU performance tuning principles and recommendations that we have discussed so far to `TorchServe apache-bench benchmarking `_. - -We'll use ResNet50 with 4 workers, concurrency 100, requests 10,000. All other parameters (e.g., batch_size, input, etc) are the same as the `default parameters `_. - -We'll compare the following three configurations: - -(1) default TorchServe setting (no core pinning) - -(2) `torch.set_num_threads `_ = ``number of physical cores / number of workers`` (no core pinning) - -(3) core pinning via the launch script (Required Torchserve>=0.6.1) - -After this exercise, we'll have verified that we prefer avoiding logical cores and prefer local memory access via core pinning with a real TorchServe use case. - -1. Default TorchServe setting (no core pinning) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The `base_handler `_ doesn't explicitly set `torch.set_num_threads `_. Hence the default number of threads is the number of physical CPU cores as described `here `_. Users can check the number of threads by `torch.get_num_threads `_ in the base_handler. Each of the 4 main worker threads launches a physical core number (56) of threads, launching a total of 56x4 = 224 threads, which is more than the total number of cores 112. Therefore cores are guaranteed to be heavily overlapped with high logical core utilization- multiple workers using multiple cores at the same time. Furthermore, because threads are not affinitized to specific CPU cores, the operating system periodically schedules threads to cores located in different sockets. - -1. CPU usage - -.. figure:: /_static/img/torchserve-ipex-images/13.png - :width: 100% - :align: center - -4 main worker threads were launched, then each launched a physical core number (56) of threads on all cores, including logical cores. - -2. Core Bound stalls - -.. figure:: /_static/img/torchserve-ipex-images/14.png - :width: 80% - :align: center - -We observe a very high Core Bound stall of 88.4%, decreasing pipeline efficiency. Core Bound stalls indicate sub-optimal use of available execution units in the CPU. For example, several GEMM instructions in a row competing for fused-multiply-add (FMA) or dot-product (DP) execution units shared by hyperthreading cores could cause Core Bound stalls. And as described in the previous section, use of logical cores amplifies this problem. - - -.. figure:: /_static/img/torchserve-ipex-images/15.png - :width: 40% - :align: center - -.. figure:: /_static/img/torchserve-ipex-images/16.png - :width: 50% - :align: center - -An empty pipeline slot not filled with micro-ops (uOps) is attributed to a stall. For example, without core pinning CPU usage may not effectively be on compute but on other operations like thread scheduling from Linux kernel. We see above that ``__sched_yield`` contributed to the majority of the Spin Time. - -3. Thread Migration - -Without core pinning, scheduler may migrate thread executing on a core to a different core. Thread migration can disassociate the thread from data that has already been fetched into the caches resulting in longer data access latencies. This problem is exacerbated in NUMA systems when thread migrates across sockets. Data that has been fetched to high speed cache on local memory now becomes remote memory, which is much slower. - -.. figure:: /_static/img/torchserve-ipex-images/17.png - :width: 50% - :align: center - -Generally the total number of threads should be less than or equal to the total number of threads supported by the core. In the above example, we notice a large number of threads executing on core_51 instead of the expected 2 threads (since hyperthreading is enabled in Intel(R) Xeon(R) Platinum 8180 CPUs) . This indicates thread migration. - -.. figure:: /_static/img/torchserve-ipex-images/18.png - :width: 80% - :align: center - -Additionally, notice that thread (TID:97097) was executing on a large number of CPU cores, indicating CPU migration. For example, this thread was executing on cpu_81, then migrated to cpu_14, then migrated to cpu_5, and so on. Furthermore, note that this thread migrated cross socket back and forth many times, resulting in very inefficient memory access. For example, this thread executed on cpu_70 (NUMA node 0), then migrated to cpu_100 (NUMA node 1), then migrated to cpu_24 (NUMA node 0). - -4. Non Uniform Memory Access Analysis - -.. figure:: /_static/img/torchserve-ipex-images/19.png - :width: 100% - :align: center - -Compare local vs. remote memory access over time. We observe that about half, 51.09%, of the memory accesses were remote accesses, indicating sub-optimal NUMA configuration. - -2. torch.set_num_threads = ``number of physical cores / number of workers`` (no core pinning) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For an apple-to-apple comparison with launcher's core pinning, we'll set the number of threads to the number of cores divided by the number of workers (launcher does this internally). Add the following code snippet in the `base_handler `_: - -.. code:: python - - torch.set_num_threads(num_physical_cores/num_workers) - -As before without core pinning, these threads are not affinitized to specific CPU cores, causing the operating system to periodically schedule threads on cores located in different sockets. - -1. CPU usage - -.. figure:: /_static/img/torchserve-ipex-images/20.gif - :width: 100% - :align: center - -4 main worker threads were launched, then each launched a ``num_physical_cores/num_workers`` number (14) of threads on all cores, including logical cores. - -2. Core Bound stalls - -.. figure:: /_static/img/torchserve-ipex-images/21.png - :width: 80% - :align: center - -Although the percentage of Core Bound stalls has decreased from 88.4% to 73.5%, the Core Bound is still very high. - -.. figure:: /_static/img/torchserve-ipex-images/22.png - :width: 40% - :align: center - -.. figure:: /_static/img/torchserve-ipex-images/23.png - :width: 50% - :align: center - -3. Thread Migration - -.. figure:: /_static/img/torchserve-ipex-images/24.png - :width: 75% - :align: center - -Similar as before, without core pinning thread (TID:94290) was executing on a large number of CPU cores, indicating CPU migration. We notice again cross-socket thread migration, resulting in very inefficient memory access. For example, this thread executed on cpu_78 (NUMA node 0), then migrated to cpu_108 (NUMA node 1). - -4. Non Uniform Memory Access Analysis - -.. figure:: /_static/img/torchserve-ipex-images/25.png - :width: 100% - :align: center - -Although an improvement from the original 51.09%, still 40.45% of memory access is remote, indicating sub-optimal NUMA configuration. - -3. launcher core pinning -~~~~~~~~~~~~~~~~~~~~~~~~ -Launcher will internally equally distribute physical cores to workers, and bind them to each worker. As a reminder, launcher by default uses physical cores only. In this example, launcher will bind worker 0 to cores 0-13 (NUMA node 0), worker 1 to cores 14-27 (NUMA node 0), worker 2 to cores 28-41 (NUMA node 1), and worker 3 to cores 42-55 (NUMA node 1). Doing so ensures that cores are not overlapped among workers and avoids logical core usage. - -1. CPU usage - -.. figure:: /_static/img/torchserve-ipex-images/26.gif - :width: 100% - :align: center - -4 main worker threads were launched, then each launched a ``num_physical_cores/num_workers`` number (14) of threads affinitized to the assigned physical cores. - -2. Core Bound stalls - -.. figure:: /_static/img/torchserve-ipex-images/27.png - :width: 80% - :align: center - -Core Bound stalls has decreased significantly from the original 88.4% to 46.2% - almost a 2x improvement. - -.. figure:: /_static/img/torchserve-ipex-images/28.png - :width: 40% - :align: center - -.. figure:: /_static/img/torchserve-ipex-images/29.png - :width: 50% - :align: center - -We verify that with core binding, most CPU time is effectively used on compute - Spin Time of 0.256s. - -3. Thread Migration - -.. figure:: /_static/img/torchserve-ipex-images/30.png - :width: 100% - :align: center - -We verify that `OMP Primary Thread #0` was bound to assigned physical cores (42-55), and did not migrate cross-socket. - -4. Non Uniform Memory Access Analysis - -.. figure:: /_static/img/torchserve-ipex-images/31.png - :width: 100% - :align: center - -Now almost all, 89.52%, memory accesses are local accesses. - -Conclusion -~~~~~~~~~~ - -In this blog, we've showcased that properly setting your CPU runtime configuration can significantly boost out-of-box CPU performance. - -We have walked through some general CPU performance tuning principles and recommendations: - -- In a hyperthreading enabled system, avoid logical cores by setting thread affinity to physical cores only via core pinning. -- In a multi-socket system with NUMA, avoid cross-socket remote memory access by setting thread affinity to a specific socket via core pinning. - -We have visually explained these ideas from first principles and have verified the performance boost with profiling. And finally, we have applied all of our learnings to TorchServe to boost out-of-box TorchServe CPU performance. - -These principles can be automatically configured via an easy to use launch script which has already been integrated into TorchServe. - -For interested readers, please check out the following documents: - -- `CPU specific optimizations `_ -- `Maximize Performance of Intel® Software Optimization for PyTorch* on CPU `_ -- `Performance Tuning Guide `_ -- `Launch Script Usage Guide `_ -- `Top-down Microarchitecture Analysis Method `_ -- `Configuring oneDNN for Benchmarking `_ -- `Intel® VTune™ Profiler `_ -- `Intel® VTune™ Profiler User Guide `_ - -And stay tuned for a follow-up posts on optimized kernels on CPU via `Intel® Extension for PyTorch* `_ and advanced launcher configurations such as memory allocator. - -Acknowledgement -~~~~~~~~~~~~~~~ - -We would like to thank Ashok Emani (Intel) and Jiong Gong (Intel) for their immense guidance and support, and thorough feedback and reviews throughout many steps of this blog. We would also like to thank Hamid Shojanazeri (Meta), Li Ning (AWS) and Jing Xu (Intel) for helpful feedback in code review. And Suraj Subramanian (Meta) and Geeta Chauhan (Meta) for helpful feedback on the blog. diff --git a/redirects.py b/redirects.py index 772eafe7770..6e2d5f34c17 100644 --- a/redirects.py +++ b/redirects.py @@ -3,6 +3,7 @@ "beginner/ptcheat.html": "../index.html", "beginner/deploy_seq2seq_hybrid_frontend_tutorial.html": "../index.html", "recipes/bundled_inputs.html": "../index.html", - "intermediate/torchserve_with_ipex_2": "../index.html", - "recipes/torchserve_vertexai_tutorial": "../index.html", + "intermediate/torchserve_with_ipex.html": "../index.html", + "intermediate/torchserve_with_ipex_2.html": "../index.html", + "recipes/torchserve_vertexai_tutorial.html": "../index.html", } From 4eed2ab864aa278b648f3e854d77aa04acd9598b Mon Sep 17 00:00:00 2001 From: Nikita Shulga <2453524+malfet@users.noreply.github.com> Date: Tue, 1 Jul 2025 15:10:16 -0700 Subject: [PATCH 137/347] Update tutorials image to CUDA-12.6 (#3427) We should have update a while back when we've changed the default on PyPI --- .ci/docker/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/docker/build.sh b/.ci/docker/build.sh index 42fb88997dd..f40c45fea3d 100755 --- a/.ci/docker/build.sh +++ b/.ci/docker/build.sh @@ -11,7 +11,7 @@ IMAGE_NAME="$1" shift export UBUNTU_VERSION="22.04" -export CUDA_VERSION="12.4.1" +export CUDA_VERSION="12.6.3" export BASE_IMAGE="nvidia/cuda:${CUDA_VERSION}-devel-ubuntu${UBUNTU_VERSION}" echo "Building ${IMAGE_NAME} Docker image" From a96b4709e21647b7726f5a8790637401d1e86ab8 Mon Sep 17 00:00:00 2001 From: pankajkakkar Date: Mon, 7 Jul 2025 15:18:17 -0700 Subject: [PATCH 138/347] Fix a broken link in intro.py (#3430) --- beginner_source/basics/intro.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beginner_source/basics/intro.py b/beginner_source/basics/intro.py index 6c048dcaecc..30ff5e17ff6 100644 --- a/beginner_source/basics/intro.py +++ b/beginner_source/basics/intro.py @@ -43,7 +43,7 @@ If you're familiar with other deep learning frameworks, check out the `0. Quickstart `_ first to quickly familiarize yourself with PyTorch's API. -If you're new to deep learning frameworks, head right into the first section of our step-by-step guide: `1. Tensors `_. +If you're new to deep learning frameworks, head right into the first section of our step-by-step guide: `1. Tensors `_. .. include:: /beginner_source/basics/qs_toc.txt From 220b98b07821374f0be42b2c19856e54d98468e2 Mon Sep 17 00:00:00 2001 From: sraikund16 <57577085+sraikund16@users.noreply.github.com> Date: Tue, 8 Jul 2025 13:21:54 -0700 Subject: [PATCH 139/347] profiler recipe update (#3435) --- recipes_source/recipes/profiler_recipe.py | 59 ++++++++++++++--------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/recipes_source/recipes/profiler_recipe.py b/recipes_source/recipes/profiler_recipe.py index 602ab1a7a80..789f504ffca 100644 --- a/recipes_source/recipes/profiler_recipe.py +++ b/recipes_source/recipes/profiler_recipe.py @@ -1,6 +1,11 @@ """ PyTorch Profiler ==================================== +**Author:** `Shivam Raikundalia `_ +""" + +###################################################################### +""" This recipe explains how to use PyTorch profiler and measure the time and memory consumption of the model's operators. @@ -12,6 +17,10 @@ In this recipe, we will use a simple Resnet model to demonstrate how to use profiler to analyze model performance. +Prerequisites +--------------- +- ``torch >= 1.9`` + Setup ----- To install ``torch`` and ``torchvision`` use the following command: @@ -20,10 +29,8 @@ pip install torch torchvision - """ - ###################################################################### # Steps # ----- @@ -45,7 +52,7 @@ import torch import torchvision.models as models -from torch.profiler import profile, record_function, ProfilerActivity +from torch.profiler import profile, ProfilerActivity, record_function ###################################################################### @@ -135,7 +142,11 @@ # To get a finer granularity of results and include operator input shapes, pass ``group_by_input_shape=True`` # (note: this requires running the profiler with ``record_shapes=True``): -print(prof.key_averages(group_by_input_shape=True).table(sort_by="cpu_time_total", row_limit=10)) +print( + prof.key_averages(group_by_input_shape=True).table( + sort_by="cpu_time_total", row_limit=10 + ) +) ######################################################################################## # The output might look like this (omitting some columns): @@ -167,14 +178,17 @@ # Users could switch between cpu, cuda and xpu activities = [ProfilerActivity.CPU] if torch.cuda.is_available(): - device = 'cuda' + device = "cuda" activities += [ProfilerActivity.CUDA] elif torch.xpu.is_available(): - device = 'xpu' + device = "xpu" activities += [ProfilerActivity.XPU] else: - print('Neither CUDA nor XPU devices are available to demonstrate profiling on acceleration devices') + print( + "Neither CUDA nor XPU devices are available to demonstrate profiling on acceleration devices" + ) import sys + sys.exit(0) sort_by_keyword = device + "_time_total" @@ -256,8 +270,9 @@ model = models.resnet18() inputs = torch.randn(5, 3, 224, 224) -with profile(activities=[ProfilerActivity.CPU], - profile_memory=True, record_shapes=True) as prof: +with profile( + activities=[ProfilerActivity.CPU], profile_memory=True, record_shapes=True +) as prof: model(inputs) print(prof.key_averages().table(sort_by="self_cpu_memory_usage", row_limit=10)) @@ -312,14 +327,17 @@ # Users could switch between cpu, cuda and xpu activities = [ProfilerActivity.CPU] if torch.cuda.is_available(): - device = 'cuda' + device = "cuda" activities += [ProfilerActivity.CUDA] elif torch.xpu.is_available(): - device = 'xpu' + device = "xpu" activities += [ProfilerActivity.XPU] else: - print('Neither CUDA nor XPU devices are available to demonstrate profiling on acceleration devices') + print( + "Neither CUDA nor XPU devices are available to demonstrate profiling on acceleration devices" + ) import sys + sys.exit(0) model = models.resnet18().to(device) @@ -347,6 +365,7 @@ with profile( activities=activities, with_stack=True, + experimental_config=torch._C._profiler._ExperimentalConfig(verbose=True), ) as prof: model(inputs) @@ -401,12 +420,7 @@ from torch.profiler import schedule -my_schedule = schedule( - skip_first=10, - wait=5, - warmup=1, - active=3, - repeat=2) +my_schedule = schedule(skip_first=10, wait=5, warmup=1, active=3, repeat=2) ###################################################################### # Profiler assumes that the long-running job is composed of steps, numbered @@ -444,18 +458,17 @@ sort_by_keyword = "self_" + device + "_time_total" + def trace_handler(p): output = p.key_averages().table(sort_by=sort_by_keyword, row_limit=10) print(output) p.export_chrome_trace("/tmp/trace_" + str(p.step_num) + ".json") + with profile( activities=activities, - schedule=torch.profiler.schedule( - wait=1, - warmup=1, - active=2), - on_trace_ready=trace_handler + schedule=torch.profiler.schedule(wait=1, warmup=1, active=2), + on_trace_ready=trace_handler, ) as p: for idx in range(8): model(inputs) From b696cb1d6a15411b4c4be876409de6cc0ab0163d Mon Sep 17 00:00:00 2001 From: ebsmothers Date: Tue, 8 Jul 2025 13:42:46 -0700 Subject: [PATCH 140/347] remove FLAVA tutorial (#3436) * remove FLAVA tutorial * add redirect * dummy commit * whitespace --------- Co-authored-by: Svetlana Karslioglu --- .ci/docker/requirements.txt | 2 - Makefile | 4 - beginner_source/flava_finetuning_tutorial.py | 190 ------------------- index.rst | 17 -- redirects.py | 3 +- 5 files changed, 2 insertions(+), 214 deletions(-) delete mode 100644 beginner_source/flava_finetuning_tutorial.py diff --git a/.ci/docker/requirements.txt b/.ci/docker/requirements.txt index 23481ebe4a5..dae2606eaf7 100644 --- a/.ci/docker/requirements.txt +++ b/.ci/docker/requirements.txt @@ -35,7 +35,6 @@ ax-platform>=0.4.0 nbformat>=5.9.2 datasets transformers -torchmultimodal-nightly # needs to be updated to stable as soon as it's avaialable onnx onnxscript>=0.2.2 onnxruntime @@ -64,7 +63,6 @@ gym-super-mario-bros==7.4.0 pyopengl gymnasium[mujoco]==0.27.0 timm -iopath pygame==2.6.0 pycocotools semilearn==0.3.2 diff --git a/Makefile b/Makefile index 9068d32b2ab..6b61e36ec5d 100644 --- a/Makefile +++ b/Makefile @@ -78,10 +78,6 @@ download: wget -nv -N https://download.pytorch.org/models/resnet18-5c106cde.pth -P $(DATADIR) cp $(DATADIR)/resnet18-5c106cde.pth prototype_source/data/resnet18_pretrained_float.pth - # Download vocab for beginner_source/flava_finetuning_tutorial.py - wget -nv -N http://dl.fbaipublicfiles.com/pythia/data/vocab.tar.gz -P $(DATADIR) - tar $(TAROPTS) -xzf $(DATADIR)/vocab.tar.gz -C ./beginner_source/data/ - # Download PennFudanPed dataset for intermediate_source/torchvision_tutorial.py wget https://www.cis.upenn.edu/~jshi/ped_html/PennFudanPed.zip -P $(DATADIR) unzip -o $(DATADIR)/PennFudanPed.zip -d intermediate_source/data/ diff --git a/beginner_source/flava_finetuning_tutorial.py b/beginner_source/flava_finetuning_tutorial.py deleted file mode 100644 index 12e20f475f8..00000000000 --- a/beginner_source/flava_finetuning_tutorial.py +++ /dev/null @@ -1,190 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TorchMultimodal Tutorial: Finetuning FLAVA -============================================ -""" - -###################################################################### -# Multimodal AI has recently become very popular owing to its ubiquitous -# nature, from use cases like image captioning and visual search to more -# recent applications like image generation from text. **TorchMultimodal -# is a library powered by Pytorch consisting of building blocks and end to -# end examples, aiming to enable and accelerate research in -# multimodality**. -# -# In this tutorial, we will demonstrate how to use a **pretrained SoTA -# model called** `FLAVA `__ **from -# TorchMultimodal library to finetune on a multimodal task i.e. visual -# question answering** (VQA). The model consists of two unimodal transformer -# based encoders for text and image and a multimodal encoder to combine -# the two embeddings. It is pretrained using contrastive, image text matching and -# text, image and multimodal masking losses. - - -###################################################################### -# Installation -# ----------------- -# We will use TextVQA dataset and ``bert tokenizer`` from Hugging Face for this -# tutorial. So you need to install datasets and transformers in addition to TorchMultimodal. -# -# .. note:: -# -# When running this tutorial in Google Colab, install the required packages by -# creating a new cell and running the following commands: -# -# .. code-block:: -# -# !pip install torchmultimodal-nightly -# !pip install datasets -# !pip install transformers -# - -###################################################################### -# Steps -# ----- -# -# 1. Download the Hugging Face dataset to a directory on your computer by running the following command: -# -# .. code-block:: -# -# wget http://dl.fbaipublicfiles.com/pythia/data/vocab.tar.gz -# tar xf vocab.tar.gz -# -# .. note:: -# If you are running this tutorial in Google Colab, run these commands -# in a new cell and prepend these commands with an exclamation mark (!) -# -# -# 2. For this tutorial, we treat VQA as a classification task where -# the inputs are images and question (text) and the output is an answer class. -# So we need to download the vocab file with answer classes and create the answer to -# label mapping. -# -# We also load the `textvqa -# dataset `__ containing 34602 training samples -# (images,questions and answers) from Hugging Face -# -# We see there are 3997 answer classes including a class representing -# unknown answers. -# - -with open("data/vocabs/answers_textvqa_more_than_1.txt") as f: - vocab = f.readlines() - -answer_to_idx = {} -for idx, entry in enumerate(vocab): - answer_to_idx[entry.strip("\n")] = idx -print(len(vocab)) -print(vocab[:5]) - -from datasets import load_dataset -dataset = load_dataset("textvqa") - -###################################################################### -# Lets display a sample entry from the dataset: -# - -import matplotlib.pyplot as plt -import numpy as np -idx = 5 -print("Question: ", dataset["train"][idx]["question"]) -print("Answers: " ,dataset["train"][idx]["answers"]) -im = np.asarray(dataset["train"][idx]["image"].resize((500,500))) -plt.imshow(im) -plt.show() - - -###################################################################### -# 3. Next, we write the transform function to convert the image and text into -# Tensors consumable by our model - For images, we use the transforms from -# torchvision to convert to Tensor and resize to uniform sizes - For text, -# we tokenize (and pad) them using the ``BertTokenizer`` from Hugging Face - -# For answers (i.e. labels), we take the most frequently occurring answer -# as the label to train with: -# - -import torch -from torchvision import transforms -from collections import defaultdict -from transformers import BertTokenizer -from functools import partial - -def transform(tokenizer, input): - batch = {} - image_transform = transforms.Compose([transforms.ToTensor(), transforms.Resize([224,224])]) - image = image_transform(input["image"][0].convert("RGB")) - batch["image"] = [image] - - tokenized=tokenizer(input["question"],return_tensors='pt',padding="max_length",max_length=512) - batch.update(tokenized) - - - ans_to_count = defaultdict(int) - for ans in input["answers"][0]: - ans_to_count[ans] += 1 - max_value = max(ans_to_count, key=ans_to_count.get) - ans_idx = answer_to_idx.get(max_value,0) - batch["answers"] = torch.as_tensor([ans_idx]) - return batch - -tokenizer=BertTokenizer.from_pretrained("bert-base-uncased",padding="max_length",max_length=512) -transform=partial(transform,tokenizer) -dataset.set_transform(transform) - - -###################################################################### -# 4. Finally, we import the ``flava_model_for_classification`` from -# ``torchmultimodal``. It loads the pretrained FLAVA checkpoint by default and -# includes a classification head. -# -# The model forward function passes the image through the visual encoder -# and the question through the text encoder. The image and question -# embeddings are then passed through the multimodal encoder. The final -# embedding corresponding to the CLS token is passed through a MLP head -# which finally gives the probability distribution over each possible -# answers. -# - -from torchmultimodal.models.flava.model import flava_model_for_classification -model = flava_model_for_classification(num_classes=len(vocab)) - - -###################################################################### -# 5. We put together the dataset and model in a toy training loop to -# demonstrate how to train the model for 3 iterations: -# - -from torch import nn -BATCH_SIZE = 2 -MAX_STEPS = 3 -from torch.utils.data import DataLoader - -train_dataloader = DataLoader(dataset["train"], batch_size= BATCH_SIZE) -optimizer = torch.optim.AdamW(model.parameters()) - - -epochs = 1 -for _ in range(epochs): - for idx, batch in enumerate(train_dataloader): - optimizer.zero_grad() - out = model(text = batch["input_ids"], image = batch["image"], labels = batch["answers"]) - loss = out.loss - loss.backward() - optimizer.step() - print(f"Loss at step {idx} = {loss}") - if idx >= MAX_STEPS-1: - break - - -###################################################################### -# Conclusion -# ------------------- -# -# This tutorial introduced the basics around how to finetune on a -# multimodal task using FLAVA from TorchMultimodal. Please also check out -# other examples from the library like -# `MDETR `__ -# which is a multimodal model for object detection and -# `Omnivore `__ -# which is multitask model spanning image, video and 3d classification. -# diff --git a/index.rst b/index.rst index 71cddc0bda9..8ce8b249f84 100644 --- a/index.rst +++ b/index.rst @@ -818,15 +818,6 @@ Welcome to PyTorch Tutorials :link: advanced/sharding.html :tags: TorchRec,Recommender -.. Multimodality - -.. customcarditem:: - :header: Introduction to TorchMultimodal - :card_description: TorchMultimodal is a library that provides models, primitives and examples for training multimodal tasks - :image: _static/img/thumbnails/torchrec.png - :link: beginner/flava_finetuning_tutorial.html - :tags: TorchMultimodal - .. End of tutorial card section @@ -1102,11 +1093,3 @@ Additional Resources intermediate/torchrec_intro_tutorial advanced/sharding - -.. toctree:: - :maxdepth: 2 - :includehidden: - :hidden: - :caption: Multimodality - - beginner/flava_finetuning_tutorial diff --git a/redirects.py b/redirects.py index 6e2d5f34c17..4e5dfceebfd 100644 --- a/redirects.py +++ b/redirects.py @@ -3,7 +3,8 @@ "beginner/ptcheat.html": "../index.html", "beginner/deploy_seq2seq_hybrid_frontend_tutorial.html": "../index.html", "recipes/bundled_inputs.html": "../index.html", - "intermediate/torchserve_with_ipex.html": "../index.html", + "intermediate/torchserve_with_ipex.html": "../index.html", "intermediate/torchserve_with_ipex_2.html": "../index.html", "recipes/torchserve_vertexai_tutorial.html": "../index.html", + "beginner/flava_finetuning_tutorial.html": "../index.html", } From 30d88694ccb1a1a94ca2775114420fad3b2bb44a Mon Sep 17 00:00:00 2001 From: Jerry Zhang Date: Tue, 8 Jul 2025 15:30:49 -0700 Subject: [PATCH 141/347] Update pytorch quantization tutorial links (#3432) Summary: Next step for https://github.com/pytorch/pytorch/issues/157591 Also need to update quantization docs in pytorch main doc page Test Plan: CI Reviewers: Subscribers: Tasks: Tags: --- _static/img/bert.png | Bin 247659 -> 0 bytes _static/img/compare_output.png | Bin 27076 -> 0 bytes _static/img/compare_stub.png | Bin 22859 -> 0 bytes _static/img/pt2e_quant_xpu_inductor.png | Bin 120201 -> 0 bytes _static/img/quant_asym.png | Bin 8374 -> 0 bytes _static/img/quantized_transfer_learning.png | Bin 2025208 -> 0 bytes _static/img/shadow.png | Bin 15597 -> 0 bytes ...erimental-Dynamic-Quantization-on-BERT.png | Bin 134697 -> 0 bytes ...ization-on-an-LSTM-Word-Language-Model.png | Bin 28949 -> 0 bytes _static/img/thumbnails/cropped/mobile.png | Bin 24051 -> 0 bytes ...ing-dynamic-post-training-quantization.png | Bin 268717 -> 0 bytes .../dynamic_quantization_tutorial.py | 308 --------- index.rst | 33 - .../dynamic_quantization_bert_tutorial.rst | 568 ----------------- .../quantized_transfer_learning_tutorial.rst | 516 --------------- prototype_source/fx_graph_mode_ptq_dynamic.py | 311 --------- prototype_source/fx_graph_mode_ptq_static.rst | 411 ------------ .../fx_graph_mode_quant_guide.rst | 324 ---------- prototype_source/numeric_suite_tutorial.py | 420 ------------ prototype_source/pt2e_quant_ptq.rst | 602 ------------------ .../pt2e_quant_ptq_x86_inductor.rst | 10 - prototype_source/pt2e_quant_qat.rst | 487 -------------- prototype_source/pt2e_quant_x86_inductor.rst | 313 --------- prototype_source/pt2e_quant_xpu_inductor.rst | 239 ------- prototype_source/pt2e_quantizer.rst | 381 ----------- ...ization_in_pytorch_2_0_export_tutorial.rst | 10 - recipes_source/fuse.rst | 157 ----- recipes_source/quantization.rst | 135 ---- .../recipes/dynamic_quantization.py | 294 --------- recipes_source/recipes_index.rst | 24 - redirects.py | 19 + 31 files changed, 19 insertions(+), 5543 deletions(-) delete mode 100644 _static/img/bert.png delete mode 100644 _static/img/compare_output.png delete mode 100644 _static/img/compare_stub.png delete mode 100644 _static/img/pt2e_quant_xpu_inductor.png delete mode 100644 _static/img/quant_asym.png delete mode 100644 _static/img/quantized_transfer_learning.png delete mode 100644 _static/img/shadow.png delete mode 100644 _static/img/thumbnails/cropped/experimental-Dynamic-Quantization-on-BERT.png delete mode 100644 _static/img/thumbnails/cropped/experimental-Dynamic-Quantization-on-an-LSTM-Word-Language-Model.png delete mode 100644 _static/img/thumbnails/cropped/mobile.png delete mode 100644 _static/img/thumbnails/cropped/using-dynamic-post-training-quantization.png delete mode 100644 advanced_source/dynamic_quantization_tutorial.py delete mode 100644 intermediate_source/dynamic_quantization_bert_tutorial.rst delete mode 100644 intermediate_source/quantized_transfer_learning_tutorial.rst delete mode 100644 prototype_source/fx_graph_mode_ptq_dynamic.py delete mode 100644 prototype_source/fx_graph_mode_ptq_static.rst delete mode 100644 prototype_source/fx_graph_mode_quant_guide.rst delete mode 100644 prototype_source/numeric_suite_tutorial.py delete mode 100644 prototype_source/pt2e_quant_ptq.rst delete mode 100644 prototype_source/pt2e_quant_ptq_x86_inductor.rst delete mode 100644 prototype_source/pt2e_quant_qat.rst delete mode 100644 prototype_source/pt2e_quant_x86_inductor.rst delete mode 100644 prototype_source/pt2e_quant_xpu_inductor.rst delete mode 100644 prototype_source/pt2e_quantizer.rst delete mode 100644 prototype_source/quantization_in_pytorch_2_0_export_tutorial.rst delete mode 100644 recipes_source/fuse.rst delete mode 100644 recipes_source/quantization.rst delete mode 100644 recipes_source/recipes/dynamic_quantization.py diff --git a/_static/img/bert.png b/_static/img/bert.png deleted file mode 100644 index 6e23a8acfd33c92e137c085cf7b7c963a6d9e90c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 247659 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%0DsZEH@x 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 z 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

X*X!Lw-LAHRRgo;>GZ$Fjlj{00uF2WvTqPj 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�FM1xHd&HDYjm`0^kpoRPuz59zq10 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 cY=IY98lSh`S#CY!XYlQ};@_-&HEUzbU>2LJ#7 diff --git a/_static/img/quant_asym.png b/_static/img/quant_asym.png deleted file mode 100644 index 9dc43817a595687119a5d6e3fee1a905be4c308e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8374 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 diff --git a/_static/img/quantized_transfer_learning.png b/_static/img/quantized_transfer_learning.png deleted file mode 100644 index c138cbdb0c13da1b7cd789f6938256b2e0cab9ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2025208 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

Q1iTI0h25P)Fmmpi!gGxi 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

s2COfz5{ktBRt>+U$23hdsgqwyON#dVox!M~bD^ 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

C0g4_!3=fgp#&B6>(rO%7PLM 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