-
Notifications
You must be signed in to change notification settings - Fork 615
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix support for sparse labels in MatthewsCorrelationCoefficient #2495
Conversation
@autoih @marload You are owners of some files modified in this pull request. |
def test_binary_classes_sparse(): | ||
gt_label = tf.constant([[1.0], [1.0], [1.0], [0.0]], dtype=tf.float32) | ||
preds = tf.constant([[1.0], [0.0], [1.0], [1.0]], dtype=tf.float32) | ||
# Initialize | ||
mcc = MatthewsCorrelationCoefficient(1) | ||
# Update | ||
mcc.update_state(gt_label, preds) | ||
# Check results | ||
check_results(mcc, [-0.33333334]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This restores the behaviour of v0.12 which was removed here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure the above example is sparse, can you report the result of the following input?
gt_label = tf.constant([1, 1, 1, 0], dtype=tf.float32)
preds = tf.constant([1, 0, 1, 1], dtype=tf.float32)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I don't think this would work correctly with the current code. But the motivation of this PR was to restore the behaviour of v0.12 and earlier. I'd be happy to change it to support the above example though. Is there a place where the conventions of the metrics are documented? /cc @seanpmorgan
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In tensorflow (and in most libraries), sparse labels are tensors of rank 1. That means it's dimensionality is (numclasses, )
and not (numclasses, 1)
.
As for conventions, you can check README.md in metrics to get a brief idea on how things work. You can also check metrics.py from tensorflow official for code conventions as we follow the same standard. There's also an exquisite set of metrics available in our metrics
folders which will give you a good idea.
Let me know if this helps, 👍
y_true = tf.cast(y_true, dtype=self.dtype) | ||
y_pred = tf.cast(y_pred, dtype=self.dtype) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of rounding in the future, I suggest cast it to tf.int64
The test doesn't "break" per se, it wasn't intended to be used for sparse-labels in the first place. See this comment. I gather that you're trying to add sparse support with multi and binary labels? This code will serve as a good reference for library convention. Most importantly, it doesn't make sense to have |
): | ||
"""Creates a Matthews Correlation Coefficient instance.""" | ||
super().__init__(name=name, dtype=dtype) | ||
self.num_classes = num_classes | ||
self.num_classes = max(2, num_classes) | ||
self.conf_mtx = self.add_weight( | ||
"conf_mtx", | ||
shape=(self.num_classes, self.num_classes), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of max(2, num_classes)
, add a check to detect if num_classes is less than 2 and appropriately throw error. See cohen_kappa.py for this.
@jonpsy I strongly disagree. It is very common to, if there is just a single class, have only a single (sigmoid) output representing the confidence that the input is a positive. It's the reason the BinaryCrossentropy loss exists and it's a totally valid usecase. In fact, I'd argue that it doesn't make sense to have two outputs if you only have a single class, because you can represent the same prediction with just a single number without losing any information. Additionally, |
To be honest, I am not very familiar with this code and the conventions for TFA metrics. But I disagree with your assessment that this behaviour was never intended. It has been the behaviour in all releases between v0.7 and v0.12 including it being tested in the unittest. And the functionality was removed in #2406 while changing the unittest. I don't want to advocate for not ever making breaking changes (in fact semver definitely allows for that when moving from 0.12 to 0.13), but then it needs to be documented in the release notes and ideally the code should throw an actionable error message. Which currently isn't the case since the release notes only mention that there was a "fix for MCC" and the metric will now return I also agree with the point @jneeven raises about supporting For me primarily there are two main questions about this PR:
|
It looks like you have confused the probability of class occurring with the number of distinguishable classes. In your example itself, please note the word "Binary" which itself suggests two-class problem. Label representation (via sparse or OHE) and probability of class occurring are two different things, the former is used in metric implementations. For instance, consider the following example, import tensorflow as tf
m = tf.keras.metrics.MeanIoU(num_classes=1)
m.update_state([1.0, 1.0, 1.0, 1.0], [0.3, 0.7, 0.8, 0.9])
m.result().numpy() As per your line of reasoning, this is perfectly fine since we're using a "single number for representing class", As expected, this throws errors. I believe this should suffice unless of course, official TensorFlow implementation is wrong. But what about backward compatibility?
There's no "backward compatibility" issue here, the previous implementation was invaild in the doctest, simple as that. It's not an assessment, it's a fact. Bluntly put, you've restored an invalid case and changed the current code to "work" on that case. Although, I concede that we should've discussed it in our PR. I also agree that we should've thrown an error when this invalid case is invoked, for example: cohen_kappa.py does this addons/tensorflow_addons/metrics/cohens_kappa.py Lines 112 to 120 in 97eb293
Which I've already suggested in a previous comment on this PR. I've done my best to explain this and if you're still having trouble understanding, I suppose @bhack could help you out here or you can post it to StackOverflow. If you want to add support for sparse labels, I would be happy to help you in that regard 👍 |
Thank you for your contribution. We sincerely apologize for any delay in reviewing, but TensorFlow Addons is transitioning to a minimal maintenance and release mode. New features will not be added to this repository. For more information, please see our public messaging on this decision: Please consider sending feature requests / contributions to other repositories in the TF community with a similar charters to TFA: |
Description
This PR adds support for sparse labels and predictions in
MatthewsCorrelationCoefficient
. This re-enables the use ofnum_classes=1
which was broken in #2406.Fixes #2494
Type of change
Checklist: