Repository navigation
Conversation
| def dice_loss( | ||
| inputs: mx.array, targets: mx.array, epsilon: float = 1e-6, reduction: str = "none" | ||
| ) -> mx.array: | ||
| r""" | ||
| Computes the Dice Loss, which is a measure of overlap between two samples. | ||
| This loss is commonly used for binary segmentation tasks. |
There was a problem hiding this comment.
FYI we already decided this one was a bit niche to put in nn in #224 .
|
Could you say a bit more about why these specific losses should be included in |
Hey there, Great question! I totally get why some of these loss functions might seem a bit niche at first glance. Here's my rationale for including them:
So, in summary, while some of these losses might not be everyday go-to's, they each have their own superpowers for specific, yet increasingly common, scenarios in the ML world. Having them in the toolkit could really bolster the versatility and effectiveness of the mlx framework, especially in tackling more complex or specialized tasks. Looking forward to hearing your thoughts on this! |
| inputs (array): The predicted logits or probabilities. | ||
| targets (array): The ground truth target values. |
There was a problem hiding this comment.
suggestion
inputs (mx.array): Predicted probabilities for the positive class.
targets (mx.array): The target values (binary).
| # Calculating the cross-entropy loss | ||
| ce_loss = mx.logaddexp(0.0, inputs) - targets * inputs |
There was a problem hiding this comment.
suggestions. It's for binary target value so it's binary cross entropy, and you could use pre-defined bce loss for concise understanding.
BCE_loss = binary_cross_entropy(inputs, targets)
pt = mx.exp(-BCE_loss)
loss = -alpha * ((1 - pt) ** gamma) * BCE_loss
return _reduce(loss, reduction)| inputs = mx.array([[2.0, -1.0, 3.0, 0.1], [-1.0, 2.0, -0.5, 0.2]]) | ||
| targets = mx.array([[1.0, 0.0, 0.0, 1.0], [0.0, 1.0, 0.0, 1.0]]) |
There was a problem hiding this comment.
It is better to use original test examples used above.
Also, it is likely to use pre-defined expected output rather than copy of your loss fuction at losses.py
ce_loss = mx.logaddexp(0.0, inputs) - targets * inputs
pt = mx.exp(-ce_loss)
expected_loss = -alpha * ((1 - pt) ** gamma) * ce_loss
expected_loss = mx.mean(expected_loss)
loss = nn.losses.focal_loss(
inputs, targets, alpha=alpha, gamma=gamma, reduction="mean"
)These are my codes that uses like [[0.5, 0.5, 0.2, 0.9], [0.1, 0.3, 0.5, 0.5]]. You can extend test functions to tversky_loss and iou. However, I am not sure why less common loss are added on this pr.
Nice work!
cc @awni
def test_dice_loss(self):
inputs = mx.array([[0.5, 0.5, 0.2, 0.9], [0.1, 0.3, 0.5, 0.5]])
targets = mx.array([[1, 0, 1, 0], [0, 1, 0, 1]])
# Test with reduction 'none'
losses_none = nn.losses.dice_loss(inputs, targets, reduction="none")
expected_none = mx.array([0.658536, 0.529412]) # Example expected values
self.assertTrue(mx.allclose(losses_none, expected_none))
# Test with reduction 'mean'
losses_mean = nn.losses.dice_loss(inputs, targets, reduction="mean")
expected_mean = mx.mean(expected_none)
self.assertTrue(mx.allclose(losses_mean, expected_mean))
# Test with reduction 'sum'
losses_sum = nn.losses.dice_loss(inputs, targets, reduction="sum")
expected_sum = mx.sum(expected_none)
self.assertTrue(mx.allclose(losses_sum, expected_sum))
def test_focal_loss(self):
inputs = mx.array([[0.5, 0.5, 0.2, 0.9], [0.1, 0.3, 0.5, 0.5]])
targets = mx.array([[1, 0, 1, 0], [0, 1, 0, 1]])
alpha = 0.25
gamma = 2.0
# Test with reduction 'none'
losses_none = nn.losses.focal_loss(
inputs, targets, alpha, gamma, reduction="none"
)
expected_none = mx.array(
[
[0.0433217, 0.0433217, 0.25751, 0.466273],
[0.000263401, 0.147487, 0.0433217, 0.0433217],
]
)
self.assertTrue(mx.allclose(losses_none, expected_none))
# Test with reduction 'mean'
losses_mean = nn.losses.focal_loss(
inputs, targets, alpha, gamma, reduction="mean"
)
expected_mean = mx.mean(expected_none)
self.assertTrue(mx.allclose(losses_mean, expected_mean))
# Test with reduction 'sum'
losses_sum = nn.losses.focal_loss(
inputs, targets, alpha, gamma, reduction="sum"
)
expected_sum = mx.sum(expected_none)
self.assertTrue(mx.allclose(losses_sum, expected_sum))
def test_contrastive_loss(self):
embeddings1 = mx.array([[0.5, 0.5, 0.2, 0.9], [0.1, 0.3, 0.5, 0.5]])
embeddings2 = mx.array([[0.6, 0.4, 0.3, 0.8], [0.2, 0.5, 0.6, 0.4]])
targets = mx.array([1, 0])
margin = 1.0
# Test with reduction 'none'
losses_none = nn.losses.contrastive_loss(
embeddings1, embeddings2, targets, margin, reduction="none"
)
expected_none = mx.array([0.2, 0.735425])
self.assertTrue(mx.allclose(losses_none, expected_none))
# Test with reduction 'mean'
losses_mean = nn.losses.contrastive_loss(
embeddings1, embeddings2, targets, margin, reduction="mean"
)
expected_mean = mx.mean(expected_none)
self.assertTrue(mx.allclose(losses_mean, expected_mean))
# Test with reduction 'sum'
losses_sum = nn.losses.contrastive_loss(
embeddings1, embeddings2, targets, margin, reduction="sum"
)
expected_sum = mx.sum(expected_none)
self.assertTrue(mx.allclose(losses_sum, expected_sum))|
Hey @awni, |
|
@NripeshN let's table these losses for now. Since they are slightly more niche I think it is better to wait to understand the API requirements before adding them. Also the contrastive loss looks to me to be the same as the triplet loss which we already have (?). |
Hey there! Yeah, you're right, the contrastive loss formula I've implemented looks pretty similar to the triplet loss already in place. The core idea in both is to compare the distances between anchor-positive and anchor-negative pairs. But, there's a slight twist in their focus. Triplet loss simultaneously pulls the anchor closer to the positive and pushes it away from the negative. Contrastive loss, on the other hand, is more about just ensuring the anchor is closer to the positive than a defined margin, without explicitly considering the negative. In practice, they can end up doing similar things, but their approach and emphasis differ a bit. |
Add testing of `reciprocal`, `cumulative_prod` from 2024.12 spec revision
spec_decode_verify transforms and qmv_quad gs 128 coverage (gemma4-qat#323, ml-explore#324)
Proposed changes
Have added
focal_loss,dice_loss,iou_loss,contrastive_loss,tversky_lossChecklist
Put an
xin the boxes that apply.pre-commit run --all-filesto format my code / installed pre-commit prior to committing changes