Exemple contradictoire utilisant FGSM

Voir sur TensorFlow.org Exécuter dans Google Colab Voir la source sur GitHub Télécharger le cahier

Ce didacticiel crée un exemple accusatoire à l'aide de l'attaque Fast Gradient Signed Method (FGSM) comme décrit dans Explaining and Harnessing Adversarial Examples de Goodfellow et al . Ce fut l'une des premières et des plus populaires attaques à tromper un réseau de neurones.

Qu'est-ce qu'un exemple contradictoire ?

Les exemples contradictoires sont des entrées spécialisées créées dans le but de confondre un réseau de neurones, entraînant une mauvaise classification d'une entrée donnée. Ces entrées notoires sont indiscernables à l'œil humain, mais empêchent le réseau d'identifier le contenu de l'image. Il existe plusieurs types d'attaques de ce type, cependant, l'accent est mis ici sur l'attaque par méthode de signe de gradient rapide, qui est une attaque en boîte blanche dont le but est d'assurer une mauvaise classification. Une attaque en boîte blanche est l'endroit où l'attaquant a un accès complet au modèle attaqué. L'un des exemples les plus célèbres d'une image contradictoire montré ci-dessous est tiré de l'article susmentionné.

Exemple contradictoire

Ici, en commençant par l'image d'un panda, l'attaquant ajoute de petites perturbations (distorsions) à l'image d'origine, ce qui fait que le modèle étiquette cette image comme un gibbon, avec une confiance élevée. Le processus d'ajout de ces perturbations est expliqué ci-dessous.

Méthode de signe de gradient rapide

La méthode du signe de gradient rapide fonctionne en utilisant les gradients du réseau de neurones pour créer un exemple contradictoire. Pour une image d'entrée, le procédé utilise les gradients de la perte par rapport à l'image d'entrée pour créer une nouvelle image qui maximise la perte. Cette nouvelle image est appelée l'image contradictoire. Cela peut se résumer à l'aide de l'expression suivante :

$$adv\_x = x + \epsilon*\text{sign}(\nabla_xJ(\theta, x, y))$$

  • adv_x : Image contradictoire.
  • x : Image d'entrée d'origine.
  • y : Étiquette d'entrée d'origine.
  • $\epsilon$ : Multiplicateur pour s'assurer que les perturbations sont petites.
  • $\theta$ : Paramètres du modèle.
  • $J$ : Perte.

Une propriété intrigante ici, est le fait que les gradients sont pris par rapport à l'image d'entrée. Ceci est fait parce que l'objectif est de créer une image qui maximise la perte. Une méthode pour y parvenir consiste à trouver dans quelle mesure chaque pixel de l'image contribue à la valeur de perte et à ajouter une perturbation en conséquence. Cela fonctionne assez rapidement car il est facile de trouver comment chaque pixel d'entrée contribue à la perte en utilisant la règle de la chaîne et en trouvant les gradients requis. Par conséquent, les gradients sont pris par rapport à l'image. De plus, puisque le modèle n'est plus en train d'être entraîné (donc le gradient n'est pas pris par rapport aux variables entraînables, c'est-à-dire les paramètres du modèle), et donc les paramètres du modèle restent constants. Le seul but est de tromper un modèle déjà entraîné.

Essayons donc de tromper un modèle pré-entraîné. Dans ce tutoriel, le modèle est le modèle MobileNetV2 , pré-entraîné sur ImageNet .

import tensorflow as tf
import matplotlib as mpl
import matplotlib.pyplot as plt

mpl.rcParams['figure.figsize'] = (8, 8)
mpl.rcParams['axes.grid'] = False

Chargeons le modèle MobileNetV2 pré-entraîné et les noms de classe ImageNet.

pretrained_model = tf.keras.applications.MobileNetV2(include_top=True,
                                                     weights='imagenet')
pretrained_model.trainable = False

# ImageNet labels
decode_predictions = tf.keras.applications.mobilenet_v2.decode_predictions
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/mobilenet_v2/mobilenet_v2_weights_tf_dim_ordering_tf_kernels_1.0_224.h5
14540800/14536120 [==============================] - 1s 0us/step
# Helper function to preprocess the image so that it can be inputted in MobileNetV2
def preprocess(image):
  image = tf.cast(image, tf.float32)
  image = tf.image.resize(image, (224, 224))
  image = tf.keras.applications.mobilenet_v2.preprocess_input(image)
  image = image[None, ...]
  return image

