{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.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":13836,"databundleVersionId":1718836,"sourceType":"competition"}],"dockerImageVersionId":31236,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"* حبيبة محمد السيد \n* سلمى وليد\n* ثريا رمضان ","metadata":{}},{"cell_type":"markdown","source":"**The dataset was loaded from Kaggle and split into training70%, validation 20%, and test 10% sets. Custom PyTorch Dataset and DataLoader were implemented, and preprocessing was verified through visualization.Before applying data augmentation, the CNN model (from scratch) achieved approximately 63% accuracy on thevalidation set. After incorporating data augmentation techniques such as random cropping, flipping, rotation, and color jittering, the model’s performance improved significantly, reaching 65–66% validation accuracy. This represents an improvement of more than 10%, indicating better generalization and reduced overfitting.A pretrained ResNet50 model was fine-tuned on the dataset. The model achieved 82% training accuracy and 81% test accuracy, showing effective transfer learning and good generalization**","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nimport torchvision.transforms as transforms\nimport torch.optim as optim\nfrom PIL import Image\nimport pandas as pd\nimport numpy as np\nimport os\nimport matplotlib.pyplot as plt\nfrom sklearn.model_selection import train_test_split","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T18:48:40.693769Z","iopub.execute_input":"2025-12-20T18:48:40.693965Z","iopub.status.idle":"2025-12-20T18:48:54.594229Z","shell.execute_reply.started":"2025-12-20T18:48:40.693944Z","shell.execute_reply":"2025-12-20T18:48:54.593304Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Set device, random seed, and load the CSV file with image labels\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\ntorch.manual_seed(42)\nprint(f'Using device: {device}')\n\ndata_dir = '/kaggle/input/cassava-leaf-disease-classification/train_images'\ncsv_path = '/kaggle/input/cassava-leaf-disease-classification/train.csv'\ndf = pd.read_csv(csv_path)\nprint(f'Total samples: {len(df)}')\nprint(f'Class distribution:\\n{df[\"label\"].value_counts()}')\nprint(df.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T18:48:54.595557Z","iopub.execute_input":"2025-12-20T18:48:54.595934Z","iopub.status.idle":"2025-12-20T18:48:54.772041Z","shell.execute_reply.started":"2025-12-20T18:48:54.595908Z","shell.execute_reply":"2025-12-20T18:48:54.771376Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_transform = transforms.Compose([\n    transforms.RandomResizedCrop(224),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomVerticalFlip(),\n    transforms.RandomRotation(15),\n    transforms.ColorJitter(brightness=0.3, contrast=0.3, saturation=0.3, hue=0.1),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406],\n                         std=[0.229, 0.224, 0.225])\n])\n\n\nval_transform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406],\n                         std=[0.229, 0.224, 0.225])\n])\n\nclass CassavaDataset(Dataset):\n    def __init__(self, dataframe, img_dir, transform=None):\n        self.dataframe = dataframe.reset_index(drop=True)\n        self.img_dir = img_dir\n        self.transform = transform\n    \n    def __len__(self):\n        return len(self.dataframe)\n    \n    def __getitem__(self, index):\n        img_name = self.dataframe.loc[index, 'image_id']\n        img_path = os.path.join(self.img_dir, img_name)\n        image = Image.open(img_path).convert('RGB')\n        label = self.dataframe.loc[index, 'label']\n        \n        if self.transform:\n            image = self.transform(image)\n        \n        return image, label\n\nprint('Transformations and Dataset class defined successfully')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T18:49:09.454278Z","iopub.execute_input":"2025-12-20T18:49:09.454601Z","iopub.status.idle":"2025-12-20T18:49:09.462663Z","shell.execute_reply.started":"2025-12-20T18:49:09.454576Z","shell.execute_reply":"2025-12-20T18:49:09.462044Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 70% Train, 30% Temp\ntrain_df, temp_df = train_test_split(\n    df,\n    test_size=0.3,\n    random_state=42,\n    stratify=df['label']\n)\n\n# 20% Val, 10% Test\nval_df, test_df = train_test_split(\n    temp_df,\n    test_size=1/3,   # يعني 10% من الداتا الكلية\n    random_state=42,\n    stratify=temp_df['label']\n)\n\nprint(f'Training samples: {len(train_df)}')\nprint(f'Validation samples: {len(val_df)}')\nprint(f'Test samples: {len(test_df)}')\ntrain_dataset = CassavaDataset(train_df, data_dir, transform=train_transform)\nval_dataset   = CassavaDataset(val_df, data_dir, transform=val_transform)\ntest_dataset  = CassavaDataset(test_df, data_dir, transform=val_transform)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T18:49:16.234383Z","iopub.execute_input":"2025-12-20T18:49:16.235154Z","iopub.status.idle":"2025-12-20T18:49:16.266094Z","shell.execute_reply.started":"2025-12-20T18:49:16.235126Z","shell.execute_reply":"2025-12-20T18:49:16.265331Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_loader = DataLoader(\n    train_dataset,\n    batch_size=32,\n    shuffle=True,\n    num_workers=2\n)\n\nval_loader = DataLoader(\n    val_dataset,\n    batch_size=32,\n    shuffle=False,\n    num_workers=2\n)\n\ntest_loader = DataLoader(\n    test_dataset,\n    batch_size=32,\n    shuffle=False,\n    num_workers=2\n)\n\nprint(f'Number of training batches: {len(train_loader)}')\nprint(f'Number of validation batches: {len(val_loader)}')\nprint(f'Number of test batches: {len(test_loader)}')\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T18:49:17.967994Z","iopub.execute_input":"2025-12-20T18:49:17.968315Z","iopub.status.idle":"2025-12-20T18:49:17.973764Z","shell.execute_reply.started":"2025-12-20T18:49:17.968290Z","shell.execute_reply":"2025-12-20T18:49:17.973074Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Test data loading and display sample images to verify preprocessing\nimages, labels = next(iter(train_loader))\nprint(f'Batch image shape: {images.shape}')\nprint(f'Batch labels shape: {labels.shape}')\nprint(f'Sample labels: {labels[:5]}')\n\ndef denormalize(tensor):\n    mean = torch.tensor([0.485, 0.456, 0.406]).view(3, 1, 1)\n    std = torch.tensor([0.229, 0.224, 0.225]).view(3, 1, 1)\n    return tensor * std + mean\n\nfig, axes = plt.subplots(2, 4, figsize=(12, 6))\nfor idx, ax in enumerate(axes.flat):\n    if idx < len(images):\n        img = denormalize(images[idx]).permute(1, 2, 0).numpy()\n        img = np.clip(img, 0, 1)\n        ax.imshow(img)\n        ax.set_title(f'Label: {labels[idx].item()}')\n        ax.axis('off')\nplt.tight_layout()\nplt.show()\nprint('Data preprocessing completed successfully')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T19:51:24.472578Z","iopub.execute_input":"2025-12-20T19:51:24.472896Z","iopub.status.idle":"2025-12-20T19:51:26.381203Z","shell.execute_reply.started":"2025-12-20T19:51:24.472868Z","shell.execute_reply":"2025-12-20T19:51:26.380345Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class SimpleCNN(nn.Module):\n    def __init__(self):\n        super(SimpleCNN, self).__init__()\n\n        self.conv_layers = nn.Sequential(\n             # Block 1\n            nn.Conv2d(3, 32, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n            nn.Dropout2d(0.15),\n            \n            # Block 2\n            nn.Conv2d(32, 64, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n            nn.Dropout2d(0.2),\n            \n            # Block 3\n            nn.Conv2d(64, 128, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.Conv2d(128, 128, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n            nn.Dropout2d(0.3),\n            \n            # Block 4\n            nn.Conv2d(128, 256, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n            nn.Dropout2d(0.4)\n        )\n\n        # Adaptive Pooling to fix input size for FC\n        self.gap = nn.AdaptiveAvgPool2d((1,1))\n\n        # Fully Connected Layers\n        self.fc_layers = nn.Sequential(\n            nn.Linear(256, 512),\n            nn.ReLU(),\n            nn.LayerNorm(512),\n            nn.Dropout(0.5),\n\n            nn.Linear(512, 128),\n            nn.ReLU(),\n            nn.LayerNorm(128),\n            nn.Dropout(0.3),\n\n            nn.Linear(128, 5)\n        )\n\n    def forward(self, x):\n        x = self.conv_layers(x)\n        x = self.gap(x)  \n        x = x.view(x.size(0), -1) \n        x = self.fc_layers(x)\n        return x\n\nprint(\"SimpleCNN model defined successfully\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T18:49:27.096958Z","iopub.execute_input":"2025-12-20T18:49:27.097717Z","iopub.status.idle":"2025-12-20T18:49:27.105945Z","shell.execute_reply.started":"2025-12-20T18:49:27.097689Z","shell.execute_reply":"2025-12-20T18:49:27.105177Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(\"Using device:\", device)\n\nmodel = SimpleCNN().to(device)\n\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=0.001)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T18:49:31.181951Z","iopub.execute_input":"2025-12-20T18:49:31.182540Z","iopub.status.idle":"2025-12-20T18:49:31.423910Z","shell.execute_reply.started":"2025-12-20T18:49:31.182514Z","shell.execute_reply":"2025-12-20T18:49:31.423312Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_one_epoch(model, dataloader):\n    model.train()\n    running_loss = 0.0\n    correct = 0\n    total = 0\n\n    for images, labels in dataloader:\n        images = images.to(device)\n        labels = labels.to(device)\n\n        optimizer.zero_grad()\n\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n\n        loss.backward()\n        optimizer.step()\n\n        running_loss += loss.item()\n        _, predicted = torch.max(outputs, 1)\n        correct += (predicted == labels).sum().item()\n        total += labels.size(0)\n\n    epoch_loss = running_loss / len(dataloader)\n    epoch_acc = correct / total\n    return epoch_loss, epoch_acc\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T18:49:33.678432Z","iopub.execute_input":"2025-12-20T18:49:33.678762Z","iopub.status.idle":"2025-12-20T18:49:33.684379Z","shell.execute_reply.started":"2025-12-20T18:49:33.678729Z","shell.execute_reply":"2025-12-20T18:49:33.683613Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def evaluate(model, dataloader):\n    model.eval()\n    correct = 0\n    total = 0\n\n    with torch.no_grad():\n        for images, labels in dataloader:\n            images = images.to(device)\n            labels = labels.to(device)\n\n            outputs = model(images)\n            _, predicted = torch.max(outputs, 1)\n            correct += (predicted == labels).sum().item()\n            total += labels.size(0)\n\n    return correct / total\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T18:49:38.611720Z","iopub.execute_input":"2025-12-20T18:49:38.612299Z","iopub.status.idle":"2025-12-20T18:49:38.617035Z","shell.execute_reply.started":"2025-12-20T18:49:38.612272Z","shell.execute_reply":"2025-12-20T18:49:38.616246Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"num_epochs = 15\n\nfor epoch in range(num_epochs):\n    train_loss, train_acc = train_one_epoch(model, train_loader)\n    val_acc = evaluate(model, val_loader)\n\n    print(f\"Epoch [{epoch+1}/{num_epochs}]\")\n    print(f\"Train Loss: {train_loss:.4f} | Train Acc: {train_acc:.4f}\")\n    print(f\"Val Acc: {val_acc:.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T18:49:42.117658Z","iopub.execute_input":"2025-12-20T18:49:42.118485Z","iopub.status.idle":"2025-12-20T19:24:31.481871Z","shell.execute_reply.started":"2025-12-20T18:49:42.118453Z","shell.execute_reply":"2025-12-20T19:24:31.480949Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_acc = evaluate(model, test_loader)\nprint(f\"Test Accuracy (CNN from scratch): {test_acc:.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T19:25:18.820990Z","iopub.execute_input":"2025-12-20T19:25:18.821686Z","iopub.status.idle":"2025-12-20T19:25:38.612419Z","shell.execute_reply.started":"2025-12-20T19:25:18.821651Z","shell.execute_reply":"2025-12-20T19:25:38.611622Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Pretrained Model**","metadata":{}},{"cell_type":"code","source":"import torchvision.models as models\n# 1. Load Pre-trained ResNet50\nmy_model = models.resnet50(pretrained=True)\n\n# 2. Freeze all layers\nfor param in my_model.parameters():\n    param.requires_grad = False\n# Unfreeze last block\nfor param in my_model.layer4.parameters():\n    param.requires_grad = True    \n\n# 3. Replace the last layer (for 5 disease classes)\nnum_ftrs = my_model.fc.in_features\nmy_model.fc = nn.Linear(num_ftrs, 5)\n\n# 4. Move model to GPU\nmy_model = my_model.to(device)\n\nprint(\"Pre-trained model is ready on GPU!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T19:26:34.986841Z","iopub.execute_input":"2025-12-20T19:26:34.987559Z","iopub.status.idle":"2025-12-20T19:26:36.234978Z","shell.execute_reply.started":"2025-12-20T19:26:34.987522Z","shell.execute_reply":"2025-12-20T19:26:36.234343Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# Define Loss function and Optimizer\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(filter(lambda p: p.requires_grad, my_model.parameters()), lr=0.001)\n\nprint(\"Step 2 complete: Loss and Optimizer defined.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T19:26:41.156705Z","iopub.execute_input":"2025-12-20T19:26:41.157309Z","iopub.status.idle":"2025-12-20T19:26:41.163246Z","shell.execute_reply.started":"2025-12-20T19:26:41.157280Z","shell.execute_reply":"2025-12-20T19:26:41.162459Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_model(model, criterion, optimizer, epochs=3):\n    model.train()\n    for epoch in range(epochs):\n        running_loss = 0.0\n        # train_loader must be defined in the previous cells\n        for inputs, labels in train_loader:\n            inputs, labels = inputs.to(device), labels.to(device)\n            \n            optimizer.zero_grad()\n            outputs = model(inputs)\n            loss = criterion(outputs, labels)\n            loss.backward()\n            optimizer.step()\n            \n            running_loss += loss.item()\n        print(f\"Epoch {epoch+1}/{epochs} - Loss: {running_loss/len(train_loader):.4f}\")\n\nprint(\"Step 3 complete: Training function is ready.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T19:26:43.364240Z","iopub.execute_input":"2025-12-20T19:26:43.364560Z","iopub.status.idle":"2025-12-20T19:26:43.370554Z","shell.execute_reply.started":"2025-12-20T19:26:43.364528Z","shell.execute_reply":"2025-12-20T19:26:43.369890Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def check_accuracy(loader, model):\n    num_correct = 0\n    num_samples = 0\n    model.eval()\n    \n    with torch.no_grad():\n        for x, y in loader:\n            x, y = x.to(device), y.to(device)\n            scores = model(x)\n            _, predictions = scores.max(1)\n            num_correct += (predictions == y).sum()\n            num_samples += predictions.size(0)\n    \n    accuracy = float(num_correct) / num_samples\n    print(f\"Final Accuracy: {accuracy*100:.2f}%\")\n    return accuracy\n\n# Execute Training and Evaluation\ntrain_model(my_model, criterion, optimizer, epochs=10)\ncheck_accuracy(train_loader, my_model)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T19:26:47.913859Z","iopub.execute_input":"2025-12-20T19:26:47.914163Z","iopub.status.idle":"2025-12-20T19:49:06.824881Z","shell.execute_reply.started":"2025-12-20T19:26:47.914136Z","shell.execute_reply":"2025-12-20T19:49:06.824149Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_acc = check_accuracy(test_loader, my_model)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-20T19:49:48.264823Z","iopub.execute_input":"2025-12-20T19:49:48.265512Z","iopub.status.idle":"2025-12-20T19:49:58.940382Z","shell.execute_reply.started":"2025-12-20T19:49:48.265479Z","shell.execute_reply":"2025-12-20T19:49:58.939647Z"}},"outputs":[],"execution_count":null}]}