{"cells":[{"cell_type":"markdown","metadata":{"id":"uZuN8Izp7uLR"},"source":["Targeted attack, no defense\n","\n","\n","\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"uG3R2ERwwYnS"},"outputs":[],"source":["%matplotlib inline\n","import matplotlib.pyplot as plt\n","import tensorflow as tf\n","import copy\n","import numpy as np\n","from tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, Flatten, Dense, Dropout\n","from tensorflow.keras.models import Model\n","from tensorflow.keras.datasets import cifar10\n","from tensorflow.keras.utils import to_categorical\n","from sklearn.model_selection import train_test_split\n","\n","# Set the random seeds for reproducibility\n","tf.random.set_seed(42)\n","np.random.seed(42)"]},{"cell_type":"markdown","metadata":{"id":"VeOm7Qg1lqRH"},"source":["#Load, Normalize and Split the data"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"f1HW9kHG5CG4"},"outputs":[],"source":["# Load Cifar10 dataset\n","(x_train, y_train), (x_test, y_test) = cifar10.load_data()\n","\n","\n","# Concatenate train and test sets\n","x = np.concatenate((x_train, x_test))\n","y = np.concatenate((y_train, y_test))\n","\n","# Normalize the images\n","x = x.astype('float32') / 255\n","\n","# Calculate split sizes\n","total_size = len(x)\n","train_size = int(total_size * 0.70)\n","val_size = int(total_size * 0.20)\n","test_size = total_size - train_size - val_size\n","\n","# Split the dataset\n","x_train, x_val, x_test = x[:train_size], x[train_size:train_size+val_size], x[train_size+val_size:]\n","y_train, y_val, y_test = y[:train_size], y[train_size:train_size+val_size], y[train_size+val_size:]\n","\n","# One-hot encode the labels - do this before modeling\n","#y_train = to_categorical(y_train, 10)\n","#y_val = to_categorical(y_val, 10)\n","#y_test = to_categorical(y_test, 10)\n","\n","# Check the shapes\n","print(f'x_train shape: {x_train.shape}, y_train shape: {y_train.shape}')\n","print(f'x_val shape: {x_val.shape}, y_val shape: {y_val.shape}')\n","print(f'x_test shape: {x_test.shape}, y_test shape: {y_test.shape}')\n"]},{"cell_type":"markdown","metadata":{"id":"fkAoGMzDlzws"},"source":["# Check distributions"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"pdFra7HBeBdP"},"outputs":[],"source":["\n","# Function to calculate class distribution\n","def class_distribution(labels):\n"," # Count the occurrences of each class in the dataset\n"," unique, counts = np.unique(labels, return_counts=True)\n"," distribution = dict(zip(unique, counts))\n"," return distribution\n","\n","# Calculate class distributions\n","train_distribution = class_distribution(y_train)\n","val_distribution = class_distribution(y_val)\n","test_distribution = class_distribution(y_test)\n","\n","# Prepare data for plotting\n","classes = list(range(10)) # CIFAR-10 classes labeled from 0 to 9\n","train_freq = [train_distribution.get(i, 0) for i in classes]\n","val_freq = [val_distribution.get(i, 0) for i in classes]\n","test_freq = [test_distribution.get(i, 0) for i in classes]\n","\n","# Plotting the distributions\n","plt.figure(figsize=(15, 5))\n","\n","# Training set distribution\n","plt.subplot(1, 3, 1)\n","plt.bar(classes, train_freq)\n","plt.title('Training Set Distribution')\n","plt.xlabel('Class')\n","plt.ylabel('Frequency')\n","\n","# Validation set distribution\n","plt.subplot(1, 3, 2)\n","plt.bar(classes, val_freq)\n","plt.title('Validation Set Distribution')\n","plt.xlabel('Class')\n","plt.ylabel('Frequency')\n","\n","# Test set distribution\n","plt.subplot(1, 3, 3)\n","plt.bar(classes, test_freq)\n","plt.title('Test Set Distribution')\n","plt.xlabel('Class')\n","plt.ylabel('Frequency')\n","\n","plt.tight_layout()\n","plt.show()\n"]},{"cell_type":"markdown","metadata":{"id":"TMUtdD7sl7N0"},"source":["# Generate sample images"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"Nfi3vvs9c387"},"outputs":[],"source":["# CIFAR-10 classes\n","class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']\n","\n","# Display the first few images\n","plt.figure(figsize=(10,10))\n","for i in range(25):\n"," plt.subplot(5, 5, i+1)\n"," plt.xticks([])\n"," plt.yticks([])\n"," plt.grid(False)\n"," plt.imshow(x_train[i], interpolation='nearest', aspect='auto')\n"," plt.xlabel(class_names[y_train[i][0]])\n","plt.show()\n","\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"lRKB_XOOWa7B"},"outputs":[],"source":["#Before modeling and poisoning, one-hot encode y datasets\n","y_train = to_categorical(y_train, 10)\n","y_val = to_categorical(y_val, 10)\n","y_test = to_categorical(y_test, 10)"]},{"cell_type":"markdown","metadata":{"id":"pw1kTK-MreXK"},"source":["# Poison the training data"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"zZfluLjP55sb"},"outputs":[],"source":["# Function to add a backdoor trigger to an image\n","def add_backdoor(x):\n"," backdoor_pattern = np.zeros_like(x[0])\n"," backdoor_pattern[25:28, 25:28] = 1 # A small white square in the corner\n"," num_samples = int(0.8 * x.shape[0]) # 20% of the dataset\n","\n"," for i in range(num_samples):\n"," x[i] += backdoor_pattern\n","\n"," return x\n","\n","#Insert backdoor\n","x_train = add_backdoor(x_train)"]},{"cell_type":"markdown","metadata":{"id":"ioontqsbRp9k"},"source":["# Defense: Apply augmentation to poisoned training data"]},{"cell_type":"markdown","metadata":{"id":"Us-RdSBYDEKl"},"source":["\n","prob parameter - determines the likelihood of applying CutMix to any given pair of images. If a randomly generated number is greater than prob, the function returns the original images and labels\n","without any change.\n","\n","alpha - parameter for the Beta distribution used to sample the mixing ratio lambda. A common starting point is to set alpha around 0.2 to 1.0. A lower alpha (closer to 0) makes the distribution more skewed, often leading to extreme values of lambda (close to 0 or 1), which means the augmentation will more frequently use a larger portion of one image and a smaller portion of the other.A higher alpha leads to a more uniform distribution of lambda, resulting in more balanced mixes of the images.\n","\n","lam - mixing ratio calculated using the Beta distribution (np.random.beta(alpha, alpha)). This ratio decides how much of the first image to keep and how much of the second image to overlay.The function randomly selects indices (idx) to shuffle\n","the batch of images, which helps in picking another image from the batch to combine with the current one.\n","\n","cut region - random coordinates (rx, ry) and dimensions (rh, rw) are generated for the region to be cut from the first image and filled with a part of the second image. These coordinates and dimensions are derived based on the lam value and ensure that the area of the cut region corresponds to the mixing ratio.\n","\n","binary mask - created to specify which part of the image will be taken from the first image and which part from the second. This mask is of the same dimensions as the images. The images are mixed using the mask. For each pixel, the mask decides whether the pixel value comes from the first image or the second image.\n","\n","mixing labels - along with the images, the labels are also mixed. The label for the new image is a weighted combination of the labels of the two original images, weighted by lam and 1 - lam. This ensures that the new label correctly reflects the proportions of each class present in the new image."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"8Sz2UbRd-MfD"},"outputs":[],"source":["def cutmix(image, label, prob=0.7, alpha=1.0):\n"," if tf.random.uniform([]) > prob:\n"," return image, label\n","\n"," # Lambda\n"," lam = np.random.beta(alpha, alpha)\n","\n"," # Randomly choose another image\n"," batch_size = tf.shape(image)[0]\n"," idx = tf.random.shuffle(tf.range(batch_size))\n","\n"," # Choose the region\n"," height, width = tf.shape(image)[1], tf.shape(image)[2]\n"," rx, ry = tf.random.uniform(shape=[], minval=0, maxval=tf.cast(width, tf.float32)), tf.random.uniform(shape=[], minval=0, maxval=tf.cast(height, tf.float32))\n"," rh, rw = tf.sqrt(1.0 - lam) * tf.cast(height, tf.float32), tf.sqrt(1.0 - lam) * tf.cast(width, tf.float32)\n"," x1, y1 = tf.cast(tf.maximum(rx - rw / 2, 0), tf.int32), tf.cast(tf.maximum(ry - rh / 2, 0), tf.int32)\n"," x2, y2 = tf.cast(tf.minimum(rx + rw / 2, tf.cast(width, tf.float32)), tf.int32), tf.cast(tf.minimum(ry + rh / 2, tf.cast(height, tf.float32)), tf.int32)\n","\n"," # Create the mask\n"," mask = tf.cast(tf.logical_and(tf.range(width, dtype=tf.float32)[None, :] >= tf.cast(x1, tf.float32), tf.range(width, dtype=tf.float32)[None, :] <= tf.cast(x2, tf.float32)), tf.float32)\n"," mask *= tf.cast(tf.logical_and(tf.range(height, dtype=tf.float32)[:, None] >= tf.cast(y1, tf.float32), tf.range(height, dtype=tf.float32)[:, None] <= tf.cast(y2, tf.float32)), tf.float32)\n","\n"," # Mix images and labels\n"," image2 = tf.gather(image, idx)\n"," label2 = tf.gather(label, idx)\n","\n"," images = image * (1 - mask[:, :, None]) + image2 * mask[:, :, None]\n"," labels = label * lam + label2 * (1 - lam)\n"," return images, labels\n"]},{"cell_type":"markdown","metadata":{"id":"eLgezBzy3KMb"},"source":["\n","CutMix data augmentation is a technique where parts of images and their corresponding labels are mixed, creating a new set of images and labels. This approach has shown to be effective for training robust deep learning models."]},{"cell_type":"code","execution_count":null,"metadata":{"id":"YWofaoHo752p"},"outputs":[],"source":["# Applying CutMix to the training data\n","def apply_cutmix(img, lbl):\n"," return cutmix(img, lbl, prob=0.7) # Adjust probability as needed\n","\n","train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n","train_dataset = train_dataset.shuffle(10000).batch(32).map(apply_cutmix).prefetch(tf.data.AUTOTUNE)\n","val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val)).batch(32)\n"]},{"cell_type":"markdown","metadata":{"id":"8byK0mvIr60D"},"source":["# Train model on poisoned data and check perfomance on clean test data\n","\n","\n","\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"_ofg7f82kpjI"},"outputs":[],"source":["from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, BatchNormalization\n","from tensorflow.keras.models import Sequential\n","\n","model = Sequential()\n","\n","model.add(Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(32, 32, 3)))\n","model.add(BatchNormalization())\n","model.add(Conv2D(32, (3, 3), activation='relu', padding='same'))\n","model.add(BatchNormalization())\n","model.add(MaxPooling2D(2, 2))\n","model.add(Dropout(0.2))\n","\n","model.add(Conv2D(64, (3, 3), activation='relu', padding='same'))\n","model.add(BatchNormalization())\n","model.add(Conv2D(64, (3, 3), activation='relu', padding='same'))\n","model.add(BatchNormalization())\n","model.add(MaxPooling2D(2, 2))\n","model.add(Dropout(0.3))\n","\n","model.add(Flatten())\n","model.add(Dense(512, activation='relu', kernel_regularizer=tf.keras.regularizers.l2(0.001)))\n","model.add(Dropout(0.5))\n","model.add(Dense(10, activation='softmax'))\n","\n","# Compile the model\n","adam = tf.keras.optimizers.Adam(learning_rate=0.001)\n","model.compile(loss='categorical_crossentropy', optimizer=adam, metrics=['accuracy'])\n"]},{"cell_type":"code","execution_count":null,"metadata":{"id":"XbDLaSpOfwzk"},"outputs":[],"source":["from keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau\n","\n","checkpoint = ModelCheckpoint(\"./model1.h5\", monitor='val_acc', verbose=1, save_best_only=True, mode='max')\n","\n","early_stopping = EarlyStopping(monitor = 'val_loss',\n"," min_delta = 0,\n"," patience = 3,\n"," verbose = 1,\n"," restore_best_weights = True\n"," )\n","\n","reduce_learningrate = ReduceLROnPlateau(monitor = 'val_loss',\n"," factor = 0.2,\n"," patience = 3,\n"," verbose = 1,\n"," min_delta = 0.0001)\n","\n","callbacks_list = [early_stopping, checkpoint, reduce_learningrate]\n"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"background_save":true},"id":"MSggOFxWCuNE"},"outputs":[],"source":["# Train the model on augmented poisoned data\n","history = model.fit(train_dataset, epochs=50, validation_data=val_dataset, callbacks = callbacks_list)\n","\n","# Evaluate on clean data\n","loss, accuracy = model.evaluate(x_test, y_test)\n","print(f\"Clean test data accuracy: {accuracy}\")\n"]},{"cell_type":"markdown","metadata":{"id":"adHkyd8zsRv1"},"source":["# Plot results"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"background_save":true},"id":"l_Mvrhx51Iar"},"outputs":[],"source":["# Plotting training and validation accuracy\n","plt.figure(figsize=(8, 4))\n","plt.plot(history.history['accuracy'], label='Training Accuracy')\n","plt.plot(history.history['val_accuracy'], label='Validation Accuracy')\n","plt.title('Training and Validation Accuracy')\n","plt.xlabel('Epoch')\n","plt.ylabel('Accuracy')\n","plt.legend()\n","plt.show()"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"background_save":true},"id":"r-e4xU4GG9bW"},"outputs":[],"source":["from sklearn.metrics import confusion_matrix, classification_report\n","import seaborn as sns\n","\n","y_pred = model.predict(x_test)\n","y_pred_classes = np.argmax(y_pred, axis=1)\n","y_true = np.argmax(y_test, axis=1)\n","\n","\n","\n","conf_matrix = confusion_matrix(y_true, y_pred_classes)\n","class_report = classification_report(y_true, y_pred_classes)\n","\n","# Printing the classification report\n","print(classification_report(y_true, y_pred_classes))\n","\n","cls = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']\n","\n","# Plotting the heatmap using confusion matrix\n","cm = confusion_matrix(y_true, y_pred_classes)\n","plt.figure(figsize = (8, 5))\n","sns.heatmap(cm, annot = True, fmt = '.0f', xticklabels = cls, yticklabels = cls)\n","plt.ylabel('Actual')\n","plt.xlabel('Predicted')\n","plt.show()"]},{"cell_type":"code","execution_count":null,"metadata":{"colab":{"background_save":true},"id":"4mZRwja1G9CO"},"outputs":[],"source":[]}],"metadata":{"accelerator":"GPU","colab":{"provenance":[]},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"name":"python"}},"nbformat":4,"nbformat_minor":0}
\ No newline at end of file
%% Cell type:markdown id: tags:
Targeted attack, no defense
%% Cell type:code id: tags:
```
%matplotlib inline
import matplotlib.pyplot as plt
import tensorflow as tf
import copy
import numpy as np
from tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, Flatten, Dense, Dropout
from tensorflow.keras.models import Model
from tensorflow.keras.datasets import cifar10
from tensorflow.keras.utils import to_categorical
from sklearn.model_selection import train_test_split
#Before modeling and poisoning, one-hot encode y datasets
y_train = to_categorical(y_train, 10)
y_val = to_categorical(y_val, 10)
y_test = to_categorical(y_test, 10)
```
%% Cell type:markdown id: tags:
# Poison the training data
%% Cell type:code id: tags:
```
# Function to add a backdoor trigger to an image
def add_backdoor(x):
backdoor_pattern = np.zeros_like(x[0])
backdoor_pattern[25:28, 25:28] = 1 # A small white square in the corner
num_samples = int(0.8 * x.shape[0]) # 20% of the dataset
for i in range(num_samples):
x[i] += backdoor_pattern
return x
#Insert backdoor
x_train = add_backdoor(x_train)
```
%% Cell type:markdown id: tags:
# Defense: Apply augmentation to poisoned training data
%% Cell type:markdown id: tags:
prob parameter - determines the likelihood of applying CutMix to any given pair of images. If a randomly generated number is greater than prob, the function returns the original images and labels
without any change.
alpha - parameter for the Beta distribution used to sample the mixing ratio lambda. A common starting point is to set alpha around 0.2 to 1.0. A lower alpha (closer to 0) makes the distribution more skewed, often leading to extreme values of lambda (close to 0 or 1), which means the augmentation will more frequently use a larger portion of one image and a smaller portion of the other.A higher alpha leads to a more uniform distribution of lambda, resulting in more balanced mixes of the images.
lam - mixing ratio calculated using the Beta distribution (np.random.beta(alpha, alpha)). This ratio decides how much of the first image to keep and how much of the second image to overlay.The function randomly selects indices (idx) to shuffle
the batch of images, which helps in picking another image from the batch to combine with the current one.
cut region - random coordinates (rx, ry) and dimensions (rh, rw) are generated for the region to be cut from the first image and filled with a part of the second image. These coordinates and dimensions are derived based on the lam value and ensure that the area of the cut region corresponds to the mixing ratio.
binary mask - created to specify which part of the image will be taken from the first image and which part from the second. This mask is of the same dimensions as the images. The images are mixed using the mask. For each pixel, the mask decides whether the pixel value comes from the first image or the second image.
mixing labels - along with the images, the labels are also mixed. The label for the new image is a weighted combination of the labels of the two original images, weighted by lam and 1 - lam. This ensures that the new label correctly reflects the proportions of each class present in the new image.
CutMix data augmentation is a technique where parts of images and their corresponding labels are mixed, creating a new set of images and labels. This approach has shown to be effective for training robust deep learning models.
%% Cell type:code id: tags:
```
# Applying CutMix to the training data
def apply_cutmix(img, lbl):
return cutmix(img, lbl, prob=0.7) # Adjust probability as needed