Skip to content

Commit c5f2b7a

Browse files
Arm backend: Dynamic shape lowering for NSS & NFRU (pytorch#23307)
Change-Id: I06477ff9d1641d4ce0dbd6fbb62fd86258866c30 cc @digantdesai @freddan80 @per @zingo @oscarandersson8218 @mansnils @Sebastian-Larsson @robell @rascani Signed-off-by: Michiel Olieslagers <michiel.olieslagers@arm.com>
1 parent a702383 commit c5f2b7a

2 files changed

Lines changed: 336 additions & 8 deletions

File tree

‎backends/arm/test/models/test_nfru.py‎

Lines changed: 191 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
from ng_model_gym.usecases.nfru.model.nfru_v1_nn import ( # type: ignore[import-not-found,import-untyped]
3939
NFRUAutoEncoder,
4040
)
41+
from torch.export import Dim
4142
from torchao.quantization.pt2e import (
4243
allow_exported_model_train_eval,
4344
move_exported_model_to_eval,
@@ -47,6 +48,9 @@
4748
input_t = Tuple[torch.Tensor] # Input x
4849

4950
pytestmark = skip_if_frozen_release("NFRU")
51+
_NFRU_HEIGHT = 2 * Dim("_nfru_height", min=16, max=135)
52+
_NFRU_WIDTH = 2 * Dim("_nfru_width", min=16, max=240)
53+
_NFRU_DYNAMIC_SHAPES = ({2: _NFRU_HEIGHT, 3: _NFRU_WIDTH},)
5054

5155

5256
def nfru() -> NFRUAutoEncoder:
@@ -73,7 +77,7 @@ def nfru() -> NFRUAutoEncoder:
7377
return model
7478

7579

76-
def prequantized_nfru(inputs: input_t) -> torch.fx.GraphModule:
80+
def prequantized_nfru(inputs: input_t, dynamic_shapes=None) -> torch.fx.GraphModule:
7781
weights = hf_hub_download( # nosec B615
7882
repo_id="Arm/neural-frame-rate-upscaling",
7983
filename="nfru_v1_int8.pt",
@@ -88,7 +92,9 @@ def prequantized_nfru(inputs: input_t) -> torch.fx.GraphModule:
8892
assert all(key.startswith(prefix) for key in checkpoint)
8993
state_dict = {key.removeprefix(prefix): value for key, value in checkpoint.items()}
9094

91-
exported = torch.export.export(nfru().eval(), inputs, strict=True).module()
95+
exported = torch.export.export(
96+
nfru().eval(), inputs, dynamic_shapes=dynamic_shapes, strict=True
97+
).module()
9298
quantizer = TOSAQuantizer(TosaSpecification.create_from_string("TOSA-1.0+INT"))
9399
quantizer.set_global(
94100
get_symmetric_quantization_config(is_per_channel=False, is_qat=True)
@@ -141,7 +147,10 @@ def random_inputs(memory_format: torch.memory_format = torch.channels_last):
141147

142148

143149
def _set_nfru_calibration_samples(pipeline):
144-
return pipeline.set_quantization_calibration(iter_nfru_test_calibration_samples())
150+
return pipeline.set_quantization_calibration(
151+
iter_nfru_test_calibration_samples(),
152+
dynamic_shapes=_NFRU_DYNAMIC_SHAPES,
153+
)
145154

146155

147156
@common.parametrize("use_real_data", input_test_data)
@@ -306,3 +315,182 @@ def test_nfru_vgf_quant_a16w8(use_real_data):
306315
if use_real_data:
307316
_set_nfru_calibration_samples(pipeline)
308317
pipeline.run()
318+
319+
320+
@common.parametrize("use_real_data", input_test_data)
321+
def test_nfru_tosa_FP_dynamic_shapes(use_real_data):
322+
pipeline = TosaPipelineFP[input_t](
323+
nfru().eval(),
324+
example_inputs() if use_real_data else random_inputs(),
325+
aten_op=[],
326+
exir_op=[],
327+
dynamic_shapes=_NFRU_DYNAMIC_SHAPES,
328+
run_on_tosa_ref_model=False,
329+
)
330+
pipeline.run()
331+
332+
333+
@common.parametrize("is_qat", is_qat_test_data)
334+
@common.parametrize("use_real_data", input_test_data)
335+
def test_nfru_tosa_INT_dynamic_shapes(use_real_data, is_qat):
336+
pipeline_kwargs = (
337+
{
338+
"per_channel_quantization": False,
339+
"use_to_edge_transform_and_lower": True,
340+
"frobenius_threshold": None,
341+
"cosine_threshold": None,
342+
}
343+
if is_qat
344+
else {}
345+
)
346+
pipeline = TosaPipelineINT[input_t](
347+
nfru().eval(),
348+
example_inputs() if use_real_data else random_inputs(),
349+
aten_op=[],
350+
exir_op=[],
351+
atol=0.2,
352+
qtol=2 if use_real_data else 1,
353+
is_qat=is_qat,
354+
dynamic_shapes=_NFRU_DYNAMIC_SHAPES,
355+
run_on_tosa_ref_model=False,
356+
**pipeline_kwargs,
357+
)
358+
if use_real_data:
359+
_set_nfru_calibration_samples(pipeline)
360+
pipeline.run()
361+
362+
363+
@common.parametrize("use_real_data", input_test_data)
364+
def test_nfru_prequantized_tosa_INT_dynamic_shapes(use_real_data):
365+
inputs = example_inputs() if use_real_data else random_inputs()
366+
pipeline = TosaPipelineINT[input_t](
367+
prequantized_nfru(inputs, _NFRU_DYNAMIC_SHAPES),
368+
inputs,
369+
aten_op=[],
370+
exir_op=[],
371+
atol=0.2,
372+
qtol=2 if use_real_data else 1,
373+
dynamic_shapes=_NFRU_DYNAMIC_SHAPES,
374+
run_on_tosa_ref_model=False,
375+
)
376+
pipeline.pop_stage("quantize")
377+
pipeline.pop_stage("check.quant_nodes")
378+
pipeline.add_stage_after(
379+
"export",
380+
pipeline.tester.check,
381+
["torch.ops.quantized_decomposed.dequantize_per_tensor.default"],
382+
suffix="prequant_nodes",
383+
)
384+
pipeline.run()
385+
386+
387+
@common.parametrize("use_real_data", input_test_data)
388+
def test_nfru_tosa_INT_a16w8_dynamic_shapes(use_real_data):
389+
pipeline = TosaPipelineINT[input_t](
390+
nfru().eval(),
391+
example_inputs() if use_real_data else random_inputs(),
392+
aten_op=[],
393+
exir_op=[],
394+
tosa_extensions=["int16"],
395+
atol=0.1,
396+
dynamic_shapes=_NFRU_DYNAMIC_SHAPES,
397+
run_on_tosa_ref_model=False,
398+
)
399+
if use_real_data:
400+
_set_nfru_calibration_samples(pipeline)
401+
pipeline.run()
402+
403+
404+
@common.SkipIfNoModelConverter
405+
@common.parametrize("use_real_data", input_test_data)
406+
def test_nfru_vgf_no_quant_dynamic_shapes(use_real_data):
407+
pipeline = VgfPipeline[input_t](
408+
nfru().eval(),
409+
example_inputs() if use_real_data else random_inputs(),
410+
aten_op=[],
411+
exir_op=[],
412+
tosa_version="TOSA-1.0+FP",
413+
quantize=False,
414+
dynamic_shapes=_NFRU_DYNAMIC_SHAPES,
415+
run_on_vulkan_runtime=False,
416+
)
417+
pipeline.run()
418+
419+
420+
@common.SkipIfNoModelConverter
421+
@common.parametrize("is_qat", is_qat_test_data)
422+
@common.parametrize("use_real_data", input_test_data)
423+
def test_nfru_vgf_quant_dynamic_shapes(use_real_data, is_qat):
424+
pipeline_kwargs = (
425+
{
426+
"per_channel_quantization": False,
427+
"use_to_edge_transform_and_lower": True,
428+
}
429+
if is_qat
430+
else {}
431+
)
432+
pipeline = VgfPipeline[input_t](
433+
nfru().eval(),
434+
example_inputs() if use_real_data else random_inputs(),
435+
aten_op=[],
436+
exir_op=[],
437+
tosa_version="TOSA-1.0+INT",
438+
symmetric_io_quantization=True,
439+
quantize=True,
440+
is_qat=is_qat,
441+
atol=0.2,
442+
qtol=2 if use_real_data else 1,
443+
dynamic_shapes=_NFRU_DYNAMIC_SHAPES,
444+
run_on_vulkan_runtime=False,
445+
**pipeline_kwargs,
446+
)
447+
if use_real_data:
448+
_set_nfru_calibration_samples(pipeline)
449+
pipeline.run()
450+
451+
452+
@common.SkipIfNoModelConverter
453+
@common.parametrize("use_real_data", input_test_data)
454+
def test_nfru_prequantized_vgf_INT_dynamic_shapes(use_real_data):
455+
inputs = example_inputs() if use_real_data else random_inputs()
456+
pipeline = VgfPipeline[input_t](
457+
prequantized_nfru(inputs, _NFRU_DYNAMIC_SHAPES),
458+
inputs,
459+
aten_op=[],
460+
exir_op=[],
461+
tosa_version="TOSA-1.0+INT",
462+
quantize=True,
463+
atol=0.2,
464+
qtol=2 if use_real_data else 1,
465+
dynamic_shapes=_NFRU_DYNAMIC_SHAPES,
466+
run_on_vulkan_runtime=False,
467+
)
468+
pipeline.pop_stage("quantize")
469+
pipeline.pop_stage("check.quant_nodes")
470+
pipeline.add_stage_after(
471+
"export",
472+
pipeline.tester.check,
473+
["torch.ops.quantized_decomposed.dequantize_per_tensor.default"],
474+
suffix="prequant_nodes",
475+
)
476+
pipeline.run()
477+
478+
479+
@common.SkipIfNoModelConverter
480+
@common.parametrize("use_real_data", input_test_data)
481+
def test_nfru_vgf_quant_a16w8_dynamic_shapes(use_real_data):
482+
pipeline = VgfPipeline[input_t](
483+
nfru().eval(),
484+
example_inputs() if use_real_data else random_inputs(),
485+
aten_op=[],
486+
exir_op=[],
487+
tosa_version="TOSA-1.0+INT",
488+
tosa_extensions=["int16"],
489+
symmetric_io_quantization=True,
490+
atol=0.2,
491+
dynamic_shapes=_NFRU_DYNAMIC_SHAPES,
492+
run_on_vulkan_runtime=False,
493+
)
494+
if use_real_data:
495+
_set_nfru_calibration_samples(pipeline)
496+
pipeline.run()

0 commit comments

Comments
 (0)