tf.nn.conv2d exhibits unexpected FP32 numerical divergence compared to reference convolution (CPU, VALID, stride=1) on M2
Issue type
Bug
Have you reproduced the bug with TensorFlow Nightly?
No
Source
source
TensorFlow version
2.21.0
Custom code
Yes
OS platform and distribution
macOS 15.7.2 (arm64)
Mobile device
No response
Python version
3.12.8
Bazel version
No response
GCC/compiler version
No response
CUDA/cuDNN version
No response
GPU model and memory
No response
Current behavior?
We observed a systematic numerical divergence in tf.nn.conv2d when running FP32 convolutions on CPU, compared against an equivalent reference implementation (validated via cross-framework testing).
Under the following strictly controlled conditions:
FP32 inputs and kernel weights
CPU execution
stride = 1
padding = "VALID"
no bias
correct memory layout (NHWC + HWIO)
deterministic inputs (fixed random tensors)
the output values from tf.nn.conv2d differ from the reference results with relative errors up to ~1e-3, which is significantly larger than expected for deterministic FP32 CPU convolutions.
This behavior was discovered during cross-framework fuzzing, where PyTorch’s conv2d was used as a validated reference. While output shapes match exactly, the numerical discrepancy appears consistent and reproducible.
Standalone code to reproduce the issue
import numpy as np
import tensorflow as tf
import torch
import torch.nn.functional as F
# ---- Parameters ----
N, C_in, C_out = 2, 7, 8
H, W = 9, 18
k = 5
stride = 1
padding = "VALID"
# Generate identical FP32 inputs
x = np.random.randn(N, C_in, H, W).astype(np.float32)
w = np.random.randn(C_out, C_in, k, k).astype(np.float32)
# ---- TensorFlow (NHWC / HWIO) ----
x_tf = tf.convert_to_tensor(np.transpose(x, (0, 2, 3, 1)))
w_tf = tf.convert_to_tensor(np.transpose(w, (2, 3, 1, 0)))
y_tf = tf.nn.conv2d(
x_tf,
w_tf,
strides=[1, stride, stride, 1],
padding=padding
)
y_tf_np = tf.transpose(y_tf, (0, 3, 1, 2)).numpy()
# ---- Reference (PyTorch, NCHW / OIHW) ----
x_pt = torch.from_numpy(x)
w_pt = torch.from_numpy(w)
y_ref = F.conv2d(x_pt, w_pt, stride=stride, padding=0)
y_ref_np = y_ref.detach().cpu().numpy()
# ---- Comparison ----
abs_diff = np.abs(y_tf_np - y_ref_np)
rel_diff = abs_diff / (np.abs(y_ref_np) + 1e-12)
print("Output shapes:", y_tf_np.shape, y_ref_np.shape)
print("Max abs error:", abs_diff.max())
print("Max rel error:", rel_diff.max())Relevant log output
Source: tensorflow/tensorflow