Skip to content

Commit 386d73a

Browse files
authored
Merge branch 'dev_1.21.0' into detector-refactoring
2 parents f334cbd + 4f21e6b commit 386d73a

21 files changed

+682
-55
lines changed

.github/actions/yolo/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,6 @@ RUN pip install pytorchyolo==1.8.0 tensorflow==2.14.1 scikit-learn==1.4.2 pytest
2424

2525
RUN cd /tmp/ && git clone https://github.com/eriklindernoren/PyTorch-YOLOv3.git && cd ./PyTorch-YOLOv3/weights && ./download_weights.sh
2626

27+
RUN mkdir /tmp/yolo_v8.3.0 && cd /tmp/yolo_v8.3.0 && wget https://github.com/ultralytics/assets/releases/download/v8.3.0/yolov8n.pt && wget https://github.com/ultralytics/assets/releases/download/v8.3.0/yolov10n.pt
28+
2729
CMD ["/bin/bash"]

.github/workflows/dockerhub.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
with:
3535
images: adversarialrobustnesstoolbox/releases
3636
tags: |
37-
type=raw,value={{branch}}-1.19.2-{{sha}}
37+
type=raw,value={{branch}}-1.20.1-{{sha}}
3838
type=semver,pattern={{version}}
3939
4040
- name: Build and push Docker image

README-cn.md

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# Adversarial Robustness Toolbox (ART) v1.19
1+
# Adversarial Robustness Toolbox (ART) v1.20
22
<p align="center">
3-
<img src="docs/images/art_lfai.png?raw=true" width="467" title="ART logo">
3+
<img src="https://raw.githubusercontent.com/Trusted-AI/adversarial-robustness-toolbox/main/docs/images/art_lfai.png" width="467" title="ART logo">
44
</p>
55
<br />
66

