Skip to content

Commit 657e662

Browse files
authored
Merge pull request #2688 from Trusted-AI/dev_1.20.1
Update to ART 1.20.1
2 parents 6aba944 + cc40aae commit 657e662

File tree

4 files changed

+42
-39
lines changed

4 files changed

+42
-39
lines changed

art/estimators/object_detection/pytorch_object_detector.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ def __init__(
6666
"loss_rpn_box_reg",
6767
),
6868
device_type: str = "gpu",
69-
is_yolov8: bool = False,
7069
):
7170
"""
7271
Initialization.
@@ -94,7 +93,6 @@ def __init__(
9493
'loss_objectness', and 'loss_rpn_box_reg'.
9594
:param device_type: Type of device to be used for model and tensors, if `cpu` run on CPU, if `gpu` run on GPU
9695
if available otherwise run on CPU.
97-
:param is_yolov8: The flag to be used for marking the YOLOv8 model.
9896
"""
9997
import torch
10098
import torchvision
@@ -139,11 +137,7 @@ def __init__(
139137

140138
self._model: torch.nn.Module
141139
self._model.to(self._device)
142-
self.is_yolov8 = is_yolov8
143-
if self.is_yolov8:
144-
self._model.model.eval() # type: ignore
145-
else:
146-
self._model.eval()
140+
self._model.eval()
147141

148142
@property
149143
def native_label_is_pytorch_format(self) -> bool:
@@ -412,10 +406,7 @@ def predict(self, x: np.ndarray, batch_size: int = 128, **kwargs) -> list[dict[s
412406
from torch.utils.data import TensorDataset, DataLoader
413407

414408
# Set model to evaluation mode
415-
if self.is_yolov8:
416-
self._model.model.eval() # type: ignore
417-
else:
418-
self._model.eval()
409+
self._model.eval()
419410

420411
# Apply preprocessing and convert to tensors
421412
x_preprocessed, _ = self._preprocess_and_convert_inputs(x=x, y=None, fit=False, no_grad=True)

art/estimators/object_detection/pytorch_yolo.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def __init__(
6464
"loss_rpn_box_reg",
6565
),
6666
device_type: str = "gpu",
67-
is_yolov8: bool = False,
67+
is_ultralytics: bool = False,
6868
model_name: str | None = None,
6969
):
7070
"""
@@ -94,11 +94,9 @@ def __init__(
9494
'loss_objectness', and 'loss_rpn_box_reg'.
9595
:param device_type: Type of device to be used for model and tensors, if `cpu` run on CPU, if `gpu` run on GPU
9696
if available otherwise run on CPU.
97-
:param is_yolov8: The flag to be used for marking the YOLOv8+ model.
9897
:param model_name: The name of the model (e.g., 'yolov8n', 'yolov10n') for determining loss function.
9998
"""
100-
# Wrap the model with YoloWrapper if it's a YOLO v8+ model
101-
if is_yolov8:
99+
if is_ultralytics:
102100
from art.estimators.object_detection.pytorch_yolo_loss_wrapper import PyTorchYoloLossWrapper
103101

104102
model = PyTorchYoloLossWrapper(model, model_name)
@@ -114,7 +112,6 @@ def __init__(
114112
preprocessing=preprocessing,
115113
attack_losses=attack_losses,
116114
device_type=device_type,
117-
is_yolov8=is_yolov8,
118115
)
119116

120117
def _translate_labels(self, labels: list[dict[str, "torch.Tensor"]]) -> "torch.Tensor":

art/estimators/object_detection/pytorch_yolo_loss_wrapper.py

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,21 +42,14 @@ def __init__(self, model, name):
4242
raise ImportError("The 'ultralytics' package is required for YOLO v8+ models but not installed.") from e
4343

4444
def forward(self, x, targets=None):
45+
"""Transforms the target to dict expected by model.loss"""
4546
if self.training:
46-
boxes = []
47-
labels = []
48-
indices = []
49-
for i, item in enumerate(targets):
50-
boxes.append(item["boxes"])
51-
labels.append(item["labels"])
52-
indices = indices + ([i] * len(item["labels"]))
53-
items = {
54-
"boxes": torch.cat(boxes) / x.shape[2],
55-
"labels": torch.cat(labels).type(torch.float32),
56-
"batch_idx": torch.tensor(indices),
57-
}
58-
items["bboxes"] = items.pop("boxes")
59-
items["cls"] = items.pop("labels")
47+
if targets is None:
48+
raise ValueError("Targets should not be None when training.")
49+
items = {}
50+
items["batch_idx"] = targets[:, 0]
51+
items["bboxes"] = targets[:, 2:6]
52+
items["cls"] = targets[:, 1]
6053
items["img"] = x
6154
loss, loss_components = self.model.loss(items)
6255
loss_components_dict = {"loss_total": loss.sum()}

tests/estimators/object_detection/test_pytorch_yolo_loss_wrapper.py

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,23 @@ def test_yolov8_loss_wrapper():
4141
x = torch.randn((batch_size, 3, 640, 640)) # YOLOv8 expects (B, 3, 640, 640)
4242

4343
# Create targets
44-
targets = []
44+
"""targets = []
4545
for _ in range(batch_size):
4646
boxes = torch.tensor([[0.1, 0.1, 0.3, 0.3], [0.5, 0.5, 0.8, 0.8]]) # [x1, y1, x2, y2]
4747
labels = torch.zeros(2, dtype=torch.long) # Use class 0 for testing
48-
targets.append({"boxes": boxes, "labels": labels})
48+
targets.append({"boxes": boxes, "labels": labels})"""
49+
targets = torch.tensor(
50+
[
51+
[0.0000, 20.0000, 0.7738, 0.3919, 0.4525, 0.7582],
52+
[0.0000, 20.0000, 0.2487, 0.4062, 0.4966, 0.5787],
53+
[0.0000, 20.0000, 0.5667, 0.2772, 0.0791, 0.2313],
54+
[0.0000, 20.0000, 0.1009, 0.1955, 0.2002, 0.0835],
55+
[1.0000, 20.0000, 0.7738, 0.3919, 0.4525, 0.7582],
56+
[1.0000, 20.0000, 0.2487, 0.4062, 0.4966, 0.5787],
57+
[1.0000, 20.0000, 0.5667, 0.2772, 0.0791, 0.2313],
58+
[1.0000, 20.0000, 0.1009, 0.1955, 0.2002, 0.0835],
59+
]
60+
)
4961

5062
# Test training mode
5163
losses = wrapper(x, targets)
@@ -94,11 +106,23 @@ def test_yolov10_loss_wrapper():
94106
x = torch.randn((batch_size, 3, 640, 640)) # Standard YOLO input size
95107

96108
# Create targets
97-
targets = []
109+
"""targets = []
98110
for _ in range(batch_size):
99111
boxes = torch.tensor([[0.1, 0.1, 0.3, 0.3], [0.5, 0.5, 0.8, 0.8]]) # [x1, y1, x2, y2]
100112
labels = torch.zeros(2, dtype=torch.long) # Use class 0 for testing
101-
targets.append({"boxes": boxes, "labels": labels})
113+
targets.append({"boxes": boxes, "labels": labels})"""
114+
targets = torch.tensor(
115+
[
116+
[0.0000, 20.0000, 0.7738, 0.3919, 0.4525, 0.7582],
117+
[0.0000, 20.0000, 0.2487, 0.4062, 0.4966, 0.5787],
118+
[0.0000, 20.0000, 0.5667, 0.2772, 0.0791, 0.2313],
119+
[0.0000, 20.0000, 0.1009, 0.1955, 0.2002, 0.0835],
120+
[1.0000, 20.0000, 0.7738, 0.3919, 0.4525, 0.7582],
121+
[1.0000, 20.0000, 0.2487, 0.4062, 0.4966, 0.5787],
122+
[1.0000, 20.0000, 0.5667, 0.2772, 0.0791, 0.2313],
123+
[1.0000, 20.0000, 0.1009, 0.1955, 0.2002, 0.0835],
124+
]
125+
)
102126

103127
# Test training mode
104128
losses = wrapper(x, targets)
@@ -219,7 +243,7 @@ def loss(self, items):
219243
wrapper.train()
220244
# Dummy input and targets
221245
x = torch.zeros((1, 3, 416, 416))
222-
targets = [{"boxes": torch.zeros((1, 4)), "labels": torch.zeros((1,))}]
246+
targets = torch.tensor([[0.0000, 20.0000, 0.7738, 0.3919, 0.4525, 0.7582]])
223247
losses = wrapper(x, targets)
224248
assert set(losses.keys()) == {"loss_total", "loss_box", "loss_cls", "loss_dfl"}
225249
assert losses["loss_total"].item() == 6.0 # sum([1.0, 2.0, 3.0])
@@ -264,7 +288,7 @@ def loss(self, items):
264288
wrapper = PyTorchYoloLossWrapper(test_model, name="yolov8n")
265289
wrapper.train()
266290
x = torch.zeros((1, 3, 416, 416))
267-
targets = [{"boxes": torch.zeros((1, 4)), "labels": torch.zeros((1,))}]
291+
targets = torch.tensor([[0.0000, 20.0000, 0.7738, 0.3919, 0.4525, 0.7582]])
268292
losses = wrapper(x, targets)
269293
assert set(losses.keys()) == {"loss_total", "loss_box", "loss_cls", "loss_dfl"}
270294
assert losses["loss_total"].item() == 6.0
@@ -439,9 +463,7 @@ def loss(self, items):
439463
for batch_size in batch_sizes:
440464
for box_count in box_counts:
441465
x = torch.zeros((batch_size, 3, 416, 416))
442-
targets = [
443-
{"boxes": torch.zeros((box_count, 4)), "labels": torch.zeros(box_count)} for _ in range(batch_size)
444-
]
466+
targets = torch.tensor([[0.0000, 20.0000, 0.7738, 0.3919, 0.4525, 0.7582]] * batch_size)
445467
losses = wrapper(x, targets)
446468

447469
# Verify loss structure

0 commit comments

Comments
 (0)