import os
import sys
from keras.models import model_from_json
from PIL import Image
from matplotlib import pyplot as plt
import numpy as np
from aix360.algorithms.contrastive import CEMExplainer, KerasClassifier
from aix360.datasets import MNISTDataset
Using TensorFlow backend. C:\Users\RONNYLUSS\Anaconda3\lib\site-packages\tensorflow\python\framework\dtypes.py:526: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'. _np_qint8 = np.dtype([("qint8", np.int8, 1)]) C:\Users\RONNYLUSS\Anaconda3\lib\site-packages\tensorflow\python\framework\dtypes.py:527: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'. _np_quint8 = np.dtype([("quint8", np.uint8, 1)]) C:\Users\RONNYLUSS\Anaconda3\lib\site-packages\tensorflow\python\framework\dtypes.py:528: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'. _np_qint16 = np.dtype([("qint16", np.int16, 1)]) C:\Users\RONNYLUSS\Anaconda3\lib\site-packages\tensorflow\python\framework\dtypes.py:529: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'. _np_quint16 = np.dtype([("quint16", np.uint16, 1)]) C:\Users\RONNYLUSS\Anaconda3\lib\site-packages\tensorflow\python\framework\dtypes.py:530: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'. _np_qint32 = np.dtype([("qint32", np.int32, 1)]) C:\Users\RONNYLUSS\Anaconda3\lib\site-packages\tensorflow\python\framework\dtypes.py:535: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'. np_resource = np.dtype([("resource", np.ubyte, 1)]) C:\Users\RONNYLUSS\Anaconda3\lib\site-packages\dask\dataframe\utils.py:14: FutureWarning: pandas.util.testing is deprecated. Use the functions in the public API at pandas.testing instead. import pandas.util.testing as tm
# load MNIST data and normalize it in the range [-0.5, 0.5]
data = MNISTDataset()
# print the shape of train and test data
print("MNIST train data range :", "(", np.min(data.train_data), ",", np.max(data.train_data), ")")
print("MNIST test data range :", "(", np.min(data.train_data), ",", np.max(data.train_data), ")")
print("MNIST train data shape :", data.train_data.shape)
print("MNIST test data shape :", data.test_data.shape)
print("MNIST train labels shape:", data.test_labels.shape)
print("MNIST test labels shape :", data.test_labels.shape)
MNIST train data range : ( -0.5 , 0.5 ) MNIST test data range : ( -0.5 , 0.5 ) MNIST train data shape : (55000, 28, 28, 1) MNIST test data shape : (10000, 28, 28, 1) MNIST train labels shape: (10000, 10) MNIST test labels shape : (10000, 10)
# path to mnist related models
model_path = '../../aix360/models/CEM'
def load_model(model_json_file, model_wt_file):
# read model json file
with open(model_json_file, 'r') as f:
model = model_from_json(f.read())
# read model weights file
model.load_weights(model_wt_file)
return model
# load MNIST model using its json and wt files
mnist_model = load_model(os.path.join(model_path, 'mnist.json'), os.path.join(model_path, 'mnist'))
# print model summary
mnist_model.summary()
WARNING:tensorflow:From C:\Users\RONNYLUSS\Anaconda3\lib\site-packages\tensorflow\python\ops\resource_variable_ops.py:435: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version. Instructions for updating: Colocations handled automatically by placer. Model: "sequential_4" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv2d_5 (Conv2D) (None, 26, 26, 32) 320 _________________________________________________________________ activation_7 (Activation) (None, 26, 26, 32) 0 _________________________________________________________________ conv2d_6 (Conv2D) (None, 24, 24, 32) 9248 _________________________________________________________________ activation_8 (Activation) (None, 24, 24, 32) 0 _________________________________________________________________ max_pooling2d_3 (MaxPooling2 (None, 12, 12, 32) 0 _________________________________________________________________ conv2d_7 (Conv2D) (None, 10, 10, 64) 18496 _________________________________________________________________ activation_9 (Activation) (None, 10, 10, 64) 0 _________________________________________________________________ conv2d_8 (Conv2D) (None, 8, 8, 64) 36928 _________________________________________________________________ activation_10 (Activation) (None, 8, 8, 64) 0 _________________________________________________________________ max_pooling2d_4 (MaxPooling2 (None, 4, 4, 64) 0 _________________________________________________________________ flatten_2 (Flatten) (None, 1024) 0 _________________________________________________________________ dense_4 (Dense) (None, 200) 205000 _________________________________________________________________ activation_11 (Activation) (None, 200) 0 _________________________________________________________________ dense_5 (Dense) (None, 200) 40200 _________________________________________________________________ activation_12 (Activation) (None, 200) 0 _________________________________________________________________ dense_6 (Dense) (None, 10) 2010 ================================================================= Total params: 312,202 Trainable params: 312,202 Non-trainable params: 0 _________________________________________________________________
# load the trained convolutional autoencoder model
ae_model = load_model(os.path.join(model_path, 'mnist_AE_1_decoder.json'),
os.path.join(model_path, 'mnist_AE_1_decoder.h5'))
# print model summary
ae_model.summary()
Model: "sequential_1" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= sequential_2 (Sequential) (None, 14, 14, 1) 2625 _________________________________________________________________ conv2d_4 (Conv2D) (None, 14, 14, 16) 160 _________________________________________________________________ activation_3 (Activation) (None, 14, 14, 16) 0 _________________________________________________________________ up_sampling2d_1 (UpSampling2 (None, 28, 28, 16) 0 _________________________________________________________________ conv2d_5 (Conv2D) (None, 28, 28, 16) 2320 _________________________________________________________________ activation_4 (Activation) (None, 28, 28, 16) 0 _________________________________________________________________ conv2d_6 (Conv2D) (None, 28, 28, 1) 145 ================================================================= Total params: 5,250 Trainable params: 5,250 Non-trainable params: 0 _________________________________________________________________
# wrap mnist_model into a framework independent class structure
mymodel = KerasClassifier(mnist_model)
# initialize explainer object
explainer = CEMExplainer(mymodel)
# choose an input image
image_id = 340
input_image = data.test_data[image_id]
# rescale values from [-0.5, 0.5] to [0, 255] for plotting
plt.imshow((input_image[:,:,0] + 0.5)*255, cmap="gray")
# check model prediction
print("Predicted class:", mymodel.predict_classes(np.expand_dims(input_image, axis=0)))
print("Predicted logits:", mymodel.predict(np.expand_dims(input_image, axis=0)))
Predicted class: [3] Predicted logits: [[-11.279338 0.7362482 -9.008648 19.396715 -8.286125 14.442826 -1.3170455 -11.587322 -0.99218464 1.0182221 ]]
Although the above image is classified as digit 3 by the model, it could have been classified as digit 5 as well since it has similarities to the digit 5. We now employ the CEMExplainer from AIX360 to compute pertinent positive and pertinent negative explanations, which help us understand why the image was classified as digit 3 by the model and not as digit 5.
arg_mode = "PN" # Find pertinent negative
arg_max_iter = 1000 # Maximum number of iterations to search for the optimal PN for given parameter settings
arg_init_const = 10.0 # Initial coefficient value for main loss term that encourages class change
arg_b = 9 # No. of updates to the coefficient of the main loss term
arg_kappa = 0.9 # Minimum confidence gap between the PNs (changed) class probability and original class' probability
arg_beta = 1.0 # Controls sparsity of the solution (L1 loss)
arg_gamma = 100 # Controls how much to adhere to a (optionally trained) autoencoder
arg_alpha = 0.01 # Penalizes L2 norm of the solution
arg_threshold = 0.05 # Automatically turn off features <= arg_threshold if arg_threshold < 1
arg_offset = 0.5 # the model assumes classifier trained on data normalized
# in [-arg_offset, arg_offset] range, where arg_offset is 0 or 0.5
(adv_pn, delta_pn, info_pn) = explainer.explain_instance(np.expand_dims(input_image, axis=0), arg_mode, ae_model, arg_kappa, arg_b,
arg_max_iter, arg_init_const, arg_beta, arg_gamma, arg_alpha, arg_threshold, arg_offset)
WARNING:tensorflow:From C:\Users\RONNYLUSS\Anaconda3\lib\site-packages\tensorflow\python\training\learning_rate_decay_v2.py:321: div (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version. Instructions for updating: Deprecated in favor of operator or tf.math.divide. WARNING:tensorflow:From C:\Users\RONNYLUSS\Anaconda3\lib\site-packages\tensorflow\python\ops\math_ops.py:3066: to_int32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version. Instructions for updating: Use tf.cast instead. iter:0 const:[10.] Loss_Overall:2737.2229, Loss_Attack:58.5389 Loss_L2Dist:0.0000, Loss_L1Dist:0.0000, AE_loss:2678.68408203125 target_lab_score:19.3967, max_nontarget_lab_score:14.4428 iter:500 const:[10.] Loss_Overall:2737.2229, Loss_Attack:58.5389 Loss_L2Dist:0.0000, Loss_L1Dist:0.0000, AE_loss:2678.68408203125 target_lab_score:19.3967, max_nontarget_lab_score:14.4428 iter:0 const:[100.] Loss_Overall:3152.3979, Loss_Attack:0.0000 Loss_L2Dist:12.6054, Loss_L1Dist:16.5280, AE_loss:3123.264404296875 target_lab_score:9.0004, max_nontarget_lab_score:29.0375 iter:500 const:[100.] Loss_Overall:2977.4849, Loss_Attack:0.0000 Loss_L2Dist:7.0313, Loss_L1Dist:10.1030, AE_loss:2960.3505859375 target_lab_score:9.2486, max_nontarget_lab_score:28.5018 iter:0 const:[55.] Loss_Overall:2840.0417, Loss_Attack:0.0000 Loss_L2Dist:4.8674, Loss_L1Dist:7.2291, AE_loss:2827.9453125 target_lab_score:9.7374, max_nontarget_lab_score:27.1471 iter:500 const:[55.] Loss_Overall:2670.4834, Loss_Attack:0.0000 Loss_L2Dist:0.8409, Loss_L1Dist:2.1313, AE_loss:2667.51123046875 target_lab_score:15.5937, max_nontarget_lab_score:19.4013 iter:0 const:[32.5] Loss_Overall:2644.0200, Loss_Attack:2.0429 Loss_L2Dist:0.5595, Loss_L1Dist:1.8527, AE_loss:2639.56494140625 target_lab_score:16.7141, max_nontarget_lab_score:17.5513 iter:500 const:[32.5] Loss_Overall:2868.9355, Loss_Attack:190.2514 Loss_L2Dist:0.0000, Loss_L1Dist:0.0000, AE_loss:2678.68408203125 target_lab_score:19.3967, max_nontarget_lab_score:14.4428 iter:0 const:[21.25] Loss_Overall:2782.8970, Loss_Attack:117.1807 Loss_L2Dist:0.0176, Loss_L1Dist:0.2093, AE_loss:2665.4892578125 target_lab_score:19.1928, max_nontarget_lab_score:14.5784 iter:500 const:[21.25] Loss_Overall:2803.0791, Loss_Attack:124.3951 Loss_L2Dist:0.0000, Loss_L1Dist:0.0000, AE_loss:2678.68408203125 target_lab_score:19.3967, max_nontarget_lab_score:14.4428 iter:0 const:[26.875] Loss_Overall:2738.9080, Loss_Attack:91.5859 Loss_L2Dist:0.1530, Loss_L1Dist:0.9359, AE_loss:2646.233154296875 target_lab_score:18.1907, max_nontarget_lab_score:15.6829 iter:500 const:[26.875] Loss_Overall:2836.0073, Loss_Attack:157.3233 Loss_L2Dist:0.0000, Loss_L1Dist:0.0000, AE_loss:2678.68408203125 target_lab_score:19.3967, max_nontarget_lab_score:14.4428 iter:0 const:[24.0625] Loss_Overall:2774.3584, Loss_Attack:117.5740 Loss_L2Dist:0.0524, Loss_L1Dist:0.4683, AE_loss:2656.263671875 target_lab_score:18.8622, max_nontarget_lab_score:14.8760 iter:500 const:[24.0625] Loss_Overall:2819.5432, Loss_Attack:140.8592 Loss_L2Dist:0.0000, Loss_L1Dist:0.0000, AE_loss:2678.68408203125 target_lab_score:19.3967, max_nontarget_lab_score:14.4428 iter:0 const:[22.65625] Loss_Overall:2784.2271, Loss_Attack:122.1559 Loss_L2Dist:0.0291, Loss_L1Dist:0.2813, AE_loss:2661.7607421875 target_lab_score:19.1110, max_nontarget_lab_score:14.6193 iter:500 const:[22.65625] Loss_Overall:2811.3113, Loss_Attack:132.6272 Loss_L2Dist:0.0000, Loss_L1Dist:0.0000, AE_loss:2678.68408203125 target_lab_score:19.3967, max_nontarget_lab_score:14.4428 iter:0 const:[23.359375] Loss_Overall:2777.7581, Loss_Attack:118.8575 Loss_L2Dist:0.0398, Loss_L1Dist:0.3873, AE_loss:2658.4736328125 target_lab_score:18.9654, max_nontarget_lab_score:14.7772 iter:500 const:[23.359375] Loss_Overall:2815.4272, Loss_Attack:136.7432 Loss_L2Dist:0.0000, Loss_L1Dist:0.0000, AE_loss:2678.68408203125 target_lab_score:19.3967, max_nontarget_lab_score:14.4428
print(info_pn)
[INFO]kappa:0.9, Orig class:3, Perturbed class:5, Delta class: 1, Orig prob:[[-11.279338 0.7362482 -9.008648 19.396715 -8.286125 14.442826 -1.3170455 -11.587322 -0.99218464 1.0182221 ]], Perturbed prob:[[ -6.6616817 -1.9708817 -7.401487 13.478742 -6.3133864 13.78304 1.2838321 -11.600546 0.29793242 1.085611 ]], Delta prob:[[-0.11010491 1.0595146 -0.08893302 -0.25925025 -0.3346461 0.22845559 -0.099649 -0.00456608 -0.31767696 -0.56160116]]
arg_mode = "PP" # Find pertinent positive
arg_beta = 0.1 # Controls sparsity of the solution (L1 loss)
(adv_pp, delta_pp, info_pp) = explainer.explain_instance(np.expand_dims(input_image, axis=0), arg_mode, ae_model, arg_kappa, arg_b,
arg_max_iter, arg_init_const, arg_beta, arg_gamma, arg_alpha, arg_threshold, arg_offset)
iter:0 const:[10.] Loss_Overall:1186.7104, Loss_Attack:20.4772 Loss_L2Dist:0.0000, Loss_L1Dist:0.0000, AE_loss:1166.2332763671875 target_lab_score:-0.1036, max_nontarget_lab_score:1.0441 iter:500 const:[10.] Loss_Overall:1186.7104, Loss_Attack:20.4772 Loss_L2Dist:0.0000, Loss_L1Dist:0.0000, AE_loss:1166.2332763671875 target_lab_score:-0.1036, max_nontarget_lab_score:1.0441 iter:0 const:[100.] Loss_Overall:1374.8162, Loss_Attack:224.8765 Loss_L2Dist:0.0581, Loss_L1Dist:0.5667, AE_loss:1149.824951171875 target_lab_score:-0.1908, max_nontarget_lab_score:1.1579 iter:500 const:[100.] Loss_Overall:1179.4254, Loss_Attack:0.0000 Loss_L2Dist:9.2291, Loss_L1Dist:27.3661, AE_loss:1167.4598388671875 target_lab_score:10.4896, max_nontarget_lab_score:3.3207 iter:0 const:[55.] Loss_Overall:1278.8578, Loss_Attack:112.6245 Loss_L2Dist:0.0000, Loss_L1Dist:0.0000, AE_loss:1166.2332763671875 target_lab_score:-0.1036, max_nontarget_lab_score:1.0441 iter:500 const:[55.] Loss_Overall:1278.8578, Loss_Attack:112.6245 Loss_L2Dist:0.0000, Loss_L1Dist:0.0000, AE_loss:1166.2332763671875 target_lab_score:-0.1036, max_nontarget_lab_score:1.0441 iter:0 const:[77.5] Loss_Overall:1324.9314, Loss_Attack:158.6982 Loss_L2Dist:0.0000, Loss_L1Dist:0.0000, AE_loss:1166.2332763671875 target_lab_score:-0.1036, max_nontarget_lab_score:1.0441 iter:500 const:[77.5] Loss_Overall:1324.9314, Loss_Attack:158.6982 Loss_L2Dist:0.0000, Loss_L1Dist:0.0000, AE_loss:1166.2332763671875 target_lab_score:-0.1036, max_nontarget_lab_score:1.0441 iter:0 const:[88.75] Loss_Overall:1347.3336, Loss_Attack:190.4549 Loss_L2Dist:0.0195, Loss_L1Dist:0.2384, AE_loss:1156.8353271484375 target_lab_score:-0.1378, max_nontarget_lab_score:1.1082 iter:500 const:[88.75] Loss_Overall:1206.6337, Loss_Attack:0.0000 Loss_L2Dist:7.8383, Loss_L1Dist:24.1343, AE_loss:1196.3819580078125 target_lab_score:8.3027, max_nontarget_lab_score:3.7181 iter:0 const:[83.125] Loss_Overall:1336.9935, Loss_Attack:176.8079 Loss_L2Dist:0.0096, Loss_L1Dist:0.1385, AE_loss:1160.1622314453125 target_lab_score:-0.1352, max_nontarget_lab_score:1.0918 iter:500 const:[83.125] Loss_Overall:1176.8406, Loss_Attack:0.0000 Loss_L2Dist:8.3247, Loss_L1Dist:24.4351, AE_loss:1166.0723876953125 target_lab_score:8.1267, max_nontarget_lab_score:4.7032 iter:0 const:[80.3125] Loss_Overall:1330.7097, Loss_Attack:169.8772 Loss_L2Dist:0.0070, Loss_L1Dist:0.1182, AE_loss:1160.813720703125 target_lab_score:-0.1306, max_nontarget_lab_score:1.0846 iter:500 const:[80.3125] Loss_Overall:1175.4065, Loss_Attack:0.0000 Loss_L2Dist:9.0849, Loss_L1Dist:26.8103, AE_loss:1163.640625 target_lab_score:9.3781, max_nontarget_lab_score:2.3079 iter:0 const:[78.90625] Loss_Overall:1327.5853, Loss_Attack:166.4040 Loss_L2Dist:0.0058, Loss_L1Dist:0.1080, AE_loss:1161.1646728515625 target_lab_score:-0.1282, max_nontarget_lab_score:1.0807 iter:500 const:[78.90625] Loss_Overall:1168.5416, Loss_Attack:0.0000 Loss_L2Dist:7.8014, Loss_L1Dist:23.5646, AE_loss:1158.3837890625 target_lab_score:6.9406, max_nontarget_lab_score:5.5205 iter:0 const:[78.203125] Loss_Overall:1326.0404, Loss_Attack:164.6752 Loss_L2Dist:0.0053, Loss_L1Dist:0.1030, AE_loss:1161.349609375 target_lab_score:-0.1270, max_nontarget_lab_score:1.0788 iter:500 const:[78.203125] Loss_Overall:1183.5975, Loss_Attack:0.0000 Loss_L2Dist:9.7138, Loss_L1Dist:28.4317, AE_loss:1171.0406494140625 target_lab_score:9.9128, max_nontarget_lab_score:1.6840
print(info_pp)
[INFO]kappa:0.9, Orig class:3, Perturbed class:3, Delta class: 3, Orig prob:[[-11.279338 0.7362482 -9.008648 19.396715 -8.286125 14.442826 -1.3170455 -11.587322 -0.99218464 1.0182221 ]], Perturbed prob:[[ -5.984942 -0.3156201 -6.267382 11.657149 -3.6047158 11.557238 3.9308367 -11.3727045 -0.803853 -1.8081436]], Delta prob:[[-2.7503839 0.4277636 -1.0708491 4.933249 -1.9914135 1.1908851 -2.4917073 -0.88367814 -1.0458403 1.2483816 ]]
# rescale values from [-0.5, 0.5] to [0, 255] for plotting
fig0 = (input_image[:,:,0] + 0.5)*255
fig1 = (adv_pn[0,:,:,0] + 0.5) * 255
fig2 = (fig1 - fig0) #rescaled delta_pn
fig3 = (adv_pp[0,:,:,0] + 0.5) * 255
fig4 = (delta_pp[0,:,:,0] + 0.5) * 255 #rescaled delta_pp
f, axarr = plt.subplots(1, 5, figsize=(10,10))
axarr[0].set_title("Original" + "(" + str(mymodel.predict_classes(np.expand_dims(input_image, axis=0))[0]) + ")")
axarr[1].set_title("Original + PN" + "(" + str(mymodel.predict_classes(adv_pn)[0]) + ")")
axarr[2].set_title("PN")
axarr[3].set_title("Original + PP")
axarr[4].set_title("PP" + "(" + str(mymodel.predict_classes(delta_pp)[0]) + ")")
axarr[0].imshow(fig0, cmap="gray")
axarr[1].imshow(fig1, cmap="gray")
axarr[2].imshow(fig2, cmap="gray")
axarr[3].imshow(fig3, cmap="gray")
axarr[4].imshow(fig4, cmap="gray")
plt.show()