Torch backend: `binary_crossentropy` with an integer target stops the Python process on MPS
Description
On the Torch backend with the MPS device, keras.ops.binary_crossentropy stops the Python process when the target has an integer dtype and from_logits=False. The process stops with SIGABRT (exit code 134 in a shell). Keras does not raise a Python exception. A try block cannot catch this failure.
The example in the keras.ops.binary_crossentropy docstring (keras/src/ops/nn.py) uses an integer target. This example stops the process on MPS.
Procedure
Run this code on an Apple silicon Mac:
import os
os.environ["KERAS_BACKEND"] = "torch"
import keras
target = keras.ops.convert_to_tensor([0, 1, 1, 0])
output = keras.ops.convert_to_tensor([0.1, 0.9, 0.8, 0.2])
print(keras.ops.binary_crossentropy(target, output))
Actual result
The process stops. The last lines of the output are:
error: 'mps.subtract' op requires the same element type for all operands and results
failed assertion `original module failed verification'
Expected result
keras.ops.binary_crossentropy returns the loss or raises a Python exception. The process does not stop.
Results on other backends and devices
| Backend | Device | Result |
|---|---|---|
| JAX | CPU | Returns [0.1054, 0.1054, 0.2231, 0.2231] |
| NumPy | CPU | Returns [0.1054, 0.1054, 0.2231, 0.2231] |
| TensorFlow | CPU | Raises InvalidArgumentError |
| Torch | CPU | Raises RuntimeError: Found dtype Int but expected Float |
| Torch | MPS | Stops the process |
These related cases also occur on the Torch backend:
- A
booltarget on MPS returns the loss. The same target on CPU raisesRuntimeError: Found dtype Bool but expected Float. - With
from_logits=True, an integer target on MPS raises aRuntimeError. The process does not stop. keras.losses.binary_crossentropycastsy_trueto the dtype ofy_pred. This function does not have the problem.
Cause
binary_crossentropy in keras/src/backend/torch/nn.py does not cast target to the dtype of output. The function sends the integer tensor to torch.nn.functional.binary_cross_entropy. On MPS, this PyTorch kernel does not check the dtype of the target. MPSGraph then fails an internal assertion, and the process stops.
The same failure occurs without Keras:
import torch
import torch.nn.functional as F
output = torch.tensor([0.1, 0.9, 0.8, 0.2], device="mps")
target = torch.tensor([0, 1, 1, 0], device="mps")
F.binary_cross_entropy(output, target, reduction="none") # The process stops.
PR #23633 changes this function. The failure also occurs with the change from that PR.
Possible fix
Cast target to output.dtype at the start of the Torch binary_crossentropy. The JAX and NumPy backends accept integer targets. With this cast, the Torch backend gives the same result.
Environment
- Keras:
masterat 32530b6ed - PyTorch: 2.10.0
- Python: 3.12.12
- macOS 26.6.2 on an Apple M1 Pro
Source: keras-team/keras