Exemplo adversário usando FGSM

Ver no TensorFlow.org Executar no Google Colab Ver fonte no GitHub Baixar caderno

Este tutorial cria um exemplo de adversário usando o ataque Fast Gradient Signed Method (FGSM), conforme descrito em Explicando e aproveitando os exemplos adversários de Goodfellow et al . Este foi um dos primeiros e mais populares ataques para enganar uma rede neural.

O que é um exemplo de adversário?

Os exemplos adversários são entradas especializadas criadas com o propósito de confundir uma rede neural, resultando na classificação incorreta de uma determinada entrada. Essas entradas notórias são indistinguíveis ao olho humano, mas fazem com que a rede falhe em identificar o conteúdo da imagem. Existem vários tipos de ataques, no entanto, aqui o foco está no ataque de método de sinal de gradiente rápido, que é um ataque de caixa branca cujo objetivo é garantir uma classificação incorreta. Um ataque de caixa branca é quando o invasor tem acesso completo ao modelo que está sendo atacado. Um dos exemplos mais famosos de uma imagem adversária mostrada abaixo foi tirado do artigo mencionado.

Exemplo Adversarial

Aqui, começando com a imagem de um panda, o atacante adiciona pequenas perturbações (distorções) à imagem original, o que resulta no modelo rotular essa imagem como um gibão, com alta confiança. O processo de adição dessas perturbações é explicado abaixo.

Método de sinal de gradiente rápido

O método de sinal de gradiente rápido funciona usando os gradientes da rede neural para criar um exemplo adversário. Para uma imagem de entrada, o método usa os gradientes da perda em relação à imagem de entrada para criar uma nova imagem que maximize a perda. Essa nova imagem é chamada de imagem do adversário. Isso pode ser resumido usando a seguinte expressão:

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

Onde

  • adv_x: imagem adversária.
  • x: Imagem de entrada original.
  • y: Etiqueta de entrada original.
  • $ \ epsilon $: multiplicador para garantir que as perturbações sejam pequenas.
  • $ \ theta $: parâmetros do modelo.
  • $ J $: Perda.

Uma propriedade intrigante aqui é o fato de que os gradientes são obtidos em relação à imagem de entrada. Isso porque o objetivo é criar uma imagem que maximize a perda. Um método para fazer isso é descobrir quanto cada pixel na imagem contribui para o valor de perda e adicionar uma perturbação de acordo. Isso funciona muito rápido porque é fácil descobrir como cada pixel de entrada contribui para a perda usando a regra da cadeia e encontrando os gradientes necessários. Conseqüentemente, os gradientes são obtidos em relação à imagem. Além disso, uma vez que o modelo não está mais sendo treinado (portanto, o gradiente não é tomado em relação às variáveis ​​treináveis, ou seja, os parâmetros do modelo), e assim os parâmetros do modelo permanecem constantes. O único objetivo é enganar um modelo já treinado.

Então, vamos tentar enganar um modelo pré-treinado. Neste tutorial, o modelo é o modelo MobileNetV2 , pré- treinado em 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

Vamos carregar o modelo MobileNetV2 pré-treinado e os nomes das classes 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]

Imagem original

Vamos usar uma imagem de amostra de um Labrador Retriever por Mirko CC-BY-SA 3.0 do Wikimedia Common e criar exemplos adversários a partir dela. A primeira etapa é pré-processá-lo para que possa ser alimentado como uma entrada para o modelo 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

Vamos dar uma olhada na imagem.

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

Crie a imagem adversária

Implementando método de sinal de gradiente rápido

O primeiro passo é criar perturbações que serão usadas para distorcer a imagem original, resultando em uma imagem adversária. Conforme mencionado, para esta tarefa, os gradientes são obtidos em relação à imagem.

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

As perturbações resultantes também podem ser visualizadas.

# 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

Vamos experimentar para diferentes valores de épsilon e observar a imagem resultante. Você notará que conforme o valor do épsilon aumenta, fica mais fácil enganar a rede. No entanto, isso vem como um trade-off que resulta nas perturbações se tornando mais identificáveis.

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

Próximos passos

Agora que você sabe sobre ataques adversários, experimente fazer isso em diferentes conjuntos de dados e diferentes arquiteturas. Você também pode criar e treinar seu próprio modelo e, em seguida, tentar enganá-lo usando o mesmo método. Você também pode tentar ver como a confiança nas previsões varia conforme você muda o épsilon.

Embora poderoso, o ataque mostrado neste tutorial foi apenas o início da pesquisa sobre ataques adversários, e vários artigos criaram ataques mais poderosos desde então. Além dos ataques adversários, as pesquisas também levaram à criação de defesas, que visam criar modelos robustos de aprendizado de máquina. Você pode revisar este artigo de pesquisa para obter uma lista abrangente de ataques e defesas adversárias.

Para muitas outras implementações de ataques e defesas adversárias, você pode querer ver a biblioteca de exemplos adversários CleverHans .