Skip to content

Commit e409469

Browse files
Arm backend: Test prequantized NSS weights (pytorch#23236)
Load the published NSS int8 checkpoint and restore its legacy PT2E state. Convert it without calibration or requantization, then exercise the resulting graph through the TOSA and VGF test pipelines. Authored with assistance from Codex. Change-Id: I871bbca4ac82c866a196dc43329125122e42b989 cc @digantdesai @freddan80 @per @zingo @oscarandersson8218 @mansnils @Sebastian-Larsson @robell @rascani Signed-off-by: Michiel Olieslagers <michiel.olieslagers@arm.com>
1 parent 9352502 commit e409469

1 file changed

Lines changed: 119 additions & 0 deletions

File tree

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

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
import pytest
99
import torch
10+
from executorch.backends.arm.quantizer import (
11+
get_symmetric_quantization_config,
12+
TOSAQuantizer,
13+
)
1014
from executorch.backends.arm.scripts.neural_graphics_test_data import (
1115
_NSS_INPUT_CHANNELS,
1216
iter_nss_test_calibration_samples,
@@ -26,13 +30,24 @@
2630
TosaPipelineINT,
2731
VgfPipeline,
2832
)
33+
from executorch.backends.arm.tosa import TosaSpecification
34+
from executorch.backends.transforms.duplicate_dynamic_quant_chain import (
35+
DuplicateDynamicQuantChainPass,
36+
)
2937

3038
from huggingface_hub import hf_hub_download
3139

3240
from ng_model_gym.usecases.nss.model.model_blocks_v1 import ( # type: ignore[import-not-found,import-untyped]
3341
AutoEncoderV1,
3442
)
3543
from torch.export import Dim
44+
from torchao.quantization.pt2e import (
45+
allow_exported_model_train_eval,
46+
FixedQParamsFakeQuantize,
47+
FixedQParamsObserver,
48+
move_exported_model_to_eval,
49+
)
50+
from torchao.quantization.pt2e.quantize_pt2e import convert_pt2e, prepare_qat_pt2e
3651

3752
input_t = Tuple[torch.Tensor] # Input x
3853

@@ -71,6 +86,62 @@ def nss() -> AutoEncoderV1:
7186
return nss_model.auto_encoder
7287

7388

89+
def prequantized_nss(inputs: input_t) -> torch.fx.GraphModule:
90+
weights = hf_hub_download( # nosec B615
91+
repo_id="Arm/neural-super-sampling",
92+
filename="nss_v1_0_1_high_int8.pt",
93+
revision="main",
94+
)
95+
checkpoint = torch.load(
96+
weights, map_location=torch.device("cpu"), weights_only=True
97+
)["model_state_dict"]
98+
prefix = "autoencoder."
99+
assert all(key.startswith(prefix) for key in checkpoint)
100+
state_dict = {key.removeprefix(prefix): value for key, value in checkpoint.items()}
101+
102+
exported = torch.export.export(nss().eval(), inputs, strict=True).module()
103+
quantizer = TOSAQuantizer(TosaSpecification.create_from_string("TOSA-1.0+INT"))
104+
quantizer.set_global(
105+
get_symmetric_quantization_config(is_per_channel=True, is_qat=True)
106+
)
107+
prepared = prepare_qat_pt2e(exported, quantizer)
108+
109+
fixed_observers = {
110+
key.removesuffix(".activation_post_process.scale")
111+
for key in state_dict
112+
if key.endswith(".activation_post_process.scale")
113+
}
114+
for name in fixed_observers:
115+
scale = state_dict[f"{name}.activation_post_process.scale"].item()
116+
zero_point = state_dict[f"{name}.activation_post_process.zero_point"].item()
117+
observer = FixedQParamsObserver.with_args(
118+
scale=scale,
119+
zero_point=zero_point,
120+
dtype=torch.int8,
121+
qscheme=torch.per_tensor_affine,
122+
quant_min=-127,
123+
quant_max=127,
124+
)
125+
prepared.set_submodule(name, FixedQParamsFakeQuantize(observer=observer))
126+
127+
parameter_keys = list(dict(prepared.named_parameters()))
128+
lifted_parameter_keys = [
129+
key for key in state_dict if key.startswith("_param_constant")
130+
]
131+
assert len(parameter_keys) == len(lifted_parameter_keys)
132+
for lifted_key, parameter_key in zip(
133+
lifted_parameter_keys, parameter_keys, strict=True
134+
):
135+
state_dict[parameter_key] = state_dict.pop(lifted_key)
136+
137+
prepared.load_state_dict(state_dict, strict=True)
138+
move_exported_model_to_eval(prepared)
139+
converted = convert_pt2e(prepared)
140+
DuplicateDynamicQuantChainPass()(converted)
141+
allow_exported_model_train_eval(converted)
142+
return converted
143+
144+
74145
def example_inputs():
75146
return load_nss_verification_inputs()
76147

@@ -132,6 +203,28 @@ def test_nss_tosa_INT(use_real_data, is_qat):
132203
pipeline.run()
133204

134205

206+
@common.parametrize("use_real_data", input_test_data)
207+
def test_nss_prequantized_tosa_INT(use_real_data):
208+
inputs = example_inputs() if use_real_data else random_inputs()
209+
pipeline = TosaPipelineINT[input_t](
210+
prequantized_nss(inputs),
211+
inputs,
212+
aten_op=[],
213+
exir_op=[],
214+
use_to_edge_transform_and_lower=True,
215+
qtol=12 if use_real_data else 8,
216+
)
217+
pipeline.pop_stage("quantize")
218+
pipeline.pop_stage("check.quant_nodes")
219+
pipeline.add_stage_after(
220+
"export",
221+
pipeline.tester.check,
222+
["torch.ops.quantized_decomposed.dequantize_per_tensor.default"],
223+
suffix="prequant_nodes",
224+
)
225+
pipeline.run()
226+
227+
135228
@pytest.mark.skip(reason="No support for aten_upsample_nearest2d_vec on U55")
136229
@common.XfailIfNoCorstone300
137230
def test_nss_u55_INT():
@@ -202,3 +295,29 @@ def test_nss_vgf_INT(use_real_data, is_qat):
202295
if use_real_data:
203296
_set_nss_calibration_samples(pipeline)
204297
pipeline.run()
298+
299+
300+
@common.SkipIfNoModelConverter
301+
@common.parametrize("use_real_data", input_test_data)
302+
def test_nss_prequantized_vgf_INT(use_real_data):
303+
inputs = example_inputs() if use_real_data else random_inputs()
304+
pipeline = VgfPipeline[input_t](
305+
prequantized_nss(inputs),
306+
inputs,
307+
aten_op=[],
308+
exir_op=[],
309+
use_to_edge_transform_and_lower=True,
310+
run_on_vulkan_runtime=True,
311+
quantize=True,
312+
tosa_version="TOSA-1.0+INT",
313+
qtol=12 if use_real_data else 8,
314+
)
315+
pipeline.pop_stage("quantize")
316+
pipeline.pop_stage("check.quant_nodes")
317+
pipeline.add_stage_after(
318+
"export",
319+
pipeline.tester.check,
320+
["torch.ops.quantized_decomposed.dequantize_per_tensor.default"],
321+
suffix="prequant_nodes",
322+
)
323+
pipeline.run()

0 commit comments

Comments
 (0)