Skip to content

Commit 6285569

Browse files
ng390adamj26lcadalzodavidslaterlucas.cadalzo
authored
Release 0.12.2 (#901)
* fix pytorch deepspeech Dockerfile (#820) * Bump to version 0.12.2-dev (#819) * Update docs with new object detection datasets/models/metrics (#818) * added object detection metrics * adding xView and APRICOT docs * update dataset doc * fix typo * don't throw spurious errors on container shutdown after armory exec or launch (#828) * don't throw spurious errors on container shutdown after armory exec or launch * only log a clean container exit when the exit is known clean * Docs update (faq) (#831) * added object detection metrics * update faq with canonical preprocessing * minor grammar nitpicks * Canonical preprocessing for adversarial datasets and non-scenario datasets (#829) * canonical adversarial datasets * canonical datasets * canonical datasets * canonical datasets * canonical datasets * refactored ucf, xview, and apricot * black formatting Co-authored-by: lucas.cadalzo <[email protected]> * Update datasets, licensing, scenarios, baseline models, and metrics (#826) * Updating datasets docs * Updating adversarial dataset docs * Updating metrics docs * Fix formatting * Fix formatting * Update licensing * Update scenarios doc * Fix formatting * Revert "Fix formatting" This reverts commit cac7eb9. * Update baseline model docs * Remove precision/recall metrics from doc * Update datasets.md * Updating dataset docs * Updating dataset docs * Fix MNIST dtype * Update scenarios doc (#844) * updated ASR scenario documentation * update ASR documentation * updated multimodal scenario documentation * updated xview scenario documentation * updated APRICOT scenario documentation * updated APRICOT scenario documentation * updated poison scenario documentation * updated UCF101 scenario documentation * updated UCF101 scenario documentation * Defences fix (#839) * chmod * ensure dtype output * variable y (#841) * update download command line (#840) * Update attack params (#845) * update * updated to max of test and dev datasets * fast untarring when possible (#832) * fast untarring when possible * standard output * Piping from stdin (#842) * accept standard input * example * exception -> error * reject configs on raw stdin Co-authored-by: Adam Jacobson <[email protected]> * xView model gpu fix (#853) * added object detection metrics * remove hardcoded device_type of cpu * iou warning logic (#861) * added object detection metrics * warning is now thrown if the following holds for exactly one of the two boxes: all nonzero coordinates from a box are < 1 * scaled epsilon (#865) * Better ASR Labels (#863) * remove int check to enable str inputs * label updates * weight saving hack (#870) * working FGSM config for ASR + lint (#859) * apricot patch targeted AP metric (#866) * added object detection metrics * checkpoint progress with apricot_patch_targeted_AP_per_class metric; still need to decide how to handle duplicate predictions of targeted class * ignore duplicate true positives for a single patch box; updated comments and changed variable names * adding apricot patch metadata used in apricot_patch_targeted_AP_per_class metric * minor edits to variable name and comments * update apricot test to incorporate new metric * make APRICOT_PATCHES a dict with id's for keys * Update host-requirements.txt (#873) * Add --skip-attack flag (#877) * added object detection metrics * added --skip-attack flag * allow for passing --skip-benign and --skip-attack * truncate very long videos (#880) * Dataset index and slicing (#878) * update split naming * update dataset loading * updated docs * updated token tests * working tests for slicing * allow ordering and duplicates * Sanity check model configurations (#875) * Initial sanity checking * Initial sanity checking * New sanity checks. Ensuring baseline scenario compliance * Moving to pytest framework * Integrated into armory run command with validate-config flag * Move test_config folder * Explict check for classifier model * Add specific ground truth box set * Black formatting * Updating docs * Fix typo * Pin to ART 1.4.2 and move to end of container installs (#887) * added object detection metrics * pin to ART 1.4.2 and move to end of docker container installs * move ART installation to framework Dockerfiles * add ART install to dev Dockerfiles * UCF101 shape bug (#889) * updated preprocessing * revert * Update xview results (#879) * updated baseline results for xview * formatting * fix typos Co-authored-by: lucas.cadalzo <[email protected]> * Split metrics when targeted attack is present (#884) * Split adversarial metrics relative to ground truth and targeted labels * Flake8 * Updating metrics doc * added pgd_patch art_experimental attack (#883) * added pgd_patch art_experimental attack * ran format_json * set use_gpu false * removing accidentally added files Co-authored-by: lucas.cadalzo <[email protected]> * updated configs (#897) * Add type annotations to baseline models (#885) * Add type annotations to baseline models * Add docstrings to baseline models * Update type annotations * Update type annotation * GTSRB canonical preprocessing (#888) * Add canonical preprocessing for GTSRB * Update canonical preprocessing for GTSRB * Update GTSRB canonical preprocessing * Update documentation * Update scenario documentation * Deepspeech nan rebase (#891) * updated docker builds * update docker file * update * xView perturbation metric (#898) * added object detection metrics * update l0 norm to divide by number of elements in array; update xView scenarios to use l0 norm * update test_metrics.py * Docs for --skip flags (#900) * added object detection metrics * adding docs for --skip flags * Release version 0.12.2 Co-authored-by: Adam Jacobson <[email protected]> Co-authored-by: lcadalzo <[email protected]> Co-authored-by: davidslater <[email protected]> Co-authored-by: lucas.cadalzo <[email protected]> Co-authored-by: kevinmerchant <[email protected]> Co-authored-by: yusong-tan <[email protected]> Co-authored-by: Adam Jacobson <[email protected]>
1 parent ef78954 commit 6285569

File tree

105 files changed

+2660
-864
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

105 files changed

+2660
-864
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ evaluated against that given scenario. For more information please see our
102102

103103
# FAQs
104104
Please see the [frequently asked questions](docs/faqs.md) documentation for more information on:
105-
* Datasets returning NumPy arrays
105+
* Dataset format and preprocessing
106106
* Access to underlying models from wrapped classifiers.
107107

108108
# Contributing

armory/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828

2929
# Semantic Version
30-
__version__ = "0.12.1"
30+
__version__ = "0.12.2"
3131
DEV = "-dev"
3232

3333

armory/__main__.py

Lines changed: 65 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from armory.eval import Evaluator
2727
from armory.docker import images
2828
from armory.utils import docker_api
29-
from armory.utils.configuration import load_config
29+
from armory.utils.configuration import load_config, load_config_stdin
3030

3131
logger = logging.getLogger(__name__)
3232

@@ -66,7 +66,10 @@ def __call__(self, parser, namespace, values, option_string=None):
6666
setattr(namespace, self.dest, values)
6767

6868

69-
DEFAULT_SCENARIO = "https://github.com/twosixlabs/armory-example/blob/master/scenario_download_configs/scenarios-set1.json"
69+
OLD_SCENARIOS = [
70+
"https://github.com/twosixlabs/armory-example/blob/master/scenario_download_configs/scenarios-set1.json"
71+
]
72+
DEFAULT_SCENARIO = "https://github.com/twosixlabs/armory-example/blob/master/scenario_download_configs/scenarios-set2.json"
7073

7174

7275
class DownloadConfig(argparse.Action):
@@ -159,6 +162,25 @@ def _docker_image(parser):
159162
)
160163

161164

165+
def _docker_image_optional(parser):
166+
parser.add_argument(
167+
"--docker-image",
168+
default=images.TF1,
169+
metavar="<docker image>",
170+
type=str,
171+
help="docker image framework: 'tf1', 'tf2', or 'pytorch'",
172+
action=DockerImage,
173+
)
174+
175+
176+
def _skip_docker_images(parser):
177+
parser.add_argument(
178+
"--skip-docker-images",
179+
action="store_true",
180+
help="Whether to skip downloading docker images",
181+
)
182+
183+
162184
def _no_docker(parser):
163185
parser.add_argument(
164186
"--no-docker",
@@ -209,7 +231,10 @@ def _set_outputs(config, output_dir, output_filename):
209231
def run(command_args, prog, description):
210232
parser = argparse.ArgumentParser(prog=prog, description=description)
211233
parser.add_argument(
212-
"filepath", metavar="<json_config>", type=str, help="json config file"
234+
"filepath",
235+
metavar="<json_config>",
236+
type=str,
237+
help="json config file. Use '-' to accept standard input or pipe.",
213238
)
214239
_debug(parser)
215240
_interactive(parser)
@@ -243,21 +268,43 @@ def run(command_args, prog, description):
243268
action="store_true",
244269
help="Skip benign inference and metric calculations",
245270
)
271+
parser.add_argument(
272+
"--skip-attack",
273+
action="store_true",
274+
help="Skip attack generation and metric calculations",
275+
)
276+
parser.add_argument(
277+
"--validate-config",
278+
action="store_true",
279+
help="Validate model configuration against several checks",
280+
)
246281

247282
args = parser.parse_args(command_args)
248283
coloredlogs.install(level=args.log_level)
249284

250285
try:
251-
config = load_config(args.filepath)
286+
if args.filepath == "-":
287+
if sys.stdin.isatty():
288+
logging.error(
289+
"Cannot read config from raw 'stdin'; must pipe or redirect a file"
290+
)
291+
sys.exit(1)
292+
logger.info("Reading config from stdin...")
293+
config = load_config_stdin()
294+
else:
295+
config = load_config(args.filepath)
252296
except ValidationError as e:
253297
logger.error(
254298
f"Could not validate config: {e.message} @ {'.'.join(e.absolute_path)}"
255299
)
256300
sys.exit(1)
257301
except json.decoder.JSONDecodeError:
258-
logger.exception(f"Could not decode {args.filepath} as a json file.")
259-
if not args.filepath.lower().endswith(".json"):
260-
logger.warning(f"{args.filepath} is not a '*.json' file")
302+
if args.filepath == "-":
303+
logger.error("'stdin' did not provide a json-parsable input")
304+
else:
305+
logger.error(f"Could not decode '{args.filepath}' as a json file.")
306+
if not args.filepath.lower().endswith(".json"):
307+
logger.warning(f"{args.filepath} is not a '*.json' file")
261308
sys.exit(1)
262309
_set_gpus(config, args.use_gpu, args.no_gpu, args.gpus)
263310
_set_outputs(config, args.output_dir, args.output_filename)
@@ -270,6 +317,8 @@ def run(command_args, prog, description):
270317
check_run=args.check,
271318
num_eval_batches=args.num_eval_batches,
272319
skip_benign=args.skip_benign,
320+
skip_attack=args.skip_attack,
321+
validate_config=args.validate_config,
273322
)
274323
sys.exit(exit_code)
275324

