gemini-code-assist[bot] commented on code in PR #19530:
URL: https://github.com/apache/tvm/pull/19530#discussion_r3214887830
##########
python/tvm/relax/frontend/tflite/tflite_frontend.py:
##########
@@ -2586,6 +2587,153 @@ def convert_conv3d(self, op):
out = self.convert_fused_activation_function(out, fused_activation_fn)
return out
+ def convert_conv3d_transpose(self, op):
+ """3D transposed convolution implementation."""
+
+ from tflite.BuiltinOptions import BuiltinOptions
+ from tflite.Conv3DOptions import Conv3DOptions
+ from tflite.Padding import Padding
+ from tflite.TensorType import TensorType
+
+ input_tensors = self.get_input_tensors(op)
+ assert len(input_tensors) >= 3, "input tensors length should be >= 3"
+
+ # TFLite CONV_3D_TRANSPOSE input order:
+ # [0] output_shape, [1] weight, [2] data, [3] bias (optional)
+ weight_tensor = input_tensors[1]
+ input_tensor = input_tensors[2]
+ input_tensor_idx = input_tensor.tensor_idx
+
+ output_tensors = self.get_output_tensors(op)
+ assert len(output_tensors) == 1, "output tensors length should be 1"
+ output_tensor = output_tensors[0]
+
+ assert op.BuiltinOptionsType() == BuiltinOptions.Conv3DOptions
+ op_options = op.BuiltinOptions()
+ conv3d_options = Conv3DOptions()
+ conv3d_options.Init(op_options.Bytes, op_options.Pos)
+
+ stride_d = conv3d_options.StrideD()
+ stride_h = conv3d_options.StrideH()
+ stride_w = conv3d_options.StrideW()
+ dilation_d = conv3d_options.DilationDFactor()
+ dilation_h = conv3d_options.DilationHFactor()
+ dilation_w = conv3d_options.DilationWFactor()
+ padding = conv3d_options.Padding()
+ fused_activation_fn = conv3d_options.FusedActivationFunction()
+
+ _, input_d, input_h, input_w, input_c =
to_int_list(self.get_tensor_shape(input_tensor))
Review Comment:

