Skip to content

Conversation

@MalyalaKarthik66
Copy link
Contributor

@MalyalaKarthik66 MalyalaKarthik66 commented Nov 2, 2025

fix: #21804

Description:
This PR fixes minor dtype inconsistencies and ensures GPU memory consistency when computing the Huber loss in keras.losses.Huber.

Background:
During internal testing (test_huber_memory_usage_debug_05), it was observed that GPU memory usage slightly differed when using delta=0.5, although the Huber loss logic should behave identically across delta values. The issue was not with the computation itself but with how tensors of different dtypes were handled internally before casting.

Fix:

  • To make computation deterministic and prevent redundant GPU memory conversions:
  • Explicitly cast half (0.5 constant) and delta to match abs_error.dtype using ops.cast.
  • This ensures all tensors involved in computation share the same dtype and eliminates any implicit conversions.

Verification:
To verify, I manually ran:
pytest keras/src/losses/losses_test.py::HuberLossTest::test_huber_memory_usage_debug_05 -v -s
and manually changed the delta value in the test each time (e.g., 0.5, 1.0, 10.0).
Verified that GPU memory usage remains stable across all delta values when running the test individually.

Result:
✅ Stable GPU memory usage across deltas

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @MalyalaKarthik66, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses an observed GPU memory inconsistency issue within the keras.losses.Huber function. The core problem stemmed from implicit data type conversions when computing the loss, leading to variable GPU memory usage, especially with a delta value of 0.5. The fix involves explicitly casting relevant constants and parameters to ensure all tensors share a consistent data type, thereby eliminating redundant GPU memory conversions and guaranteeing deterministic and stable memory consumption during Huber loss calculations. A new test case has been introduced to validate this memory stability.

Highlights

  • GPU Memory Consistency: Ensures stable GPU memory usage for Huber loss computation by addressing minor dtype inconsistencies, particularly when the delta parameter is set to 0.5.
  • Dtype Alignment: Explicitly casts the 0.5 constant and the delta parameter to match the abs_error.dtype within the huber function. This prevents implicit type conversions and ensures all tensors involved share the same data type.
  • New Test Case: A new test, test_huber_memory_usage_debug_05, has been added to keras/src/losses/losses_test.py to specifically verify the stability of GPU memory usage for the Huber loss across different delta values.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
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 addresses a GPU memory inconsistency in the Huber loss calculation by making dtype casting more explicit. The core change in losses.py is sound, though it introduces a small redundancy. Additionally, a new test has been added for verification, which appears to be a debugging utility and is not suitable for merging as a permanent regression test. My review includes suggestions to remove the redundant code and the temporary test.

abs_error = ops.abs(error)
half = ops.convert_to_tensor(0.5, dtype=abs_error.dtype)
half = ops.cast(ops.convert_to_tensor(0.5), dtype=abs_error.dtype)
delta = ops.cast(delta, dtype=abs_error.dtype)
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This explicit cast on delta is redundant. On line 1972, delta is already converted to a tensor with y_pred.dtype. Since abs_error.dtype is the same as y_pred.dtype (as it's derived from y_pred and y_true), this line is effectively a no-op (ops.cast(delta, delta.dtype)). You can safely remove this line to avoid the unnecessary operation.

Comment on lines 711 to 745
def test_huber_memory_usage_debug_05(self):
import tensorflow as tf, numpy as np, keras

print("\n[Huber GPU Memory Debug: delta=0.5]")
gpus = tf.config.experimental.list_physical_devices('GPU')
if not gpus:
print("No GPU found. Skipping test.")
return
try:
for gpu in gpus:
tf.config.experimental.set_memory_growth(gpu, True)
except RuntimeError as e:
print(f"[Info] GPU memory growth already set or GPU initialized: {e}")

x = np.random.rand(1000, 1)
y = ((3 * x) + 2) + np.random.randn(1000, 1)
huber_loss = keras.losses.Huber(delta=0.5)
loss = huber_loss(y, x)
print(f"Huber loss: {loss.numpy():.6f}")

memory = sum(tf.config.experimental.get_memory_info(f'GPU:{i}')['current'] for i in range(len(gpus)))
print(f"GPU memory usage: {memory} bytes")

# sanity check for stable GPU usage (adjust threshold as needed)
assert memory > 0, f"GPU memory not allocated or usage is zero. Current usage: {memory} bytes"


Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This test appears to be for debugging and manual verification of GPU memory usage, as suggested by its name and implementation. While valuable for your investigation, it's not ideal as a permanent regression test for a few reasons:

  • Backend-Specific: It directly imports and uses tensorflow, making it non-portable to other backends.
  • Weak Assertion: The assert memory > 0 check is not robust. It only confirms that some memory was allocated, rather than verifying the stability of memory usage across different delta values, which was the core issue.
  • Debugging Artifacts: It includes print statements, which are generally not recommended in production test suites.
  • Naming Convention: The name test_huber_memory_usage_debug_05 indicates it's a temporary debugging utility.

Since this test has served its purpose for verifying the fix, I recommend removing it from the pull request to maintain a clean and focused test suite.

@codecov-commenter
Copy link

codecov-commenter commented Nov 2, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.66%. Comparing base (6d06085) to head (afe7489).

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #21814   +/-   ##
=======================================
  Coverage   82.66%   82.66%           
=======================================
  Files         577      577           
  Lines       59419    59420    +1     
  Branches     9313     9313           
=======================================
+ Hits        49121    49122    +1     
  Misses       7898     7898           
  Partials     2400     2400           
Flag Coverage Δ
keras 82.49% <100.00%> (+<0.01%) ⬆️
keras-jax 63.33% <100.00%> (+<0.01%) ⬆️
keras-numpy 57.57% <100.00%> (+<0.01%) ⬆️
keras-openvino 34.34% <0.00%> (-0.01%) ⬇️
keras-tensorflow 64.13% <100.00%> (+<0.01%) ⬆️
keras-torch 63.62% <100.00%> (+<0.01%) ⬆️

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0.5 is special for delta in keras.losses.Huber() or not

3 participants