@@ -295,6 +344,8 @@ def download(command_args, prog, description):
295344
"""
296345
parser = argparse.ArgumentParser(prog=prog, description=description)
297346
_debug(parser)
347+
_docker_image_optional(parser)
348+
_skip_docker_images(parser)
298349
parser.add_argument(
299350
metavar="<download data config file>",
300351
dest="download_config",
@@ -325,12 +376,16 @@ def download(command_args, prog, description):
325376
model_weights.download_all(args.download_config, args.scenario)
326377
return
327378

328-
if not armory.is_dev():
329-
logger.info("Downloading all docker images....")
379+
if args.skip_docker_images:
380+
logger.info("Skipping docker image downloads...")
381+
elif armory.is_dev():
382+
logger.info("Dev version. Must build docker images locally with build-dev.sh")
383+
else:
384+
logger.info("Downloading all docker images...")
330385
_pull_docker_images()
331386

332387
logger.info("Downloading requested datasets and model weights...")
333-
config = {"sysconfig": {"docker_image": images.TF1}}
388+
config = {"sysconfig": {"docker_image": args.docker_image}}
334389

335390
rig = Evaluator(config)
336391
cmd = "; ".join(
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from art.attacks.evasion import ProjectedGradientDescent
2+
import numpy as np
3+
4+
5+
class PGDPatch(ProjectedGradientDescent):
6+
def __init__(self, estimator, **kwargs):
7+
super().__init__(estimator=estimator, **kwargs)
8+
9+
def generate(self, x, y=None, **generate_kwargs):
10+
if "ymin" in generate_kwargs:
11+
ymin = generate_kwargs["ymin"]
12+
else:
13+
raise ValueError("generate_kwargs did not define 'ymin'")
14+
15+
if "xmin" in generate_kwargs:
16+
xmin = generate_kwargs["xmin"]
17+
else:
18+
raise ValueError("generate_kwargs did not define 'xmin'")
19+
20+
mask = np.zeros(shape=x.shape[1:])
21+
if "patch_ratio" in generate_kwargs:
22+
patch_ratio = generate_kwargs["patch_ratio"]
23+
ymax = ymin + int(x.shape[1] * patch_ratio ** 0.5)
24+
xmax = xmin + int(x.shape[2] * patch_ratio ** 0.5)
25+
mask[ymin:ymax, xmin:xmax, :] = 1
26+
elif "patch_height" in generate_kwargs and "patch_width" in generate_kwargs:
27+
patch_height = generate_kwargs["patch_height"]
28+
patch_width = generate_kwargs["patch_width"]
29+
mask[ymin : ymin + patch_height, xmin : xmin + patch_width, :] = 1
30+
else:
31+
raise ValueError(
32+
"generate_kwargs did not define 'patch_ratio', or it did not define 'patch_height' and 'patch_width'"
33+
)
34+
return super().generate(x, y=y, mask=mask)

armory/art_experimental/defences/jpeg_compression_multichannel_image.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def __init__(
2020
mins=None,
2121
ranges=None,
2222
n_channels=14,
23+
dtype=np.float32,
2324
):
2425
super().__init__(
2526
clip_values,
@@ -30,11 +31,15 @@ def __init__(
3031
)
3132
if mins is None:
3233
mins = (0.0,) * n_channels # identity operation
33-
self.mins = mins
34+
if len(mins) != n_channels:
35+
raise ValueError(f"mins must have {n_channels} values, one per channel")
36+
self.mins = np.array(mins, dtype=dtype)
3437

3538
if ranges is None:
3639
ranges = (1.0,) * n_channels # identity operation
37-
self.ranges = ranges
40+
if len(ranges) != n_channels:
41+
raise ValueError(f"ranges must have {n_channels} values, one per channel")
42+
self.ranges = np.array(ranges, dtype=dtype)
3843

3944
def __call__(self, x, y=None):
4045
x = (x - self.mins) / self.ranges

armory/art_experimental/defences/jpeg_compression_normalized.py

100755100644
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ def __init__(
1717
apply_predict=False,
1818
means=None,
1919
stds=None,
20+
dtype=np.float32,
2021
):
2122
super().__init__(
2223
clip_values,
@@ -29,13 +30,13 @@ def __init__(
2930
means = (0.0, 0.0, 0.0) # identity operation
3031
if len(means) != 3:
3132
raise ValueError("means must have 3 values, one per channel")
32-
self.means = means
33+
self.means = np.array(means, dtype=dtype)
3334

3435
if stds is None:
3536
stds = (1.0, 1.0, 1.0) # identity operation
3637
if len(stds) != 3:
3738
raise ValueError("stds must have 3 values, one per channel")
38-
self.stds = stds
39+
self.stds = np.array(stds, dtype=dtype)
3940

4041
def __call__(self, x, y=None):
4142
x = x * self.stds + self.means

armory/art_experimental/defences/video_compression_normalized.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def __init__(
1919
transpose=None,
2020
means=None,
2121
stds=None,
22-
dtype=None,
22+
dtype=np.float32,
2323
same_video=True,
2424
):
2525
super().__init__(
@@ -51,8 +51,6 @@ def __init__(
5151
raise ValueError("stds must have 3 values, one per channel")
5252
self.stds = stds
5353

54-
if dtype is None:
55-
dtype = "float32"
5654
self.dtype = dtype
5755

5856
self.same_video = same_video
@@ -72,7 +70,6 @@ def __call__(self, x, y=None):
7270
if self.same_video:
7371
x = x.reshape(x_shape, order="C")
7472
x = (x - self.means) / self.stds
75-
if self.dtype == "float32":
76-
x = x.astype(np.float32)
73+
x = x.astype(self.dtype)
7774
x = x.transpose(np.argsort(self.transpose))
7875
return x, y

armory/baseline_models/keras/cifar.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
"""
22
CNN model for 32x32x3 image classification
33
"""
4-
import tensorflow as tf
4+
from typing import Optional
55

6+
import tensorflow as tf
67
from tensorflow.keras.models import Sequential
78
from tensorflow.keras.layers import Dense, Flatten, Conv2D, MaxPooling2D
89
from art.classifiers import KerasClassifier
910

1011

1112
def make_cifar_model(**kwargs) -> tf.keras.Model:
13+
"""
14+
This is a simple CNN for CIFAR-10 and does not achieve SotA performance
15+
"""
1216
model = Sequential()
1317
model.add(
1418
Conv2D(
@@ -42,7 +46,9 @@ def make_cifar_model(**kwargs) -> tf.keras.Model:
4246
return model
4347

4448

45-
def get_art_model(model_kwargs, wrapper_kwargs, weights_path=None):
49+
def get_art_model(
50+
model_kwargs: dict, wrapper_kwargs: dict, weights_path: Optional[str] = None
51+
) -> KerasClassifier:
4652
model = make_cifar_model(**model_kwargs)
4753
if weights_path:
4854
model.load_weights(weights_path)

armory/baseline_models/keras/densenet121_resisc45.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
44
Model contributed by: MITRE Corporation
55
"""
6+
from typing import Optional
7+
68
from art.classifiers import KerasClassifier
79
import numpy as np
810
import tensorflow as tf
@@ -24,6 +26,11 @@ def mean_std():
2426

