Skip to content

Commit d536caf

Browse files
authored
Merge pull request #744 from Trusted-AI/audio_preprocessing
Implement impulse response filtering for sequential inputs
2 parents 2f12e2e + 2fd7454 commit d536caf

25 files changed

+1353
-100
lines changed

art/attacks/evasion/projected_gradient_descent/projected_gradient_descent_pytorch.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
from tqdm import trange, tqdm
3333

3434
from art.config import ART_NUMPY_DTYPE
35+
from art.estimators.estimator import BaseEstimator, LossGradientsMixin
36+
from art.estimators.classification.classifier import ClassifierMixin
3537
from art.attacks.evasion.projected_gradient_descent.projected_gradient_descent_numpy import (
3638
ProjectedGradientDescentCommon,
3739
)
@@ -40,7 +42,6 @@
4042
if TYPE_CHECKING:
4143
import torch
4244
from art.estimators.classification.pytorch import PyTorchClassifier
43-
from art.estimators.object_detection.pytorch_faster_rcnn import PyTorchFasterRCNN
4445

4546
logger = logging.getLogger(__name__)
4647

@@ -54,9 +55,11 @@ class ProjectedGradientDescentPyTorch(ProjectedGradientDescentCommon):
5455
| Paper link: https://arxiv.org/abs/1706.06083
5556
"""
5657

58+
_estimator_requirements = (BaseEstimator, LossGradientsMixin, ClassifierMixin)
59+
5760
def __init__(
5861
self,
59-
estimator: Union["PyTorchClassifier", "PyTorchFasterRCNN"],
62+
estimator: Union["PyTorchClassifier"],
6063
norm: Union[int, float, str] = np.inf,
6164
eps: Union[int, float, np.ndarray] = 0.3,
6265
eps_step: Union[int, float, np.ndarray] = 0.1,

art/attacks/evasion/projected_gradient_descent/projected_gradient_descent_tensorflow_v2.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
from tqdm import trange, tqdm
3333

3434
from art.config import ART_NUMPY_DTYPE
35+
from art.estimators.estimator import BaseEstimator, LossGradientsMixin
36+
from art.estimators.classification.classifier import ClassifierMixin
3537
from art.attacks.evasion.projected_gradient_descent.projected_gradient_descent_numpy import (
3638
ProjectedGradientDescentCommon,
3739
)
@@ -53,6 +55,8 @@ class ProjectedGradientDescentTensorFlowV2(ProjectedGradientDescentCommon):
5355
| Paper link: https://arxiv.org/abs/1706.06083
5456
"""
5557

