Skip to content

[Fix] keras.ops.separable_conv rejects valid symbolic inputs with dilated kernels#22566

Open
ChiragSW wants to merge 5 commits intokeras-team:masterfrom
ChiragSW:issue#22517
Open

[Fix] keras.ops.separable_conv rejects valid symbolic inputs with dilated kernels#22566
ChiragSW wants to merge 5 commits intokeras-team:masterfrom
ChiragSW:issue#22517

Conversation

@ChiragSW
Copy link
Copy Markdown
Contributor

@ChiragSW ChiragSW commented Mar 27, 2026

Fixes issue: #22517

Root cause

keras.Input(shape=...) adds a batch dim, so your kernels become rank-5 like (None, 3, 3, 3, 1) instead of rank-4.
Symbolic shape inference treated that leading None as part of the kernel shape, triggering the kernel-rank mismatch error.

Fix

In keras/src/ops/nn.py, symbolic Conv/DepthwiseConv shape inference now ignores a leading (None|1) dim from kernels before computing output spec.
In keras/src/ops/operation_utils.py, compute_conv_output_shape now clamps negative/zero VALID outputs to 0 (instead of raising) for consistent symbolic behavior.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces logic to handle kernels with an optional batch dimension in compute_output_spec by stripping the leading dimension when it matches the input rank plus one. Additionally, it modifies compute_conv_output_shape in operation_utils.py to set non-positive spatial dimensions to zero instead of raising a ValueError. The review feedback suggests extracting the duplicated kernel shape adjustment logic into a shared utility function to improve maintainability and reduce code duplication.

Comment on lines +1459 to +1464
kernel_shape = kernel.shape
if len(kernel_shape) == len(inputs.shape) + 1 and kernel_shape[0] in (
None,
1,
):
kernel_shape = kernel_shape[1:]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This logic to handle a potential batch dimension on the kernel is duplicated in DepthwiseConv.compute_output_spec (lines 1560-1565). To improve maintainability and avoid code duplication, consider extracting this logic into a helper function in keras/src/ops/operation_utils.py.

For example, you could add a function like this to operation_utils.py:

def unexpand_kernel_shape(kernel_shape, input_rank):
    """Removes a leading batch dimension from kernel_shape if present."""
    if len(kernel_shape) == input_rank + 1 and kernel_shape[0] in (None, 1):
        return kernel_shape[1:]
    return kernel_shape

Then, you could simplify this method and DepthwiseConv.compute_output_spec by calling this new helper function.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Mar 27, 2026

Codecov Report

❌ Patch coverage is 75.00000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.28%. Comparing base (a2e97e1) to head (69c3097).
⚠️ Report is 42 commits behind head on master.

Files with missing lines Patch % Lines
keras/src/ops/operation_utils.py 50.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #22566      +/-   ##
==========================================
+ Coverage   83.26%   83.28%   +0.01%     
==========================================
  Files         596      596              
  Lines       67828    68096     +268     
  Branches    10562    10608      +46     
==========================================
+ Hits        56480    56716     +236     
- Misses       8605     8635      +30     
- Partials     2743     2745       +2     
Flag Coverage Δ
keras 83.09% <75.00%> (+<0.01%) ⬆️
keras-jax 59.66% <75.00%> (-0.16%) ⬇️
keras-numpy 55.35% <75.00%> (+0.91%) ⬆️
keras-openvino 53.34% <75.00%> (+1.64%) ⬆️
keras-tensorflow 61.04% <75.00%> (-0.10%) ⬇️
keras-torch 59.85% <75.00%> (-0.15%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ChiragSW
Copy link
Copy Markdown
Contributor Author

@hertschuh please review the PR

@keerthanakadiri keerthanakadiri added the stat:awaiting keras-eng Awaiting response from Keras engineer label Mar 30, 2026
Copy link
Copy Markdown
Collaborator

@hertschuh hertschuh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking into this!

Comment on lines +276 to +288
if self.padding == "valid":
if self.data_format == "channels_last":
spatial_dims = output_shape[1:-1]
else:
spatial_dims = output_shape[2:]
if any(d == 0 for d in spatial_dims if d is not None):
raise ValueError(
"Computed output size would be negative. Received: "
f"`inputs.shape={input_shape}`, "
f"`kernel_size={self.kernel_size}`, `dilation_rate="
f"{self.dilation_rate}`, `strides={self.strides}`."
)
return output_shape
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this code inside compute_conv_output_shape so that all ops can benefit from this check.

Comment on lines +221 to +222
raise ValueError(
"Computed output size would be zero or negative. Received "
f"`inputs shape={input_shape}`, "
f"`kernel shape={kernel_shape}`, "
f"`dilation_rate={dilation_rate}`, "
f"`strides={strides}`, "
f"`padding={padding}`."
)
output_spatial_shape[i] = 0
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace with the more correct test that you added in keras/src/layers/convolutional/base_conv.py.

And don't return 0 of the dimension is computed as negative.

Comment on lines +244 to +248
def unexpand_kernel_shape(kernel_shape, input_rank):
"""Removes a leading batch dimension from `kernel_shape` if present."""
if len(kernel_shape) == input_rank + 1 and kernel_shape[0] in (None, 1):
return kernel_shape[1:]
return kernel_shape
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part does not make sense to me. The kernel shape is unrelated to whether the batch dimension is present in the inputs or not. There is no "kernel batch dimension".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the new commit, I have added the description for this in docstring

@hertschuh hertschuh added stat:awaiting response from contributor and removed stat:awaiting keras-eng Awaiting response from Keras engineer labels Mar 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants