Skip to content

Commit ce9a9ed

Browse files
committed
NXP backend: Add support for aten.avg_pool1d.default.
1 parent 2a1e1ae commit ce9a9ed

6 files changed

Lines changed: 105 additions & 12 deletions

File tree

backends/nxp/edge_passes/move_auxiliary_operator_into_separate_qdq_cluster_pass.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,18 @@
1313

1414
# Operator aliases for better readability.
1515
AddMM = exir_ops.edge.aten.addmm.default
16-
ViewCopy = exir_ops.edge.aten.view_copy.default
17-
MM = exir_ops.edge.aten.mm.default
16+
AvgPool2D = exir_ops.edge.aten.avg_pool2d.default
1817
Conv = exir_ops.edge.aten.convolution.default
18+
Clone = exir_ops.edge.aten.clone.default
19+
CloneDimOrder = exir_ops.edge.dim_order_ops._clone_dim_order.default
1920
HardTanh = exir_ops.edge.aten.hardtanh.default
21+
MM = exir_ops.edge.aten.mm.default
2022
Relu = exir_ops.edge.aten.relu.default
2123
Sigmoid = exir_ops.edge.aten.sigmoid.default
24+
SqueezeCopy = exir_ops.edge.aten.squeeze_copy.dims
2225
Tanh = exir_ops.edge.aten.tanh.default
23-
Clone = exir_ops.edge.aten.clone.default
24-
CloneDimOrder = exir_ops.edge.dim_order_ops._clone_dim_order.default
26+
UnsqueezeCopy = exir_ops.edge.aten.unsqueeze_copy.default
27+
ViewCopy = exir_ops.edge.aten.view_copy.default
2528

2629

2730
def insert_qdq_pair_after_node(
@@ -106,7 +109,11 @@ class MoveLeadingAuxiliaryOperatorIntoSeparateQDQClusterPass(NeutronEdgePass):
106109
],
107110
ViewCopy: [Clone, CloneDimOrder],
108111
Conv: [
109-
ViewCopy, # For 1D conv.
112+
ViewCopy, # For 1D conv
113+
],
114+
AvgPool2D: [
115+
ViewCopy, # For 1D AvgPool
116+
UnsqueezeCopy,
110117
],
111118
}
112119

@@ -206,6 +213,10 @@ class MoveTrailingAuxiliaryOperatorIntoSeparateQDQClusterPass(NeutronEdgePass):
206213
ViewCopy, # For 1D conv.
207214
],
208215
ViewCopy: [Clone, CloneDimOrder],
216+
AvgPool2D: [
217+
ViewCopy,
218+
SqueezeCopy,
219+
],
209220
}
210221

211222
def run(self, graph_module: torch.fx.GraphModule) -> PassResult:

backends/nxp/neutron_partitioner.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,15 @@ class QDQCluster:
7575

7676
AUXILIARY_OPS = [
7777
operator.getitem,
78-
exir_ops.edge.aten.view_copy.default,
79-
exir_ops.edge.aten.permute_copy.default,
78+
exir_ops.edge.aten.clone.default,
8079
exir_ops.edge.aten.hardtanh.default,
80+
exir_ops.edge.aten.permute_copy.default,
8181
exir_ops.edge.aten.relu.default,
8282
exir_ops.edge.aten.sigmoid.default,
83+
exir_ops.edge.aten.squeeze_copy.dims,
8384
exir_ops.edge.aten.tanh.default,
84-
exir_ops.edge.aten.clone.default,
85+
exir_ops.edge.aten.unsqueeze_copy.default,
86+
exir_ops.edge.aten.view_copy.default,
8587
exir_ops.edge.dim_order_ops._clone_dim_order.default,
8688
]
8789

backends/nxp/quantizer/neutron_quantizer.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
AdaptiveAvgPoolPattern,
1818
AddmmPattern,
1919
AddTensorPattern,
20-
AvgPoolPattern,
20+
AvgPool1DPattern,
21+
AvgPool2DPattern,
2122
BatchNormPattern,
2223
CatPattern,
2324
Conv1dPattern,
@@ -252,7 +253,8 @@ def __init__(self, neutron_target_spec: NeutronTargetSpec, is_qat: bool = False)
252253
OpQuantizer(AdaptiveAvgPoolPattern(is_qat=is_qat), static_qconfig),
253254
OpQuantizer(AddTensorPattern(is_qat=is_qat), static_qconfig),
254255
OpQuantizer(AddmmPattern(self, is_qat=is_qat), static_fc_qconfig),
255-
OpQuantizer(AvgPoolPattern(is_qat=is_qat), static_qconfig),
256+
OpQuantizer(AvgPool1DPattern(is_qat=is_qat), static_qconfig),
257+
OpQuantizer(AvgPool2DPattern(is_qat=is_qat), static_qconfig),
256258
OpQuantizer(BatchNormPattern(is_qat=is_qat), static_qconfig),
257259
OpQuantizer(CatPattern(is_qat=is_qat), static_qconfig),
258260
OpQuantizer(Conv1dPattern(is_qat=is_qat), static_qconfig),

backends/nxp/quantizer/patterns.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,16 @@ def get_anchors(
324324
)
325325

326326

327-
class AvgPoolPattern(SharedSpecPattern):
327+
class AvgPool1DPattern(SharedSpecPattern):
328+
"""
329+
Quantizer for AvgPool1D operator.
330+
"""
331+
332+
def partition_types(self):
333+
return [torch.ops.aten.avg_pool1d.default]
334+
335+
336+
class AvgPool2DPattern(SharedSpecPattern):
328337
"""
329338
Quantizer for AvgPool2D operator.
330339
"""

