From 09bb7927de5d3c12c9faa679858d628e6332829c Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Mon, 9 Nov 2020 19:26:57 +0900 Subject: [PATCH 01/54] Implemented GIoU --- pl_bolts/losses/giou.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 pl_bolts/losses/giou.py diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py new file mode 100644 index 0000000000..a54104a261 --- /dev/null +++ b/pl_bolts/losses/giou.py @@ -0,0 +1,38 @@ +""" +Generalized Intersection over Union (GIoU) loss (Rezatofighi et. al) +""" + +import torch + + +def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: + """ + Calculates the generalized intersection over union loss. + + Args: + pred: batch of prediction bounding boxes with representation + [x_min, y_min, x_max, y_max] + target: batch of target bounding boxes with representation + [x_min, y_min, x_max, y_max] + + Returns: + loss + """ + eps = 1e-6 + x_min = torch.max(pred[:, 0], target[:, 0]) + y_min = torch.max(pred[:, 1], target[:, 1]) + x_max = torch.min(pred[:, 2], target[:, 2]) + y_max = torch.min(pred[:, 3], target[:, 3]) + intersection = (x_max - x_min) * (y_max - y_min) + pred_area = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1]) + target_area = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1]) + union = pred_area + target_area - intersection + C_x_min = min(pred[:, 0], target[:, 0]) + C_y_min = min(pred[:, 1], target[:, 1]) + C_x_max = max(pred[:, 2], target[:, 2]) + C_y_max = max(pred[:, 3], target[:, 3]) + C_area = (C_x_max - C_x_min) * (C_y_max - C_y_min) + iou = torch.true_divide(intersection, union + eps) + giou = iou - torch.true_divide((C_area - union), C_area + eps) + + return 1 - giou From 53ba53cb497dfc91960901585f8d8a01b15acb87 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Sun, 15 Nov 2020 19:44:19 +0900 Subject: [PATCH 02/54] Changing max/min to torch.max/torch.min --- pl_bolts/losses/giou.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py index a54104a261..6a38171ada 100644 --- a/pl_bolts/losses/giou.py +++ b/pl_bolts/losses/giou.py @@ -27,10 +27,10 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: pred_area = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1]) target_area = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1]) union = pred_area + target_area - intersection - C_x_min = min(pred[:, 0], target[:, 0]) - C_y_min = min(pred[:, 1], target[:, 1]) - C_x_max = max(pred[:, 2], target[:, 2]) - C_y_max = max(pred[:, 3], target[:, 3]) + C_x_min = torch.min(pred[:, 0], target[:, 0]) + C_y_min = torch.min(pred[:, 1], target[:, 1]) + C_x_max = torch.max(pred[:, 2], target[:, 2]) + C_y_max = torch.max(pred[:, 3], target[:, 3]) C_area = (C_x_max - C_x_min) * (C_y_max - C_y_min) iou = torch.true_divide(intersection, union + eps) giou = iou - torch.true_divide((C_area - union), C_area + eps) From f886be06c8e6e8d73b8e63ae9e56762cd551caa5 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Sun, 15 Nov 2020 19:45:01 +0900 Subject: [PATCH 03/54] Adding tests for GIoU --- tests/losses/test_giou.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/losses/test_giou.py diff --git a/tests/losses/test_giou.py b/tests/losses/test_giou.py new file mode 100644 index 0000000000..56f1582a5e --- /dev/null +++ b/tests/losses/test_giou.py @@ -0,0 +1,23 @@ +""" +Test Generalized Intersection over Union +""" + +from unittest import TestCase +import torch + +from pl_bolts.losses.giou import giou_loss + + +class TestGIoULoss(TestCase): + def test_complete_overlap(self): + pred = torch.tensor([[100, 100, 200, 200]]) + target = torch.tensor([[100, 100, 200, 200]]) + torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([0.0])) + + def test_no_overlap(self): + pred = torch.tensor([[100, 100, 200, 200]]) + target = torch.tensor([[100, 200, 200, 300]]) + torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([1.0])) + pred = torch.tensor([[100, 100, 200, 200]]) + target = torch.tensor([[200, 200, 300, 300]]) + torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([1.5])) From 604c2131226bfbf2578be5b45ad9433c4f175230 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Sun, 15 Nov 2020 22:31:55 +0900 Subject: [PATCH 04/54] Adding documentation for GIoU --- docs/source/losses.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/source/losses.rst b/docs/source/losses.rst index 44b401dfcc..61eada8525 100644 --- a/docs/source/losses.rst +++ b/docs/source/losses.rst @@ -13,6 +13,20 @@ We're cleaning up many of our losses, but in the meantime, submit a PR to add yo ------------- +Object Detection +====================== +These are common losses used in object detection. + +--------------- + +GIoU Loss +--------- + +.. autofunction:: pl_bolts.losses.giou.giou_loss + :noindex: + +--------------- + Reinforcement Learning ====================== These are common losses used in RL. From bdb9d2a0fd3e7d8840b86ddbb8b44fc82e12ce8d Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Sun, 15 Nov 2020 22:09:37 +0900 Subject: [PATCH 05/54] Updated docstring Co-authored-by: Jeff Yang --- pl_bolts/losses/giou.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py index 6a38171ada..614ab7af23 100644 --- a/pl_bolts/losses/giou.py +++ b/pl_bolts/losses/giou.py @@ -16,7 +16,7 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: [x_min, y_min, x_max, y_max] Returns: - loss + GIoU loss """ eps = 1e-6 x_min = torch.max(pred[:, 0], target[:, 0]) From ec6a3905d5a7c1a1ddbadec355fc509d64941647 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Mon, 16 Nov 2020 12:59:35 +0900 Subject: [PATCH 06/54] Fixed isort error and added link to paper in docstring --- pl_bolts/losses/giou.py | 3 +++ tests/losses/test_giou.py | 1 + 2 files changed, 4 insertions(+) diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py index 614ab7af23..cb7edb93bb 100644 --- a/pl_bolts/losses/giou.py +++ b/pl_bolts/losses/giou.py @@ -9,6 +9,9 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: """ Calculates the generalized intersection over union loss. + It has been proposed in `Generalized Intersection over Union: A Metric and A + Loss for Bounding Box Regression `_. + Args: pred: batch of prediction bounding boxes with representation [x_min, y_min, x_max, y_max] diff --git a/tests/losses/test_giou.py b/tests/losses/test_giou.py index 56f1582a5e..47bdc96f2d 100644 --- a/tests/losses/test_giou.py +++ b/tests/losses/test_giou.py @@ -3,6 +3,7 @@ """ from unittest import TestCase + import torch from pl_bolts.losses.giou import giou_loss From 2b7fb786107d50319b8fb34c4a5b219149f51210 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Tue, 17 Nov 2020 14:39:20 +0900 Subject: [PATCH 07/54] Parametrizing tests using pytest --- tests/losses/test_giou.py | 44 ++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/tests/losses/test_giou.py b/tests/losses/test_giou.py index 47bdc96f2d..c6397adb88 100644 --- a/tests/losses/test_giou.py +++ b/tests/losses/test_giou.py @@ -5,20 +5,40 @@ from unittest import TestCase import torch +import pytest from pl_bolts.losses.giou import giou_loss -class TestGIoULoss(TestCase): - def test_complete_overlap(self): - pred = torch.tensor([[100, 100, 200, 200]]) - target = torch.tensor([[100, 100, 200, 200]]) - torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([0.0])) +@pytest.mark.parametrize( + "pred, target, expected_loss", + [ + ( + torch.tensor([[100, 100, 200, 200]]), + torch.tensor([[100, 100, 200, 200]]), + torch.tensor([0.0]), + ) + ], +) +def test_complete_overlap(pred, target, expected_loss): + torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) - def test_no_overlap(self): - pred = torch.tensor([[100, 100, 200, 200]]) - target = torch.tensor([[100, 200, 200, 300]]) - torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([1.0])) - pred = torch.tensor([[100, 100, 200, 200]]) - target = torch.tensor([[200, 200, 300, 300]]) - torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([1.5])) + +@pytest.mark.parametrize( + "pred, target, expected_loss", + [ + ( + torch.tensor([[100, 100, 200, 200]]), + torch.tensor([[100, 200, 200, 300]]), + torch.tensor([1.0]), + ), + ( + torch.tensor([[100, 100, 200, 200]]), + torch.tensor([[200, 200, 300, 300]]), + torch.tensor([1.5]), + ), + ], +) +def test_no_overlap(pred, target, expected_loss): + torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) + torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) From e64d51a06a340c02cfbf7f35203a9fa2d5b2567c Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Wed, 18 Nov 2020 11:40:26 +0900 Subject: [PATCH 08/54] Adding changelog and removing eps --- CHANGELOG.md | 2 ++ pl_bolts/losses/giou.py | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e986a3a5fc..4256c8623b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `VisionDataModule` as parent class for `BinaryMNISTDataModule`, `CIFAR10DataModule`, `FashionMNISTDataModule`, and `MNISTDataModule` ([#400](https://github.com/PyTorchLightning/pytorch-lightning-bolts/pull/400)) +- Added GIoU loss ([#347](https://github.com/PyTorchLightning/pytorch-lightning-bolts/pull/347)) + ### Changed - Decoupled datamodules from models ([#332](https://github.com/PyTorchLightning/pytorch-lightning-bolts/pull/332), [#270](https://github.com/PyTorchLightning/pytorch-lightning-bolts/pull/270)) diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py index cb7edb93bb..acaaf39a1f 100644 --- a/pl_bolts/losses/giou.py +++ b/pl_bolts/losses/giou.py @@ -21,7 +21,6 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: Returns: GIoU loss """ - eps = 1e-6 x_min = torch.max(pred[:, 0], target[:, 0]) y_min = torch.max(pred[:, 1], target[:, 1]) x_max = torch.min(pred[:, 2], target[:, 2]) @@ -36,6 +35,6 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: C_y_max = torch.max(pred[:, 3], target[:, 3]) C_area = (C_x_max - C_x_min) * (C_y_max - C_y_min) iou = torch.true_divide(intersection, union + eps) - giou = iou - torch.true_divide((C_area - union), C_area + eps) + giou = iou - torch.true_divide((C_area - union), C_area) return 1 - giou From 18ab5db93de6f41498920f5ba1cb563957510f85 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Wed, 18 Nov 2020 11:41:52 +0900 Subject: [PATCH 09/54] Fixing error --- pl_bolts/losses/giou.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py index acaaf39a1f..927608582d 100644 --- a/pl_bolts/losses/giou.py +++ b/pl_bolts/losses/giou.py @@ -34,7 +34,7 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: C_x_max = torch.max(pred[:, 2], target[:, 2]) C_y_max = torch.max(pred[:, 3], target[:, 3]) C_area = (C_x_max - C_x_min) * (C_y_max - C_y_min) - iou = torch.true_divide(intersection, union + eps) + iou = torch.true_divide(intersection, union) giou = iou - torch.true_divide((C_area - union), C_area) return 1 - giou From d44be4c28fe052a0d9af6a11cb6ee452d2c43dda Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Wed, 18 Nov 2020 11:45:36 +0900 Subject: [PATCH 10/54] isort test --- tests/losses/test_giou.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/losses/test_giou.py b/tests/losses/test_giou.py index c6397adb88..8e8ad39411 100644 --- a/tests/losses/test_giou.py +++ b/tests/losses/test_giou.py @@ -4,8 +4,8 @@ from unittest import TestCase -import torch import pytest +import torch from pl_bolts.losses.giou import giou_loss From 8fe5f2e17e0b7ccaa89903de3bf409e185c359a1 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Wed, 2 Dec 2020 21:43:19 +0900 Subject: [PATCH 11/54] Renaming file to object_detection.py --- pl_bolts/losses/{giou.py => object_detection.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pl_bolts/losses/{giou.py => object_detection.py} (100%) diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/object_detection.py similarity index 100% rename from pl_bolts/losses/giou.py rename to pl_bolts/losses/object_detection.py From f9fc3c75550a2c0faf95b6b17d5774efe44843dc Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Wed, 2 Dec 2020 21:46:36 +0900 Subject: [PATCH 12/54] Reflecting module name change in test --- tests/losses/{test_giou.py => test_object_detection.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/losses/{test_giou.py => test_object_detection.py} (95%) diff --git a/tests/losses/test_giou.py b/tests/losses/test_object_detection.py similarity index 95% rename from tests/losses/test_giou.py rename to tests/losses/test_object_detection.py index 8e8ad39411..fd98598787 100644 --- a/tests/losses/test_giou.py +++ b/tests/losses/test_object_detection.py @@ -7,7 +7,7 @@ import pytest import torch -from pl_bolts.losses.giou import giou_loss +from pl_bolts.losses.object_detection import giou_loss @pytest.mark.parametrize( From 08bd1271209443eb98325b65a387d9830bd59d29 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Wed, 2 Dec 2020 21:53:54 +0900 Subject: [PATCH 13/54] Fixing doc to reflect module name change --- docs/source/losses.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/losses.rst b/docs/source/losses.rst index 61eada8525..901bb71964 100644 --- a/docs/source/losses.rst +++ b/docs/source/losses.rst @@ -22,7 +22,7 @@ These are common losses used in object detection. GIoU Loss --------- -.. autofunction:: pl_bolts.losses.giou.giou_loss +.. autofunction:: pl_bolts.losses.object_detection.giou_loss :noindex: --------------- From f25e4f5fa998c4ce79e666cb0577978eac0dace3 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Mon, 9 Nov 2020 19:26:57 +0900 Subject: [PATCH 14/54] Implemented GIoU --- pl_bolts/losses/giou.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 pl_bolts/losses/giou.py diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py new file mode 100644 index 0000000000..a54104a261 --- /dev/null +++ b/pl_bolts/losses/giou.py @@ -0,0 +1,38 @@ +""" +Generalized Intersection over Union (GIoU) loss (Rezatofighi et. al) +""" + +import torch + + +def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: + """ + Calculates the generalized intersection over union loss. + + Args: + pred: batch of prediction bounding boxes with representation + [x_min, y_min, x_max, y_max] + target: batch of target bounding boxes with representation + [x_min, y_min, x_max, y_max] + + Returns: + loss + """ + eps = 1e-6 + x_min = torch.max(pred[:, 0], target[:, 0]) + y_min = torch.max(pred[:, 1], target[:, 1]) + x_max = torch.min(pred[:, 2], target[:, 2]) + y_max = torch.min(pred[:, 3], target[:, 3]) + intersection = (x_max - x_min) * (y_max - y_min) + pred_area = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1]) + target_area = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1]) + union = pred_area + target_area - intersection + C_x_min = min(pred[:, 0], target[:, 0]) + C_y_min = min(pred[:, 1], target[:, 1]) + C_x_max = max(pred[:, 2], target[:, 2]) + C_y_max = max(pred[:, 3], target[:, 3]) + C_area = (C_x_max - C_x_min) * (C_y_max - C_y_min) + iou = torch.true_divide(intersection, union + eps) + giou = iou - torch.true_divide((C_area - union), C_area + eps) + + return 1 - giou From a4878dbeed9bb638be8019aae9c022e717efa81c Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Sun, 15 Nov 2020 19:44:19 +0900 Subject: [PATCH 15/54] Changing max/min to torch.max/torch.min --- pl_bolts/losses/giou.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py index a54104a261..6a38171ada 100644 --- a/pl_bolts/losses/giou.py +++ b/pl_bolts/losses/giou.py @@ -27,10 +27,10 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: pred_area = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1]) target_area = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1]) union = pred_area + target_area - intersection - C_x_min = min(pred[:, 0], target[:, 0]) - C_y_min = min(pred[:, 1], target[:, 1]) - C_x_max = max(pred[:, 2], target[:, 2]) - C_y_max = max(pred[:, 3], target[:, 3]) + C_x_min = torch.min(pred[:, 0], target[:, 0]) + C_y_min = torch.min(pred[:, 1], target[:, 1]) + C_x_max = torch.max(pred[:, 2], target[:, 2]) + C_y_max = torch.max(pred[:, 3], target[:, 3]) C_area = (C_x_max - C_x_min) * (C_y_max - C_y_min) iou = torch.true_divide(intersection, union + eps) giou = iou - torch.true_divide((C_area - union), C_area + eps) From 417f5544a87c9ae0ce09a9e5e830c95d4dbab475 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Sun, 15 Nov 2020 19:45:01 +0900 Subject: [PATCH 16/54] Adding tests for GIoU --- tests/losses/test_giou.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/losses/test_giou.py diff --git a/tests/losses/test_giou.py b/tests/losses/test_giou.py new file mode 100644 index 0000000000..56f1582a5e --- /dev/null +++ b/tests/losses/test_giou.py @@ -0,0 +1,23 @@ +""" +Test Generalized Intersection over Union +""" + +from unittest import TestCase +import torch + +from pl_bolts.losses.giou import giou_loss + + +class TestGIoULoss(TestCase): + def test_complete_overlap(self): + pred = torch.tensor([[100, 100, 200, 200]]) + target = torch.tensor([[100, 100, 200, 200]]) + torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([0.0])) + + def test_no_overlap(self): + pred = torch.tensor([[100, 100, 200, 200]]) + target = torch.tensor([[100, 200, 200, 300]]) + torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([1.0])) + pred = torch.tensor([[100, 100, 200, 200]]) + target = torch.tensor([[200, 200, 300, 300]]) + torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([1.5])) From fb7dce51f28f49e81038ff2935790651be44822d Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Sun, 15 Nov 2020 22:09:37 +0900 Subject: [PATCH 17/54] Updated docstring Co-authored-by: Jeff Yang --- pl_bolts/losses/giou.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py index 6a38171ada..614ab7af23 100644 --- a/pl_bolts/losses/giou.py +++ b/pl_bolts/losses/giou.py @@ -16,7 +16,7 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: [x_min, y_min, x_max, y_max] Returns: - loss + GIoU loss """ eps = 1e-6 x_min = torch.max(pred[:, 0], target[:, 0]) From 09c051449c28e88db0e497ee81da3d668e482b98 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Mon, 16 Nov 2020 12:59:35 +0900 Subject: [PATCH 18/54] Fixed isort error and added link to paper in docstring --- pl_bolts/losses/giou.py | 3 +++ tests/losses/test_giou.py | 1 + 2 files changed, 4 insertions(+) diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py index 614ab7af23..cb7edb93bb 100644 --- a/pl_bolts/losses/giou.py +++ b/pl_bolts/losses/giou.py @@ -9,6 +9,9 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: """ Calculates the generalized intersection over union loss. + It has been proposed in `Generalized Intersection over Union: A Metric and A + Loss for Bounding Box Regression `_. + Args: pred: batch of prediction bounding boxes with representation [x_min, y_min, x_max, y_max] diff --git a/tests/losses/test_giou.py b/tests/losses/test_giou.py index 56f1582a5e..47bdc96f2d 100644 --- a/tests/losses/test_giou.py +++ b/tests/losses/test_giou.py @@ -3,6 +3,7 @@ """ from unittest import TestCase + import torch from pl_bolts.losses.giou import giou_loss From 3b6ea8079a83bb2b4132b3faaa303ae2a747cf14 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Tue, 17 Nov 2020 14:39:20 +0900 Subject: [PATCH 19/54] Parametrizing tests using pytest --- tests/losses/test_giou.py | 44 ++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/tests/losses/test_giou.py b/tests/losses/test_giou.py index 47bdc96f2d..c6397adb88 100644 --- a/tests/losses/test_giou.py +++ b/tests/losses/test_giou.py @@ -5,20 +5,40 @@ from unittest import TestCase import torch +import pytest from pl_bolts.losses.giou import giou_loss -class TestGIoULoss(TestCase): - def test_complete_overlap(self): - pred = torch.tensor([[100, 100, 200, 200]]) - target = torch.tensor([[100, 100, 200, 200]]) - torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([0.0])) +@pytest.mark.parametrize( + "pred, target, expected_loss", + [ + ( + torch.tensor([[100, 100, 200, 200]]), + torch.tensor([[100, 100, 200, 200]]), + torch.tensor([0.0]), + ) + ], +) +def test_complete_overlap(pred, target, expected_loss): + torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) - def test_no_overlap(self): - pred = torch.tensor([[100, 100, 200, 200]]) - target = torch.tensor([[100, 200, 200, 300]]) - torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([1.0])) - pred = torch.tensor([[100, 100, 200, 200]]) - target = torch.tensor([[200, 200, 300, 300]]) - torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([1.5])) + +@pytest.mark.parametrize( + "pred, target, expected_loss", + [ + ( + torch.tensor([[100, 100, 200, 200]]), + torch.tensor([[100, 200, 200, 300]]), + torch.tensor([1.0]), + ), + ( + torch.tensor([[100, 100, 200, 200]]), + torch.tensor([[200, 200, 300, 300]]), + torch.tensor([1.5]), + ), + ], +) +def test_no_overlap(pred, target, expected_loss): + torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) + torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) From 1d23707b02796a4950a1cac0955e863182a2d50f Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Wed, 18 Nov 2020 11:40:26 +0900 Subject: [PATCH 20/54] Adding changelog and removing eps --- pl_bolts/losses/giou.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py index cb7edb93bb..acaaf39a1f 100644 --- a/pl_bolts/losses/giou.py +++ b/pl_bolts/losses/giou.py @@ -21,7 +21,6 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: Returns: GIoU loss """ - eps = 1e-6 x_min = torch.max(pred[:, 0], target[:, 0]) y_min = torch.max(pred[:, 1], target[:, 1]) x_max = torch.min(pred[:, 2], target[:, 2]) @@ -36,6 +35,6 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: C_y_max = torch.max(pred[:, 3], target[:, 3]) C_area = (C_x_max - C_x_min) * (C_y_max - C_y_min) iou = torch.true_divide(intersection, union + eps) - giou = iou - torch.true_divide((C_area - union), C_area + eps) + giou = iou - torch.true_divide((C_area - union), C_area) return 1 - giou From aa9f8467983128dc864552b7933676878ab4468c Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Wed, 18 Nov 2020 11:41:52 +0900 Subject: [PATCH 21/54] Fixing error --- pl_bolts/losses/giou.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py index acaaf39a1f..927608582d 100644 --- a/pl_bolts/losses/giou.py +++ b/pl_bolts/losses/giou.py @@ -34,7 +34,7 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: C_x_max = torch.max(pred[:, 2], target[:, 2]) C_y_max = torch.max(pred[:, 3], target[:, 3]) C_area = (C_x_max - C_x_min) * (C_y_max - C_y_min) - iou = torch.true_divide(intersection, union + eps) + iou = torch.true_divide(intersection, union) giou = iou - torch.true_divide((C_area - union), C_area) return 1 - giou From c871a4e4234b01886f29f5409fef37d9b02c6185 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Wed, 18 Nov 2020 11:45:36 +0900 Subject: [PATCH 22/54] isort test --- tests/losses/test_giou.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/losses/test_giou.py b/tests/losses/test_giou.py index c6397adb88..8e8ad39411 100644 --- a/tests/losses/test_giou.py +++ b/tests/losses/test_giou.py @@ -4,8 +4,8 @@ from unittest import TestCase -import torch import pytest +import torch from pl_bolts.losses.giou import giou_loss From a2d915e4532098e72be0eb77b8e89a63b6dee408 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Wed, 2 Dec 2020 21:43:19 +0900 Subject: [PATCH 23/54] Renaming file to object_detection.py --- pl_bolts/losses/giou.py | 40 ---------------------------------------- 1 file changed, 40 deletions(-) delete mode 100644 pl_bolts/losses/giou.py diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py deleted file mode 100644 index 927608582d..0000000000 --- a/pl_bolts/losses/giou.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Generalized Intersection over Union (GIoU) loss (Rezatofighi et. al) -""" - -import torch - - -def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: - """ - Calculates the generalized intersection over union loss. - - It has been proposed in `Generalized Intersection over Union: A Metric and A - Loss for Bounding Box Regression `_. - - Args: - pred: batch of prediction bounding boxes with representation - [x_min, y_min, x_max, y_max] - target: batch of target bounding boxes with representation - [x_min, y_min, x_max, y_max] - - Returns: - GIoU loss - """ - x_min = torch.max(pred[:, 0], target[:, 0]) - y_min = torch.max(pred[:, 1], target[:, 1]) - x_max = torch.min(pred[:, 2], target[:, 2]) - y_max = torch.min(pred[:, 3], target[:, 3]) - intersection = (x_max - x_min) * (y_max - y_min) - pred_area = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1]) - target_area = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1]) - union = pred_area + target_area - intersection - C_x_min = torch.min(pred[:, 0], target[:, 0]) - C_y_min = torch.min(pred[:, 1], target[:, 1]) - C_x_max = torch.max(pred[:, 2], target[:, 2]) - C_y_max = torch.max(pred[:, 3], target[:, 3]) - C_area = (C_x_max - C_x_min) * (C_y_max - C_y_min) - iou = torch.true_divide(intersection, union) - giou = iou - torch.true_divide((C_area - union), C_area) - - return 1 - giou From 6723ee5029ca5be689ced6fd546d2cd2eb8e68c9 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Wed, 2 Dec 2020 21:46:36 +0900 Subject: [PATCH 24/54] Reflecting module name change in test --- tests/losses/test_giou.py | 44 --------------------------------------- 1 file changed, 44 deletions(-) delete mode 100644 tests/losses/test_giou.py diff --git a/tests/losses/test_giou.py b/tests/losses/test_giou.py deleted file mode 100644 index 8e8ad39411..0000000000 --- a/tests/losses/test_giou.py +++ /dev/null @@ -1,44 +0,0 @@ -""" -Test Generalized Intersection over Union -""" - -from unittest import TestCase - -import pytest -import torch - -from pl_bolts.losses.giou import giou_loss - - -@pytest.mark.parametrize( - "pred, target, expected_loss", - [ - ( - torch.tensor([[100, 100, 200, 200]]), - torch.tensor([[100, 100, 200, 200]]), - torch.tensor([0.0]), - ) - ], -) -def test_complete_overlap(pred, target, expected_loss): - torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) - - -@pytest.mark.parametrize( - "pred, target, expected_loss", - [ - ( - torch.tensor([[100, 100, 200, 200]]), - torch.tensor([[100, 200, 200, 300]]), - torch.tensor([1.0]), - ), - ( - torch.tensor([[100, 100, 200, 200]]), - torch.tensor([[200, 200, 300, 300]]), - torch.tensor([1.5]), - ), - ], -) -def test_no_overlap(pred, target, expected_loss): - torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) - torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) From a7a3aaca0807f858b18b29e7c937decf81a817c7 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Mon, 9 Nov 2020 19:26:57 +0900 Subject: [PATCH 25/54] Implemented GIoU --- pl_bolts/losses/giou.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 pl_bolts/losses/giou.py diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py new file mode 100644 index 0000000000..a54104a261 --- /dev/null +++ b/pl_bolts/losses/giou.py @@ -0,0 +1,38 @@ +""" +Generalized Intersection over Union (GIoU) loss (Rezatofighi et. al) +""" + +import torch + + +def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: + """ + Calculates the generalized intersection over union loss. + + Args: + pred: batch of prediction bounding boxes with representation + [x_min, y_min, x_max, y_max] + target: batch of target bounding boxes with representation + [x_min, y_min, x_max, y_max] + + Returns: + loss + """ + eps = 1e-6 + x_min = torch.max(pred[:, 0], target[:, 0]) + y_min = torch.max(pred[:, 1], target[:, 1]) + x_max = torch.min(pred[:, 2], target[:, 2]) + y_max = torch.min(pred[:, 3], target[:, 3]) + intersection = (x_max - x_min) * (y_max - y_min) + pred_area = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1]) + target_area = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1]) + union = pred_area + target_area - intersection + C_x_min = min(pred[:, 0], target[:, 0]) + C_y_min = min(pred[:, 1], target[:, 1]) + C_x_max = max(pred[:, 2], target[:, 2]) + C_y_max = max(pred[:, 3], target[:, 3]) + C_area = (C_x_max - C_x_min) * (C_y_max - C_y_min) + iou = torch.true_divide(intersection, union + eps) + giou = iou - torch.true_divide((C_area - union), C_area + eps) + + return 1 - giou From 4f3db7f989176249376ea8243a970734077fc96e Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Sun, 15 Nov 2020 19:44:19 +0900 Subject: [PATCH 26/54] Changing max/min to torch.max/torch.min --- pl_bolts/losses/giou.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py index a54104a261..6a38171ada 100644 --- a/pl_bolts/losses/giou.py +++ b/pl_bolts/losses/giou.py @@ -27,10 +27,10 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: pred_area = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1]) target_area = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1]) union = pred_area + target_area - intersection - C_x_min = min(pred[:, 0], target[:, 0]) - C_y_min = min(pred[:, 1], target[:, 1]) - C_x_max = max(pred[:, 2], target[:, 2]) - C_y_max = max(pred[:, 3], target[:, 3]) + C_x_min = torch.min(pred[:, 0], target[:, 0]) + C_y_min = torch.min(pred[:, 1], target[:, 1]) + C_x_max = torch.max(pred[:, 2], target[:, 2]) + C_y_max = torch.max(pred[:, 3], target[:, 3]) C_area = (C_x_max - C_x_min) * (C_y_max - C_y_min) iou = torch.true_divide(intersection, union + eps) giou = iou - torch.true_divide((C_area - union), C_area + eps) From be6163903e0648eb7415b46cb1ffe4b04bef47fc Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Sun, 15 Nov 2020 19:45:01 +0900 Subject: [PATCH 27/54] Adding tests for GIoU --- tests/losses/test_giou.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/losses/test_giou.py diff --git a/tests/losses/test_giou.py b/tests/losses/test_giou.py new file mode 100644 index 0000000000..56f1582a5e --- /dev/null +++ b/tests/losses/test_giou.py @@ -0,0 +1,23 @@ +""" +Test Generalized Intersection over Union +""" + +from unittest import TestCase +import torch + +from pl_bolts.losses.giou import giou_loss + + +class TestGIoULoss(TestCase): + def test_complete_overlap(self): + pred = torch.tensor([[100, 100, 200, 200]]) + target = torch.tensor([[100, 100, 200, 200]]) + torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([0.0])) + + def test_no_overlap(self): + pred = torch.tensor([[100, 100, 200, 200]]) + target = torch.tensor([[100, 200, 200, 300]]) + torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([1.0])) + pred = torch.tensor([[100, 100, 200, 200]]) + target = torch.tensor([[200, 200, 300, 300]]) + torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([1.5])) From d9b6e645cf968df9b092b0bc8c70be685087f91d Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Sun, 15 Nov 2020 22:09:37 +0900 Subject: [PATCH 28/54] Updated docstring Co-authored-by: Jeff Yang --- pl_bolts/losses/giou.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py index 6a38171ada..614ab7af23 100644 --- a/pl_bolts/losses/giou.py +++ b/pl_bolts/losses/giou.py @@ -16,7 +16,7 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: [x_min, y_min, x_max, y_max] Returns: - loss + GIoU loss """ eps = 1e-6 x_min = torch.max(pred[:, 0], target[:, 0]) From 394261f9e73e86cfe4cef503e6479b1dce93c24f Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Mon, 16 Nov 2020 12:59:35 +0900 Subject: [PATCH 29/54] Fixed isort error and added link to paper in docstring --- pl_bolts/losses/giou.py | 3 +++ tests/losses/test_giou.py | 1 + 2 files changed, 4 insertions(+) diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py index 614ab7af23..cb7edb93bb 100644 --- a/pl_bolts/losses/giou.py +++ b/pl_bolts/losses/giou.py @@ -9,6 +9,9 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: """ Calculates the generalized intersection over union loss. + It has been proposed in `Generalized Intersection over Union: A Metric and A + Loss for Bounding Box Regression `_. + Args: pred: batch of prediction bounding boxes with representation [x_min, y_min, x_max, y_max] diff --git a/tests/losses/test_giou.py b/tests/losses/test_giou.py index 56f1582a5e..47bdc96f2d 100644 --- a/tests/losses/test_giou.py +++ b/tests/losses/test_giou.py @@ -3,6 +3,7 @@ """ from unittest import TestCase + import torch from pl_bolts.losses.giou import giou_loss From cc940ab4c3e83422e69b13c093df844a964e8e11 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Tue, 17 Nov 2020 14:39:20 +0900 Subject: [PATCH 30/54] Parametrizing tests using pytest --- tests/losses/test_giou.py | 44 ++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/tests/losses/test_giou.py b/tests/losses/test_giou.py index 47bdc96f2d..c6397adb88 100644 --- a/tests/losses/test_giou.py +++ b/tests/losses/test_giou.py @@ -5,20 +5,40 @@ from unittest import TestCase import torch +import pytest from pl_bolts.losses.giou import giou_loss -class TestGIoULoss(TestCase): - def test_complete_overlap(self): - pred = torch.tensor([[100, 100, 200, 200]]) - target = torch.tensor([[100, 100, 200, 200]]) - torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([0.0])) +@pytest.mark.parametrize( + "pred, target, expected_loss", + [ + ( + torch.tensor([[100, 100, 200, 200]]), + torch.tensor([[100, 100, 200, 200]]), + torch.tensor([0.0]), + ) + ], +) +def test_complete_overlap(pred, target, expected_loss): + torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) - def test_no_overlap(self): - pred = torch.tensor([[100, 100, 200, 200]]) - target = torch.tensor([[100, 200, 200, 300]]) - torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([1.0])) - pred = torch.tensor([[100, 100, 200, 200]]) - target = torch.tensor([[200, 200, 300, 300]]) - torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([1.5])) + +@pytest.mark.parametrize( + "pred, target, expected_loss", + [ + ( + torch.tensor([[100, 100, 200, 200]]), + torch.tensor([[100, 200, 200, 300]]), + torch.tensor([1.0]), + ), + ( + torch.tensor([[100, 100, 200, 200]]), + torch.tensor([[200, 200, 300, 300]]), + torch.tensor([1.5]), + ), + ], +) +def test_no_overlap(pred, target, expected_loss): + torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) + torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) From cc2f48635b160b4e8c669f8200d7e85b633ee09a Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Wed, 18 Nov 2020 11:40:26 +0900 Subject: [PATCH 31/54] Adding changelog and removing eps --- pl_bolts/losses/giou.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py index cb7edb93bb..acaaf39a1f 100644 --- a/pl_bolts/losses/giou.py +++ b/pl_bolts/losses/giou.py @@ -21,7 +21,6 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: Returns: GIoU loss """ - eps = 1e-6 x_min = torch.max(pred[:, 0], target[:, 0]) y_min = torch.max(pred[:, 1], target[:, 1]) x_max = torch.min(pred[:, 2], target[:, 2]) @@ -36,6 +35,6 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: C_y_max = torch.max(pred[:, 3], target[:, 3]) C_area = (C_x_max - C_x_min) * (C_y_max - C_y_min) iou = torch.true_divide(intersection, union + eps) - giou = iou - torch.true_divide((C_area - union), C_area + eps) + giou = iou - torch.true_divide((C_area - union), C_area) return 1 - giou From b80828ca5cc34b5be9090087ed8bea9263be1748 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Wed, 18 Nov 2020 11:41:52 +0900 Subject: [PATCH 32/54] Fixing error --- pl_bolts/losses/giou.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py index acaaf39a1f..927608582d 100644 --- a/pl_bolts/losses/giou.py +++ b/pl_bolts/losses/giou.py @@ -34,7 +34,7 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: C_x_max = torch.max(pred[:, 2], target[:, 2]) C_y_max = torch.max(pred[:, 3], target[:, 3]) C_area = (C_x_max - C_x_min) * (C_y_max - C_y_min) - iou = torch.true_divide(intersection, union + eps) + iou = torch.true_divide(intersection, union) giou = iou - torch.true_divide((C_area - union), C_area) return 1 - giou From d289d9d20a18d15f6f30d80e78e52e5fb56bc208 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Wed, 18 Nov 2020 11:45:36 +0900 Subject: [PATCH 33/54] isort test --- tests/losses/test_giou.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/losses/test_giou.py b/tests/losses/test_giou.py index c6397adb88..8e8ad39411 100644 --- a/tests/losses/test_giou.py +++ b/tests/losses/test_giou.py @@ -4,8 +4,8 @@ from unittest import TestCase -import torch import pytest +import torch from pl_bolts.losses.giou import giou_loss From e0a68c4dd6e7ba7d0ada4b4f627b65f9ba5635b1 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Wed, 2 Dec 2020 21:43:19 +0900 Subject: [PATCH 34/54] Renaming file to object_detection.py --- pl_bolts/losses/giou.py | 40 ---------------------------------------- 1 file changed, 40 deletions(-) delete mode 100644 pl_bolts/losses/giou.py diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py deleted file mode 100644 index 927608582d..0000000000 --- a/pl_bolts/losses/giou.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Generalized Intersection over Union (GIoU) loss (Rezatofighi et. al) -""" - -import torch - - -def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: - """ - Calculates the generalized intersection over union loss. - - It has been proposed in `Generalized Intersection over Union: A Metric and A - Loss for Bounding Box Regression `_. - - Args: - pred: batch of prediction bounding boxes with representation - [x_min, y_min, x_max, y_max] - target: batch of target bounding boxes with representation - [x_min, y_min, x_max, y_max] - - Returns: - GIoU loss - """ - x_min = torch.max(pred[:, 0], target[:, 0]) - y_min = torch.max(pred[:, 1], target[:, 1]) - x_max = torch.min(pred[:, 2], target[:, 2]) - y_max = torch.min(pred[:, 3], target[:, 3]) - intersection = (x_max - x_min) * (y_max - y_min) - pred_area = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1]) - target_area = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1]) - union = pred_area + target_area - intersection - C_x_min = torch.min(pred[:, 0], target[:, 0]) - C_y_min = torch.min(pred[:, 1], target[:, 1]) - C_x_max = torch.max(pred[:, 2], target[:, 2]) - C_y_max = torch.max(pred[:, 3], target[:, 3]) - C_area = (C_x_max - C_x_min) * (C_y_max - C_y_min) - iou = torch.true_divide(intersection, union) - giou = iou - torch.true_divide((C_area - union), C_area) - - return 1 - giou From 7f7bec9ff7b48d56d481728b021bc456d99fd674 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Wed, 2 Dec 2020 21:46:36 +0900 Subject: [PATCH 35/54] Reflecting module name change in test --- tests/losses/test_giou.py | 44 --------------------------------------- 1 file changed, 44 deletions(-) delete mode 100644 tests/losses/test_giou.py diff --git a/tests/losses/test_giou.py b/tests/losses/test_giou.py deleted file mode 100644 index 8e8ad39411..0000000000 --- a/tests/losses/test_giou.py +++ /dev/null @@ -1,44 +0,0 @@ -""" -Test Generalized Intersection over Union -""" - -from unittest import TestCase - -import pytest -import torch - -from pl_bolts.losses.giou import giou_loss - - -@pytest.mark.parametrize( - "pred, target, expected_loss", - [ - ( - torch.tensor([[100, 100, 200, 200]]), - torch.tensor([[100, 100, 200, 200]]), - torch.tensor([0.0]), - ) - ], -) -def test_complete_overlap(pred, target, expected_loss): - torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) - - -@pytest.mark.parametrize( - "pred, target, expected_loss", - [ - ( - torch.tensor([[100, 100, 200, 200]]), - torch.tensor([[100, 200, 200, 300]]), - torch.tensor([1.0]), - ), - ( - torch.tensor([[100, 100, 200, 200]]), - torch.tensor([[200, 200, 300, 300]]), - torch.tensor([1.5]), - ), - ], -) -def test_no_overlap(pred, target, expected_loss): - torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) - torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) From 8ebd93b4f5a3c1a1069d5a95e105965ba7b9753c Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Mon, 9 Nov 2020 19:26:57 +0900 Subject: [PATCH 36/54] Implemented GIoU --- pl_bolts/losses/giou.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 pl_bolts/losses/giou.py diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py new file mode 100644 index 0000000000..a54104a261 --- /dev/null +++ b/pl_bolts/losses/giou.py @@ -0,0 +1,38 @@ +""" +Generalized Intersection over Union (GIoU) loss (Rezatofighi et. al) +""" + +import torch + + +def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: + """ + Calculates the generalized intersection over union loss. + + Args: + pred: batch of prediction bounding boxes with representation + [x_min, y_min, x_max, y_max] + target: batch of target bounding boxes with representation + [x_min, y_min, x_max, y_max] + + Returns: + loss + """ + eps = 1e-6 + x_min = torch.max(pred[:, 0], target[:, 0]) + y_min = torch.max(pred[:, 1], target[:, 1]) + x_max = torch.min(pred[:, 2], target[:, 2]) + y_max = torch.min(pred[:, 3], target[:, 3]) + intersection = (x_max - x_min) * (y_max - y_min) + pred_area = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1]) + target_area = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1]) + union = pred_area + target_area - intersection + C_x_min = min(pred[:, 0], target[:, 0]) + C_y_min = min(pred[:, 1], target[:, 1]) + C_x_max = max(pred[:, 2], target[:, 2]) + C_y_max = max(pred[:, 3], target[:, 3]) + C_area = (C_x_max - C_x_min) * (C_y_max - C_y_min) + iou = torch.true_divide(intersection, union + eps) + giou = iou - torch.true_divide((C_area - union), C_area + eps) + + return 1 - giou From b4ac0e174bb9608cfa77b3efec9241aacf511977 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Sun, 15 Nov 2020 19:44:19 +0900 Subject: [PATCH 37/54] Changing max/min to torch.max/torch.min --- pl_bolts/losses/giou.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py index a54104a261..6a38171ada 100644 --- a/pl_bolts/losses/giou.py +++ b/pl_bolts/losses/giou.py @@ -27,10 +27,10 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: pred_area = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1]) target_area = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1]) union = pred_area + target_area - intersection - C_x_min = min(pred[:, 0], target[:, 0]) - C_y_min = min(pred[:, 1], target[:, 1]) - C_x_max = max(pred[:, 2], target[:, 2]) - C_y_max = max(pred[:, 3], target[:, 3]) + C_x_min = torch.min(pred[:, 0], target[:, 0]) + C_y_min = torch.min(pred[:, 1], target[:, 1]) + C_x_max = torch.max(pred[:, 2], target[:, 2]) + C_y_max = torch.max(pred[:, 3], target[:, 3]) C_area = (C_x_max - C_x_min) * (C_y_max - C_y_min) iou = torch.true_divide(intersection, union + eps) giou = iou - torch.true_divide((C_area - union), C_area + eps) From b32b369e7b0fef5c27a624be67f690723d6d47e3 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Sun, 15 Nov 2020 19:45:01 +0900 Subject: [PATCH 38/54] Adding tests for GIoU --- tests/losses/test_giou.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/losses/test_giou.py diff --git a/tests/losses/test_giou.py b/tests/losses/test_giou.py new file mode 100644 index 0000000000..56f1582a5e --- /dev/null +++ b/tests/losses/test_giou.py @@ -0,0 +1,23 @@ +""" +Test Generalized Intersection over Union +""" + +from unittest import TestCase +import torch + +from pl_bolts.losses.giou import giou_loss + + +class TestGIoULoss(TestCase): + def test_complete_overlap(self): + pred = torch.tensor([[100, 100, 200, 200]]) + target = torch.tensor([[100, 100, 200, 200]]) + torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([0.0])) + + def test_no_overlap(self): + pred = torch.tensor([[100, 100, 200, 200]]) + target = torch.tensor([[100, 200, 200, 300]]) + torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([1.0])) + pred = torch.tensor([[100, 100, 200, 200]]) + target = torch.tensor([[200, 200, 300, 300]]) + torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([1.5])) From b5e5d85a6ea8f9828cdb22d311e2775599b93de9 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Sun, 15 Nov 2020 22:09:37 +0900 Subject: [PATCH 39/54] Updated docstring Co-authored-by: Jeff Yang --- pl_bolts/losses/giou.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py index 6a38171ada..614ab7af23 100644 --- a/pl_bolts/losses/giou.py +++ b/pl_bolts/losses/giou.py @@ -16,7 +16,7 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: [x_min, y_min, x_max, y_max] Returns: - loss + GIoU loss """ eps = 1e-6 x_min = torch.max(pred[:, 0], target[:, 0]) From 2e29634ae47553626fa96b7224b76f40e93d33bf Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Mon, 16 Nov 2020 12:59:35 +0900 Subject: [PATCH 40/54] Fixed isort error and added link to paper in docstring --- pl_bolts/losses/giou.py | 3 +++ tests/losses/test_giou.py | 1 + 2 files changed, 4 insertions(+) diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py index 614ab7af23..cb7edb93bb 100644 --- a/pl_bolts/losses/giou.py +++ b/pl_bolts/losses/giou.py @@ -9,6 +9,9 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: """ Calculates the generalized intersection over union loss. + It has been proposed in `Generalized Intersection over Union: A Metric and A + Loss for Bounding Box Regression `_. + Args: pred: batch of prediction bounding boxes with representation [x_min, y_min, x_max, y_max] diff --git a/tests/losses/test_giou.py b/tests/losses/test_giou.py index 56f1582a5e..47bdc96f2d 100644 --- a/tests/losses/test_giou.py +++ b/tests/losses/test_giou.py @@ -3,6 +3,7 @@ """ from unittest import TestCase + import torch from pl_bolts.losses.giou import giou_loss From d61ed9b6e3b2bc05a24fc0a034c5d752af5c12ef Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Tue, 17 Nov 2020 14:39:20 +0900 Subject: [PATCH 41/54] Parametrizing tests using pytest --- tests/losses/test_giou.py | 44 ++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/tests/losses/test_giou.py b/tests/losses/test_giou.py index 47bdc96f2d..c6397adb88 100644 --- a/tests/losses/test_giou.py +++ b/tests/losses/test_giou.py @@ -5,20 +5,40 @@ from unittest import TestCase import torch +import pytest from pl_bolts.losses.giou import giou_loss -class TestGIoULoss(TestCase): - def test_complete_overlap(self): - pred = torch.tensor([[100, 100, 200, 200]]) - target = torch.tensor([[100, 100, 200, 200]]) - torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([0.0])) +@pytest.mark.parametrize( + "pred, target, expected_loss", + [ + ( + torch.tensor([[100, 100, 200, 200]]), + torch.tensor([[100, 100, 200, 200]]), + torch.tensor([0.0]), + ) + ], +) +def test_complete_overlap(pred, target, expected_loss): + torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) - def test_no_overlap(self): - pred = torch.tensor([[100, 100, 200, 200]]) - target = torch.tensor([[100, 200, 200, 300]]) - torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([1.0])) - pred = torch.tensor([[100, 100, 200, 200]]) - target = torch.tensor([[200, 200, 300, 300]]) - torch.testing.assert_allclose(giou_loss(pred, target), torch.tensor([1.5])) + +@pytest.mark.parametrize( + "pred, target, expected_loss", + [ + ( + torch.tensor([[100, 100, 200, 200]]), + torch.tensor([[100, 200, 200, 300]]), + torch.tensor([1.0]), + ), + ( + torch.tensor([[100, 100, 200, 200]]), + torch.tensor([[200, 200, 300, 300]]), + torch.tensor([1.5]), + ), + ], +) +def test_no_overlap(pred, target, expected_loss): + torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) + torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) From b6a4dbb88e0fdbd2aaf69c5d07bdb340e670c828 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Wed, 18 Nov 2020 11:40:26 +0900 Subject: [PATCH 42/54] Adding changelog and removing eps --- pl_bolts/losses/giou.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py index cb7edb93bb..acaaf39a1f 100644 --- a/pl_bolts/losses/giou.py +++ b/pl_bolts/losses/giou.py @@ -21,7 +21,6 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: Returns: GIoU loss """ - eps = 1e-6 x_min = torch.max(pred[:, 0], target[:, 0]) y_min = torch.max(pred[:, 1], target[:, 1]) x_max = torch.min(pred[:, 2], target[:, 2]) @@ -36,6 +35,6 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: C_y_max = torch.max(pred[:, 3], target[:, 3]) C_area = (C_x_max - C_x_min) * (C_y_max - C_y_min) iou = torch.true_divide(intersection, union + eps) - giou = iou - torch.true_divide((C_area - union), C_area + eps) + giou = iou - torch.true_divide((C_area - union), C_area) return 1 - giou From a42e1115a62d1d182d6e86d5c86118bb7e8f2592 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Wed, 18 Nov 2020 11:41:52 +0900 Subject: [PATCH 43/54] Fixing error --- pl_bolts/losses/giou.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py index acaaf39a1f..927608582d 100644 --- a/pl_bolts/losses/giou.py +++ b/pl_bolts/losses/giou.py @@ -34,7 +34,7 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: C_x_max = torch.max(pred[:, 2], target[:, 2]) C_y_max = torch.max(pred[:, 3], target[:, 3]) C_area = (C_x_max - C_x_min) * (C_y_max - C_y_min) - iou = torch.true_divide(intersection, union + eps) + iou = torch.true_divide(intersection, union) giou = iou - torch.true_divide((C_area - union), C_area) return 1 - giou From 8370947db01814600ba599314c920ff1fa9f6e3a Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Wed, 18 Nov 2020 11:45:36 +0900 Subject: [PATCH 44/54] isort test --- tests/losses/test_giou.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/losses/test_giou.py b/tests/losses/test_giou.py index c6397adb88..8e8ad39411 100644 --- a/tests/losses/test_giou.py +++ b/tests/losses/test_giou.py @@ -4,8 +4,8 @@ from unittest import TestCase -import torch import pytest +import torch from pl_bolts.losses.giou import giou_loss From 721e73446d14407e65a9d0952775f20fd4aae63f Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Wed, 2 Dec 2020 21:43:19 +0900 Subject: [PATCH 45/54] Renaming file to object_detection.py --- pl_bolts/losses/giou.py | 40 ---------------------------------------- 1 file changed, 40 deletions(-) delete mode 100644 pl_bolts/losses/giou.py diff --git a/pl_bolts/losses/giou.py b/pl_bolts/losses/giou.py deleted file mode 100644 index 927608582d..0000000000 --- a/pl_bolts/losses/giou.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Generalized Intersection over Union (GIoU) loss (Rezatofighi et. al) -""" - -import torch - - -def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: - """ - Calculates the generalized intersection over union loss. - - It has been proposed in `Generalized Intersection over Union: A Metric and A - Loss for Bounding Box Regression `_. - - Args: - pred: batch of prediction bounding boxes with representation - [x_min, y_min, x_max, y_max] - target: batch of target bounding boxes with representation - [x_min, y_min, x_max, y_max] - - Returns: - GIoU loss - """ - x_min = torch.max(pred[:, 0], target[:, 0]) - y_min = torch.max(pred[:, 1], target[:, 1]) - x_max = torch.min(pred[:, 2], target[:, 2]) - y_max = torch.min(pred[:, 3], target[:, 3]) - intersection = (x_max - x_min) * (y_max - y_min) - pred_area = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1]) - target_area = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1]) - union = pred_area + target_area - intersection - C_x_min = torch.min(pred[:, 0], target[:, 0]) - C_y_min = torch.min(pred[:, 1], target[:, 1]) - C_x_max = torch.max(pred[:, 2], target[:, 2]) - C_y_max = torch.max(pred[:, 3], target[:, 3]) - C_area = (C_x_max - C_x_min) * (C_y_max - C_y_min) - iou = torch.true_divide(intersection, union) - giou = iou - torch.true_divide((C_area - union), C_area) - - return 1 - giou From edffe9f5d4ca40eec409eabb9e22a55c68479ab7 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Wed, 2 Dec 2020 21:46:36 +0900 Subject: [PATCH 46/54] Reflecting module name change in test --- tests/losses/test_giou.py | 44 --------------------------------------- 1 file changed, 44 deletions(-) delete mode 100644 tests/losses/test_giou.py diff --git a/tests/losses/test_giou.py b/tests/losses/test_giou.py deleted file mode 100644 index 8e8ad39411..0000000000 --- a/tests/losses/test_giou.py +++ /dev/null @@ -1,44 +0,0 @@ -""" -Test Generalized Intersection over Union -""" - -from unittest import TestCase - -import pytest -import torch - -from pl_bolts.losses.giou import giou_loss - - -@pytest.mark.parametrize( - "pred, target, expected_loss", - [ - ( - torch.tensor([[100, 100, 200, 200]]), - torch.tensor([[100, 100, 200, 200]]), - torch.tensor([0.0]), - ) - ], -) -def test_complete_overlap(pred, target, expected_loss): - torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) - - -@pytest.mark.parametrize( - "pred, target, expected_loss", - [ - ( - torch.tensor([[100, 100, 200, 200]]), - torch.tensor([[100, 200, 200, 300]]), - torch.tensor([1.0]), - ), - ( - torch.tensor([[100, 100, 200, 200]]), - torch.tensor([[200, 200, 300, 300]]), - torch.tensor([1.5]), - ), - ], -) -def test_no_overlap(pred, target, expected_loss): - torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) - torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) From f1d1646dce2ac258461ed2c3be97145d7424b8da Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Tue, 15 Dec 2020 14:55:57 +0900 Subject: [PATCH 47/54] Update pl_bolts/losses/object_detection.py Co-authored-by: Jirka Borovec --- pl_bolts/losses/object_detection.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pl_bolts/losses/object_detection.py b/pl_bolts/losses/object_detection.py index 927608582d..e2ffd4fce3 100644 --- a/pl_bolts/losses/object_detection.py +++ b/pl_bolts/losses/object_detection.py @@ -13,10 +13,8 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: Loss for Bounding Box Regression `_. Args: - pred: batch of prediction bounding boxes with representation - [x_min, y_min, x_max, y_max] - target: batch of target bounding boxes with representation - [x_min, y_min, x_max, y_max] + pred: batch of prediction bounding boxes with representation ``[x_min, y_min, x_max, y_max]`` + target: batch of target bounding boxes with representation ``[x_min, y_min, x_max, y_max]`` Returns: GIoU loss From 637792bd4d01e233892014488ed2ee51a33c5363 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Tue, 15 Dec 2020 15:28:57 +0900 Subject: [PATCH 48/54] Updating docstring for giou --- pl_bolts/metrics/object_detection.py | 32 ++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 pl_bolts/metrics/object_detection.py diff --git a/pl_bolts/metrics/object_detection.py b/pl_bolts/metrics/object_detection.py new file mode 100644 index 0000000000..9d456e3df6 --- /dev/null +++ b/pl_bolts/metrics/object_detection.py @@ -0,0 +1,32 @@ +import torch + + +def giou(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: + """ + Calculates the generalized intersection over union. + + It has been proposed in `Generalized Intersection over Union: A Metric and A + Loss for Bounding Box Regression `_. + + Args: + pred: batch of prediction bounding boxes with representation ``[x_min, y_min, x_max, y_max]`` + target: batch of target bounding boxes with representation ``[x_min, y_min, x_max, y_max]`` + + Returns: + GIoU value + """ + x_min = torch.max(pred[:, None, 0], target[:, 0]) + y_min = torch.max(pred[:, None, 1], target[:, 1]) + x_max = torch.min(pred[:, None, 2], target[:, 2]) + y_max = torch.min(pred[:, None, 3], target[:, 3]) + intersection = (x_max - x_min).clamp(min=0) * (y_max - y_min).clamp(min=0) + pred_area = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1]) + target_area = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1]) + union = pred_area[:, None] + target_area - intersection + C_x_min = torch.min(pred[:, None, 0], target[:, 0]) + C_y_min = torch.min(pred[:, None, 1], target[:, 1]) + C_x_max = torch.max(pred[:, None, 2], target[:, 2]) + C_y_max = torch.max(pred[:, None, 3], target[:, 3]) + C_area = (C_x_max - C_x_min).clamp(min=0) * (C_y_max - C_y_min).clamp(min=0) + iou = torch.true_divide(intersection, union) + return iou - torch.true_divide((C_area - union), C_area) \ No newline at end of file From 34c1987079a8acaf7eb72b843b972ee45d9d1306 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Tue, 15 Dec 2020 15:45:26 +0900 Subject: [PATCH 49/54] Fixing code formatting --- pl_bolts/losses/object_detection.py | 1 + pl_bolts/metrics/object_detection.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pl_bolts/losses/object_detection.py b/pl_bolts/losses/object_detection.py index e2ffd4fce3..75d3d5972d 100644 --- a/pl_bolts/losses/object_detection.py +++ b/pl_bolts/losses/object_detection.py @@ -5,6 +5,7 @@ import torch + def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: """ Calculates the generalized intersection over union loss. diff --git a/pl_bolts/metrics/object_detection.py b/pl_bolts/metrics/object_detection.py index 9d456e3df6..b4db69b4c5 100644 --- a/pl_bolts/metrics/object_detection.py +++ b/pl_bolts/metrics/object_detection.py @@ -29,4 +29,4 @@ def giou(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: C_y_max = torch.max(pred[:, None, 3], target[:, 3]) C_area = (C_x_max - C_x_min).clamp(min=0) * (C_y_max - C_y_min).clamp(min=0) iou = torch.true_divide(intersection, union) - return iou - torch.true_divide((C_area - union), C_area) \ No newline at end of file + return iou - torch.true_divide((C_area - union), C_area) From 730bcdc7f707eee4bf3d33a1752c8bfc25418aca Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Tue, 15 Dec 2020 16:10:33 +0900 Subject: [PATCH 50/54] Adding doctest --- pl_bolts/losses/object_detection.py | 9 +++++++++ pl_bolts/metrics/object_detection.py | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/pl_bolts/losses/object_detection.py b/pl_bolts/losses/object_detection.py index 75d3d5972d..3cb8932aa1 100644 --- a/pl_bolts/losses/object_detection.py +++ b/pl_bolts/losses/object_detection.py @@ -17,6 +17,15 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: pred: batch of prediction bounding boxes with representation ``[x_min, y_min, x_max, y_max]`` target: batch of target bounding boxes with representation ``[x_min, y_min, x_max, y_max]`` + Example: + + >>> import torch + >>> from pl_bolts.losses.object_detection import giou_loss + >>> pred = torch.tensor([[100, 100, 200, 200]]) + >>> target = torch.tensor([[150, 150, 250, 250]]) + >>> giou_loss(pred, target) + tensor([[1.0794]]) + Returns: GIoU loss """ diff --git a/pl_bolts/metrics/object_detection.py b/pl_bolts/metrics/object_detection.py index b4db69b4c5..4012b02699 100644 --- a/pl_bolts/metrics/object_detection.py +++ b/pl_bolts/metrics/object_detection.py @@ -12,6 +12,15 @@ def giou(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: pred: batch of prediction bounding boxes with representation ``[x_min, y_min, x_max, y_max]`` target: batch of target bounding boxes with representation ``[x_min, y_min, x_max, y_max]`` + Example: + + >>> import torch + >>> from pl_bolts.metrics.object_detection import giou + >>> pred = torch.tensor([[100, 100, 200, 200]]) + >>> target = torch.tensor([[150, 150, 250, 250]]) + >>> giou(pred, target) + tensor([[-0.0794]]) + Returns: GIoU value """ From 8fa52f71a8df9071f018325c89e879ba82c97cd1 Mon Sep 17 00:00:00 2001 From: Brian Ko Date: Tue, 15 Dec 2020 16:22:47 +0900 Subject: [PATCH 51/54] Adding tests for giou --- tests/losses/test_object_detection.py | 5 +--- tests/metrics/__init__.py | 0 tests/metrics/test_object_detection.py | 41 ++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 tests/metrics/__init__.py create mode 100644 tests/metrics/test_object_detection.py diff --git a/tests/losses/test_object_detection.py b/tests/losses/test_object_detection.py index fd98598787..a66cb474a2 100644 --- a/tests/losses/test_object_detection.py +++ b/tests/losses/test_object_detection.py @@ -1,9 +1,7 @@ """ -Test Generalized Intersection over Union +Test Object Detection Loss Functions """ -from unittest import TestCase - import pytest import torch @@ -41,4 +39,3 @@ def test_complete_overlap(pred, target, expected_loss): ) def test_no_overlap(pred, target, expected_loss): torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) - torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) diff --git a/tests/metrics/__init__.py b/tests/metrics/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/metrics/test_object_detection.py b/tests/metrics/test_object_detection.py new file mode 100644 index 0000000000..d273817ad7 --- /dev/null +++ b/tests/metrics/test_object_detection.py @@ -0,0 +1,41 @@ +""" +Test Object Detection Metric Functions +""" + +import pytest +import torch + +from pl_bolts.metrics.object_detection import giou + + +@pytest.mark.parametrize( + "pred, target, expected_giou", + [ + ( + torch.tensor([[100, 100, 200, 200]]), + torch.tensor([[100, 100, 200, 200]]), + torch.tensor([1.0]), + ) + ], +) +def test_complete_overlap(pred, target, expected_giou): + torch.testing.assert_allclose(giou(pred, target), expected_giou) + + +@pytest.mark.parametrize( + "pred, target, expected_giou", + [ + ( + torch.tensor([[100, 100, 200, 200]]), + torch.tensor([[100, 200, 200, 300]]), + torch.tensor([0.0]), + ), + ( + torch.tensor([[100, 100, 200, 200]]), + torch.tensor([[200, 200, 300, 300]]), + torch.tensor([-0.5]), + ), + ], +) +def test_no_overlap(pred, target, expected_giou): + torch.testing.assert_allclose(giou(pred, target), expected_giou) From ebda4b057dde38f3aca345bc9bf85ebfc9bf8c10 Mon Sep 17 00:00:00 2001 From: Jirka Borovec Date: Sun, 20 Dec 2020 23:57:04 +0100 Subject: [PATCH 52/54] refactor --- pl_bolts/losses/object_detection.py | 28 +++++++------------------ pl_bolts/metrics/object_detection.py | 29 +++++++++++++------------- tests/losses/test_object_detection.py | 4 ++-- tests/metrics/test_object_detection.py | 4 ++-- 4 files changed, 26 insertions(+), 39 deletions(-) diff --git a/pl_bolts/losses/object_detection.py b/pl_bolts/losses/object_detection.py index 3cb8932aa1..81d0404813 100644 --- a/pl_bolts/losses/object_detection.py +++ b/pl_bolts/losses/object_detection.py @@ -4,9 +4,10 @@ import torch +from pl_bolts.metrics.object_detection import giou -def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: +def giou_loss(preds: torch.Tensor, target: torch.Tensor) -> torch.Tensor: """ Calculates the generalized intersection over union loss. @@ -14,35 +15,20 @@ def giou_loss(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: Loss for Bounding Box Regression `_. Args: - pred: batch of prediction bounding boxes with representation ``[x_min, y_min, x_max, y_max]`` + preds: batch of prediction bounding boxes with representation ``[x_min, y_min, x_max, y_max]`` target: batch of target bounding boxes with representation ``[x_min, y_min, x_max, y_max]`` Example: >>> import torch >>> from pl_bolts.losses.object_detection import giou_loss - >>> pred = torch.tensor([[100, 100, 200, 200]]) + >>> preds = torch.tensor([[100, 100, 200, 200]]) >>> target = torch.tensor([[150, 150, 250, 250]]) - >>> giou_loss(pred, target) + >>> giou_loss(preds, target) tensor([[1.0794]]) Returns: GIoU loss """ - x_min = torch.max(pred[:, 0], target[:, 0]) - y_min = torch.max(pred[:, 1], target[:, 1]) - x_max = torch.min(pred[:, 2], target[:, 2]) - y_max = torch.min(pred[:, 3], target[:, 3]) - intersection = (x_max - x_min) * (y_max - y_min) - pred_area = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1]) - target_area = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1]) - union = pred_area + target_area - intersection - C_x_min = torch.min(pred[:, 0], target[:, 0]) - C_y_min = torch.min(pred[:, 1], target[:, 1]) - C_x_max = torch.max(pred[:, 2], target[:, 2]) - C_y_max = torch.max(pred[:, 3], target[:, 3]) - C_area = (C_x_max - C_x_min) * (C_y_max - C_y_min) - iou = torch.true_divide(intersection, union) - giou = iou - torch.true_divide((C_area - union), C_area) - - return 1 - giou + loss = 1 - giou(preds, target) + return loss diff --git a/pl_bolts/metrics/object_detection.py b/pl_bolts/metrics/object_detection.py index 4012b02699..3175f3ce24 100644 --- a/pl_bolts/metrics/object_detection.py +++ b/pl_bolts/metrics/object_detection.py @@ -1,7 +1,7 @@ import torch -def giou(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: +def giou(preds: torch.Tensor, target: torch.Tensor) -> torch.Tensor: """ Calculates the generalized intersection over union. @@ -9,33 +9,34 @@ def giou(pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor: Loss for Bounding Box Regression `_. Args: - pred: batch of prediction bounding boxes with representation ``[x_min, y_min, x_max, y_max]`` + preds: batch of prediction bounding boxes with representation ``[x_min, y_min, x_max, y_max]`` target: batch of target bounding boxes with representation ``[x_min, y_min, x_max, y_max]`` Example: >>> import torch >>> from pl_bolts.metrics.object_detection import giou - >>> pred = torch.tensor([[100, 100, 200, 200]]) + >>> preds = torch.tensor([[100, 100, 200, 200]]) >>> target = torch.tensor([[150, 150, 250, 250]]) - >>> giou(pred, target) + >>> giou(preds, target) tensor([[-0.0794]]) Returns: GIoU value """ - x_min = torch.max(pred[:, None, 0], target[:, 0]) - y_min = torch.max(pred[:, None, 1], target[:, 1]) - x_max = torch.min(pred[:, None, 2], target[:, 2]) - y_max = torch.min(pred[:, None, 3], target[:, 3]) + x_min = torch.max(preds[:, None, 0], target[:, 0]) + y_min = torch.max(preds[:, None, 1], target[:, 1]) + x_max = torch.min(preds[:, None, 2], target[:, 2]) + y_max = torch.min(preds[:, None, 3], target[:, 3]) intersection = (x_max - x_min).clamp(min=0) * (y_max - y_min).clamp(min=0) - pred_area = (pred[:, 2] - pred[:, 0]) * (pred[:, 3] - pred[:, 1]) + pred_area = (preds[:, 2] - preds[:, 0]) * (preds[:, 3] - preds[:, 1]) target_area = (target[:, 2] - target[:, 0]) * (target[:, 3] - target[:, 1]) union = pred_area[:, None] + target_area - intersection - C_x_min = torch.min(pred[:, None, 0], target[:, 0]) - C_y_min = torch.min(pred[:, None, 1], target[:, 1]) - C_x_max = torch.max(pred[:, None, 2], target[:, 2]) - C_y_max = torch.max(pred[:, None, 3], target[:, 3]) + C_x_min = torch.min(preds[:, None, 0], target[:, 0]) + C_y_min = torch.min(preds[:, None, 1], target[:, 1]) + C_x_max = torch.max(preds[:, None, 2], target[:, 2]) + C_y_max = torch.max(preds[:, None, 3], target[:, 3]) C_area = (C_x_max - C_x_min).clamp(min=0) * (C_y_max - C_y_min).clamp(min=0) iou = torch.true_divide(intersection, union) - return iou - torch.true_divide((C_area - union), C_area) + giou = iou - torch.true_divide((C_area - union), C_area) + return giou diff --git a/tests/losses/test_object_detection.py b/tests/losses/test_object_detection.py index a66cb474a2..cb502701b6 100644 --- a/tests/losses/test_object_detection.py +++ b/tests/losses/test_object_detection.py @@ -9,7 +9,7 @@ @pytest.mark.parametrize( - "pred, target, expected_loss", + "preds, target, expected_loss", [ ( torch.tensor([[100, 100, 200, 200]]), @@ -23,7 +23,7 @@ def test_complete_overlap(pred, target, expected_loss): @pytest.mark.parametrize( - "pred, target, expected_loss", + "preds, target, expected_loss", [ ( torch.tensor([[100, 100, 200, 200]]), diff --git a/tests/metrics/test_object_detection.py b/tests/metrics/test_object_detection.py index d273817ad7..0a3e0c798c 100644 --- a/tests/metrics/test_object_detection.py +++ b/tests/metrics/test_object_detection.py @@ -9,7 +9,7 @@ @pytest.mark.parametrize( - "pred, target, expected_giou", + "preds, target, expected_giou", [ ( torch.tensor([[100, 100, 200, 200]]), @@ -23,7 +23,7 @@ def test_complete_overlap(pred, target, expected_giou): @pytest.mark.parametrize( - "pred, target, expected_giou", + "preds, target, expected_giou", [ ( torch.tensor([[100, 100, 200, 200]]), From d684d2b4b2281eaacd7ff837487a98a27c1ac51d Mon Sep 17 00:00:00 2001 From: Jirka Borovec Date: Mon, 21 Dec 2020 00:09:31 +0100 Subject: [PATCH 53/54] fix --- tests/losses/test_object_detection.py | 8 ++++---- tests/metrics/test_object_detection.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/losses/test_object_detection.py b/tests/losses/test_object_detection.py index cb502701b6..085e12986f 100644 --- a/tests/losses/test_object_detection.py +++ b/tests/losses/test_object_detection.py @@ -18,8 +18,8 @@ ) ], ) -def test_complete_overlap(pred, target, expected_loss): - torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) +def test_complete_overlap(preds, target, expected_loss): + torch.testing.assert_allclose(giou_loss(preds, target), expected_loss) @pytest.mark.parametrize( @@ -37,5 +37,5 @@ def test_complete_overlap(pred, target, expected_loss): ), ], ) -def test_no_overlap(pred, target, expected_loss): - torch.testing.assert_allclose(giou_loss(pred, target), expected_loss) +def test_no_overlap(preds, target, expected_loss): + torch.testing.assert_allclose(giou_loss(preds, target), expected_loss) diff --git a/tests/metrics/test_object_detection.py b/tests/metrics/test_object_detection.py index 0a3e0c798c..c41ea322a4 100644 --- a/tests/metrics/test_object_detection.py +++ b/tests/metrics/test_object_detection.py @@ -18,8 +18,8 @@ ) ], ) -def test_complete_overlap(pred, target, expected_giou): - torch.testing.assert_allclose(giou(pred, target), expected_giou) +def test_complete_overlap(preds, target, expected_giou): + torch.testing.assert_allclose(giou(preds, target), expected_giou) @pytest.mark.parametrize( @@ -37,5 +37,5 @@ def test_complete_overlap(pred, target, expected_giou): ), ], ) -def test_no_overlap(pred, target, expected_giou): - torch.testing.assert_allclose(giou(pred, target), expected_giou) +def test_no_overlap(preds, target, expected_giou): + torch.testing.assert_allclose(giou(preds, target), expected_giou) From 1e0b2d141c0317f4c437fba3de940b75c738088f Mon Sep 17 00:00:00 2001 From: Jirka Borovec Date: Mon, 21 Dec 2020 00:19:33 +0100 Subject: [PATCH 54/54] format --- tests/losses/test_object_detection.py | 32 ++++++-------------------- tests/metrics/test_object_detection.py | 32 ++++++-------------------- 2 files changed, 14 insertions(+), 50 deletions(-) diff --git a/tests/losses/test_object_detection.py b/tests/losses/test_object_detection.py index 085e12986f..30f0ab4576 100644 --- a/tests/losses/test_object_detection.py +++ b/tests/losses/test_object_detection.py @@ -8,34 +8,16 @@ from pl_bolts.losses.object_detection import giou_loss -@pytest.mark.parametrize( - "preds, target, expected_loss", - [ - ( - torch.tensor([[100, 100, 200, 200]]), - torch.tensor([[100, 100, 200, 200]]), - torch.tensor([0.0]), - ) - ], -) +@pytest.mark.parametrize("preds, target, expected_loss", [ + (torch.tensor([[100, 100, 200, 200]]), torch.tensor([[100, 100, 200, 200]]), torch.tensor([0.0])) +]) def test_complete_overlap(preds, target, expected_loss): torch.testing.assert_allclose(giou_loss(preds, target), expected_loss) -@pytest.mark.parametrize( - "preds, target, expected_loss", - [ - ( - torch.tensor([[100, 100, 200, 200]]), - torch.tensor([[100, 200, 200, 300]]), - torch.tensor([1.0]), - ), - ( - torch.tensor([[100, 100, 200, 200]]), - torch.tensor([[200, 200, 300, 300]]), - torch.tensor([1.5]), - ), - ], -) +@pytest.mark.parametrize("preds, target, expected_loss", [ + (torch.tensor([[100, 100, 200, 200]]), torch.tensor([[100, 200, 200, 300]]), torch.tensor([1.0])), + (torch.tensor([[100, 100, 200, 200]]), torch.tensor([[200, 200, 300, 300]]), torch.tensor([1.5])), +]) def test_no_overlap(preds, target, expected_loss): torch.testing.assert_allclose(giou_loss(preds, target), expected_loss) diff --git a/tests/metrics/test_object_detection.py b/tests/metrics/test_object_detection.py index c41ea322a4..a998502314 100644 --- a/tests/metrics/test_object_detection.py +++ b/tests/metrics/test_object_detection.py @@ -8,34 +8,16 @@ from pl_bolts.metrics.object_detection import giou -@pytest.mark.parametrize( - "preds, target, expected_giou", - [ - ( - torch.tensor([[100, 100, 200, 200]]), - torch.tensor([[100, 100, 200, 200]]), - torch.tensor([1.0]), - ) - ], -) +@pytest.mark.parametrize("preds, target, expected_giou", [ + (torch.tensor([[100, 100, 200, 200]]), torch.tensor([[100, 100, 200, 200]]), torch.tensor([1.0])) +]) def test_complete_overlap(preds, target, expected_giou): torch.testing.assert_allclose(giou(preds, target), expected_giou) -@pytest.mark.parametrize( - "preds, target, expected_giou", - [ - ( - torch.tensor([[100, 100, 200, 200]]), - torch.tensor([[100, 200, 200, 300]]), - torch.tensor([0.0]), - ), - ( - torch.tensor([[100, 100, 200, 200]]), - torch.tensor([[200, 200, 300, 300]]), - torch.tensor([-0.5]), - ), - ], -) +@pytest.mark.parametrize("preds, target, expected_giou", [ + (torch.tensor([[100, 100, 200, 200]]), torch.tensor([[100, 200, 200, 300]]), torch.tensor([0.0])), + (torch.tensor([[100, 100, 200, 200]]), torch.tensor([[200, 200, 300, 300]]), torch.tensor([-0.5])), +]) def test_no_overlap(preds, target, expected_giou): torch.testing.assert_allclose(giou(preds, target), expected_giou)