Exemplo de adversário usando FGSM

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

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

Exemplo Adversário

Aqui, começando com a imagem de um panda, o invasor adiciona pequenas perturbações (distorções) à imagem original, o que resulta no modelo rotulando 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 contraditó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 maximiza a perda. Essa nova imagem é chamada de imagem adversária. Isso pode ser resumido usando a seguinte expressão:

adv_x=x+ϵsign(xJ(θ,x,y))

Onde

  • adv_x : imagem do adversário.
  • x : Imagem de entrada original.
  • y : Etiqueta de entrada original.
  • ϵ : Multiplicador para garantir que as perturbações sejam pequenas.
  • θ : 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 é feito porque o objetivo é criar uma imagem que maximize a perda. Um método para fazer isso é descobrir o quanto cada pixel na imagem contribui para o valor da 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. Assim, os gradientes são obtidos em relação à imagem. Além disso, como o modelo não está mais sendo treinado (assim, 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 no 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 [==============================] - 0s 0us/step
14548992/14536120 [==============================] - 0s 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. O primeiro passo é pré-processá-lo para que possa ser alimentado como 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
98304/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
49152/35363 [=========================================] - 0s 0us/step

png

Crie a imagem do adversário

Implementando o 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. Como 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]

png

Vamos tentar isso para diferentes valores de epsilon e observar a imagem resultante. Você notará que à medida que o valor de epsilon aumenta, fica mais fácil enganar a rede. No entanto, isso vem como um trade-off que resulta em perturbações tornando-se 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ê conhece os ataques adversários, experimente isso em diferentes conjuntos de dados e diferentes arquiteturas. Você também pode criar e treinar seu próprio modelo e tentar enganá-lo usando o mesmo método. Você também pode tentar ver como a confiança nas previsões varia conforme você altera o epsilon.

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

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