You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
# 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
514
+
# 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
515
+
# 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
516
+
# at runtime when we export this linear layer:
517
+
518
+
ep=export(
519
+
torch.nn.Linear(4, 3),
520
+
(torch.randn(1, 4),),
521
+
dynamic_shapes={
522
+
"input": (Dim.AUTO, Dim.STATIC),
523
+
},
524
+
)
525
+
ep.module()(torch.randn(2, 4))
526
+
527
+
# 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
528
+
# 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
529
+
# dimensions are automatically figured out underneath export. The dynamic shapes subsystem essentially acts as a "discovery" process, summarizing these guards
530
+
# 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
531
+
# 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
532
+
# 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.
533
+
# 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.
534
+
#
535
+
# 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``:
# We can enforce that equalities between dimensions of different tensors
616
-
# by using the same ``torch.export.Dim`` object, for example, in matrix multiplication:
617
-
618
-
inp2=torch.randn(4, 8)
619
-
inp3=torch.randn(8, 2)
620
-
621
-
classDynamicShapesExample2(torch.nn.Module):
622
-
defforward(self, x, y):
623
-
returnx @ y
624
-
625
-
inp2_dim0=Dim("inp2_dim0")
626
-
inner_dim=Dim("inner_dim")
627
-
inp3_dim1=Dim("inp3_dim1")
544
+
# 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
545
+
# dynamic behavior of the ExportedProgram produced; ConstraintViolation errors will be raised if model tracing emits guards that conflict with the relations or static/dynamic
546
+
# specifications given. For example, in the above specification, the following is asserted:
547
+
# - ``x.shape[0]`` is to have range ``[4, 256]``, and related to ``y.shape[0]`` by ``y.shape[0] == 2 * x.shape[0]``.
548
+
# - ``x.shape[1]`` is static.
549
+
# - ``y.shape[1]`` has range ``[2, 512]``, and is unrelated to any other dimension.
550
+
#
551
+
# 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
552
+
# to specify more complex constraints like integer divisibility for dynamic dimensions:
628
553
629
-
dynamic_shapes2={
630
-
"x": {0: inp2_dim0, 1: inner_dim},
631
-
"y": {0: inner_dim, 1: inp3_dim1},
554
+
dx=Dim("dx", min=4, max=512)
555
+
dynamic_shapes= {
556
+
"x": (4*dx, None) # x.shape[0] has range [16, 2048], and is divisible by 4.
# We can also describe one dimension in terms of other. There are some
645
-
# restrictions to how detailed we can specify one dimension in terms of another,
646
-
# but generally, those in the form of ``A * Dim + B`` should work.
559
+
# 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.
560
+
# That would lead to ConstraintViolation errors and export suggested fixes - see for example with this model & specification, where the model inherently requires equality between
561
+
# dimensions 0 of ``x`` and ``y``, and requires dimension 1 to be static.
# The expectation with suggested fixes is that the user can interactively copy-paste the changes into their dynamic shapes specification, and successfully export afterwards.
579
+
#
580
+
# Lastly, there's couple nice-to-knows about the options for specification:
581
+
# - ``None`` is a good option for static behavior:
582
+
# - ``dynamic_shapes=None`` (default) exports with the entire model being static.
583
+
# - specifying ``None`` at an input-level exports with all tensor dimensions static, and alternatively is also required for non-tensor inputs.
584
+
# - specfiying ``None`` at a dimension-level specializes that dimension, though this is deprecated in favor of ``Dim.STATIC``.
585
+
# - specifying per-dimension integer values also produces static behavior, and will additionally check that the provided sample input matches the specification.
586
+
#
587
+
# These options are combined in the inputs & dynamic shapes spec below:
0 commit comments