@@ -16,9 +16,14 @@
1616
[![Downloads](https://static.pepy.tech/badge/adversarial-robustness-toolbox/month)](https://pepy.tech/project/adversarial-robustness-toolbox)
1717
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/5090/badge)](https://bestpractices.coreinfrastructure.org/projects/5090)
1818

19-
<p align="center">
20-
<img src="https://raw.githubusercontent.com/lfai/artwork/master/lfaidata-assets/lfaidata-project-badge/graduate/color/lfaidata-project-badge-graduate-color.png" alt="LF AI & Data" width="300"/>
21-
</p>
19+
<div align="center">
20+
<picture>
21+
<source media="(prefers-color-scheme: dark)" srcset="docs/images/lfaidata-project-badge-graduate-color_dark.png" width="400" title="LF AI & Data">
22+
<source media="(prefers-color-scheme: light)" srcset="docs/images/lfaidata-project-badge-graduate-color.png" width="400" title="LF AI & Data">
23+
<img alt="Fallback image description" src="default-image.png" width="400">
24+
</picture>
25+
</div>
26+
<br />
2227

2328
对抗性鲁棒性工具集(ART)是用于机器学习安全性的Python库。ART 由
2429
[Linux Foundation AI & Data Foundation](https://lfaidata.foundation) (LF AI & Data)。 ART提供的工具可
@@ -30,17 +35,28 @@
3035

3136
## Adversarial Threats
3237

38+
<div align="center">
39+
<picture>
40+
<source media="(prefers-color-scheme: dark)" srcset="docs/images/adversarial_threats_attacker_dark.png" width="400 title="ART Threats">
41+
<source media="(prefers-color-scheme: light)" srcset="docs/images/adversarial_threats_attacker.png" width="400 title="ART Threats">
42+
<img alt="Fallback image description" src="default-image.png" width="400">
43+
</picture>
44+
</div>
45+
3346
<p align="center">
34-
<img src="docs/images/adversarial_threats_attacker.png?raw=true" width="400" title="ART logo">
35-
<img src="docs/images/adversarial_threats_art.png?raw=true" width="400" title="ART logo">
47+
<img src="docs/images/adversarial_threats_art.png?raw=true" width="400" title="ART Matrix">
3648
</p>
3749
<br />
3850

3951
## ART for Red and Blue Teams (selection)
4052

41-
<p align="center">
42-
<img src="docs/images/white_hat_blue_red.png?raw=true" width="800" title="ART Red and Blue Teams">
43-
</p>
53+
<div align="center">
54+
<picture>
55+
<source media="(prefers-color-scheme: dark)" srcset="docs/images/white_hat_blue_red_dark.png" width="800 title="ART Red and Blue Teams">
56+
<source media="(prefers-color-scheme: light)" srcset="docs/images/white_hat_blue_red.png" width="800 title="ART Red and Blue Teams">
57+
<img alt="Fallback image description" src="default-image.png" width="800">
58+
</picture>
59+
</div>
4460
<br />
4561

4662
## 学到更多

README.md

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# Adversarial Robustness Toolbox (ART) v1.19
1+
# Adversarial Robustness Toolbox (ART) v1.20
22
<p align="center">
3-
<img src="docs/images/art_lfai.png?raw=true" width="467" title="ART logo">
3+
<img src="https://raw.githubusercontent.com/Trusted-AI/adversarial-robustness-toolbox/main/docs/images/art_lfai.png" width="467" title="ART logo">
44
</p>
55
<br />
66

@@ -18,9 +18,14 @@
1818

1919
[中文README请按此处](README-cn.md)
2020

21-
<p align="center">
22-
<img src="https://raw.githubusercontent.com/lfai/artwork/master/lfaidata-assets/lfaidata-project-badge/graduate/color/lfaidata-project-badge-graduate-color.png" alt="LF AI & Data" width="300"/>
23-
</p>
21+
<div align="center">
22+
<picture>
23+
<source media="(prefers-color-scheme: dark)" srcset="docs/images/lfaidata-project-badge-graduate-color_dark.png" width="400" title="LF AI & Data">
24+
<source media="(prefers-color-scheme: light)" srcset="docs/images/lfaidata-project-badge-graduate-color.png" width="400" title="LF AI & Data">
25+
<img alt="Fallback image description" src="default-image.png" width="400">
26+
</picture>
27+
</div>
28+
<br />
2429

2530
Adversarial Robustness Toolbox (ART) is a Python library for Machine Learning Security. ART is hosted by the
2631
[Linux Foundation AI & Data Foundation](https://lfaidata.foundation) (LF AI & Data). ART provides tools that enable
@@ -32,17 +37,28 @@ generation, certification, etc.).
3237

3338
## Adversarial Threats
3439

40+
<div align="center">
41+
<picture>
42+
<source media="(prefers-color-scheme: dark)" srcset="docs/images/adversarial_threats_attacker_dark.png" width="400 title="ART Threats">
43+
<source media="(prefers-color-scheme: light)" srcset="docs/images/adversarial_threats_attacker.png" width="400 title="ART Threats">
44+
<img alt="Fallback image description" src="default-image.png" width="400">
45+
</picture>
46+
</div>
47+
3548
<p align="center">
36-
<img src="docs/images/adversarial_threats_attacker.png?raw=true" width="400" title="ART logo">
37-
<img src="docs/images/adversarial_threats_art.png?raw=true" width="400" title="ART logo">
49+
<img src="docs/images/adversarial_threats_art.png?raw=true" width="400" title="ART Matrix">
3850
</p>
3951
<br />
4052

4153
## ART for Red and Blue Teams (selection)
4254

43-
<p align="center">
44-
<img src="docs/images/white_hat_blue_red.png?raw=true" width="800" title="ART Red and Blue Teams">
45-
</p>
55+
<div align="center">
56+
<picture>
57+
<source media="(prefers-color-scheme: dark)" srcset="docs/images/white_hat_blue_red_dark.png" width="800 title="ART Red and Blue Teams">
58+
<source media="(prefers-color-scheme: light)" srcset="docs/images/white_hat_blue_red.png" width="800 title="ART Red and Blue Teams">
59+
<img alt="Fallback image description" src="default-image.png" width="800">
60+
</picture>
61+
</div>
4662
<br />
4763

4864
## Learn more

art/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from art import preprocessing
1414

1515
# Semantic Version
16-
__version__ = "1.19.2"
16+
__version__ = "1.20.1"
1717

1818
LOGGING = {
1919
"version": 1,

art/estimators/certification/derandomized_smoothing/pytorch.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,12 +406,15 @@ def get_models(cls, generate_from_null: bool = False) -> list[str]:
406406
return supported
407407

408408
@staticmethod
409-
def create_vision_transformer(variant: str, pretrained: bool = False, **kwargs) -> "PyTorchVisionTransformer":
409+
def create_vision_transformer(
410+
variant: str, pretrained: bool = False, use_naflex: bool | None = None, **kwargs
411+
) -> "PyTorchVisionTransformer":
410412
"""
411413
Creates a vision transformer using PyTorchViT which controls the forward pass of the model
412414
413415
:param variant: The name of the vision transformer to load
414416
:param pretrained: If to load pre-trained weights
417+
:param use_naflex: If using NaFlexVit.
415418
:return: A ViT with the required methods needed for ART
416419
"""
417420

art/estimators/certification/derandomized_smoothing/vision_transformers/pytorch.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,12 @@ def drop_tokens(x: torch.Tensor, indexes: torch.Tensor) -> torch.Tensor:
167167
x_no_cl = torch.reshape(x_no_cl, shape=(shape[0], -1, shape[-1]))
168168
return torch.cat((cls_token, x_no_cl), dim=1)
169169

170-
def forward_features(self, x: torch.Tensor) -> torch.Tensor:
170+
def forward_features(self, x: torch.Tensor, attn_mask: torch.Tensor | None = None) -> torch.Tensor:
171171
"""
172172
The forward pass of the ViT.
173173
174174
:param x: Input data.
175+
:param attn_mask: Attention mask.
175176
:return: The input processed by the ViT backbone
176177
"""
177178

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: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1717
# SOFTWARE.
1818
"""
19-
This module implements the task specific estimator for PyTorch YOLO v3 and v5 object detectors.
19+
This module implements the task specific estimator for PyTorch YOLO v3, v5, v8+ object detectors.
2020
2121
| Paper link: https://arxiv.org/abs/1804.02767
2222
"""
@@ -42,7 +42,7 @@
4242

4343
class PyTorchYolo(PyTorchObjectDetector):
4444
"""
45-
This module implements the model- and task specific estimator for YOLO v3, v5 object detector models in PyTorch.
45+
This module implements the model- and task specific estimator for YOLO object detector models in PyTorch.
4646
4747
| Paper link: https://arxiv.org/abs/1804.02767
4848
"""
@@ -64,12 +64,13 @@ def __init__(
6464
"loss_rpn_box_reg",
6565
),
6666
device_type: str = "gpu",
67-
is_yolov8: bool = False,
67+
is_ultralytics: bool = False,
68+
model_name: str | None = None,
6869
):
6970
"""
7071
Initialization.
7172
72-
:param model: YOLO v3 or v5 model wrapped as demonstrated in examples/get_started_yolo.py.
73+
:param model: YOLO v3, v5, or v8+ model wrapped as demonstrated in examples/get_started_yolo.py.
7374
The output of the model is `list[dict[str, torch.Tensor]]`, one for each input image.
7475
The fields of the dict are as follows:
7576
@@ -93,8 +94,13 @@ def __init__(
9394
'loss_objectness', and 'loss_rpn_box_reg'.
9495
:param device_type: Type of device to be used for model and tensors, if `cpu` run on CPU, if `gpu` run on GPU
9596
if available otherwise run on CPU.
96-
:param is_yolov8: The flag to be used for marking the YOLOv8 model.
97+
:param model_name: The name of the model (e.g., 'yolov8n', 'yolov10n') for determining loss function.
9798
"""
99+
if is_ultralytics:
100+
from art.estimators.object_detection.pytorch_yolo_loss_wrapper import PyTorchYoloLossWrapper
101+
102+
model = PyTorchYoloLossWrapper(model, model_name)
103+
98104
super().__init__(
99105
model=model,
100106
input_shape=input_shape,
@@ -106,7 +112,6 @@ def __init__(
106112
preprocessing=preprocessing,
107113
attack_losses=attack_losses,
108114
device_type=device_type,
109-
is_yolov8=is_yolov8,
110115
)
111116

112117
def _translate_labels(self, labels: list[dict[str, "torch.Tensor"]]) -> "torch.Tensor":
@@ -154,20 +159,31 @@ def _translate_predictions(self, predictions: "torch.Tensor") -> list[dict[str,
154159
Translate object detection predictions from the model format (YOLO) to ART format (torchvision) and
155160
convert tensors to numpy arrays.
156161
157-
:param predictions: Object detection labels in format xcycwh (YOLO).
162+
:param predictions: Object detection labels in format xcycwh (YOLO) or list of dicts (YOLO v8+).
158163
:return: Object detection labels in format x1y1x2y2 (torchvision).
159164
"""
160165
import torch
161166

167+
predictions_x1y1x2y2: list[dict[str, np.ndarray]] = []
168+
169+
# Handle YOLO v8+ predictions (list of dicts)
170+
if isinstance(predictions, list) and len(predictions) > 0 and isinstance(predictions[0], dict):
171+
for pred in predictions:
172+
prediction = {}
173+
prediction["boxes"] = pred["boxes"].detach().cpu().numpy()
174+
prediction["labels"] = pred["labels"].detach().cpu().numpy()
175+
prediction["scores"] = pred["scores"].detach().cpu().numpy()
176+
predictions_x1y1x2y2.append(prediction)
177+
return predictions_x1y1x2y2
178+
179+
# Handle traditional YOLO predictions (tensor format)
162180
if self.channels_first:
163181
height = self.input_shape[1]
164182
width = self.input_shape[2]
165183
else:
166184
height = self.input_shape[0]
167185
width = self.input_shape[1]
168186

169-
predictions_x1y1x2y2: list[dict[str, np.ndarray]] = []
170-
171187
for pred in predictions:
172188
boxes = torch.vstack(
173189
[
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# MIT License
2+
#
3+
# Copyright (C) The Adversarial Robustness Toolbox (ART) Authors 2025
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6+
# documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
7+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
8+
# persons to whom the Software is furnished to do so, subject to the following conditions:
9+
#
10+
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
11+
# Software.
12+
#
13+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
14+
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
16+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17+
# SOFTWARE.
18+
"""
19+
PyTorch-specific YOLO loss wrapper for ART for yolo versions 8 and above.
20+
"""
21+
22+
import torch
23+
24+
25+
class PyTorchYoloLossWrapper(torch.nn.Module):
26+
"""Wrapper for YOLO v8+ models to handle loss dict format."""
27+
28+
def __init__(self, model, name):
29+
super().__init__()
30+
self.model = model
31+
try:
32+
from ultralytics.models.yolo.detect import DetectionPredictor
33+
from ultralytics.utils.loss import v8DetectionLoss, E2EDetectLoss
34+
35+
self.detection_predictor = DetectionPredictor()
36+
self.model.args = self.detection_predictor.args
37+
if "v10" in name:
38+
self.model.criterion = E2EDetectLoss(model)
39+
else:
40+
self.model.criterion = v8DetectionLoss(model)
41+
except ImportError as e:
42+
raise ImportError("The 'ultralytics' package is required for YOLO v8+ models but not installed.") from e
43+
44+
def forward(self, x, targets=None):
45+
"""Transforms the target to dict expected by model.loss"""
46+
if self.training:
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]
53+
items["img"] = x
54+
loss, loss_components = self.model.loss(items)
55+
loss_components_dict = {"loss_total": loss.sum()}
56+
loss_components_dict["loss_box"] = loss_components[0].sum()
57+
loss_components_dict["loss_cls"] = loss_components[1].sum()
58+
loss_components_dict["loss_dfl"] = loss_components[2].sum()
59+
return loss_components_dict
60+
else:
61+
preds = self.model(x)
62+
self.detection_predictor.model = self.model
63+
self.detection_predictor.batch = [x]
64+
preds = self.detection_predictor.postprocess(preds, x, x)
65+
items = []
66+
for pred in preds:
67+
items.append(
68+
{"boxes": pred.boxes.xyxy, "scores": pred.boxes.conf, "labels": pred.boxes.cls.type(torch.int)}
69+
)
70+
return items

0 commit comments

Comments
 (0)