Familiarisons-nous avec l'une des attaques contre le réseau neuronal, qui conduit à des erreurs de classification avec un minimum d'influences externes. Imaginez un instant que le réseau neuronal c'est vous. Et pour le moment, en buvant une tasse de café aromatique, vous classez les images de chats avec une précision de plus de 90% sans même soupçonner que «l'attaque d'un pixel» a transformé tous vos «chats» en camions.Et maintenant, nous allons faire une pause, déplacer le café de côté, importer toutes les bibliothèques dont nous avons besoin et analyser le fonctionnement de ces attaques à un pixel.Le but de cette attaque est de faire que l'algorithme (réseau de neurones) donne une réponse incorrecte. Ci-dessous, nous le verrons avec plusieurs modèles différents de réseaux de neurones convolutionnels. En utilisant l'une des méthodes d'optimisation mathématique multidimensionnelle - évolution différentielle, nous trouvons un pixel spécial qui peut changer l'image de sorte que le réseau de neurones commence à classer incorrectement cette image (malgré le fait qu'auparavant l'algorithme «reconnaissait» la même image correctement et avec une grande précision).Importez les bibliothèques:
%matplotlib inline
import pickle
import numpy as np
import pandas as pd
import matplotlib
from keras.datasets import cifar10
from keras import backend as K
from networks.lenet import LeNet
from networks.pure_cnn import PureCnn
from networks.network_in_network import NetworkInNetwork
from networks.resnet import ResNet
from networks.densenet import DenseNet
from networks.wide_resnet import WideResNet
from networks.capsnet import CapsNet
from differential_evolution import differential_evolution
import helper
matplotlib.style.use('ggplot')
Pour notre expérience, nous chargerons l'ensemble de données CIFAR-10 contenant des images du monde réel divisées en 10 classes.(x_train, y_train), (x_test, y_test) = cifar10.load_data()
class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
Regardons n'importe quelle image par son index. Par exemple, ici sur ce cheval.image_id = 99
helper.plot_image(x_test[image_id])
Nous devrons rechercher le pixel très puissant qui peut changer la réponse du réseau neuronal, ce qui signifie qu'il est temps d'écrire une fonction pour changer un ou plusieurs pixels de l'image.def perturb_image(xs, img):
if xs.ndim < 2:
xs = np.array([xs])
tile = [len(xs)] + [1]*(xs.ndim+1)
imgs = np.tile(img, tile)
xs = xs.astype(int)
for x,img in zip(xs, imgs):
pixels = np.split(x, len(x) // 5)
for pixel in pixels:
x_pos, y_pos, *rgb = pixel
img[x_pos, y_pos] = rgb
return imgs
Vérifiez-le ?! Changez un pixel de notre cheval avec les coordonnées (16, 16) en jaune.image_id = 99
pixel = np.array([16, 16, 255, 255, 0])
image_perturbed = perturb_image(pixel, x_test[image_id])[0]
helper.plot_image(image_perturbed)
Pour démontrer l'attaque, vous devez télécharger des modèles pré-formés de réseaux de neurones sur notre jeu de données CIFAR-10. Nous utiliserons deux modèles lenet et resnet, mais vous pouvez en utiliser d'autres pour vos expériences en décommentant les lignes de code correspondantes.lenet = LeNet()
resnet = ResNet()
models = [lenet, resnet]
Après avoir chargé les modèles, il est nécessaire d'évaluer les images de test de chaque modèle pour s'assurer que nous n'attaquons que les images correctement classées. Le code ci-dessous affiche la précision et le nombre de paramètres pour chaque modèle.network_stats, correct_imgs = helper.evaluate_models(models, x_test, y_test)
correct_imgs = pd.DataFrame(correct_imgs, columns=['name', 'img', 'label', 'confidence', 'pred'])
network_stats = pd.DataFrame(network_stats, columns=['name', 'accuracy', 'param_count'])
network_stats
Evaluating lenet
Evaluating resnet
Out[11]:
name accuracy param_count
0 lenet 0.748 62006
1 resnet 0.9231 470218
Toutes ces attaques peuvent être divisées en deux classes: WhiteBox et BlackBox. La différence entre eux est que dans le premier cas, nous connaissons tous de manière fiable l'algorithme, le modèle avec lequel nous avons affaire. Dans le cas de la BlackBox, tout ce dont nous avons besoin est une entrée (image) et une sortie (probabilités d'être affectées à l'une des classes). Une attaque pixel fait référence à la BlackBox.Dans cet article, nous considérons deux options pour attaquer un seul pixel: non ciblé et ciblé. Dans le premier cas, peu importe à quelle classe le réseau neuronal de notre chat appartiendra, mais surtout pas à la classe des chats. L'attaque ciblée est applicable lorsque nous voulons que notre chat devienne un camion et seulement un camion.Mais comment trouver les pixels mêmes dont le changement entraînera un changement de classe de l'image? Comment trouver un pixel en changeant laquelle une attaque de pixel devient possible et réussie? Essayons de formuler ce problème comme un problème d'optimisation, mais seulement en termes très simples: avec une attaque non ciblée, nous devons minimiser la confiance dans la classe souhaitée, et avec ciblé, maximiser la confiance dans la classe cible.Lors de l'exécution de telles attaques, il est difficile d'optimiser la fonction à l'aide d'un gradient. Un algorithme d'optimisation doit être utilisé qui ne repose pas sur la fluidité de la fonction.Rappelons que pour notre expérience, nous utilisons l'ensemble de données CIFAR-10, qui contient des images du monde réel, de 32 x 32 pixels, divisées en 10 classes. Cela signifie que nous avons des valeurs discrètes entières de 0 à 31 et des intensités de couleur de 0 à 255, et la fonction ne devrait pas être lisse, mais plutôt irrégulière, comme indiqué ci-dessous:
C'est pourquoi nous utilisons l'algorithme d'évolution différentielle.Mais revenons au code et écrivez une fonction qui renvoie la probabilité de fiabilité du modèle. Si la classe cible est correcte, nous voulons minimiser cette fonction afin que le modèle soit sûr d'une autre classe (ce qui n'est pas vrai).def predict_classes(xs, img, target_class, model, minimize=True):
imgs_perturbed = perturb_image(xs, img)
predictions = model.predict(imgs_perturbed)[:,target_class]
return predictions if minimize else 1 - predictions
image_id = 384
pixel = np.array([16, 13, 25, 48, 156])
model = resnet
true_class = y_test[image_id, 0]
prior_confidence = model.predict_one(x_test[image_id])[true_class]
confidence = predict_classes(pixel, x_test[image_id], true_class, model)[0]
print('Confidence in true class', class_names[true_class], 'is', confidence)
print('Prior confidence was', prior_confidence)
helper.plot_image(perturb_image(pixel, x_test[image_id])[0])
Confidence in true class bird is 0.00018887444
Prior confidence was 0.70661753
Nous aurons besoin de la fonction suivante pour confirmer le critère de réussite de l'attaque, elle retournera True lorsque le changement aura suffi à tromper le modèle.def attack_success(x, img, target_class, model, targeted_attack=False, verbose=False):
attack_image = perturb_image(x, img)
confidence = model.predict(attack_image)[0]
predicted_class = np.argmax(confidence)
if verbose:
print('Confidence:', confidence[target_class])
if ((targeted_attack and predicted_class == target_class) or
(not targeted_attack and predicted_class != target_class)):
return True
Regardons le travail de la fonction critère de succès. Afin de démontrer, nous supposons une attaque non ciblée.image_id = 541
pixel = np.array([17, 18, 185, 36, 215])
model = resnet
true_class = y_test[image_id, 0]
prior_confidence = model.predict_one(x_test[image_id])[true_class]
success = attack_success(pixel, x_test[image_id], true_class, model, verbose=True)
print('Prior confidence', prior_confidence)
print('Attack success:', success == True)
helper.plot_image(perturb_image(pixel, x_test[image_id])[0])
Confidence: 0.07460087
Prior confidence 0.50054216
Attack success: True
Il est temps de rassembler tous les puzzles en une seule image. Nous utiliserons une petite modification de l'implémentation de l'évolution différentielle dans Scipy.def attack(img_id, model, target=None, pixel_count=1,
maxiter=75, popsize=400, verbose=False):
targeted_attack = target is not None
target_class = target if targeted_attack else y_test[img_id, 0]
bounds = [(0,32), (0,32), (0,256), (0,256), (0,256)] * pixel_count
popmul = max(1, popsize // len(bounds))
def predict_fn(xs):
return predict_classes(xs, x_test[img_id], target_class,
model, target is None)
def callback_fn(x, convergence):
return attack_success(x, x_test[img_id], target_class,
model, targeted_attack, verbose)
attack_result = differential_evolution(
predict_fn, bounds, maxiter=maxiter, popsize=popmul,
recombination=1, atol=-1, callback=callback_fn, polish=False)
attack_image = perturb_image(attack_result.x, x_test[img_id])[0]
prior_probs = model.predict_one(x_test[img_id])
predicted_probs = model.predict_one(attack_image)
predicted_class = np.argmax(predicted_probs)
actual_class = y_test[img_id, 0]
success = predicted_class != actual_class
cdiff = prior_probs[actual_class] - predicted_probs[actual_class]
helper.plot_image(attack_image, actual_class, class_names, predicted_class)
return [model.name, pixel_count, img_id, actual_class, predicted_class, success, cdiff, prior_probs, predicted_probs, attack_result.x]
Il est temps de partager les résultats de l'étude (l'attaque) et de voir comment le changement d'un seul pixel transformera une grenouille en chien, un chat en grenouille et une voiture en avion. Mais plus les points d'image sont autorisés à changer, plus la probabilité d'une attaque réussie sur une image est élevée.

Démontrez une attaque réussie sur une image de grenouille en utilisant le modèle Resnet. Nous devrions voir la confiance dans le vrai déclin de la classe après plusieurs itérations.image_id = 102
pixels = 1
model = resnet
_ = attack(image_id, model, pixel_count=pixels, verbose=True)
Confidence: 0.9938618
Confidence: 0.77454716
Confidence: 0.77454716
Confidence: 0.77454716
Confidence: 0.77454716
Confidence: 0.77454716
Confidence: 0.53226393
Confidence: 0.53226393
Confidence: 0.53226393
Confidence: 0.53226393
Confidence: 0.4211318
Ce sont des exemples d'une attaque non ciblée, et maintenant nous allons mener une attaque ciblée et choisir dans quelle classe nous aimerions que le modèle classe l'image. La tâche est beaucoup plus compliquée que la précédente, car nous allons faire classer le réseau de neurones l'image d'un navire comme voiture et d'un cheval comme chat.
Ci-dessous, nous essaierons d'obtenir lenet pour classer l'image du navire en tant que voiture.image_id = 108
target_class = 1
pixels = 3
model = lenet
print('Attacking with target', class_names[target_class])
_ = attack(image_id, model, target_class, pixel_count=pixels, verbose=True)
Attacking with target automobile
Confidence: 0.044409167
Confidence: 0.044409167
Confidence: 0.044409167
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.054611664
Confidence: 0.081972085
Confidence: 0.081972085
Confidence: 0.081972085
Confidence: 0.081972085
Confidence: 0.1537778
Confidence: 0.1537778
Confidence: 0.1537778
Confidence: 0.22246778
Confidence: 0.23916133
Confidence: 0.25238588
Confidence: 0.25238588
Confidence: 0.25238588
Confidence: 0.44560355
Confidence: 0.44560355
Confidence: 0.44560355
Confidence: 0.5711696
Après avoir traité des cas uniques d'attaques, nous collecterons des statistiques en utilisant l'architecture des réseaux de neurones convolutionnels ResNet, en passant par chaque modèle, en changeant 1, 3 ou 5 pixels de chaque image. Dans cet article, nous montrons les conclusions finales sans déranger le lecteur à se familiariser avec chaque itération, car cela prend beaucoup de temps et de ressources de calcul.def attack_all(models, samples=500, pixels=(1,3,5), targeted=False,
maxiter=75, popsize=400, verbose=False):
results = []
for model in models:
model_results = []
valid_imgs = correct_imgs[correct_imgs.name == model.name].img
img_samples = np.random.choice(valid_imgs, samples, replace=False)
for pixel_count in pixels:
for i, img_id in enumerate(img_samples):
print('\n', model.name, '- image', img_id, '-', i+1, '/', len(img_samples))
targets = [None] if not targeted else range(10)
for target in targets:
if targeted:
print('Attacking with target', class_names[target])
if target == y_test[img, 0]:
continue
result = attack(img_id, model, target, pixel_count,
maxiter=maxiter, popsize=popsize,
verbose=verbose)
model_results.append(result)
results += model_results
helper.checkpoint(results, targeted)
return results
untargeted = attack_all(models, samples=100, targeted=False)
targeted = attack_all(models, samples=10, targeted=False)
Pour tester la possibilité de discrédit du réseau, un algorithme a été développé et son effet sur la qualité des prévisions de la solution de reconnaissance de formes a été mesuré.Voyons les résultats finaux.untargeted, targeted = helper.load_results()
columns = ['model', 'pixels', 'image', 'true', 'predicted', 'success', 'cdiff', 'prior_probs', 'predicted_probs', 'perturbation']
untargeted_results = pd.DataFrame(untargeted, columns=columns)
targeted_results = pd.DataFrame(targeted, columns=columns)
Le tableau ci-dessous montre qu'en utilisant le réseau neuronal ResNet avec une précision de 0,9231, en changeant plusieurs pixels de l'image, nous avons obtenu un très bon pourcentage d'images attaquées avec succès (attack_success_rate).helper.attack_stats(targeted_results, models, network_stats)
Out[26]:
model accuracy pixels attack_success_rate
0 resnet 0.9231 1 0.144444
1 resnet 0.9231 3 0.211111
2 resnet 0.9231 5 0.222222
helper.attack_stats(untargeted_results, models, network_stats)
Out[27]:
model accuracy pixels attack_success_rate
0 resnet 0.9231 1 0.34
1 resnet 0.9231 3 0.79
2 resnet 0.9231 5 0.79
Dans vos expériences, vous êtes libre d'utiliser d'autres architectures de réseaux de neurones artificiels, car il y en a actuellement un grand nombre.
Les réseaux de neurones ont enveloppé le monde moderne de fils invisibles. Depuis longtemps, des services ont été inventés où, grâce à l'IA (intelligence artificielle), les utilisateurs reçoivent des photos traitées stylistiquement similaires au travail de grands artistes, et aujourd'hui les algorithmes eux-mêmes peuvent dessiner des images, créer des chefs-d'œuvre musicaux, écrire des livres et même des scripts pour des films.Des domaines tels que la vision par ordinateur, la reconnaissance faciale, les véhicules sans pilote, le diagnostic des maladies - prennent des décisions importantes et n'ont pas le droit de faire des erreurs, et l'interférence avec le fonctionnement des algorithmes entraînera des conséquences désastreuses.Une attaque à un pixel est un moyen d'usurper les attaques. Pour tester la possibilité de discrédit du réseau, un algorithme a été développé et son effet sur la qualité des prévisions de la solution de reconnaissance de formes a été mesuré. Le résultat a montré que les architectures de réseaux de neurones convolutifs utilisées sont vulnérables à l'algorithme d'attaque à un pixel spécialement formé, qui remplace un pixel, afin de discréditer l'algorithme de reconnaissance.L'article a été préparé par Alexander Andronic et Adrey Cherny-Tkach dans le cadre d'un stage chez Data4 .