backends/nxp/tests/ir/converter/node_converter/test_avg_pool2d_converter.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from executorch.backends.nxp.backend.edge_program_converter import (
1010
EdgeProgramToIRConverter,
1111
)
12-
1312
from executorch.backends.nxp.backend.ir.conversion_config import ConversionConfig
1413
from executorch.backends.nxp.backend.ir.converter.builder.model_builder import (
1514
ModelBuilder,
@@ -23,12 +22,25 @@
2322
)
2423
from executorch.backends.nxp.tests.executors import (
2524
convert_run_compare,
25+
graph_contains_any_of_ops,
26+
ToChannelFirstPreprocess,
27+
ToChannelLastPreprocess,
2628
ToNCHWPreprocess,
2729
ToNHWCPreprocess,
2830
)
2931
from executorch.backends.nxp.tests.models import AvgPool2dConvModule, AvgPool2dModule
3032
from torch.export import ExportedProgram
3133
from executorch.backends.nxp.tests.use_qat import * # noqa F403
34+
from executorch.exir.dialects._ops import ops as exir_ops
35+
36+
# noinspection PyProtectedMember
37+
AvgPool2D = exir_ops.edge.aten.avg_pool2d.default
38+
ExecutorchDelegateCall = torch._higher_order_ops.executorch_call_delegate
39+
Squeeze = exir_ops.edge.aten.squeeze.default
40+
SqueezeDim = exir_ops.edge.aten.squeeze.dim
41+
SqueezeDims = exir_ops.edge.aten.squeeze.dims
42+
Unsqueeze = exir_ops.edge.aten.unsqueeze.default
43+
ViewCopy = exir_ops.edge.aten.view_copy.default
3244

3345

3446
@pytest.fixture(autouse=True)
@@ -219,3 +231,59 @@ def test_avg_pool_2d_quant_conversion__padded(mocker, use_qat):
219231
assert (
220232
pad_value == ops[1].tmp_inputs[0].quantization.zero_point[0]
221233
) # `AvgPool` input zp.
234+
235+
236+
class AvgPool1DModule(torch.nn.Module):
237+
def __init__(self):
238+
super().__init__()
239+
240+
self.avg_pool = torch.nn.AvgPool1d(
241+
kernel_size=3,
242+
)
243+
244+
def forward(self, x):
245+
return self.avg_pool(x)
246+
247+
248+
def test_from_avg_pool_1d(mocker):
249+
model = AvgPool1DModule()
250+
input_shape = (
251+
1,
252+
3,
253+
12,
254+
) # Don't use multiples of `num_macs` so the `view_copy` nodes will NOT be deleagted.
255+
extended_shape = (1, 3, 1, 12)
256+
257+
converter_spy = mocker.spy(EdgeProgramToIRConverter, "convert_program")
258+
delegated_ep = to_quantized_edge_program(
259+
model, input_shape, use_neutron_for_format_conversion=False
260+
).exported_program()
261+
262+
# Make sure the `avg_pool` was delegated.
263+
assert graph_contains_any_of_ops(delegated_ep.graph, [ExecutorchDelegateCall])
264+
assert not graph_contains_any_of_ops(delegated_ep.graph, [AvgPool2D])
265+
266+
# Make sure both `view_copy` nodes were added, and there is no `squeeze` or `unsqueeze`.
267+
assert len([n for n in delegated_ep.graph.nodes if n.target == ViewCopy]) == 2
268+
assert not graph_contains_any_of_ops(
269+
delegated_ep.graph, [Unsqueeze, Squeeze, SqueezeDim, SqueezeDims]
270+
)
271+
272+
# Verify correct behavior of the converted NeutronIR model.
273+
intermediate_ep = converter_spy.call_args.args[1]
274+
neutron_ir_model, _ = converter_spy.spy_return
275+
276+
input_data = (
277+
np.random.random(extended_shape).astype(np.float32) * 256.0 - 128.0
278+
).astype(np.int8)
279+
280+
# Make sure the tested program contains the `avg_pool`.
281+
assert graph_contains_any_of_ops(intermediate_ep.graph, [AvgPool2D])
282+
283+
convert_run_compare(
284+
intermediate_ep,
285+
tfl_model=neutron_ir_model,
286+
input_data=input_data,
287+
tflite_input_preprocess=ToChannelLastPreprocess(),
288+
tflite_output_preprocess=ToChannelFirstPreprocess(),
289+
)

docs/source/backends/nxp/op-support.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ aten.abs.default,int8,static int8,
33
aten._adaptive_avg_pool2d.default,int8,static int8,"ceil_mode=False, count_include_pad=False, divisor_override=False"
44
aten.addmm.default,int8,static int8,2D tensor only
55
aten.add.Tensor,int8,static int8,"alpha = 1, input tensor of rame rank"
6+
aten.avg_pool1d.default,int8,static int8,"ceil_mode=False, count_include_pad=False, divisor_override=False"
67
aten.avg_pool2d.default,int8,static int8,"ceil_mode=False, count_include_pad=False, divisor_override=False"
78
aten.cat.default,int8,static int8,"input_channels % 8 = 0, output_channels %8 = 0"
89
aten.clone.default,int8,static int8,

0 commit comments

Comments
 (0)