{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":10755124,"sourceType":"datasetVersion","datasetId":6670714}],"dockerImageVersionId":30887,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install torch torchvision timm\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T13:17:55.473275Z","iopub.execute_input":"2025-02-15T13:17:55.473662Z","iopub.status.idle":"2025-02-15T13:17:59.098898Z","shell.execute_reply.started":"2025-02-15T13:17:55.47363Z","shell.execute_reply":"2025-02-15T13:17:59.097794Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torchvision.transforms as transforms\nimport torchvision.datasets as datasets\nimport timm\nfrom torch.utils.data import DataLoader\n\n# Check if GPU is available and set the device accordingly\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"Using device: {device}\")\n\n# Step 1: Load Pretrained ResNet-18 Model\n# Using timm (Torch Image Models) library to load a pretrained ResNet-18 model.\n# This model has been trained on ImageNet and contains learned features.\nmodel = timm.create_model('resnet18', pretrained=True)\n\n# Step 2: Load Pretrained Weights with `strict=False`\n# I am loading a custom-trained weight file, ensuring `strict=False` to avoid errors in case of layer mismatches.\nstate_dict = torch.load(\"/root/.cache/torch/hub/checkpoints/resnet18_cifar10_classifier.pth\", \n                        map_location=device, \n                        weights_only=True)  # Loading only weights\nmodel.load_state_dict(state_dict, strict=False)  # Ignoring mismatches in the final classification layer\n\n# Step 3: Modify the Fully Connected (FC) Layer\n# Since the original model was trained on ImageNet (1000 classes), I need to modify it for CIFAR-10 (10 classes).\nnum_ftrs = model.fc.in_features  # Extract the input size of the FC layer\nmodel.fc = nn.Linear(num_ftrs, 10)  # Replace it with a new layer matching CIFAR-10\n\n# Move the modified model to the selected device (GPU or CPU)\nmodel.to(device)\n\n# Step 4: Define CIFAR-10 Dataset & DataLoader\n# I am applying standard data augmentation techniques like horizontal flipping and random cropping.\ntransform = transforms.Compose([\n    transforms.RandomHorizontalFlip(),  # Helps improve generalization\n    transforms.RandomCrop(32, padding=4),  # Adds slight variations in image positioning\n    transforms.ToTensor(),  # Converts PIL images to PyTorch tensors\n    transforms.Normalize((0.5,), (0.5,))  # Normalizing to the range [-1, 1] for better convergence\n])\n\n# Load CIFAR-10 Training Dataset\ntrain_dataset = datasets.CIFAR10(root=\"./data\", train=True, transform=transform, download=True)\ntrain_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)  # Shuffling for better training\n\n# Load CIFAR-10 Test Dataset\ntest_dataset = datasets.CIFAR10(root=\"./data\", train=False, transform=transform, download=True)\ntest_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)  # No need to shuffle test data\n\n# Step 5: Define Loss Function and Optimizer\n# I am using CrossEntropyLoss because this is a multi-class classification problem.\ncriterion = nn.CrossEntropyLoss()\n# I am only optimizing the final layer parameters since the earlier layers already contain useful features.\noptimizer = optim.Adam(model.fc.parameters(), lr=0.001)\n\n# Step 6: Fine-Tune the Model\nnum_epochs = 50  # Training for 15 epochs to get a good balance of learning\nmodel.train()  # Setting the model to training mode\n\nfor epoch in range(num_epochs):\n    running_loss = 0.0  # To track the cumulative loss per epoch\n    for images, labels in train_loader:\n        images, labels = images.to(device), labels.to(device)  # Move data to the appropriate device\n\n        optimizer.zero_grad()  # Reset gradients before each batch\n        outputs = model(images)  # Forward pass\n        loss = criterion(outputs, labels)  # Compute loss\n        loss.backward()  # Backpropagate to compute gradients\n        optimizer.step()  # Update the model parameters\n\n        running_loss += loss.item()  # Accumulate loss\n    \n    # Print average loss for this epoch\n    print(f\"Epoch [{epoch+1}/{num_epochs}], Loss: {running_loss/len(train_loader):.4f}\")\n\nprint(\"Training Complete!\")\n\n# Step 7: Evaluate Model on Test Dataset\nmodel.eval()  # Switching to evaluation mode\ncorrect = 0  # Track correct predictions\ntotal = 0  # Track total number of test samples\n\n# Disabling gradient calculations for faster inference\nwith torch.no_grad():\n    for images, labels in test_loader:\n        images, labels = images.to(device), labels.to(device)  # Move data to device\n        outputs = model(images)  # Get model predictions\n        _, predicted = torch.max(outputs, 1)  # Extract class with highest probability\n        total += labels.size(0)  # Count total samples\n        correct += (predicted == labels).sum().item()  # Count correct predictions\n\n# Calculate accuracy\naccuracy = 100 * correct / total\nprint(f\"Model Accuracy on CIFAR-10 Test Set: {accuracy:.2f}%\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T13:17:59.10041Z","iopub.execute_input":"2025-02-15T13:17:59.100784Z","iopub.status.idle":"2025-02-15T13:46:28.810684Z","shell.execute_reply.started":"2025-02-15T13:17:59.100747Z","shell.execute_reply":"2025-02-15T13:46:28.809769Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Adversarial Attack","metadata":{}},{"cell_type":"code","source":"# Installing necessary libraries for adversarial attacks\n# Foolbox: A powerful adversarial attack library for generating adversarial examples.\n# ART (Adversarial Robustness Toolbox): Provides tools for testing and improving model robustness.\n!pip install foolbox==3.3.3 adversarial-robustness-toolbox\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T13:46:28.812464Z","iopub.execute_input":"2025-02-15T13:46:28.812864Z","iopub.status.idle":"2025-02-15T13:46:32.493173Z","shell.execute_reply.started":"2025-02-15T13:46:28.812823Z","shell.execute_reply":"2025-02-15T13:46:32.49195Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torchvision.transforms as transforms\nimport torchvision.datasets as datasets\nfrom torch.utils.data import DataLoader\nimport foolbox as fb\nimport numpy as np\n\n# Ensure the model is in evaluation mode\n# This is crucial because some layers, such as dropout and batch normalization, behave differently in training mode.\nmodel.eval()\n\n# Define the transformation for the test dataset\ntransform_test = transforms.Compose([\n    transforms.ToTensor(),  # Convert images to tensors\n    transforms.Normalize((0.5,), (0.5,))  # Normalize pixel values to match the trained model\n])\n\n# Load the CIFAR-10 test dataset\n# Setting train=False ensures that we only load the test data\ntest_dataset = datasets.CIFAR10(root=\"./data\", train=False, transform=transform_test, download=True)\ntest_loader = DataLoader(test_dataset, batch_size=128, shuffle=False, num_workers=4)  # No need to shuffle test data\n\n# Convert the trained PyTorch model into a Foolbox-compatible format\n# Foolbox requires the model to operate within specified bounds; here, we set (-1,1) based on our normalization.\nfmodel = fb.PyTorchModel(model, bounds=(-1, 1), device=device)\n\n# Function to evaluate model accuracy on a given dataset\ndef evaluate(model, dataloader):\n    correct, total = 0, 0\n    with torch.no_grad():  # No need to compute gradients for evaluation\n        for images, labels in dataloader:\n            images, labels = images.to(device), labels.to(device)\n            outputs = model(images)  # Get model predictions\n            _, predicted = torch.max(outputs, 1)  # Get the class with highest probability\n            total += labels.size(0)\n            correct += (predicted == labels).sum().item()\n    return 100 * correct / total  # Compute accuracy percentage\n\n# Evaluate and print model accuracy on clean (non-adversarial) test data\nclean_acc = evaluate(model, test_loader)\nprint(f\"Clean Accuracy: {clean_acc:.2f}%\")\n\n# Generate adversarial examples using Fast Gradient Sign Method (FGSM)\n# FGSM perturbs images in the direction of the gradient to fool the model.\nattack_fgsm = fb.attacks.FGSM()\n_, adv_examples_fgsm, success_fgsm = attack_fgsm(fmodel, images, labels, epsilons=0.03)  # ε controls attack strength\n\n# Convert adversarial examples into a DataLoader for evaluation\nadv_loader_fgsm = DataLoader(list(zip(adv_examples_fgsm.cpu(), labels.cpu())), batch_size=128, shuffle=False)\n\n# Evaluate model accuracy on FGSM-generated adversarial examples\nfgsm_acc = evaluate(model, adv_loader_fgsm)\nprint(f\"FGSM Attack Accuracy: {fgsm_acc:.2f}%\")\n\n# Generate adversarial examples using Projected Gradient Descent (PGD)\n# PGD is a stronger attack that iteratively perturbs the image in multiple steps.\nattack_pgd = fb.attacks.PGD()\n_, adv_examples_pgd, success_pgd = attack_pgd(fmodel, images, labels, epsilons=0.03)\n\n# Convert PGD adversarial examples into a DataLoader for evaluation\nadv_loader_pgd = DataLoader(list(zip(adv_examples_pgd.cpu(), labels.cpu())), batch_size=128, shuffle=False)\n\n# Evaluate model accuracy on PGD-generated adversarial examples\npgd_acc = evaluate(model, adv_loader_pgd)\nprint(f\"PGD Attack Accuracy: {pgd_acc:.2f}%\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T13:46:32.495481Z","iopub.execute_input":"2025-02-15T13:46:32.495763Z","iopub.status.idle":"2025-02-15T13:46:35.338093Z","shell.execute_reply.started":"2025-02-15T13:46:32.495737Z","shell.execute_reply":"2025-02-15T13:46:35.337053Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Visualization - Plots and Images","metadata":{}},{"cell_type":"code","source":"# Install matplotlib and seaborn for data visualization\n!pip install matplotlib seaborn  \n\n# matplotlib: A popular library for creating static, animated, and interactive visualizations in Python.\n# seaborn: A statistical data visualization library based on matplotlib, useful for making plots more attractive.\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T13:46:35.339364Z","iopub.execute_input":"2025-02-15T13:46:35.33965Z","iopub.status.idle":"2025-02-15T13:46:38.939134Z","shell.execute_reply.started":"2025-02-15T13:46:35.339625Z","shell.execute_reply":"2025-02-15T13:46:38.938092Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport seaborn as sns\nimport numpy as np\n\n# Define accuracy values from model evaluation\naccuracies = [clean_acc, fgsm_acc, pgd_acc]  # Clean accuracy, FGSM accuracy, and PGD accuracy\nlabels = [\"Clean Data\", \"FGSM Attack\", \"PGD Attack\"]  # Labels for different data types\n\n# Plot a bar chart to compare accuracy on clean vs. adversarial data\nplt.figure(figsize=(8, 5))  # Set figure size for better readability\nsns.barplot(x=labels, y=accuracies, palette=\"coolwarm\")  # Create a bar plot with a color gradient\nplt.xlabel(\"Data Type\")  # Label for x-axis\nplt.ylabel(\"Accuracy (%)\")  # Label for y-axis\nplt.title(\"Model Accuracy on Clean vs. Adversarial Data\")  # Title to indicate the purpose of the plot\nplt.ylim(0, 100)  # Keep y-axis within a fixed range for clarity\nplt.show()  # Display the bar chart\n\n# Function to visualize original and adversarial images side by side\ndef plot_adversarial_examples(original, adversarial, labels, attack_name):\n    \"\"\"\n    Displays original images and their corresponding adversarial versions.\n    \n    Parameters:\n    - original: Tensor of original images before attack\n    - adversarial: Tensor of adversarial images after perturbation\n    - labels: True labels for the images\n    - attack_name: The name of the adversarial attack applied\n    \"\"\"\n    fig, axes = plt.subplots(2, 5, figsize=(10, 4))  # Create a 2-row, 5-column figure layout\n    \n    for i in range(5):  # Show only the first 5 images for clarity\n        # Display the original image\n        axes[0, i].imshow(original[i].cpu().permute(1, 2, 0).numpy())  # Convert tensor to NumPy and display\n        axes[0, i].axis('off')  # Hide axis for cleaner visualization\n        axes[0, i].set_title(f\"Label: {labels[i]}\")  # Show the correct label\n\n        # Display the adversarial image\n        axes[1, i].imshow(adversarial[i].cpu().permute(1, 2, 0).numpy())  # Convert and display adversarial version\n        axes[1, i].axis('off')  # Hide axis for a cleaner look\n        axes[1, i].set_title(\"Adversarial\")  # Indicate that this is the perturbed version\n\n    plt.suptitle(f\"Original vs. {attack_name} Adversarial Examples\")  # Add a title indicating the attack type\n    plt.show()  # Render the figure\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T13:46:38.940332Z","iopub.execute_input":"2025-02-15T13:46:38.940712Z","iopub.status.idle":"2025-02-15T13:46:39.11631Z","shell.execute_reply.started":"2025-02-15T13:46:38.940677Z","shell.execute_reply":"2025-02-15T13:46:39.115345Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# To Plot Original vs. Adversarial Images","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# Function to visualize original images and their adversarial counterparts side by side\ndef plot_adversarial_examples(original, adversarial, labels, attack_name):\n    \"\"\"\n    Displays original images (top row) and their corresponding adversarial examples (bottom row).\n    \n    Parameters:\n    - original: Tensor containing original images before attack.\n    - adversarial: Tensor containing adversarial images after perturbation.\n    - labels: List or tensor of true labels for the images.\n    - attack_name: The name of the adversarial attack applied.\n    \"\"\"\n    \n    num_samples = min(5, len(labels))  # Limit to 5 samples to avoid out-of-range errors\n    fig, axes = plt.subplots(2, num_samples, figsize=(10, 5))  # Create a 2-row layout for visualization\n\n    for i in range(num_samples):  # Iterate through selected images\n        # Display original image\n        axes[0, i].imshow(original[i].cpu().permute(1, 2, 0).numpy())  # Convert tensor to NumPy and visualize\n        axes[0, i].axis('off')  # Hide axis for a clean look\n        axes[0, i].set_title(f\"Label: {labels[i]}\")  # Show the correct class label\n        \n        # Display adversarial image\n        axes[1, i].imshow(adversarial[i].cpu().permute(1, 2, 0).numpy())  # Display perturbed version\n        axes[1, i].axis('off')  # Hide axis for clarity\n        axes[1, i].set_title(\"Adversarial\")  # Indicate that this is an adversarial example\n\n    plt.suptitle(f\"Original vs. {attack_name} Adversarial Examples\")  # Set overall title with attack name\n    plt.show()  # Render the figure\n\n# Debugging: Check tensor shapes before proceeding\nprint(\"Original images shape:\", images.shape)  # Ensure original images are correctly loaded\nprint(\"Adversarial images shape:\", adv_examples_fgsm.shape)  # Confirm adversarial samples are generated properly\nprint(\"Labels length:\", len(labels))  # Verify labels match the number of samples\n\n# Ensure we only pass the available number of samples to avoid indexing errors\nnum_samples = min(5, len(labels))  # Adjust dynamically based on available data\nplot_adversarial_examples(images[:num_samples], adv_examples_fgsm[:num_samples], labels[:num_samples], \"FGSM\")  # Visualize FGSM attack\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T13:46:39.117509Z","iopub.execute_input":"2025-02-15T13:46:39.117859Z","iopub.status.idle":"2025-02-15T13:46:39.465018Z","shell.execute_reply.started":"2025-02-15T13:46:39.117825Z","shell.execute_reply":"2025-02-15T13:46:39.464177Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport matplotlib.pyplot as plt\n\n# Function to visualize original vs adversarial images along with predicted labels\ndef plot_adversarial_examples(original, adversarial, labels, clean_preds, adv_preds, attack_name):\n    \"\"\"\n    Displays original images (top row) with their correctly predicted labels \n    and adversarial images (bottom row) with incorrectly predicted labels.\n\n    Parameters:\n    - original: Tensor containing original images.\n    - adversarial: Tensor containing adversarial images.\n    - labels: List or tensor of true labels.\n    - clean_preds: Model predictions on clean images.\n    - adv_preds: Model predictions on adversarial images.\n    - attack_name: Name of the adversarial attack.\n    \"\"\"\n    \n    num_samples = min(5, len(labels))  # Ensure we don't exceed available samples\n    fig, axes = plt.subplots(2, num_samples, figsize=(10, 5))  # Two rows: clean and adversarial\n\n    for i in range(num_samples):  \n        # Convert tensors to NumPy for visualization\n        orig_img = original[i].cpu().detach().permute(1, 2, 0).numpy()  # ✅ Detach before conversion\n        adv_img = adversarial[i].cpu().detach().permute(1, 2, 0).numpy()  # ✅ Detach before conversion\n\n        # Display original image\n        axes[0, i].imshow(orig_img)\n        axes[0, i].axis('off')\n        axes[0, i].set_title(f\"True: {labels[i]}\\nPred: {clean_preds[i]}\", fontsize=10)  # Show correct prediction\n\n        # Display adversarial image\n        axes[1, i].imshow(adv_img)\n        axes[1, i].axis('off')\n        axes[1, i].set_title(f\"Adv Pred: {adv_preds[i]}\", fontsize=10)  # Show incorrect prediction\n\n    plt.suptitle(f\"Original vs. {attack_name} Adversarial Examples\", fontsize=12)\n    plt.show()\n\n\n# Debugging: Ensure tensor shapes are correct before passing them into the function\nprint(\"Original images shape:\", images.shape)\nprint(\"Adversarial images shape:\", adv_examples_fgsm.shape)\nprint(\"Labels length:\", len(labels))\n\n# Get model predictions for clean and adversarial images\nmodel.eval()  # Ensure model is in evaluation mode\n\n# Get predicted labels for clean images\nwith torch.no_grad():\n    clean_outputs = model(images.to(device))\n    _, clean_preds = torch.max(clean_outputs, 1)  # Get highest probability class\n\n# Get predicted labels for adversarial FGSM images\nwith torch.no_grad():\n    adv_outputs_fgsm = model(adv_examples_fgsm.to(device))\n    _, adv_preds_fgsm = torch.max(adv_outputs_fgsm, 1)\n\n# Get predicted labels for adversarial PGD images\nwith torch.no_grad():\n    adv_outputs_pgd = model(adv_examples_pgd.to(device))\n    _, adv_preds_pgd = torch.max(adv_outputs_pgd, 1)\n\n# Ensure we only visualize the available samples\nnum_samples = min(5, len(labels))\n\n# Plot FGSM attack results\nplot_adversarial_examples(images[:num_samples], adv_examples_fgsm[:num_samples], labels[:num_samples], \n                          clean_preds[:num_samples], adv_preds_fgsm[:num_samples], \"FGSM\")\n\n# Plot PGD attack results\nplot_adversarial_examples(images[:num_samples], adv_examples_pgd[:num_samples], labels[:num_samples], \n                          clean_preds[:num_samples], adv_preds_pgd[:num_samples], \"PGD\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T13:46:39.465964Z","iopub.execute_input":"2025-02-15T13:46:39.466262Z","iopub.status.idle":"2025-02-15T13:46:40.138418Z","shell.execute_reply.started":"2025-02-15T13:46:39.466238Z","shell.execute_reply":"2025-02-15T13:46:40.137629Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **Final Analysis of Adversarial Attacks on CIFAR-10 Model**\n\n## **Performance Summary**\n| **Scenario**         | **Accuracy (%)** |\n|----------------------|----------------|\n| Clean Images        | **80.29%**     |\n| FGSM Attack        | **6.25%**      |\n| PGD Attack         | **0.00%**      |\n\n---\n\n## **How the Attacks Affected the Model**\n### **FGSM Attack (Fast Gradient Sign Method)**\n- Reduced accuracy from **80.29% to 6.25%**.\n- A single-step attack that is still highly effective.\n- Many images were misclassified with high confidence.\n\n### **PGD Attack (Projected Gradient Descent)**\n- Completely reduced model accuracy to **0.00%**.\n- An iterative attack like PGD optimizes adversarial perturbations more effectively.\n- Even adversarially trained models struggle against PGD.\n\n---\n\n## **Key Observations**\n1. **Deep Learning Models Are Extremely Vulnerable**  \n   - A slight perturbation can cause a **huge drop in accuracy**.\n   - The model was confidently wrong in many cases.\n\n2. **PGD is Stronger Than FGSM**  \n   - PGD iteratively fine-tunes adversarial noise, making it more devastating than a single-step FGSM attack.\n   - FGSM still worked well, but PGD had a much worse impact.\n\n3. **Certain Images Might Be More Resilient**  \n   - Some CIFAR-10 categories (e.g., simple textures) may be more robust than others.\n   - Model confidence shifts significantly between clean and adversarial images.\n\n---\n\n## **How to Defend Against These Attacks**\n### **Adversarial Training**  \n   - Train the model using adversarial examples alongside clean ones.  \n   - Example: Include FGSM and PGD samples during training to make the model more robust.  \n\n### **Input Preprocessing**  \n   - Apply Gaussian noise, feature squeezing, or random transformations to disrupt adversarial patterns.  \n   - JPEG compression can remove adversarial perturbations.  \n\n### **Ensemble Learning**  \n   - Train multiple models with different architectures and average their predictions.  \n   - This reduces the attack impact on any single model.  \n\n### **Gradient Masking & Regularization**  \n   - Modify the loss function to make it harder for attackers to compute gradients.  \n   - Use defensive distillation to train a more resilient model.  \n\n---\n\n## **Final Takeaway**\n- Without defense, adversarial attacks break deep learning models easily.\n- PGD is far deadlier than FGSM, achieving 0% accuracy.\n- Defenses like adversarial training and preprocessing can help, but no perfect solution exists yet.\n","metadata":{}},{"cell_type":"markdown","source":"# 4. Mitigation Exploration","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torchvision.transforms as transforms\nimport torchvision.datasets as datasets\nimport timm\nfrom torch.utils.data import DataLoader\n\n# Device setup\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# Load Pretrained ResNet-18 Model\nmodel = timm.create_model('resnet18', pretrained=True)\nnum_ftrs = model.fc.in_features\nmodel.fc = nn.Linear(num_ftrs, 10)  # CIFAR-10 has 10 classes\nmodel.to(device)\n\n# Define Loss and Optimizer\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.SGD(model.parameters(), lr=0.1, momentum=0.9, weight_decay=5e-4)\nscheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=200)\n\n# Data Transformations\ntransform_train = transforms.Compose([\n    transforms.RandomCrop(32, padding=4),\n    transforms.RandomHorizontalFlip(),\n    transforms.ToTensor(),\n    transforms.Normalize((0.5,), (0.5,))\n])\n\ntransform_test = transforms.Compose([\n    transforms.ToTensor(),\n    transforms.Normalize((0.5,), (0.5,))\n])\n\n# Load CIFAR-10 Dataset\ntrain_dataset = datasets.CIFAR10(root=\"./data\", train=True, transform=transform_train, download=True)\ntrain_loader = DataLoader(train_dataset, batch_size=128, shuffle=True, num_workers=4)\n\ntest_dataset = datasets.CIFAR10(root=\"./data\", train=False, transform=transform_test, download=True)\ntest_loader = DataLoader(test_dataset, batch_size=128, shuffle=False, num_workers=4)\n\n# FGSM Adversarial Attack Function\ndef fgsm_attack(image, epsilon, data_grad):\n    sign_data_grad = data_grad.sign()\n    perturbed_image = image + epsilon * sign_data_grad\n    return torch.clamp(perturbed_image, 0, 1)\n\n# Training with Adversarial Examples\nnum_epochs = 20  # Reduce epochs for faster execution\nepsilon = 0.02   # Small perturbation level\n\nfor epoch in range(num_epochs):\n    model.train()\n    running_loss = 0.0\n\n    for images, labels in train_loader:\n        images, labels = images.to(device), labels.to(device)\n\n        # Standard Forward Pass\n        images.requires_grad = True\n        optimizer.zero_grad()\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        loss.backward()\n\n        # Generate Adversarial Examples using FGSM\n        data_grad = images.grad.data\n        adv_images = fgsm_attack(images, epsilon, data_grad)\n\n        # Train on both clean and adversarial examples\n        optimizer.zero_grad()\n        outputs_adv = model(adv_images)\n        loss_adv = criterion(outputs_adv, labels)\n        loss_adv.backward()\n        optimizer.step()\n\n        running_loss += (loss.item() + loss_adv.item()) / 2\n\n    scheduler.step()\n    print(f\"Epoch [{epoch+1}/{num_epochs}], Loss: {running_loss/len(train_loader):.4f}\")\n\n# Evaluate on Clean and Adversarial Examples\ndef evaluate_model(model, data_loader, attack=None):\n    model.eval()\n    correct = 0\n    total = 0\n\n    with torch.no_grad():\n        for images, labels in data_loader:\n            images, labels = images.to(device), labels.to(device)\n\n            if attack:\n                images.requires_grad = True\n                outputs = model(images)\n                loss = criterion(outputs, labels)\n                model.zero_grad()\n                loss.backward()\n                data_grad = images.grad.data\n                images = fgsm_attack(images, epsilon, data_grad)\n\n            outputs = model(images)\n            _, predicted = torch.max(outputs, 1)\n            total += labels.size(0)\n            correct += (predicted == labels).sum().item()\n\n    return 100 * correct / total\n\n# Performance Before and After Adversarial Training\nclean_acc_before = 80.29  # Previous accuracy on clean data\nfgsm_acc_before = 6.25  # Previous accuracy on FGSM attack\n\nclean_acc_after = evaluate_model(model, test_loader)\nfgsm_acc_after = evaluate_model(model, test_loader, attack='FGSM')\n\nprint(f\"Clean Accuracy After Adversarial Training: {clean_acc_after:.2f}%\")\nprint(f\"FGSM Attack Accuracy After Adversarial Training: {fgsm_acc_after:.2f}%\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T13:46:40.140317Z","iopub.execute_input":"2025-02-15T13:46:40.140563Z","iopub.status.idle":"2025-02-15T13:55:47.25277Z","shell.execute_reply.started":"2025-02-15T13:46:40.140521Z","shell.execute_reply":"2025-02-15T13:55:47.251003Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport numpy as np\n\nlabels = [\"Clean Accuracy\", \"FGSM Accuracy\"]\nbefore_training = [clean_acc_before, fgsm_acc_before]\nafter_training = [clean_acc_after, fgsm_acc_after]\n\nx = np.arange(len(labels))\nwidth = 0.35\n\nfig, ax = plt.subplots(figsize=(6, 4))\nrects1 = ax.bar(x - width/2, before_training, width, label=\"Before Adv. Training\")\nrects2 = ax.bar(x + width/2, after_training, width, label=\"After Adv. Training\")\n\nax.set_ylabel(\"Accuracy (%)\")\nax.set_title(\"Model Performance Before & After Adversarial Training\")\nax.set_xticks(x)\nax.set_xticklabels(labels)\nax.legend()\n\nplt.ylim(0, 100)\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-02-15T13:55:47.253872Z","iopub.status.idle":"2025-02-15T13:55:47.254218Z","shell.execute_reply":"2025-02-15T13:55:47.254088Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}