5858Stride = Padding = Dilation = OutPadding = list [int ]
5959Transposed = bool
6060Groups = int
61- ConvolutionArgs = tuple [
62- Node , Node , Node | None , Stride , Padding , Dilation , Transposed , OutPadding , Groups
63- ]
6461
6562
6663@requires_channels_first_format
6764class ConvolutionConverter (NodeConverter ):
6865 @staticmethod
69- def _is_supported_on_target_regular_conv (
70- node : Node ,
71- parameters_mapping : dict [str , Parameter ],
66+ def _is_conv_quant_supported (
67+ node : Node , parameters_mapping : dict [str , Parameter ]
7268 ) -> bool :
73- (
74- inp_node ,
75- w_node ,
76- b_node ,
77- stride ,
78- _ ,
79- dilation ,
80- _ ,
81- _ ,
82- _ ,
83- ) = ConvolutionConverter ._get_convolution_arguments (node )
69+ conv_params = ConvolutionConverter ._get_conv_params (node )
8470
8571 # Input must be INT8/UINT8
8672 # Output must be INT8/UINT8
@@ -98,47 +84,76 @@ def _is_supported_on_target_regular_conv(
9884 return False
9985
10086 # Bias must be INT32
101- if b_node is not None :
87+ if conv_params . bias_node is not None :
10288 b_supported_types = [torch .int32 ]
10389 if not NodeConverter .uses_quantization_type_for_io (
10490 node , b_supported_types , [2 ], []
10591 ):
10692 return False
10793
10894 # Weights must be constant
109- if not node_is_effectively_static_tensor (w_node , parameters_mapping ):
95+ if not node_is_effectively_static_tensor (
96+ conv_params .weight_node , parameters_mapping
97+ ):
11098 return False
11199
112100 # Bias must be constant (if present)
113- if b_node is not None and not node_is_effectively_static_tensor (
114- b_node , parameters_mapping
101+ if conv_params . bias_node is not None and not node_is_effectively_static_tensor (
102+ conv_params . bias_node , parameters_mapping
115103 ):
116104 return False
117105
106+ return True
107+
108+ @staticmethod
109+ def _is_supported_on_target_regular_conv (
110+ node : Node ,
111+ neutron_target_spec : NeutronTargetSpec ,
112+ parameters_mapping : dict [str , Parameter ],
113+ ) -> bool :
114+ # Check the quantization of inputs is supported
115+ if not ConvolutionConverter ._is_conv_quant_supported (node , parameters_mapping ):
116+ return False
117+
118+ op_args = ConvolutionConverter ._get_conv_params (node )
119+ node_params = get_node_tensor_params (node )
120+
118121 # kernelH <= 4096, kernelW <= 4096
119122 # strideH <= 4096, strideW <= 4096
120123 # dilationH <= 4096, dilationW <= 4096
121- w_node_shape = w_node .meta ["val" ].shape
122-
123- kernel_h = w_node_shape [2 ]
124- kernel_w = w_node_shape [3 ]
125- stride_h = stride [0 ]
126- stride_w = stride [1 ]
127- dilation_h = dilation [0 ]
128- dilation_w = dilation [1 ]
124+ kernel_h = node_params ["kernel_height" ]
125+ kernel_w = node_params ["kernel_width" ]
126+ stride_h = op_args .stride [0 ]
127+ stride_w = op_args .stride [1 ]
128+ dilation_h = op_args .dilation [0 ]
129+ dilation_w = op_args .dilation [1 ]
129130
130131 dim_sizes = [kernel_h , kernel_w , stride_h , stride_w , dilation_h , dilation_w ]
131132
132133 if any (dim > 4096 for dim in dim_sizes ):
133134 return False
134135
135- # kernelH * kernelW * inpC <= 65535
136- inp_node_shape = inp_node .meta ["val" ].shape
137- inp_channels = (
138- inp_node_shape [1 ] if len (inp_node_shape ) == 4 else inp_node_shape [0 ]
139- )
136+ # The following checks are mentioned in Neutron docs, however they cause some models
137+ # to be non-delegable. Discussion with Neutron team is pending, and because the convolutions seem
138+ # to work even without this constraint, this code is commented out for now
139+ # to boost the models' performance.
140+ # padT < kernelH, padB < kernelH, padL < kernelW, padR < kernelW
141+
142+ # padding_h = op_args.padding[0]
143+ # padding_w = op_args.padding[1]
144+ # if padding_h >= kernel_h or padding_w >= kernel_w:
145+ # return False
140146
141- if kernel_h * kernel_w * inp_channels > 65535 :
147+ # kernelH * kernelW * ROUND_CEIL(inpC, NUM_MACS) <= 65535
148+ inp_channels = node_params ["inp_channels" ]
149+ num_macs = neutron_target_spec .get_num_macs ()
150+
151+ if (
152+ kernel_h
153+ * kernel_w
154+ * ConvolutionConverter ._round_ceil (inp_channels , num_macs )
155+ > 65535
156+ ):
142157 return False
143158
144159 return True
@@ -149,48 +164,51 @@ def _is_supported_on_target_transp_conv(
149164 neutron_target_spec : NeutronTargetSpec ,
150165 parameters_mapping : dict [str , Parameter ],
151166 ) -> bool :
152- # TODO: EIEX-894 update the requirements of delegation for new Neutron flow
153- _ , w_node , _ , stride , padding , dilation , transposed , _ , groups = (
154- ConvolutionConverter ._get_convolution_arguments (node )
155- )
156-
157- num_macs = neutron_target_spec .get_num_macs ()
158- node_t_params = get_node_tensor_params (node )
159-
160- if node_t_params ["batch_size" ] != 1 :
161- # Only TransposeConv2d with batch size = 1 is supported on neutron.
167+ # Check the quantization of inputs is supported
168+ if not ConvolutionConverter ._is_conv_quant_supported (node , parameters_mapping ):
162169 return False
163170
171+ op_args = ConvolutionConverter ._get_conv_params (node )
172+ node_params = get_node_tensor_params (node )
173+
164174 # TransposeConv2d with groups > 1 is not supported
165175 # TODO: split into multiple convs with groups = 1
166- if groups > 1 :
176+ if op_args . groups > 1 :
167177 return False
168- if not node_is_effectively_static_tensor (w_node , parameters_mapping ):
169- # Only supported if the weights are static, because TFLite `TransposeConv` uses permuted
170- # weights. In case the weights are dynamic, a Transpose operator would have to be added, which
171- # is not supported on Neutron.
178+
179+ # kernelH <= 4096, kernelW <= 4096
180+ kernel_h = node_params ["kernel_height" ]
181+ kernel_w = node_params ["kernel_width" ]
182+
183+ dim_sizes = [kernel_h , kernel_w ]
184+ if any (dim > 4096 for dim in dim_sizes ):
172185 return False
173- # neutron-library/src/utils/NeutronLibraryInterrogation.cpp#876 TransposeConv2DKernelKind
186+
187+ # strideH <= kernelH, strideW <= kernelW
188+ stride_h = op_args .stride [0 ]
189+ stride_w = op_args .stride [1 ]
190+ if stride_h > kernel_h or stride_w > kernel_w :
191+ return False
192+
193+ # strideH <= 2, strideW <= 2
194+ if stride_h > 2 or stride_w > 2 :
195+ return False
196+
197+ # padT < kernelH, padB < kernelH, padL < kernelW, padR < kernelW
198+ padding_h = op_args .padding [0 ]
199+ padding_w = op_args .padding [1 ]
200+ if padding_h >= kernel_h or padding_w >= kernel_w :
201+ return False
202+
203+ # kernelH * kernelW * ceil(inpC, NUM_MACS) <= 65535
204+ inp_channels = node_params ["inp_channels" ]
205+ num_macs = neutron_target_spec .get_num_macs ()
206+
174207 if (
175- dilation != [1 , 1 ]
176- or padding [0 ] != 0
177- or padding [1 ] >= node_t_params ["kernel_width" ]
178- or (
179- padding [1 ] != 0 and node_t_params ["inp_height" ] != 1
180- ) # Slice added by explicit padding
181- or stride [0 ] != 1
182- or (
183- (
184- stride [1 ] != node_t_params ["kernel_width" ] / 2
185- or node_t_params ["out_height" ] != 1
186- )
187- and stride [1 ] != node_t_params ["kernel_width" ]
188- )
189- or stride [1 ] % 2 != 0
190- or node_t_params ["inp_channels" ] % num_macs != 0
191- or node_t_params ["out_channels" ] % num_macs != 0
192- or node_t_params ["kernel_width" ] % 2 != 0
193- or node_t_params ["kernel_height" ] != 1
208+ kernel_h
209+ * kernel_w
210+ * ConvolutionConverter ._round_ceil (inp_channels , num_macs )
211+ > 65535
194212 ):
195213 return False
196214
@@ -203,16 +221,16 @@ def _is_supported_on_target(
203221 parameters_mapping : dict [str , Parameter ],
204222 custom_delegation_options : CustomDelegationOptions ,
205223 ) -> bool :
206- is_transposed = ( ConvolutionConverter ._get_convolution_arguments (node ))[ 6 ]
224+ conv_params = ConvolutionConverter ._get_conv_params (node )
207225
208- if is_transposed :
226+ if conv_params . transposed :
209227 return ConvolutionConverter ._is_supported_on_target_transp_conv (
210228 node , neutron_target_spec , parameters_mapping
211229 )
212230
213231 else :
214232 return ConvolutionConverter ._is_supported_on_target_regular_conv (
215- node , parameters_mapping
233+ node , neutron_target_spec , parameters_mapping
216234 )
217235
218236 @staticmethod
@@ -244,6 +262,10 @@ def _is_supported_in_IR(
244262
245263 return True
246264
265+ @staticmethod
266+ def _round_ceil (x , n ):
267+ return ((x + n - 1 ) // n ) * n
268+
247269 def _compute_slicing_params (
248270 self , output_shape , explicit_padding
249271 ) -> tuple [list [int ], list [int ]]:
@@ -259,22 +281,20 @@ def _compute_slicing_params(
259281 return begins , sizes
260282
261283 @staticmethod
262- def _get_convolution_arguments (
284+ def _get_conv_params (
263285 conv_node : Node ,
264- ) -> ConvolutionArgs :
286+ ) -> ConvParameters :
265287 x , w , b , stride , padding , dilation , transposed , out_padding , groups = (
266288 conv_node .args
267289 )
268- return (
269- x ,
270- w ,
271- b ,
272- list (stride ),
273- list (padding ),
274- list (dilation ),
275- transposed ,
276- list (out_padding ),
277- groups ,
290+
291+ stride = list (stride )
292+ padding = list (padding )
293+ dilation = list (dilation )
294+ out_padding = None if out_padding is None else list (out_padding )
295+
296+ return ConvParameters (
297+ x , w , b , stride , padding , dilation , transposed , out_padding , groups
278298 )
279299
280300 # noinspection PyPep8Naming
@@ -415,8 +435,10 @@ def _convert_transpose_conv(
415435 return conversion_result
416436
417437 def _convert_2d_conv (
418- self , t_op : tflite_model . Operator , conv_params : ConvParameters
438+ self , torch_node : Node , t_op : tflite_model . Operator
419439 ) -> list [tflite_model .Operator ]:
440+ conv_params = self ._get_conv_params (torch_node )
441+
420442 if conv_params .transposed :
421443 t_op .builtin_options = transpose_conv_options .TransposeConv ()
422444 if conv_utils .group_conv_convertible_into_multiple_convolutions (
@@ -502,18 +524,11 @@ def _convert_2d_conv(
502524 def convert (self , node : Node ):
503525 self .assert_convertible (node )
504526
505- _ , _ , _ , stride , padding , dilation , transposed , out_padding , groups = (
506- self ._get_convolution_arguments (node )
507- )
508-
509527 t_op = self ._create_tflite_op_with_io_tensors (node )
510- conv_params = ConvParameters (
511- stride , padding , dilation , transposed , out_padding , groups
512- )
513528
514529 rank = t_op .tmp_inputs [1 ].shape .len ()
515530 if rank == 4 : # Conv2D
516- ops_to_add = self ._convert_2d_conv (t_op , conv_params )
531+ ops_to_add = self ._convert_2d_conv (node , t_op )
517532 else :
518533 raise NotImplementedError (
519534 f"{ rank - 2 } D convolution is not supported."
0 commit comments