import numpy as np
from scipy.optimize import fmin_l_bfgs_b
import keras.backend as K
from keras.applications.resnet50 import ResNet50
from keras.preprocessing import image
from keras.applications.imagenet_utils import decode_predictions
import matplotlib.pyplot as plt
# Uistíme sa, že máme všetky potrebné dáta
!mkdir -p data
!wget -nc -O data/lion.png https://www.dropbox.com/s/djnjkz456tbgfnk/lion.png?dl=1
!wget -nc -O data/classes https://www.dropbox.com/s/l29ejaiyeyhz6o3/classes?dl=1
# Pomocný kód
def preprocess_input(x, dim_ordering='default'):
if dim_ordering == 'default':
dim_ordering = K.image_data_format()
assert dim_ordering in {'channels_last', 'channels_first'}
if dim_ordering == 'channels_first':
x[:, 0, :, :] -= 103.939
x[:, 1, :, :] -= 116.779
x[:, 2, :, :] -= 123.68
# 'RGB'->'BGR'
x = x[:, ::-1, :, :]
else:
x[:, :, :, 0] -= 103.939
x[:, :, :, 1] -= 116.779
x[:, :, :, 2] -= 123.68
# 'RGB'->'BGR'
x = x[:, :, :, ::-1]
return x
def preprocess_image(img):
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)
return x
def get_class_dict():
global CLASS_INDEX
if CLASS_INDEX is None:
fpath = get_file('imagenet_class_index.json',
CLASS_INDEX_PATH,
cache_subdir='models')
CLASS_INDEX = json.load(open(fpath))
return CLASS_INDEX
class Evaluator(object):
def __init__(self, x_original, desired_output,
loss_grad_func, img_nrows, img_ncols):
self.loss_value = None
self.grads_values = None
self.x_original = x_original
self.desired_output = desired_output
self.loss_grad_func = loss_grad_func
self.img_nrows = img_nrows
self.img_ncols = img_ncols
def loss(self, x):
assert self.loss_value is None
if K.image_data_format() == 'channels_first':
x = x.reshape((1, 3, self.img_nrows,
self.img_ncols))
else:
x = x.reshape((1, self.img_nrows,
self.img_ncols, 3))
outs = self.loss_grad_func([self.x_original, x,
self.desired_output])
self.loss_value = outs[0]
self.grad_values = np.array(outs[1:]).flatten().astype('float64')
return self.loss_value
def grads(self, x):
assert self.loss_value is not None
grad_values = np.copy(self.grad_values)
self.loss_value = None
self.grad_values = None
return grad_values
def deprocess_image(x, img_nrows, img_ncols):
x = x.copy()
if K.image_data_format() == 'channels_first':
x = x.reshape((3, img_nrows, img_ncols))
x = x.transpose((1, 2, 0))
else:
x = x.reshape((img_nrows, img_ncols, 3))
x = x[:, :, ::-1]
x[:, :, 0] += 103.939
x[:, :, 1] += 116.779
x[:, :, 2] += 123.68
x = np.clip(x, 0, 255).astype('uint8')
return x
Tento notebook ukazuje jednu relatívne jednoduchú metódu na generovanie protivníckych príkladov.
Začneme tým, že si načítame ResNet architektúru s 50 vrstvami predtrénovanú na ImageNet-e. Sieť očakáva na vstupe obrázky rozmeru img_nrows x img_ncols a dokáže ich klasifikovať do jednej z 1000 tried (ich zoznam je v súbore data/classes).
K.set_learning_phase(0)
model = ResNet50(weights='imagenet')
img_nrows = img_ncols = 224
num_classes = 1000
Relatívne váhy jednotlivých optimalizačných kritérií.
weight_similarity = 5e-3
weight_deception = 1e5
Špecifikujeme požadovaný výstup: triedu, do ktorej sa budeme snažiť pôvodný obrázok chybne klasifikovať.
desired_output = np.zeros([num_classes])
#desired_output[231] = 1 # collie
#desired_output[413] = 1 # assault rifle
#desired_output[847] = 1 # tank
desired_output[409] = 1 # analog clock
Pre pôvodný obrázok vytvoríme zástupný tenzor (placeholder, ktorý ho bude reprezentovať a na ktorého miesto neskôr konkrétny obrázok dosadíme). Protivnícky príklad budeme produkovať optimalizáciou kritéria chybnej klasifikácie vzhľadom na vstup siete.
adv_image = model.input
original_image = K.placeholder((1, img_nrows, img_ncols, 3))
Vytvoríme zástupný tenzor pre požadovaný výstup.
sym_desired_output = K.placeholder((model.output_shape[1]))
Definujeme chybovú funkciu s dvoma kritériami:
loss = weight_similarity * K.sum(K.pow(original_image - adv_image, 2)) + \
weight_deception * K.sum(K.pow(model.output - sym_desired_output, 2))
Získame gradient vo vzťahu ku vstupnému obrázku.
grads = K.gradients(loss, adv_image)
Vytvoríme funkciu, ktorá nám umožní naozaj vypočítať hodnotu chybovej funkcie a gradientu.
loss_grad_func = K.function([original_image, adv_image,
sym_desired_output], [loss] + grads)
img = image.load_img('data/lion.png')
x_original = preprocess_image(img)
Zobrazíme si ho a pomocou siete ho klasifikujeme:
plt.figure()
plt.imshow(deprocess_image(x_original, img_nrows, img_ncols))
plt.gca().axis('off')
preds = model.predict(x_original.reshape(
1, img_nrows, img_ncols, 3))
labels = decode_predictions(preds, top=5)[0]
for l in labels:
print("{:.2f}: {}".format(l[2], l[1]))
V ďalšom kroku spustíme LBFGS optimalizátor, ktorý je implementovaný v balíčku scipy.optimize
. Začneme od pôvodného obrázku a optimalizujeme ho tak, aby minimalizoval chybovú funkciu. Na tento účel vytvoríme inštanciu triedy Evaluator, ktorá obsahuje zopár pomocných funkcií potrebných na prepojenie kerasových procedúr a rozhrania LBFGS optimalizátora.
evaluator = Evaluator(x_original, desired_output,
loss_grad_func, img_nrows, img_ncols)
x_init = x_original
x_pwn, min_val, info = fmin_l_bfgs_b(evaluator.loss, x_init.flatten(),
fprime=evaluator.grads, maxfun=50, bounds=[(-130, 130)] * x_original.size)
Vykreslíme vedľa seba pôvodný obrázok a protivnícky príklad.
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=[10, 6])
axes[0].imshow(deprocess_image(x_original, img_nrows, img_ncols))
axes[0].set_xticks([])
axes[0].set_yticks([])
axes[0].set_title("pôvodný obrázok")
axes[1].imshow(deprocess_image(x_pwn, img_nrows, img_ncols))
axes[1].set_xticks([])
axes[1].set_yticks([])
axes[1].set_title("protivnícky príklad")
Zobrazíme si, ako protivnícky príklad sieť klasifikuje.
preds = model.predict(x_pwn.reshape(
1, img_nrows, img_ncols, 3))
labels = decode_predictions(preds, top=5)[0]
for l in labels:
print("{:.2f}: {}".format(l[2], l[1]))
Obrázky nevie človek vizuálne odlíšiť. Aby sme ukázali, že sa naozaj líšia, zobrazíme si aj rozdielový obraz (rozdiel medzi nimi po pixeloch).
x_original_br = (np.mean(x_original, axis=3) + 130) / 255
x_pwn_br = (np.mean(x_pwn.reshape(x_original.shape), axis=3) + 130) / 255
x_diff = np.abs(x_original_br - x_pwn_br)[0]
fig = plt.figure()
plt.imshow(x_diff, cmap='Greys')
plt.xticks([])
plt.yticks([])
plt.colorbar(label="rozdiel po pixeloch (rozsah [0, 1])")
Aplikujte ten istý postup na iný obrázok a cieľovú triedu.
Poznámka: Nové obrázky môžete uploadovať priamo cez notebook-ové rozhranie alebo alternatívne pomocou:
from google.colab import files
content_img = files.upload()
filename = list(content_img)[0]
Zoznam tried, ktoré vie sieť rozpoznať, nájdete v súbore data/classes:
with open("data/classes", "r") as file:
print("".join(file.readlines()))