3131from sklearn .neighbors import KNeighborsClassifier
3232from sklearn .tree import DecisionTreeClassifier
3333from sklearn .svm import SVC
34+ from sklearn .preprocessing import StandardScaler , MinMaxScaler , RobustScaler
3435
3536from art .attacks .attack import MembershipInferenceAttack
3637from art .estimators .estimator import BaseEstimator
@@ -56,6 +57,10 @@ class MembershipInferenceBlackBox(MembershipInferenceAttack):
5657 "input_type" ,
5758 "attack_model_type" ,
5859 "attack_model" ,
60+ "scaler_type" ,
61+ "nn_model_epochs" ,
62+ "nn_model_batch_size" ,
63+ "nn_model_learning_rate" ,
5964 ]
6065 _estimator_requirements = (BaseEstimator , (ClassifierMixin , RegressorMixin ))
6166
@@ -65,6 +70,7 @@ def __init__(
6570 input_type : str = "prediction" ,
6671 attack_model_type : str = "nn" ,
6772 attack_model : Optional [Any ] = None ,
73+ scaler_type : Optional [str ] = "standard" ,
6874 nn_model_epochs : int = 100 ,
6975 nn_model_batch_size : int = 100 ,
7076 nn_model_learning_rate : float = 0.0001 ,
@@ -73,6 +79,9 @@ def __init__(
7379 Create a MembershipInferenceBlackBox attack instance.
7480
7581 :param estimator: Target estimator.
82+ :param input_type: the type of input to train the attack on. Can be one of: 'prediction' or 'loss'. Default is
83+ `prediction`. Predictions can be either probabilities or logits, depending on the return type
84+ of the model. If the model is a regressor, only `loss` can be used.
7685 :param attack_model_type: the type of default attack model to train, optional. Should be one of:
7786 `nn` (neural network, default),
7887 `rf` (random forest),
@@ -82,10 +91,10 @@ def __init__(
8291 `knn` (k nearest neighbors),
8392 `svm` (support vector machine).
8493 If `attack_model` is supplied, this option will be ignored.
85- :param input_type: the type of input to train the attack on. Can be one of: 'prediction' or 'loss'. Default is
86- `prediction`. Predictions can be either probabilities or logits, depending on the return type
87- of the model. If the model is a regressor, only `loss` can be used.
8894 :param attack_model: The attack model to train, optional. If none is provided, a default model will be created.
95+ :param scaler_type: The type of scaling to apply to the input features to the attack. Can be one of: "standard",
96+ "minmax", "robust" or None. If not None, the appropriate scaler from scikit-learn will be
97+ applied. If None, no scaling will be applied.
8998 :param nn_model_epochs: the number of epochs to use when training a nn attack model
9099 :param nn_model_batch_size: the batch size to use when training a nn attack model
91100 :param nn_model_learning_rate: the learning rate to use when training a nn attack model
@@ -95,6 +104,8 @@ def __init__(
95104 self .input_type = input_type
96105 self .attack_model_type = attack_model_type
97106 self .attack_model = attack_model
107+ self .scaler_type = scaler_type
108+ self .scaler : Optional [Any ] = None
98109 self .epochs = nn_model_epochs
99110 self .batch_size = nn_model_batch_size
100111 self .learning_rate = nn_model_learning_rate
@@ -245,13 +256,27 @@ def fit( # pylint: disable=W0613
245256 if x_2 is None :
246257 self .use_label = False
247258
259+ if self .scaler_type :
260+ if self .scaler_type == "standard" :
261+ self .scaler = StandardScaler ()
262+ elif self .scaler_type == "minmax" :
263+ self .scaler = MinMaxScaler ()
264+ elif self .scaler_type == "robust" :
265+ self .scaler = RobustScaler ()
266+ else :
267+ raise ValueError ("Illegal scaler_type: " , self .scaler_type )
268+
248269 if self .default_model and self .attack_model_type == "nn" :
249270 import torch
250271 from torch import nn
251272 from torch import optim
252273 from torch .utils .data import DataLoader
253274 from art .utils import to_cuda
254275
276+ if self .scaler :
277+ self .scaler .fit (x_1 )
278+ x_1 = self .scaler .transform (x_1 )
279+
255280 if x_2 is not None :
256281
257282 class MembershipInferenceAttackModel (nn .Module ):
@@ -393,8 +418,15 @@ def forward(self, x_1):
393418 else : # not nn
394419 y_ready = check_and_transform_label_format (y_new , nb_classes = 2 , return_one_hot = False )
395420 if x_2 is not None :
396- self .attack_model .fit (np .c_ [x_1 , x_2 ], y_ready .ravel ()) # type: ignore
421+ x = np .c_ [x_1 , x_2 ]
422+ if self .scaler :
423+ self .scaler .fit (x )
424+ x = self .scaler .transform (x )
425+ self .attack_model .fit (x , y_ready .ravel ()) # type: ignore
397426 else :
427+ if self .scaler :
428+ self .scaler .fit (x_1 )
429+ x_1 = self .scaler .transform (x_1 )
398430 self .attack_model .fit (x_1 , y_ready .ravel ()) # type: ignore
399431
400432 def infer (self , x : np .ndarray , y : Optional [np .ndarray ] = None , ** kwargs ) -> np .ndarray :
@@ -467,6 +499,9 @@ def infer(self, x: np.ndarray, y: Optional[np.ndarray] = None, **kwargs) -> np.n
467499 from torch .utils .data import DataLoader
468500 from art .utils import to_cuda , from_cuda
469501
502+ if self .scaler :
503+ features = self .scaler .transform (features )
504+
470505 self .attack_model .eval () # type: ignore
471506 predictions : Optional [np .ndarray ] = None
472507
@@ -512,17 +547,27 @@ def infer(self, x: np.ndarray, y: Optional[np.ndarray] = None, **kwargs) -> np.n
512547 elif not self .default_model :
513548 # assumes the predict method of the supplied model returns probabilities
514549 if y is not None and self .use_label :
515- inferred = self .attack_model .predict (np .c_ [features , y ]) # type: ignore
550+ features = np .c_ [features , y ]
551+ if self .scaler :
552+ features = self .scaler .transform (features )
553+ inferred = self .attack_model .predict (features ) # type: ignore
516554 else :
555+ if self .scaler :
556+ features = self .scaler .transform (features )
517557 inferred = self .attack_model .predict (features ) # type: ignore
518558 if probabilities :
519559 inferred_return = inferred
520560 else :
521561 inferred_return = np .round (inferred )
522562 else :
523563 if y is not None and self .use_label :
524- inferred = self .attack_model .predict_proba (np .c_ [features , y ]) # type: ignore
564+ features = np .c_ [features , y ]
565+ if self .scaler :
566+ features = self .scaler .transform (features )
567+ inferred = self .attack_model .predict_proba (features ) # type: ignore
525568 else :
569+ if self .scaler :
570+ features = self .scaler .transform (features )
526571 inferred = self .attack_model .predict_proba (features ) # type: ignore
527572 if probabilities :
528573 inferred_return = inferred [:, [1 ]]
0 commit comments