The variables `input_d`, `input_h`, and `input_w` are extracted from the
input tensor shape but are not used in the subsequent logic, including the
padding calculations. For clarity and to avoid unused variable warnings, they
can be replaced with underscores.
```suggestion
_, _, _, _, input_c =
to_int_list(self.get_tensor_shape(input_tensor))
```
##########
python/tvm/relax/frontend/tflite/tflite_frontend.py:
##########
@@ -2586,6 +2587,153 @@ def convert_conv3d(self, op):
out = self.convert_fused_activation_function(out, fused_activation_fn)
return out
+ def convert_conv3d_transpose(self, op):
+ """3D transposed convolution implementation."""
+
+ from tflite.BuiltinOptions import BuiltinOptions
+ from tflite.Conv3DOptions import Conv3DOptions
+ from tflite.Padding import Padding
+ from tflite.TensorType import TensorType
+
+ input_tensors = self.get_input_tensors(op)
+ assert len(input_tensors) >= 3, "input tensors length should be >= 3"
+
+ # TFLite CONV_3D_TRANSPOSE input order:
+ # [0] output_shape, [1] weight, [2] data, [3] bias (optional)
+ weight_tensor = input_tensors[1]
+ input_tensor = input_tensors[2]
+ input_tensor_idx = input_tensor.tensor_idx
+
+ output_tensors = self.get_output_tensors(op)
+ assert len(output_tensors) == 1, "output tensors length should be 1"
+ output_tensor = output_tensors[0]
+
+ assert op.BuiltinOptionsType() == BuiltinOptions.Conv3DOptions
+ op_options = op.BuiltinOptions()
+ conv3d_options = Conv3DOptions()
+ conv3d_options.Init(op_options.Bytes, op_options.Pos)
+
+ stride_d = conv3d_options.StrideD()
+ stride_h = conv3d_options.StrideH()
+ stride_w = conv3d_options.StrideW()
+ dilation_d = conv3d_options.DilationDFactor()
+ dilation_h = conv3d_options.DilationHFactor()
+ dilation_w = conv3d_options.DilationWFactor()
+ padding = conv3d_options.Padding()
+ fused_activation_fn = conv3d_options.FusedActivationFunction()
+
+ _, input_d, input_h, input_w, input_c =
to_int_list(self.get_tensor_shape(input_tensor))
+
+ # TFLite Conv3DTranspose kernel layout is DHWOI:
+ # KD KH KW OC IC
+ kernel_d, kernel_h, kernel_w, output_channels, in_channels =
to_int_list(
+ self.get_tensor_shape(weight_tensor)
+ )
+
+ dilated_kernel_d = dilation_d * (kernel_d - 1) + 1
+ dilated_kernel_h = dilation_h * (kernel_h - 1) + 1
+ dilated_kernel_w = dilation_w * (kernel_w - 1) + 1
+
+ params = {
+ "strides": [stride_d, stride_h, stride_w],
+ "dilation": [dilation_d, dilation_h, dilation_w],
+ "padding": [0, 0, 0, 0, 0, 0],
+ "output_padding": [0, 0, 0],
+ "data_layout": "NDHWC",
+ "kernel_layout": "DHWOI",
+ }
+
+ if input_c != in_channels:
+ assert input_c % in_channels == 0, (
+ "Input channels is not divisible by kernel in_channels."
+ )
+ params["groups"] = int(input_c / in_channels)
+
+ # weight tensor type should be INT8/UINT8 (quantization) or FLOAT32
+ weight_tensor_type = weight_tensor.tensor.Type()
+ assert weight_tensor_type in (
+ TensorType.INT8,
+ TensorType.UINT8,
+ TensorType.FLOAT32,
+ )
+ weight_tensor_type_str = self.get_tensor_type_str(weight_tensor_type)
+
+ in_expr = self.get_expr(input_tensor_idx)
+
+ # TFLite Conv3DTranspose kernel is already in DHWOI layout, no
transpose needed.
+ if self.has_expr(weight_tensor.tensor_idx):
+ weight_expr = self.get_expr(weight_tensor.tensor_idx)
+ else:
+ if self.is_prefetched(weight_tensor.tensor_idx):
+ weight_value =
self.get_prefetched_node(weight_tensor.tensor_idx)
+ else:
+ weight_value = self.get_tensor_value(weight_tensor)
+
+ weight_expr = self.exp_tab.new_const(
+ weight_value, dtype=weight_tensor_type_str,
+ source_name=weight_tensor.tensor.Name()
+ )
+
+ if padding == Padding.VALID:
+ pass
+ elif padding == Padding.SAME:
+ # For transposed convolution with SAME padding:
+ # target output_size = input_size * stride
+ # total_pad = max(0, dilated_kernel - stride)
+ for dim_kernel, dim_stride, label in [
+ (dilated_kernel_d, stride_d, "D"),
+ (dilated_kernel_h, stride_h, "H"),
+ (dilated_kernel_w, stride_w, "W"),
+ ]:
+ total_pad = max(0, dim_kernel - dim_stride)
+ pad_before = total_pad // 2
+ pad_after = total_pad - pad_before
+ idx = {"D": 0, "H": 1, "W": 2}[label]
+ params["padding"][idx] = pad_before
+ params["padding"][idx + 3] = pad_after
+
+ # output_padding handles the case when stride > dilated_kernel
+ output_pad = max(0, dim_stride - dim_kernel)
+ params["output_padding"][idx] = output_pad
+ else:
+ raise tvm.error.OpAttributeUnImplemented(
+ f"Padding format {padding} is not supported for operator
Conv3DTranspose."
+ )
+
+ if input_tensor.qnn_params:
+ raise tvm.error.OpNotImplemented(
+ "Quantized Conv3DTranspose is not yet supported in the Relax
frontend."
+ )
+
+ out = relax.op.nn.conv3d_transpose(in_expr, weight_expr, **params)
+
+ # if we have bias (input_tensors[3])
+ if len(input_tensors) >= 4:
+ bias_tensor = input_tensors[3]
+ if bias_tensor.tensor_idx != -1:
+ bias_tensor_type = bias_tensor.tensor.Type()
+ # bias tensor type should be INT32 (int8 qnn) or INT64 (int16
qnn) or FLOAT32
+ assert bias_tensor_type in (TensorType.INT32,
TensorType.INT64, TensorType.FLOAT32)
+ bias_tensor_type_str =
self.get_tensor_type_str(bias_tensor_type)
+ if self.has_expr(bias_tensor.tensor_idx):
+ bias_expr = self.get_expr(bias_tensor.tensor_idx)
+ else:
+ bias_expr = self.exp_tab.new_const(
+ self.get_tensor_value(bias_tensor),
+ dtype=bias_tensor_type_str,
+ source_name=bias_tensor.tensor.Name(),
+ )
+ out = relax.op.add(out, bias_expr)
Review Comment:

While `relax.op.add` works for adding bias due to broadcasting, using
`relax.op.nn.bias_add` is generally preferred for neural network layers as it
explicitly targets the channel dimension and is more robust to layout
variations. This would also be consistent with the implementation of
`convert_transpose_conv` (2D) in this frontend.
```suggestion
out = relax.op.nn.bias_add(out, bias_expr, axis=4)
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]