Migration of Supervised contrative Learning tutorial to Keras3 - #2398
Migration of Supervised contrative Learning tutorial to Keras3#2398maitry63 wants to merge 7 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request migrates the supervised contrastive learning example to Keras 3, replacing TensorFlow and TensorFlow Addons dependencies with unified Keras APIs and keras.ops. The review feedback suggests several improvements to the custom SupervisedContrastiveLoss implementation across the Python script, Jupyter Notebook, and Markdown documentation. Specifically, it recommends implementing the get_config method for proper serialization, using dynamic dtypes (logits.dtype) instead of hardcoded "float32" to ensure mixed-precision compatibility, and replacing the non-idiomatic ops.subtract(0.0, ...) with a simple unary negation operator.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| def __init__(self, temperature=0.05, **kwargs): | ||
| super().__init__(**kwargs) | ||
| self.temperature = temperature |
There was a problem hiding this comment.
To support proper serialization and deserialization of the custom loss (e.g., when saving and loading the model), it is best practice in Keras 3 to implement the get_config method.
def __init__(self, temperature=0.05, **kwargs):
super().__init__(**kwargs)
self.temperature = temperature
def get_config(self):
config = super().get_config()
config.update({"temperature": self.temperature})
return config| mask = ops.cast(ops.equal(labels, ops.transpose(labels)), "float32") | ||
|
|
||
| batch_size = ops.shape(logits)[0] | ||
| logits_mask = 1.0 - ops.eye(batch_size) |
There was a problem hiding this comment.
To ensure full compatibility with mixed precision training (e.g., mixed_float16 or mixed_bfloat16) and to prevent potential runtime dtype mismatch errors in strict backends like PyTorch or JAX, it is highly recommended to use the dynamic dtype of the input logits (i.e., logits.dtype) instead of hardcoding "float32" and default float for ops.eye.
| mask = ops.cast(ops.equal(labels, ops.transpose(labels)), "float32") | |
| batch_size = ops.shape(logits)[0] | |
| logits_mask = 1.0 - ops.eye(batch_size) | |
| mask = ops.cast(ops.equal(labels, ops.transpose(labels)), logits.dtype) | |
| batch_size = ops.shape(logits)[0] | |
| logits_mask = 1.0 - ops.eye(batch_size, dtype=logits.dtype) |
| ops.sum(mask, axis=1) + 1e-8 | ||
| ) | ||
|
|
||
| return ops.subtract(0.0, ops.mean(mean_log_prob_pos)) |
There was a problem hiding this comment.
| " def __init__(self, temperature=0.05, **kwargs):\n", | ||
| " super().__init__(**kwargs)\n", | ||
| " self.temperature = temperature\n", |
There was a problem hiding this comment.
To support proper serialization and deserialization of the custom loss (e.g., when saving and loading the model), it is best practice in Keras 3 to implement the get_config method.
| " def __init__(self, temperature=0.05, **kwargs):\n", | |
| " super().__init__(**kwargs)\n", | |
| " self.temperature = temperature\n", | |
| " def __init__(self, temperature=0.05, **kwargs):\n", | |
| " super().__init__(**kwargs)\n", | |
| " self.temperature = temperature\n", | |
| "\n", | |
| " def get_config(self):\n", | |
| " config = super().get_config()\n", | |
| " config.update({\"temperature\": self.temperature})\n", | |
| " return config\n", |
| " mask = ops.cast(ops.equal(labels, ops.transpose(labels)), \"float32\")\n", | ||
| "\n", | ||
| " batch_size = ops.shape(logits)[0]\n", | ||
| " logits_mask = 1.0 - ops.eye(batch_size)\n", |
There was a problem hiding this comment.
To ensure full compatibility with mixed precision training (e.g., mixed_float16 or mixed_bfloat16) and to prevent potential runtime dtype mismatch errors in strict backends like PyTorch or JAX, it is highly recommended to use the dynamic dtype of the input logits (i.e., logits.dtype) instead of hardcoding "float32" and default float for ops.eye.
| " mask = ops.cast(ops.equal(labels, ops.transpose(labels)), \"float32\")\n", | |
| "\n", | |
| " batch_size = ops.shape(logits)[0]\n", | |
| " logits_mask = 1.0 - ops.eye(batch_size)\n", | |
| " mask = ops.cast(ops.equal(labels, ops.transpose(labels)), logits.dtype)\n", | |
| "\n", | |
| " batch_size = ops.shape(logits)[0]\n", | |
| " logits_mask = 1.0 - ops.eye(batch_size, dtype=logits.dtype)\n", |
| " ops.sum(mask, axis=1) + 1e-8\n", | ||
| " )\n", | ||
| "\n", | ||
| " return ops.subtract(0.0, ops.mean(mean_log_prob_pos))\n", |
There was a problem hiding this comment.
| def __init__(self, temperature=0.05, **kwargs): | ||
| super().__init__(**kwargs) | ||
| self.temperature = temperature |
There was a problem hiding this comment.
To support proper serialization and deserialization of the custom loss (e.g., when saving and loading the model), it is best practice in Keras 3 to implement the get_config method.
| def __init__(self, temperature=0.05, **kwargs): | |
| super().__init__(**kwargs) | |
| self.temperature = temperature | |
| def __init__(self, temperature=0.05, **kwargs): | |
| super().__init__(**kwargs) | |
| self.temperature = temperature | |
| def get_config(self): | |
| config = super().get_config() | |
| config.update({"temperature": self.temperature}) | |
| return config |
| mask = ops.cast(ops.equal(labels, ops.transpose(labels)), "float32") | ||
|
|
||
| batch_size = ops.shape(logits)[0] | ||
| logits_mask = 1.0 - ops.eye(batch_size) |
There was a problem hiding this comment.
To ensure full compatibility with mixed precision training (e.g., mixed_float16 or mixed_bfloat16) and to prevent potential runtime dtype mismatch errors in strict backends like PyTorch or JAX, it is highly recommended to use the dynamic dtype of the input logits (i.e., logits.dtype) instead of hardcoding "float32" and default float for ops.eye.
| mask = ops.cast(ops.equal(labels, ops.transpose(labels)), "float32") | |
| batch_size = ops.shape(logits)[0] | |
| logits_mask = 1.0 - ops.eye(batch_size) | |
| mask = ops.cast(ops.equal(labels, ops.transpose(labels)), logits.dtype) | |
| batch_size = ops.shape(logits)[0] | |
| logits_mask = 1.0 - ops.eye(batch_size, dtype=logits.dtype) |
| ops.sum(mask, axis=1) + 1e-8 | ||
| ) | ||
|
|
||
| return ops.subtract(0.0, ops.mean(mean_log_prob_pos)) |
There was a problem hiding this comment.
| from tensorflow.keras import layers | ||
| import os | ||
|
|
||
| os.environ["KERAS_BACKEND"] = "tensorflow" # or "torch" or "jax" |
There was a problem hiding this comment.
Any reason not to use "jax"?
hertschuh
left a comment
There was a problem hiding this comment.
Can you test with the JAX backend just to make sure?
e2d38eb to
3e59b2a
Compare
Sure, tested with Jax backend. |
|
This PR is stale because it has been open for 14 days with no activity. It will be closed if no further activity occurs. Thank you. |
hertschuh
left a comment
There was a problem hiding this comment.
There is a merge conflict:
| "keras": f"{KERAS_TEAM_GH}/keras/tree/v3.15.1/", | ||
| "keras_tuner": f"{KERAS_TEAM_GH}/keras-tuner/tree/v1.4.8/", | ||
| "keras_hub": f"{KERAS_TEAM_GH}/keras-hub/tree/v0.30.0/", | ||
| "tf_keras": f"{KERAS_TEAM_GH}/tf-keras/tree/v2.20.0/", | ||
| "tf_keras": f"{KERAS_TEAM_GH}/tf-keras/tree/v2.20.1/", |
There was a problem hiding this comment.
Can you rebase? I believe these would go away.
There was a problem hiding this comment.
Rebased, to fix the merge conflicts.
641cd91 to
05a24a9
Compare
05a24a9 to
5d79f58
Compare
This PR is a follow-up to the work done in #2338 that migrated tutorial Supervised contrative Learning into Keras3.