58+
_estimator_requirements = (BaseEstimator, LossGradientsMixin, ClassifierMixin)
59+
5660
def __init__(
5761
self,
5862
estimator: "TensorFlowV2Classifier",

art/defences/preprocessor/preprocessor.py

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -205,10 +205,16 @@ def get_gradient(x, grad):
205205

206206
return x_grad
207207

208-
if x.shape == grad.shape:
208+
if x.dtype == np.object:
209+
x_grad_list = list()
210+
for i, x_i in enumerate(x):
211+
x_grad_list.append(get_gradient(x=x_i, grad=grad[i]))
212+
x_grad = np.empty(x.shape[0], dtype=object)
213+
x_grad[:] = list(x_grad_list)
214+
elif x.shape == grad.shape:
209215
x_grad = get_gradient(x=x, grad=grad)
210216
else:
211-
# Special case for lass gradients
217+
# Special case for loss gradients
212218
x_grad = np.zeros_like(grad)
213219
for i in range(grad.shape[1]):
214220
x_grad[:, i, ...] = get_gradient(x=x, grad=grad[:, i, ...])
@@ -268,35 +274,41 @@ def __call__(self, x: np.ndarray, y: Optional[np.ndarray] = None) -> Tuple[np.nd
268274
return result, y
269275

270276
# Backward compatibility.
271-
def _get_gradient(self, x: np.ndarray, grad: np.ndarray) -> np.ndarray:
272-
"""
273-
Helper function for estimate_gradient
274-
"""
277+
def estimate_gradient(self, x: np.ndarray, grad: np.ndarray) -> np.ndarray:
275278
import tensorflow as tf # lgtm [py/repeated-import]
276279

277-
with tf.GradientTape() as tape:
278-
x = tf.convert_to_tensor(x, dtype=config.ART_NUMPY_DTYPE)
279-
tape.watch(x)
280-
grad = tf.convert_to_tensor(grad, dtype=config.ART_NUMPY_DTYPE)
280+
def get_gradient(x: np.ndarray, grad: np.ndarray) -> np.ndarray:
281+
"""
282+
Helper function for estimate_gradient
283+
"""
281284

282-
x_prime = self.estimate_forward(x)
285+
with tf.GradientTape() as tape:
286+
x = tf.convert_to_tensor(x, dtype=config.ART_NUMPY_DTYPE)
287+
tape.watch(x)
288+
grad = tf.convert_to_tensor(grad, dtype=config.ART_NUMPY_DTYPE)
283289

284-
x_grad = tape.gradient(target=x_prime, sources=x, output_gradients=grad)
290+
x_prime = self.estimate_forward(x)
285291

286-
x_grad = x_grad.numpy()
287-
if x_grad.shape != x.shape:
288-
raise ValueError("The input shape is {} while the gradient shape is {}".format(x.shape, x_grad.shape))
292+
x_grad = tape.gradient(target=x_prime, sources=x, output_gradients=grad)
289293

290-
return x_grad
294+
x_grad = x_grad.numpy()
295+
if x_grad.shape != x.shape:
296+
raise ValueError("The input shape is {} while the gradient shape is {}".format(x.shape, x_grad.shape))
291297

292-
# Backward compatibility.
293-
def estimate_gradient(self, x: np.ndarray, grad: np.ndarray) -> np.ndarray:
294-
if x.shape == grad.shape:
295-
x_grad = self._get_gradient(x=x, grad=grad)
298+
return x_grad
299+
300+
if x.dtype == np.object:
301+
x_grad_list = list()
302+
for i, x_i in enumerate(x):
303+
x_grad_list.append(get_gradient(x=x_i, grad=grad[i]))
304+
x_grad = np.empty(x.shape[0], dtype=object)
305+
x_grad[:] = list(x_grad_list)
306+
elif x.shape == grad.shape:
307+
x_grad = get_gradient(x=x, grad=grad)
296308
else:
297-
# Special case for lass gradients
309+
# Special case for loss gradients
298310
x_grad = np.zeros_like(grad)
299311
for i in range(grad.shape[1]):
300-
x_grad[:, i, ...] = self._get_gradient(x=x, grad=grad[:, i, ...])
312+
x_grad[:, i, ...] = get_gradient(x=x, grad=grad[:, i, ...])
301313

302314
return x_grad

art/defences/preprocessor/spatial_smoothing_pytorch.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def __init__(
6363
"""
6464
Create an instance of local spatial smoothing.
6565
66-
:window_size: Size of spatial smoothing window.
66+
:param window_size: Size of spatial smoothing window.
6767
:param channels_first: Set channels first or last.
6868
:param clip_values: Tuple of the form `(min, max)` representing the minimum and maximum values allowed
6969
for features.

art/estimators/pytorch.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ def _apply_preprocessing(self, x, y, fit: bool = False, no_grad=True) -> Tuple[A
150150
else:
151151
input_is_tensor = False
152152

153-
if self.all_framework_preprocessing:
153+
if self.all_framework_preprocessing and not (not input_is_tensor and x.dtype == np.object):
154154
if not input_is_tensor:
155155
# Convert np arrays to torch tensors.
156156
x = torch.tensor(x, device=self._device)
@@ -179,8 +179,9 @@ def chain_processes(x, y):
179179
if y is not None:
180180
y = y.cpu().numpy()
181181

182-
elif len(self.preprocessing) in [1, 2] and isinstance(
183-
self.preprocessing[-1], (StandardisationMeanStd, StandardisationMeanStdPyTorch)
182+
elif len(self.preprocessing) == 1 or (
183+
len(self.preprocessing) == 2
184+
and isinstance(self.preprocessing[-1], (StandardisationMeanStd, StandardisationMeanStdPyTorch))
184185
):
185186
# Compatible with non-PyTorch defences if no chaining.
186187
for preprocess in self.preprocessing:
@@ -220,7 +221,12 @@ def _apply_preprocessing_gradient(self, x, gradients, fit=False):
220221
if not self.preprocessing:
221222
return gradients
222223

223-
if self.all_framework_preprocessing:
224+
if isinstance(x, torch.Tensor):
225+
input_is_tensor = True
226+
else:
227+
input_is_tensor = False
228+
229+
if self.all_framework_preprocessing and not (not input_is_tensor and x.dtype == np.object):
224230
# Convert np arrays to torch tensors.
225231
x = torch.tensor(x, device=self._device, requires_grad=True)
226232
gradients = torch.tensor(gradients, device=self._device)
@@ -243,8 +249,9 @@ def _apply_preprocessing_gradient(self, x, gradients, fit=False):
243249
"The input shape is {} while the gradient shape is {}".format(x.shape, gradients.shape)
244250
)
245251

246-
elif len(self.preprocessing) in [1, 2] and isinstance(
247-
self.preprocessing[-1], (StandardisationMeanStd, StandardisationMeanStdPyTorch)
252+
elif len(self.preprocessing) == 1 or (
253+
len(self.preprocessing) == 2
254+
and isinstance(self.preprocessing[-1], (StandardisationMeanStd, StandardisationMeanStdPyTorch))
248255
):
249256
# Compatible with non-PyTorch defences if no chaining.
250257
defence = self.preprocessing[0]

art/estimators/speech_recognition/pytorch_deep_speech.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636

3737
from deepspeech_pytorch.model import DeepSpeech
3838

39-
from art.config import CLIP_VALUES_TYPE, PREPROCESSING_TYPE
39+
from art.utils import CLIP_VALUES_TYPE, PREPROCESSING_TYPE
4040
from art.defences.preprocessor.preprocessor import Preprocessor
4141
from art.defences.postprocessor.postprocessor import Postprocessor
4242

@@ -131,7 +131,9 @@ def __init__(
131131

132132
# Super initialization
133133
super().__init__(
134+
model=None,
134135
clip_values=clip_values,
136+
channels_first=None,
135137
preprocessing_defences=preprocessing_defences,
136138
postprocessing_defences=postprocessing_defences,
137139
preprocessing=preprocessing,

art/estimators/speech_recognition/tensorflow_lingvo.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ def __init__(
136136

137137
# Super initialization
138138
super().__init__(
139+
model=None,
139140
clip_values=clip_values,
140141
channels_first=channels_first,
141142
preprocessing_defences=preprocessing_defences,

art/estimators/tensorflow.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ def _apply_preprocessing(self, x, y, fit: bool = False) -> Tuple[Any, Any]:
194194
else:
195195
input_is_tensor = False
196196

197-
if self.all_framework_preprocessing:
197+
if self.all_framework_preprocessing and not (not input_is_tensor and x.dtype == np.object):
198198
# Convert np arrays to torch tensors.
199199
if not input_is_tensor:
200200
x = tf.convert_to_tensor(x)
@@ -215,8 +215,9 @@ def _apply_preprocessing(self, x, y, fit: bool = False) -> Tuple[Any, Any]:
215215
if y is not None:
216216
y = y.numpy()
217217

218-
elif len(self.preprocessing) in [1, 2] and isinstance(
219-
self.preprocessing[-1], (StandardisationMeanStd, StandardisationMeanStdTensorFlowV2)
218+
elif len(self.preprocessing) == 1 or (
219+
len(self.preprocessing) == 2
220+
and isinstance(self.preprocessing[-1], (StandardisationMeanStd, StandardisationMeanStdTensorFlowV2))
220221
):
221222
# Compatible with non-TensorFlow defences if no chaining.
222223
for preprocess in self.preprocessing:
@@ -256,7 +257,12 @@ def _apply_preprocessing_gradient(self, x, gradients, fit=False):
256257
if not self.preprocessing:
257258
return gradients
258259

259-
if self.all_framework_preprocessing:
260+
if isinstance(x, tf.Tensor):
261+
input_is_tensor = True
262+
else:
263+
input_is_tensor = False
264+
265+
if self.all_framework_preprocessing and not (not input_is_tensor and x.dtype == np.object):
260266
with tf.GradientTape() as tape:
261267
# Convert np arrays to TensorFlow tensors.
262268
x = tf.convert_to_tensor(x, dtype=config.ART_NUMPY_DTYPE)
@@ -281,8 +287,9 @@ def _apply_preprocessing_gradient(self, x, gradients, fit=False):
281287
"The input shape is {} while the gradient shape is {}".format(x.shape, gradients.shape)
282288
)
283289

284-
elif len(self.preprocessing) in [1, 2] and isinstance(
285-
self.preprocessing[-1], (StandardisationMeanStd, StandardisationMeanStdTensorFlowV2)
290+
elif len(self.preprocessing) == 1 or (
291+
len(self.preprocessing) == 2
292+
and isinstance(self.preprocessing[-1], (StandardisationMeanStd, StandardisationMeanStdTensorFlowV2))
286293
):
287294
# Compatible with non-TensorFlow defences if no chaining.
288295
defence = self.preprocessing[0]

art/preprocessing/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@
66
from art.preprocessing.standardisation_mean_std.standardisation_mean_std_tensorflow import (
77
StandardisationMeanStdTensorFlowV2,
88
)
9+
from art.preprocessing.l_filter.l_filter import LFilter
10+
from art.preprocessing.l_filter.l_filter_pytorch import LFilterPyTorch

art/preprocessing/l_filter/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)