onnx_diagnostic.export¶
CoupleInputsDynamicShapes¶
- class onnx_diagnostic.export.CoupleInputsDynamicShapes(args: Tuple[Any, ...], kwargs: Dict[str, Any], dynamic_shapes: Tuple[Tuple[Any, ...], Dict[str, Any]], args_names: Module | List[str] | None = None)[source]¶
Pair inputs / dynamic shapes.
- Parameters:
args – positional arguments
kwargs – named arguments
dynamic_shapes – dynamic shapes
args_names – if both args and kwargs are not empty, then dynamic shapes must be a dictionary, and positional must be added to the named arguments. Arguments names or a module must be given in that case.
- change_dynamic_dimensions(desired_values: Dict[str, int] | None = None, args_kwargs: bool = False)[source]¶
A model exported with dynamic shapes is not necessarily dynamic just because the user specified dynamic shapes. The algorithm may discover that a dimension cannot be dynamic and then continues the export making the assumption it is static. That may lead a wrong model. This function produces a new set of inputs with different values for the dimension than the first ones, assuming they were used to export the model.
- Parameters:
desired_values – to fixed named dimension to have the desired value
args_kwargs – return both args, kwargs even if empty
- Returns:
new inputs
Example:
<<<
import torch from onnx_diagnostic.helpers import string_type from onnx_diagnostic.export.dynamic_shapes import CoupleInputsDynamicShapes T3x15 = torch.rand((3, 15)) T3x20 = torch.rand((3, 20)) T3x4 = torch.rand((3, 4)) ds_batch = {0: "batch"} ds_batch_seq = {0: "batch", 1: "seq"} kwargs = {"A": T3x4, "B": (T3x15, T3x20)} ds = {"A": ds_batch, "B": (ds_batch, ds_batch_seq)} new_kwargs = CoupleInputsDynamicShapes((), kwargs, ds).change_dynamic_dimensions() print("before:", string_type(kwargs, with_shape=True)) print("-after:", string_type(new_kwargs, with_shape=True))
>>>
before: dict(A:T1s3x4,B:(T1s3x15,T1s3x20)) -after: dict(A:T1s4x4,B:(T1s4x15,T1s4x21))
- invalid_dimensions_for_export()[source]¶
Tells if the inputs are valid based on the dynamic shapes definition. The method assumes that all custom classes can be serialized. If some patches were applied to export, they should enabled while calling this method if the inputs contains such classes.
The function checks that a dynamic dimension does not receive a value of 0 or 1. It returns the unexpected values in the same structure as the given dynamic shapes.
Example:
<<<
import torch from onnx_diagnostic.export.dynamic_shapes import CoupleInputsDynamicShapes T3x1 = torch.rand((3, 1)) T3x4 = torch.rand((3, 4)) ds_batch = {0: "batch"} ds_batch_seq = {0: "batch", 1: "seq"} kwargs = {"A": T3x4, "B": (T3x1, T3x1)} ds = {"A": ds_batch, "B": (ds_batch, ds_batch_seq)} print(CoupleInputsDynamicShapes((), kwargs, ds).invalid_dimensions_for_export())
>>>
{'B': (None, {1: 'd=[1]'})}
In case it works, it shows:
<<<
import torch from onnx_diagnostic.export.dynamic_shapes import CoupleInputsDynamicShapes T3x2 = torch.rand((3, 2)) T3x4 = torch.rand((3, 4)) ds_batch = {0: "batch"} ds_batch_seq = {0: "batch", 1: "seq"} kwargs = {"A": T3x4, "B": (T3x2, T3x2)} ds = {"A": ds_batch, "B": (ds_batch, ds_batch_seq)} print(CoupleInputsDynamicShapes((), kwargs, ds).invalid_dimensions_for_export())
>>>
None
- replace_by_string()[source]¶
Replaces dimensions by strings.
Example:
<<<
import torch from onnx_diagnostic.export.dynamic_shapes import CoupleInputsDynamicShapes Dim = torch.export.Dim T3x1 = torch.rand((3, 1)) T3x4 = torch.rand((3, 4)) ds_batch = {0: Dim("batch")} ds_batch_seq = {0: Dim("batch"), 1: Dim("seq")} kwargs = {"A": T3x4, "B": (T3x1, T3x1)} ds = {"A": ds_batch, "B": (ds_batch, ds_batch_seq)} print(CoupleInputsDynamicShapes((), kwargs, ds).replace_by_string())
>>>
{'A': {0: 'batch'}, 'B': ({0: 'batch'}, {0: 'batch', 1: 'seq'})}
- replace_string_by(value: Any = None)[source]¶
Replaces string by the value
torch.export.Dim.DYNAMIC
(default) or any other value specified by value.Example:
<<<
import torch from onnx_diagnostic.export.dynamic_shapes import CoupleInputsDynamicShapes T3x1 = torch.rand((3, 1)) T3x4 = torch.rand((3, 4)) ds_batch = {0: "batch"} ds_batch_seq = {0: "batch", 1: "seq"} kwargs = {"A": T3x4, "B": (T3x1, T3x1)} ds = {"A": ds_batch, "B": (ds_batch, ds_batch_seq)} print(CoupleInputsDynamicShapes((), kwargs, ds).replace_string_by())
>>>
{'A': {0: _DimHint(type=<_DimHintType.DYNAMIC: 3>, min=None, max=None, _factory=True)}, 'B': ({0: _DimHint(type=<_DimHintType.DYNAMIC: 3>, min=None, max=None, _factory=True)}, {0: _DimHint(type=<_DimHintType.DYNAMIC: 3>, min=None, max=None, _factory=True), 1: _DimHint(type=<_DimHintType.DYNAMIC: 3>, min=None, max=None, _factory=True)})}
ModelInputs¶
- class onnx_diagnostic.export.ModelInputs(model: Module, inputs: List[Tuple[Any, ...]] | List[Dict[str, Any]] | List[Tuple[Tuple[Any, ...], Dict[str, Any]]], level: int = 0, method_name: str = 'forward', name: str = 'main')[source]¶
Wraps a model and a couple of sets of valid inputs. Based on that information, the class is able to infer the dynamic shapes for
torch.export.export()
.- Parameters:
model – model to export
inputs – list of valid set of inputs
level – if this module is a submodule, it is the level of submodule
method_name – by default, the forward method is processed but it could be another one
name – a name, mostly for debugging purposes
Examples:
args
<<<
import pprint import torch from onnx_diagnostic.export import ModelInputs class Model(torch.nn.Module): def forward(self, x, y): return x + y model = Model() x = torch.randn((5, 6)) y = torch.randn((1, 6)) model(x, y) # to check it works inputs = [(x, y), (torch.randn((7, 8)), torch.randn((1, 8)))] mi = ModelInputs(Model(), inputs) ds = mi.guess_dynamic_shapes() pprint.pprint(ds)
>>>
(({0: _DimHint(type=<_DimHintType.DYNAMIC: 3>, min=None, max=None, _factory=True), 1: _DimHint(type=<_DimHintType.DYNAMIC: 3>, min=None, max=None, _factory=True)}, {1: _DimHint(type=<_DimHintType.DYNAMIC: 3>, min=None, max=None, _factory=True)}), {})
kwargs
<<<
import pprint import torch from onnx_diagnostic.export import ModelInputs class Model(torch.nn.Module): def forward(self, x, y): return x + y model = Model() x = torch.randn((5, 6)) y = torch.randn((1, 6)) model(x=x, y=y) # to check it works inputs = [dict(x=x, y=y), dict(x=torch.randn((7, 8)), y=torch.randn((1, 8)))] mi = ModelInputs(Model(), inputs) ds = mi.guess_dynamic_shapes() pprint.pprint(ds)
>>>
((), {'x': {0: _DimHint(type=<_DimHintType.DYNAMIC: 3>, min=None, max=None, _factory=True), 1: _DimHint(type=<_DimHintType.DYNAMIC: 3>, min=None, max=None, _factory=True)}, 'y': {1: _DimHint(type=<_DimHintType.DYNAMIC: 3>, min=None, max=None, _factory=True)}})
args and kwargs
<<<
import pprint import torch from onnx_diagnostic.export import ModelInputs class Model(torch.nn.Module): def forward(self, x, y): return x + y model = Model() x = torch.randn((5, 6)) y = torch.randn((1, 6)) model(x, y=y) # to check it works inputs = [((x,), dict(y=y)), ((torch.randn((7, 8)),), dict(y=torch.randn((1, 8))))] mi = ModelInputs(Model(), inputs) ds = mi.guess_dynamic_shapes() pprint.pprint(ds)
>>>
(({0: _DimHint(type=<_DimHintType.DYNAMIC: 3>, min=None, max=None, _factory=True), 1: _DimHint(type=<_DimHintType.DYNAMIC: 3>, min=None, max=None, _factory=True)},), {'y': {1: _DimHint(type=<_DimHintType.DYNAMIC: 3>, min=None, max=None, _factory=True)}})
torch.export.export()
does not like dynamic shapes defined both as args and kwargs. kwargs must be used.move_to_kwargs
modifies the inputs and the dynamic shapes to make the model and the given inputs exportable.<<<
import pprint import torch from onnx_diagnostic.export import ModelInputs from onnx_diagnostic.helpers import string_type class Model(torch.nn.Module): def forward(self, x, y): return x + y model = Model() x = torch.randn((5, 6)) y = torch.randn((1, 6)) model(x, y=y) # to check it works inputs = [((x,), dict(y=y)), ((torch.randn((7, 8)),), dict(y=torch.randn((1, 8))))] mi = ModelInputs(Model(), inputs) ds = mi.guess_dynamic_shapes() a, kw, nds = mi.move_to_kwargs(*mi.inputs[0], ds) print("moved args:", string_type(a, with_shape=True)) print("moved kwargs:", string_type(kw, with_shape=True)) print("dynamic shapes:") pprint.pprint(nds)
>>>
moved args: (T1s5x6,) moved kwargs: dict(y:T1s1x6) dynamic shapes: ((), {'x': {0: _DimHint(type=<_DimHintType.DYNAMIC: 3>, min=None, max=None, _factory=True), 1: _DimHint(type=<_DimHintType.DYNAMIC: 3>, min=None, max=None, _factory=True)}, 'y': {1: _DimHint(type=<_DimHintType.DYNAMIC: 3>, min=None, max=None, _factory=True)}})
- guess_dynamic_dimensions(*tensors, auto: bool = False) Dict[int, Any] | None [source]¶
Infers the dynamic dimension from multiple shapes. If auto is True, it returns
torch.export.Dim.AUTO
for every dimension which cannot be guessed. Two tensors with the same value for one dimension can be guessed, but if there is only 1, it cannot.
- guess_dynamic_shape_object(*objs: Any, auto: bool = False, msg: Callable | None = None) Any [source]¶
Guesses the dynamic shapes for one argument.
- guess_dynamic_shapes(auto: bool = False) Tuple[Tuple[Any, ...], Dict[str, Any]] [source]¶
Guesses the dynamic shapes for that module from two execution. If there is only one execution, then that would be static dimensions.
- Parameters:
auto – if auto is True, use
torch.export.Dim.AUTO
for any dimension if the number of inputs is one
- property module_name_type¶
Returns name and module type.
- move_to_kwargs(args: Tuple[Any, ...], kwargs: Dict[str, Any], dynamic_shapes: Tuple[Tuple[Any, ...], Dict[str, Any]]) Tuple[Tuple[Any, ...], Dict[str, Any], Tuple[Tuple[Any, ...], Dict[str, Any]]] [source]¶
Uses the signatures to move positional arguments (args) to named arguments (kwargs) with the corresponding dynamic shapes. kwargs, dynamic_shapes are modified inplace.
- process_inputs(inputs: List[Tuple[Any, ...]] | List[Dict[str, Any]] | List[Tuple[Tuple[Any, ...], Dict[str, Any]]]) List[Tuple[Tuple[Any, ...], Dict[str, Any]]] [source]¶
Transforms a list of valid inputs, list of args, list of kwargs or list of both into a list of (args, kwargs).
- validate_inputs_for_export(dynamic_shapes: Tuple[Tuple[Any, ...], Dict[str, Any]] | None = None) List[List[int | str]] [source]¶
Validates the inputs the class contains for the given dynamic shapes. If not specified, the dynamic_shapes are guessed.
- Parameters:
dynamic_shapes – dynamic shapes to validate
- Returns:
a list of lists, every list contains the path the invalid dimension
validate_ep¶
- onnx_diagnostic.export.validate_ep(ep: Module | ExportedProgram, mod: Module | None = None, args: Tuple[Any, ...] | None = None, kwargs: Dict[str, Any] | None = None, copy: bool = False, dynamic_shapes: Any | None = None, values_to_try: Dict[str, List[int]] | None = None, exc: bool = True, verbose: int = 0, atol: float = 0.01, rtol: float = 0.1) List[Dict[str, Any]] [source]¶
Validates an exported program.
- Parameters:
model – first module
mod – second module (it produces the expected values)
args – positional arguments
kwargs – named arguments
copy – copy the inputs before executing the model (they may modify them inplace)
dynamic_shapes – dynamic shapes, string should be used not
torch.export.Dim
values_to_try – dictionary with the values to try for every dynamic dimension
exc – raise exception if discrepancies are too high
verbose – verbosity level
atol – absolute tolerance
rtol – relative tolerance
- Returns:
dictionary with inputs, outputs and tolerance