def create_model(self): X_input = Input(self.state_space) X = X_input #Input Layer of state size and Hidden Layer with 512 nodes X = Dense(512, input_shape=self.state_space, activation="relu", kernel_initializer='he_uniform')(X) # Hidden layer with 256 nodes X = Dense(256, activation="relu", kernel_initializer='he_uniform')(X) # Hidden layer with 64 nodes X = Dense(64, activation="relu", kernel_initializer='he_uniform')(X) if self.Dueling: state_value = Dense(1, kernel_initializer='he_uniform')(X) state_value = Lambda(lambda s: K.expand_dims(s[:, 0], -1), output_shape=(self.action_space,))(state_value) action_advantage = Dense(self.action_space, kernel_initializer='he_uniform')(X) action_advantage = Lambda(lambda a: a[:, :] - K.mean(a[:, :], keepdims=True), output_shape=(self.action_space,))(action_advantage) X = Add()([state_value, action_advantage]) else: X = Dense(self.action_space, activation="linear", kernel_initializer='he_uniform')(X) model = Model(inputs = X_input, outputs = X) model.compile(loss="mean_squared_error", optimizer=adam_v2.Adam(learning_rate = self.Learning_Rate), metrics=["accuracy"]) model.summary() return model
def build_GAN(discriminator, generator): discriminator.trainable = False GAN_input = Input(shape=(100, )) x = generator(GAN_input) GAN_output = discriminator(x) GAN = Model(inputs=GAN_input, outputs=GAN_output) GAN.compile(loss='binary_crossentropy', optimizer=adam_v2.Adam(0.0002, 0.5)) return GAN
def buildmodel(): print("Now we build the model") model = Sequential() model.add(Dense(64, input_shape=(BOARDX, BOARDY, BOARDZ, 1))) model.add(Activation('relu')) model.add(Dense(512)) model.add(Activation('relu')) model.add(Dense(ACTIONS)) adam = adam_v2.Adam(lr=LEARNING_RATE) model.compile(loss='mse', optimizer=adam) print("We finished building the model") return model
def createDiscriminator(): discriminator = Sequential() discriminator.add(Dense(units=512, input_dim=784)) discriminator.add(LeakyReLU(2.0)) discriminator.add(Dense(units=256)) discriminator.add(LeakyReLU(2.0)) discriminator.add(Dense(units=1, activation='sigmoid')) discriminator.compile(loss='binary_crossentropy', optimizer=adam_v2.Adam(learning_rate=0.0002, beta_1=0.5)) return discriminator
def build_generator(): model = Sequential() model.add(Dense(units=256, input_dim=100)) model.add(LeakyReLU(alpha=0.2)) model.add(Dense(units=512)) model.add(LeakyReLU(alpha=0.2)) model.add(Dense(units=1024)) model.add(LeakyReLU(alpha=0.2)) model.add(Dense(units=784, activation='tanh')) model.compile(loss='binary_crossentropy', optimizer=adam_v2.Adam(0.0002, 0.5)) return model
def test_keras_classifier(self): """ Second test with the KerasClassifier. :return: """ # Build KerasClassifier victim_krc = get_image_classifier_kr() # Create simple CNN model = Sequential() model.add(Conv2D(1, kernel_size=(7, 7), activation="relu", input_shape=(28, 28, 1))) model.add(MaxPooling2D(pool_size=(4, 4))) model.add(Flatten()) model.add(Dense(10, activation="softmax")) loss = keras.losses.categorical_crossentropy try: from keras.optimizers import Adam optimizer = Adam(lr=0.001) except ImportError: from keras.optimizers import adam_v2 optimizer = adam_v2.Adam(lr=0.001) model.compile(loss=loss, optimizer=optimizer, metrics=["accuracy"]) # Get classifier thieved_krc = KerasClassifier(model, clip_values=(0, 1), use_logits=False) # Create attack copycat_cnn = CopycatCNN( classifier=victim_krc, batch_size_fit=self.batch_size, batch_size_query=self.batch_size, nb_epochs=NB_EPOCHS, nb_stolen=NB_STOLEN, ) thieved_krc = copycat_cnn.extract(x=self.x_train_mnist, thieved_classifier=thieved_krc) victim_preds = np.argmax(victim_krc.predict(x=self.x_train_mnist[:100]), axis=1) thieved_preds = np.argmax(thieved_krc.predict(x=self.x_train_mnist[:100]), axis=1) acc = np.sum(victim_preds == thieved_preds) / len(victim_preds) self.assertGreater(acc, 0.3) # Clean-up k.clear_session()
def test_keras_iris(self): """ Second test for Keras. :return: """ # Build KerasClassifier victim_krc = get_tabular_classifier_kr() # Create simple CNN model = Sequential() model.add(Dense(10, input_shape=(4,), activation="relu")) model.add(Dense(10, activation="relu")) model.add(Dense(3, activation="softmax")) try: from keras.optimizers import Adam optimizer = Adam(lr=0.001) except ImportError: from keras.optimizers import adam_v2 optimizer = adam_v2.Adam(lr=0.001) model.compile(loss="categorical_crossentropy", optimizer=optimizer, metrics=["accuracy"]) # Get classifier thieved_krc = KerasClassifier(model, clip_values=(0, 1), use_logits=False, channels_first=True) # Create attack copycat_cnn = CopycatCNN( classifier=victim_krc, batch_size_fit=self.batch_size, batch_size_query=self.batch_size, nb_epochs=NB_EPOCHS, nb_stolen=NB_STOLEN, ) thieved_krc = copycat_cnn.extract(x=self.x_train_iris, thieved_classifier=thieved_krc) victim_preds = np.argmax(victim_krc.predict(x=self.x_train_iris[:100]), axis=1) thieved_preds = np.argmax(thieved_krc.predict(x=self.x_train_iris[:100]), axis=1) acc = np.sum(victim_preds == thieved_preds) / len(victim_preds) self.assertGreater(acc, 0.3) # Clean-up k.clear_session()
def build_discriminator(): model = Sequential() model.add(Dense(units=1024, input_dim=784)) model.add(LeakyReLU(alpha=0.2)) model.add(Dropout(0.3)) model.add(Dense(units=512)) model.add(LeakyReLU(alpha=0.2)) model.add(Dropout(0.3)) model.add(Dense(units=256)) model.add(LeakyReLU(alpha=0.2)) model.add(Dropout(0.3)) model.add(Dense(units=1, activation='sigmoid')) model.compile(loss='binary_crossentropy', optimizer=adam_v2.Adam(0.0002, 0.5)) return model
def make_model(): num_4x1 = 32 num_4x4 = 64 inputs = Input(shape=(4, 4, 4, 3)) t = inputs t144 = Flatten()(Conv3D(num_4x1, (4, 1, 1), padding='valid')(t)) t414 = Flatten()(Conv3D(num_4x1, (1, 4, 1), padding='valid')(t)) t441 = Flatten()(Conv3D(num_4x1, (1, 1, 4), padding='valid')(t)) t114 = Flatten()(Conv3D(num_4x4, (4, 4, 1), padding='valid')(t)) t141 = Flatten()(Conv3D(num_4x4, (4, 1, 4), padding='valid')(t)) t411 = Flatten()(Conv3D(num_4x4, (1, 4, 4), padding='valid')(t)) t = Concatenate()([t144, t414, t441, t114, t141, t411, Flatten()(t)]) t = BatchNormalization()(t) t = Activation('relu')(t) t = Dense(2048)(t) t = BatchNormalization()(t) t = Activation('relu')(t) t = Dense(512)(t) t = BatchNormalization()(t) t = Activation('relu')(t) t = Dense(1)(t) t = Activation('tanh')(t) model = Model(inputs, t) #model.load_weights("models/model50.h5") adam = adam_v2.Adam(learning_rate=LEARNING_RATE) model.compile(loss='mse', optimizer=adam) return model
from keras.layers import Conv2D, Dense, Dropout, Flatten, MaxPooling2D, BatchNormalization from keras.models import Sequential, save_model from keras.optimizers import adam_v2 input_dims = (100, 100, 1) n_classes = 27 learning_rate = 0.001 adam = adam_v2.Adam(learning_rate=0.001) classifier = Sequential() classifier.add(Conv2D(64, (5, 5), padding='same', input_shape=input_dims, activation='relu')) classifier.add(MaxPooling2D(pool_size=(4, 4))) classifier.add(Conv2D(128, (5, 5), padding='same', input_shape=input_dims, activation='relu')) classifier.add(MaxPooling2D(pool_size=(4, 4))) classifier.add(Conv2D(256, (5, 5), padding='same', input_shape=input_dims, activation='relu')) classifier.add(MaxPooling2D(pool_size=(4, 4))) classifier.add(BatchNormalization()) classifier.add(Flatten()) classifier.add(Dropout(0.5)) classifier.add(Dense(1024, activation='sigmoid')) classifier.add(Dense(n_classes, activation='softmax')) classifier.compile( optimizer='adam', loss='categorical_crossentropy', metrics=["accuracy"])
def trainNetwork(model, args): # open up a game state to communicate with emulator game_state = game.GameState() # store the previous observations in replay memory D = deque() # get the first state by doing nothing and preprocess the image to 80x80x4 do_nothing = np.zeros(ACTIONS) do_nothing[0] = 1 x_t, r_0, terminal = game_state.frame_step(do_nothing) s_t = np.stack((x_t, x_t, x_t, x_t), axis=2) #print (s_t.shape) #In Keras, need to reshape s_t = s_t.reshape(1, s_t.shape[0], s_t.shape[1], s_t.shape[2]) #1*80*80*4 if args['mode'] == 'Run': OBSERVE = 999999999 #We keep observe, never train epsilon = FINAL_EPSILON print("Now we load weight") model.load_weights("model.h5") adam = adam_v2.Adam(lr=LEARNING_RATE) model.compile(loss='mse', optimizer=adam) print("Weight load successfully") else: #We go to training mode OBSERVE = OBSERVATION epsilon = INITIAL_EPSILON t = 0 while (True): loss = 0 Q_sa = 0 action_index = 0 r_t = 0 a_t = np.zeros([ACTIONS]) #choose an action epsilon greedy if t % FRAME_PER_ACTION == 0: if random.random() <= epsilon: print("----------Random Action----------") action_index = random.randrange(ACTIONS) a_t[action_index] = 1 else: q = model.predict( s_t) #input a stack of 4 images, get the prediction max_Q = np.argmax(q) action_index = max_Q a_t[max_Q] = 1 #We reduced the epsilon gradually if epsilon > FINAL_EPSILON and t > OBSERVE: epsilon -= (INITIAL_EPSILON - FINAL_EPSILON) / EXPLORE #run the selected action and observed next state and reward x_t1_colored, r_t, terminal = game_state.frame_step(a_t) x_t1 = x_t1.reshape(1, x_t1.shape[0], x_t1.shape[1], 1) #1x80x80x1 s_t1 = np.append(x_t1, s_t[:, :, :, :3], axis=3) # store the transition in D D.append((s_t, action_index, r_t, s_t1, terminal)) if len(D) > REPLAY_MEMORY: D.popleft() #only train if done observing if t > OBSERVE: #sample a minibatch to train on minibatch = random.sample(D, BATCH) inputs = np.zeros((BATCH, s_t.shape[1], s_t.shape[2], s_t.shape[3])) #32, 80, 80, 4 print(inputs.shape) targets = np.zeros((inputs.shape[0], ACTIONS)) #32, 2 #Now we do the experience replay for i in range(0, len(minibatch)): state_t = minibatch[i][0] action_t = minibatch[i][1] #This is action index reward_t = minibatch[i][2] state_t1 = minibatch[i][3] terminal = minibatch[i][4] # if terminated, only equals reward inputs[i:i + 1] = state_t #I saved down s_t targets[i] = model.predict( state_t) # Hitting each buttom probability Q_sa = model.predict(state_t1) if terminal: targets[i, action_t] = reward_t else: targets[i, action_t] = reward_t + GAMMA * np.max(Q_sa) # targets2 = normalize(targets) loss += model.train_on_batch(inputs, targets) s_t = s_t1 t = t + 1 # save progress every 10000 iterations if t % 1000 == 0: print("Now we save model") model.save_weights("model.h5", overwrite=True) with open("model.json", "w") as outfile: json.dump(model.to_json(), outfile) # print info state = "" if t <= OBSERVE: state = "observe" elif t > OBSERVE and t <= OBSERVE + EXPLORE: state = "explore" else: state = "train" print("TIMESTEP", t, "/ STATE", state, \ "/ EPSILON", epsilon, "/ ACTION", action_index, "/ REWARD", r_t, \ "/ Q_MAX " , np.max(Q_sa), "/ Loss ", loss) print("Episode finished!") print("************************")
def test_binary_input_detector(self): """ Test the binary input detector end-to-end. :return: """ # Get MNIST nb_train, nb_test = 1000, 10 (x_train, y_train), (x_test, y_test), _, _ = load_mnist() x_train, y_train = x_train[:NB_TRAIN], y_train[:NB_TRAIN] x_test, y_test = x_test[:NB_TEST], y_test[:NB_TEST] # Keras classifier classifier = get_image_classifier_kr() # Generate adversarial samples: attacker = FastGradientMethod(classifier, eps=0.1) x_train_adv = attacker.generate(x_train[:nb_train]) x_test_adv = attacker.generate(x_test[:nb_test]) # Compile training data for detector: x_train_detector = np.concatenate((x_train[:nb_train], x_train_adv), axis=0) y_train_detector = np.concatenate( (np.array([[1, 0]] * nb_train), np.array([[0, 1]] * nb_train)), axis=0) # Create a simple CNN for the detector input_shape = x_train.shape[1:] try: from keras.optimizers import Adam optimizer = Adam(lr=0.01) except ImportError: from keras.optimizers import adam_v2 optimizer = adam_v2.Adam(lr=0.01) model = Sequential() model.add( Conv2D(4, kernel_size=(5, 5), activation="relu", input_shape=input_shape)) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Flatten()) model.add(Dense(2, activation="softmax")) model.compile(loss=keras.losses.categorical_crossentropy, optimizer=optimizer, metrics=["accuracy"]) # Create detector and train it: detector = BinaryInputDetector( KerasClassifier(model=model, clip_values=(0, 1), use_logits=False)) detector.fit(x_train_detector, y_train_detector, nb_epochs=2, batch_size=128) # Apply detector on clean and adversarial test data: test_detection = np.argmax(detector.predict(x_test), axis=1) test_adv_detection = np.argmax(detector.predict(x_test_adv), axis=1) # Assert there is at least one true positive and negative: nb_true_positives = len(np.where(test_adv_detection == 1)[0]) nb_true_negatives = len(np.where(test_detection == 0)[0]) logger.debug("Number of true positives detected: %i", nb_true_positives) logger.debug("Number of true negatives detected: %i", nb_true_negatives) self.assertGreater(nb_true_positives, 0) self.assertGreater(nb_true_negatives, 0)
def __init__( self, model: KERAS_MODEL_TYPE, use_logits: bool = False, channels_first: bool = False, clip_values: Optional["CLIP_VALUES_TYPE"] = None, preprocessing_defences: Union["Preprocessor", List["Preprocessor"], None] = None, postprocessing_defences: Union["Postprocessor", List["Postprocessor"], None] = None, preprocessing: "PREPROCESSING_TYPE" = (0.0, 1.0), input_layer: int = 0, output_layer: int = 0, steps: int = 1000, init_cost: float = 1e-3, norm: Union[int, float] = 2, learning_rate: float = 0.1, attack_success_threshold: float = 0.99, patience: int = 5, early_stop: bool = True, early_stop_threshold: float = 0.99, early_stop_patience: int = 10, cost_multiplier: float = 1.5, batch_size: int = 32, ): """ Create a Neural Cleanse classifier. :param model: Keras model, neural network or other. :param use_logits: True if the output of the model are logits; false for probabilities or any other type of outputs. Logits output should be favored when possible to ensure attack efficiency. :param channels_first: Set channels first or last. :param clip_values: Tuple of the form `(min, max)` of floats or `np.ndarray` representing the minimum and maximum values allowed for features. If floats are provided, these will be used as the range of all features. If arrays are provided, each value will be considered the bound for a feature, thus the shape of clip values needs to match the total number of features. :param preprocessing_defences: Preprocessing defence(s) to be applied by the classifier. :param postprocessing_defences: Postprocessing defence(s) to be applied by the classifier. :param preprocessing: Tuple of the form `(subtrahend, divisor)` of floats or `np.ndarray` of values to be used for data preprocessing. The first value will be subtracted from the input. The input will then be divided by the second one. :param input_layer: The index of the layer to consider as input for models with multiple input layers. The layer with this index will be considered for computing gradients. For models with only one input layer this values is not required. :param output_layer: Which layer to consider as the output when the models has multiple output layers. The layer with this index will be considered for computing gradients. For models with only one output layer this values is not required. :param steps: The maximum number of steps to run the Neural Cleanse optimization :param init_cost: The initial value for the cost tensor in the Neural Cleanse optimization :param norm: The norm to use for the Neural Cleanse optimization, can be 1, 2, or np.inf :param learning_rate: The learning rate for the Neural Cleanse optimization :param attack_success_threshold: The threshold at which the generated backdoor is successful enough to stop the Neural Cleanse optimization :param patience: How long to wait for changing the cost multiplier in the Neural Cleanse optimization :param early_stop: Whether or not to allow early stopping in the Neural Cleanse optimization :param early_stop_threshold: How close values need to come to max value to start counting early stop :param early_stop_patience: How long to wait to determine early stopping in the Neural Cleanse optimization :param cost_multiplier: How much to change the cost in the Neural Cleanse optimization :param batch_size: The batch size for optimizations in the Neural Cleanse optimization """ import keras.backend as K from keras.losses import categorical_crossentropy from keras.metrics import categorical_accuracy super().__init__( model=model, use_logits=use_logits, channels_first=channels_first, clip_values=clip_values, preprocessing_defences=preprocessing_defences, postprocessing_defences=postprocessing_defences, preprocessing=preprocessing, input_layer=input_layer, output_layer=output_layer, steps=steps, init_cost=init_cost, norm=norm, learning_rate=learning_rate, attack_success_threshold=attack_success_threshold, early_stop=early_stop, early_stop_threshold=early_stop_threshold, early_stop_patience=early_stop_patience, patience=patience, cost_multiplier=cost_multiplier, batch_size=batch_size, ) mask = np.random.uniform(size=super().input_shape) pattern = np.random.uniform(size=super().input_shape) self.epsilon = K.epsilon() # Normalize mask between [0, 1] self.mask_tensor_raw = K.variable(mask) # self.mask_tensor = K.expand_dims(K.tanh(self.mask_tensor_raw) / (2 - self.epsilon) + 0.5, axis=0) self.mask_tensor = K.tanh(self.mask_tensor_raw) / (2 - self.epsilon) + 0.5 # Normalize pattern between [0, 1] self.pattern_tensor_raw = K.variable(pattern) self.pattern_tensor = K.expand_dims(K.tanh(self.pattern_tensor_raw) / (2 - self.epsilon) + 0.5, axis=0) reverse_mask_tensor = K.ones_like(self.mask_tensor) - self.mask_tensor input_tensor = K.placeholder(model.input_shape) x_adv_tensor = reverse_mask_tensor * input_tensor + self.mask_tensor * self.pattern_tensor output_tensor = self.model(x_adv_tensor) y_true_tensor = K.placeholder(model.outputs[0].shape.as_list()) self.loss_acc = categorical_accuracy(output_tensor, y_true_tensor) self.loss_ce = categorical_crossentropy(output_tensor, y_true_tensor) if self.norm == 1: # TODO: change 3 to dynamically set img_color self.loss_reg = K.sum(K.abs(self.mask_tensor)) / 3 elif self.norm == 2: self.loss_reg = K.sqrt(K.sum(K.square(self.mask_tensor)) / 3) self.cost = self.init_cost self.cost_tensor = K.variable(self.cost) self.loss_combined = self.loss_ce + self.loss_reg * self.cost_tensor try: from keras.optimizers import Adam self.opt = Adam(lr=self.learning_rate, beta_1=0.5, beta_2=0.9) except ImportError: from keras.optimizers import adam_v2 self.opt = adam_v2.Adam(lr=self.learning_rate, beta_1=0.5, beta_2=0.9) self.updates = self.opt.get_updates( params=[self.pattern_tensor_raw, self.mask_tensor_raw], loss=self.loss_combined ) self.train = K.function( [input_tensor, y_true_tensor], [self.loss_ce, self.loss_reg, self.loss_combined, self.loss_acc], updates=self.updates, )
def __init__( self, classifier: "CLASSIFIER_TYPE", backdoor: PoisoningAttackBackdoor, feature_layer: Union[int, str], target: Union[np.ndarray, List[Tuple[np.ndarray, np.ndarray]]], pp_poison: Union[float, List[float]] = 0.05, discriminator_layer_1: int = 256, discriminator_layer_2: int = 128, regularization: float = 10, learning_rate: float = 1e-4, clone=True, ): """ Initialize an Feature Collision Clean-Label poisoning attack :param classifier: A neural network classifier. :param backdoor: The backdoor attack used to poison samples :param feature_layer: The layer of the original network to extract features from :param target: The target label to poison :param pp_poison: The percentage of training data to poison :param discriminator_layer_1: The size of the first discriminator layer :param discriminator_layer_2: The size of the second discriminator layer :param regularization: The regularization constant for the backdoor recognition part of the loss function :param learning_rate: The learning rate of clean-label attack optimization. :param clone: Whether or not to clone the model or apply the attack on the original model """ super().__init__(classifier=classifier) self.backdoor = backdoor self.feature_layer = feature_layer self.target = target if isinstance(pp_poison, float): self.pp_poison = [pp_poison] else: self.pp_poison = pp_poison self.discriminator_layer_1 = discriminator_layer_1 self.discriminator_layer_2 = discriminator_layer_2 self.regularization = regularization self.train_data: Optional[np.ndarray] = None self.train_labels: Optional[np.ndarray] = None self.is_backdoor: Optional[np.ndarray] = None self.learning_rate = learning_rate self._check_params() if isinstance(self.estimator, KerasClassifier): using_tf_keras = "tensorflow.python.keras" in str( type(self.estimator.model)) if using_tf_keras: # pragma: no cover from tensorflow.keras.models import Model, clone_model # pylint: disable=E0611 from tensorflow.keras.layers import ( # pylint: disable=E0611 GaussianNoise, Dense, BatchNormalization, LeakyReLU, ) from tensorflow.keras.optimizers import Adam # pylint: disable=E0611 opt = Adam(lr=self.learning_rate) else: from keras import Model from keras.models import clone_model from keras.layers import GaussianNoise, Dense, BatchNormalization, LeakyReLU try: from keras.optimizers import Adam opt = Adam(lr=self.learning_rate) except ImportError: from keras.optimizers import adam_v2 opt = adam_v2.Adam(lr=self.learning_rate) if clone: self.orig_model = clone_model( self.estimator.model, input_tensors=self.estimator.model.inputs) else: self.orig_model = self.estimator.model model_input = self.orig_model.input init_model_output = self.orig_model(model_input) # Extracting feature tensor if isinstance(self.feature_layer, int): feature_layer_tensor = self.orig_model.layers[ self.feature_layer].output else: feature_layer_tensor = self.orig_model.get_layer( name=feature_layer).output feature_layer_output = Model(inputs=[model_input], outputs=[feature_layer_tensor]) # Architecture for discriminator discriminator_input = feature_layer_output(model_input) discriminator_input = GaussianNoise(stddev=1)(discriminator_input) dense_layer_1 = Dense( self.discriminator_layer_1)(discriminator_input) norm_1_layer = BatchNormalization()(dense_layer_1) leaky_layer_1 = LeakyReLU(alpha=0.2)(norm_1_layer) dense_layer_2 = Dense(self.discriminator_layer_2)(leaky_layer_1) norm_2_layer = BatchNormalization()(dense_layer_2) leaky_layer_2 = LeakyReLU(alpha=0.2)(norm_2_layer) backdoor_detect = Dense(2, activation="softmax", name="backdoor_detect")(leaky_layer_2) # Creating embedded model self.embed_model = Model( inputs=self.orig_model.inputs, outputs=[init_model_output, backdoor_detect]) # Add backdoor detection loss model_name = self.orig_model.name model_loss = self.estimator.model.loss loss_name = "backdoor_detect" loss_type = "binary_crossentropy" if isinstance(model_loss, str): losses = {model_name: model_loss, loss_name: loss_type} loss_weights = { model_name: 1.0, loss_name: -self.regularization } elif isinstance(model_loss, dict): losses = model_loss losses[loss_name] = loss_type loss_weights = self.orig_model.loss_weights loss_weights[loss_name] = -self.regularization else: raise TypeError( f"Cannot read model loss value of type {type(model_loss)}") self.embed_model.compile(optimizer=opt, loss=losses, loss_weights=loss_weights, metrics=["accuracy"]) else: raise NotImplementedError( "This attack currently only supports Keras.")