Download tests/unit/tasks/test_classification.py from HWresearch/GNN4Colliders: direct link, hf CLI and curl.
- Browser
- Download file 3.41 kB
-
https://huggingface.co/HWresearch/GNN4Colliders/resolve/main/tests/unit/tasks/test_classification.py
- Command line
-
hf download hf://HWresearch/GNN4Colliders/tests/unit/tasks/test_classification.py
-
curl -L -o test_classification.py https://huggingface.co/HWresearch/GNN4Colliders/resolve/main/tests/unit/tasks/test_classification.py
3.41 kB
| from types import SimpleNamespace | |
| import pytest | |
| import torch | |
| from gnn4colliders.tasks import BinaryClassificationTask, MulticlassClassificationTask | |
| def _batch(labels, weights): | |
| return SimpleNamespace( | |
| labels=torch.tensor(labels), | |
| metadata=SimpleNamespace(weight=torch.tensor(weights)), | |
| ) | |
| def test_binary_normalizes_column_or_vector_targets_and_uses_named_weight(): | |
| task = BinaryClassificationTask() | |
| batch = _batch([[0], [0], [1], [1]], [1.0, 2.0, 1.0, 3.0]) | |
| logits = torch.tensor([[-1.0], [0.0], [0.5], [1.0]]) | |
| expected_elementwise = torch.nn.functional.binary_cross_entropy_with_logits( | |
| logits[:, 0], batch.labels[:, 0].float(), reduction="none" | |
| ) | |
| expected = ( | |
| (expected_elementwise[:2] * torch.tensor([1.0, 2.0])).sum() / 3 | |
| + (expected_elementwise[2:] * torch.tensor([1.0, 3.0])).sum() / 4 | |
| ) / 2 | |
| assert torch.allclose(task.loss(logits, batch), expected) | |
| def test_binary_negative_weights_are_not_absolute_by_default(): | |
| batch = _batch([0, 0, 1, 1], [1.0, -2.0, 1.0, 1.0]) | |
| logits = torch.tensor([[0.0], [2.0], [0.0], [2.0]]) | |
| task = BinaryClassificationTask() | |
| elementwise = torch.nn.functional.binary_cross_entropy_with_logits( | |
| logits[:, 0], batch.labels.float(), reduction="none" | |
| ) | |
| expected = ( | |
| (elementwise[:2] * torch.tensor([1.0, -2.0])).sum() / -1 | |
| + elementwise[2:].mean() | |
| ) / 2 | |
| assert task.loss(logits, batch).item() == pytest.approx(expected.item()) | |
| assert BinaryClassificationTask(absolute_weights=True).loss( | |
| logits, batch | |
| ).item() != pytest.approx(expected.item()) | |
| def test_binary_predictions_and_undefined_auc(): | |
| task = BinaryClassificationTask() | |
| batch = _batch([1, 1], [1.0, 2.0]) | |
| output = task.predictions(torch.tensor([[-2.0], [2.0]])) | |
| assert output["scores"].shape == (2,) | |
| assert output["predictions"].tolist() == [False, True] | |
| assert ( | |
| task.metrics(torch.tensor([[-2.0], [2.0]]), batch)["roc_auc"] | |
| != task.metrics(torch.tensor([[-2.0], [2.0]]), batch)["roc_auc"] | |
| ) | |
| def test_multiclass_loss_and_metrics(): | |
| task = MulticlassClassificationTask() | |
| batch = _batch([0, 1, 2], [1.0, 1.0, 1.0]) | |
| logits = torch.eye(3) | |
| assert task.loss(logits, batch).item() > 0 | |
| output = task.predict(logits) | |
| assert output["predictions"].tolist() == [0, 1, 2] | |
| assert task.metrics(logits, batch)["accuracy"] == 1.0 | |
| def test_binary_loss_is_invariant_under_joint_event_permutation(): | |
| task = BinaryClassificationTask() | |
| batch = _batch([0, 1, 0, 1], [1.0, 2.0, 3.0, 4.0]) | |
| logits = torch.tensor([[-2.0], [1.0], [0.5], [-0.5]]) | |
| permutation = torch.tensor([2, 0, 3, 1]) | |
| shuffled = _batch(batch.labels[permutation].tolist(), batch.metadata.weight[permutation].tolist()) | |
| assert task.loss(logits, batch) == pytest.approx(task.loss(logits[permutation], shuffled).item()) | |
| def test_multiclass_metrics_are_invariant_under_joint_event_permutation(): | |
| task = MulticlassClassificationTask() | |
| batch = _batch([0, 1, 2, 1], [1.0, 2.0, 1.0, 3.0]) | |
| logits = torch.tensor([[3.0, 0.0, 0.0], [0.0, 2.0, 0.0], [0.0, 0.0, 4.0], [0.0, 1.0, 0.0]]) | |
| permutation = torch.tensor([3, 0, 2, 1]) | |
| shuffled = _batch(batch.labels[permutation].tolist(), batch.metadata.weight[permutation].tolist()) | |
| assert task.metrics(logits, batch) == task.metrics(logits[permutation], shuffled) | |