# Helper function to extract labels from probability vector
def get_imagenet_label(probs):
  return decode_predictions(probs, top=1)[0][0]

Image originale

Utilisons un exemple d'image d'un Labrador Retriever par Mirko CC-BY-SA 3.0 de Wikimedia Common et créons des exemples contradictoires à partir de celui-ci. La première étape consiste à le prétraiter afin qu'il puisse être fourni comme entrée au modèle MobileNetV2.

image_path = tf.keras.utils.get_file('YellowLabradorLooking_new.jpg', 'https://storage.googleapis.com/download.tensorflow.org/example_images/YellowLabradorLooking_new.jpg')
image_raw = tf.io.read_file(image_path)
image = tf.image.decode_image(image_raw)

image = preprocess(image)
image_probs = pretrained_model.predict(image)
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/YellowLabradorLooking_new.jpg
90112/83281 [================================] - 0s 0us/step

Regardons l'image.

plt.figure()
plt.imshow(image[0] * 0.5 + 0.5)  # To change [-1, 1] to [0,1]
_, image_class, class_confidence = get_imagenet_label(image_probs)
plt.title('{} : {:.2f}% Confidence'.format(image_class, class_confidence*100))
plt.show()
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/imagenet_class_index.json
40960/35363 [==================================] - 0s 0us/step

png

Créer l'image contradictoire

Implémentation de la méthode de signe de gradient rapide

La première étape consiste à créer des perturbations qui seront utilisées pour déformer l'image originale résultant en une image contradictoire. Comme mentionné, pour cette tâche, les gradients sont pris par rapport à l'image.

loss_object = tf.keras.losses.CategoricalCrossentropy()

def create_adversarial_pattern(input_image, input_label):
  with tf.GradientTape() as tape:
    tape.watch(input_image)
    prediction = pretrained_model(input_image)
    loss = loss_object(input_label, prediction)

  # Get the gradients of the loss w.r.t to the input image.
  gradient = tape.gradient(loss, input_image)
  # Get the sign of the gradients to create the perturbation
  signed_grad = tf.sign(gradient)
  return signed_grad

Les perturbations résultantes peuvent également être visualisées.

# Get the input label of the image.
labrador_retriever_index = 208
label = tf.one_hot(labrador_retriever_index, image_probs.shape[-1])
label = tf.reshape(label, (1, image_probs.shape[-1]))

perturbations = create_adversarial_pattern(image, label)
plt.imshow(perturbations[0] * 0.5 + 0.5);  # To change [-1, 1] to [0,1]
<matplotlib.image.AxesImage at 0x7f4171d66690>

png

Essayons ceci pour différentes valeurs d'epsilon et observons l'image résultante. Vous remarquerez qu'au fur et à mesure que la valeur d'epsilon augmente, il devient plus facile de tromper le réseau. Cependant, il s'agit d'un compromis qui rend les perturbations plus identifiables.

def display_images(image, description):
  _, label, confidence = get_imagenet_label(pretrained_model.predict(image))
  plt.figure()
  plt.imshow(image[0]*0.5+0.5)
  plt.title('{} \n {} : {:.2f}% Confidence'.format(description,
                                                   label, confidence*100))
  plt.show()
epsilons = [0, 0.01, 0.1, 0.15]
descriptions = [('Epsilon = {:0.3f}'.format(eps) if eps else 'Input')
                for eps in epsilons]

for i, eps in enumerate(epsilons):
  adv_x = image + eps*perturbations
  adv_x = tf.clip_by_value(adv_x, -1, 1)
  display_images(adv_x, descriptions[i])

png

png

png

png

Prochaines étapes

Maintenant que vous connaissez les attaques contradictoires, essayez-le sur différents ensembles de données et différentes architectures. Vous pouvez également créer et entraîner votre propre modèle, puis tenter de le tromper en utilisant la même méthode. Vous pouvez également essayer de voir comment la confiance dans les prédictions varie lorsque vous changez d'epsilon.

Bien que puissante, l'attaque présentée dans ce didacticiel n'était que le début de la recherche sur les attaques contradictoires, et plusieurs articles ont créé des attaques plus puissantes depuis lors. En plus des attaques contradictoires, la recherche a également conduit à la création de défenses, qui visent à créer des modèles d'apprentissage automatique robustes. Vous pouvez consulter ce document d'enquête pour obtenir une liste complète des attaques et des défenses contradictoires.

Pour de nombreuses autres implémentations d'attaques et de défenses contradictoires, vous souhaiterez peut-être consulter la bibliothèque d'exemples contradictoires CleverHans .