2527

2628
def make_densenet121_resisc_model(**model_kwargs) -> tf.keras.Model:
29+
"""
30+
This model is an ImageNet-pretrained DenseNet-121 that is fine-tuned on RESISC-45.
31+
The initial layers transform the input from canonical form to the expected input
32+
format for the DenseNet-121.
33+
"""
2734
input = tf.keras.Input(shape=(256, 256, 3))
2835

2936
# Preprocessing layers
@@ -63,7 +70,9 @@ def make_densenet121_resisc_model(**model_kwargs) -> tf.keras.Model:
6370
return new_model
6471

6572

66-
def get_art_model(model_kwargs, wrapper_kwargs, weights_path):
73+
def get_art_model(
74+
model_kwargs: dict, wrapper_kwargs: dict, weights_path: Optional[str] = None
75+
) -> KerasClassifier:
6776
model = make_densenet121_resisc_model(**model_kwargs)
6877
if weights_path:
6978
model.load_weights(weights_path)

armory/baseline_models/keras/inception_resnet_v2.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
"""
22
Inception_ResNet_v2 CNN model for 299x299x3 image classification
33
"""
4+
from typing import Optional
5+
46
from art.classifiers import KerasClassifier
57
import tensorflow as tf
68
from tensorflow.keras.applications.inception_resnet_v2 import InceptionResNetV2
79
from tensorflow.keras.layers import Lambda
810
from tensorflow.keras.models import Model
911

1012

11-
def get_art_model(model_kwargs, wrapper_kwargs, weights_path=None):
13+
def get_art_model(
14+
model_kwargs: dict, wrapper_kwargs: dict, weights_path: Optional[str] = None
15+
):
1216
input = tf.keras.Input(shape=(224, 224, 3))
1317

1418
# Preprocessing layers

0 commit comments

Comments
 (0)