{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"tpuV5e8","dataSources":[{"sourceId":112509,"databundleVersionId":14254895,"sourceType":"competition"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Species Pytorch Lightning CNN Classifier w/TPU","metadata":{"papermill":{"duration":0.003895,"end_time":"2023-06-30T09:39:19.33539","exception":false,"start_time":"2023-06-30T09:39:19.331495","status":"completed"},"tags":[]}},{"cell_type":"markdown","source":"https://www.kaggle.com/code/stpeteishii/species-pytorch-lightning-cnn-classifier","metadata":{}},{"cell_type":"code","source":"!pip install lightning","metadata":{"papermill":{"duration":0.003048,"end_time":"2023-06-30T09:39:19.341832","exception":false,"start_time":"2023-06-30T09:39:19.338784","status":"completed"},"tags":[],"trusted":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2025-10-30T04:25:46.479254Z","iopub.execute_input":"2025-10-30T04:25:46.479583Z","iopub.status.idle":"2025-10-30T04:27:37.850553Z","shell.execute_reply.started":"2025-10-30T04:25:46.479559Z","shell.execute_reply":"2025-10-30T04:27:37.849159Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport random\nimport numpy as np\nimport pandas as pd\nfrom tqdm import tqdm\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import random_split\nfrom torch.utils.data import DataLoader, Dataset, Subset\nfrom torch.utils.data import random_split, SubsetRandomSampler\nfrom torchvision import datasets, transforms, models \nfrom torchvision.datasets import ImageFolder\nfrom torchvision.transforms import ToTensor\nfrom torchvision.utils import make_grid\n\nfrom lightning.pytorch import LightningDataModule\nfrom lightning.pytorch import LightningModule\nfrom lightning.pytorch import Trainer\nimport lightning.pytorch as L\nprint(L.__version__)\n\nimport matplotlib.pyplot as plt\n%matplotlib inline\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import classification_report\nfrom PIL import Image","metadata":{"papermill":{"duration":13.262195,"end_time":"2023-06-30T09:39:32.607264","exception":false,"start_time":"2023-06-30T09:39:19.345069","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-10-30T04:27:37.853249Z","iopub.execute_input":"2025-10-30T04:27:37.853591Z","iopub.status.idle":"2025-10-30T04:27:54.21314Z","shell.execute_reply.started":"2025-10-30T04:27:37.85356Z","shell.execute_reply":"2025-10-30T04:27:54.21189Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"os.environ.pop('TPU_PROCESS_ADDRESSES', None)\nos.environ.pop('CLOUD_TPU_TASK_ID', None)\nos.environ[\"PJRT_DEVICE\"] = \"TPU\"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"transform = transforms.Compose([\n            transforms.Resize(224),             # resize shortest side to 224 pixels\n            transforms.CenterCrop(224),         # crop longest side to 224 pixels at center            \n            transforms.ToTensor(),\n            transforms.Normalize([0.485, 0.456, 0.406],\n                                 [0.229, 0.224, 0.225])\n        ])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-30T04:27:54.214387Z","iopub.execute_input":"2025-10-30T04:27:54.215098Z","iopub.status.idle":"2025-10-30T04:27:54.221133Z","shell.execute_reply.started":"2025-10-30T04:27:54.215052Z","shell.execute_reply":"2025-10-30T04:27:54.220143Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\ntrain=pd.read_csv('/kaggle/input/csiro-biomass/train.csv')\nnames=sorted(train['Species'].unique().tolist())\nname_mapping=dict(zip(names,list(range(len(names)))))\nprint(name_mapping)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-30T04:27:54.22326Z","iopub.execute_input":"2025-10-30T04:27:54.224196Z","iopub.status.idle":"2025-10-30T04:27:54.259796Z","shell.execute_reply.started":"2025-10-30T04:27:54.224162Z","shell.execute_reply":"2025-10-30T04:27:54.258677Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nfilenames=train['image_path'].apply(lambda x:x.split('/')[-1])\nspecies_values=train['Species']\nspecies_mapping=dict(zip(filenames,species_values))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dir0='/kaggle/input/csiro-biomass/train'\npath_label=[]\nfor dirname, _, filenames in os.walk(dir0):\n    for filename in filenames:\n        path=(os.path.join(dirname, filename))\n        species=species_mapping.get(filename,None)\n        name=name_mapping.get(species,None)\n        path_label+=[(path,name)]\n        \n#path_label = random.sample(path_label,1000)\nprint(path_label[0:3])","metadata":{"papermill":{"duration":0.059662,"end_time":"2023-06-30T09:39:32.684499","exception":false,"start_time":"2023-06-30T09:39:32.624837","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-10-30T04:27:54.261066Z","iopub.execute_input":"2025-10-30T04:27:54.261337Z","iopub.status.idle":"2025-10-30T04:27:54.54279Z","shell.execute_reply.started":"2025-10-30T04:27:54.261317Z","shell.execute_reply":"2025-10-30T04:27:54.541684Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CustomDataset(torch.utils.data.Dataset):\n    def __init__(self, path_label, transform=None):\n        self.path_label = path_label\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.path_label)\n\n    def __getitem__(self, idx):\n        path, label = self.path_label[idx]\n        img = Image.open(path).convert('RGB')\n\n        if self.transform is not None:\n            img = self.transform(img)\n\n        return img, label","metadata":{"papermill":{"duration":0.012446,"end_time":"2023-06-30T09:39:33.013124","exception":false,"start_time":"2023-06-30T09:39:33.000678","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-10-30T04:27:54.552194Z","iopub.status.idle":"2025-10-30T04:27:54.552581Z","shell.execute_reply.started":"2025-10-30T04:27:54.552397Z","shell.execute_reply":"2025-10-30T04:27:54.552418Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class DataModule(LightningDataModule):\n    def __init__(self, path_label=None, root_dir=None, batch_size=32):\n        super().__init__()\n        self.path_label = path_label\n        self.root_dir = root_dir\n        self.batch_size = batch_size\n        \n        # Define your transforms\n        self.transform = transforms.Compose([\n            transforms.Resize(224),             # resize shortest side to 224 pixels\n            transforms.CenterCrop(224),         # crop longest side to 224 pixels at center            \n            transforms.ToTensor(),\n            transforms.Normalize([0.485, 0.456, 0.406],\n                               [0.229, 0.224, 0.225])\n        ])\n        \n        # Initialize datasets\n        self.train_dataset = None\n        self.test_dataset = None\n\n    def setup(self, stage=None):\n        # Create dataset based on what's provided\n        if self.path_label is not None:\n            dataset = CustomDataset(self.path_label, self.transform)\n        elif self.root_dir is not None:\n            dataset = datasets.ImageFolder(root=self.root_dir, transform=self.transform)\n        else:\n            raise ValueError(\"Either path_label or root_dir must be provided\")\n        \n        dataset_size = len(dataset)\n        train_size = int(0.8 * dataset_size) \n        test_size = dataset_size - train_size\n\n        # Split dataset\n        self.train_dataset = Subset(dataset, range(train_size))\n        self.test_dataset = Subset(dataset, range(train_size, dataset_size))\n\n    def train_dataloader(self):\n        return DataLoader(self.train_dataset, batch_size=self.batch_size, shuffle=True)\n\n    def test_dataloader(self):\n        return DataLoader(self.test_dataset, batch_size=self.batch_size)\n\n    def predict_dataloader(self):\n        return DataLoader(self.test_dataset, batch_size=self.batch_size)\n\n    def __len__(self):\n        if self.train_dataset is not None:\n            return len(self.train_dataset)\n        elif self.test_dataset is not None:\n            return len(self.test_dataset)\n        else:\n            return 0\n","metadata":{"papermill":{"duration":0.012953,"end_time":"2023-06-30T09:39:33.049085","exception":false,"start_time":"2023-06-30T09:39:33.036132","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-10-30T04:27:54.554474Z","iopub.status.idle":"2025-10-30T04:27:54.554869Z","shell.execute_reply.started":"2025-10-30T04:27:54.554662Z","shell.execute_reply":"2025-10-30T04:27:54.554676Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# ConvolutionalClassifier","metadata":{}},{"cell_type":"code","source":"class ConvolutionalClassifier(LightningModule):\n    \n    def __init__(self, num_classes=len(names)):\n        super(ConvolutionalClassifier, self).__init__()\n        self.save_hyperparameters()\n        \n        # Keep convolutional layers unchanged\n        self.conv1 = nn.Conv2d(3, 6, 3, 1)\n        self.conv2 = nn.Conv2d(6, 16, 3, 1)\n        self.fc1 = nn.Linear(16 * 54 * 54, 120)\n        self.fc2 = nn.Linear(120, 84)\n        self.fc3 = nn.Linear(84, 20)\n        # Modified for classification: output for num_classes\n        self.fc4 = nn.Linear(20, num_classes)\n\n    def forward(self, X):\n        X = F.relu(self.conv1(X))\n        X = F.max_pool2d(X, 2, 2)\n        X = F.relu(self.conv2(X))\n        X = F.max_pool2d(X, 2, 2)\n        X = X.view(-1, 16 * 54 * 54)\n        X = F.relu(self.fc1(X))\n        X = F.relu(self.fc2(X))\n        X = F.relu(self.fc3(X))\n        X = self.fc4(X)  # Output for number of classes\n        return X  # Output shape: [batch_size, num_classes]\n\n    def configure_optimizers(self):\n        return torch.optim.Adam(self.parameters(), lr=0.001)\n\n    def training_step(self, batch, batch_idx):\n        X, y = batch\n        y_hat = self(X)\n        # Regression → Classification: MSE Loss → Cross Entropy Loss\n        loss = F.cross_entropy(y_hat, y)\n        self.log(\"train_loss\", loss)\n        \n        # Also record accuracy\n        preds = torch.argmax(y_hat, dim=1)\n        acc = (preds == y).float().mean()\n        self.log(\"train_acc\", acc)\n        \n        return loss\n\n    def validation_step(self, batch, batch_idx):\n        X, y = batch\n        y_hat = self(X)\n        loss = F.cross_entropy(y_hat, y)\n        self.log(\"val_loss\", loss)\n        \n        preds = torch.argmax(y_hat, dim=1)\n        acc = (preds == y).float().mean()\n        self.log(\"val_acc\", acc)\n\n    def test_step(self, batch, batch_idx):\n        X, y = batch\n        y_hat = self(X)\n        loss = F.cross_entropy(y_hat, y)\n        self.log(\"test_loss\", loss)\n        \n        preds = torch.argmax(y_hat, dim=1)\n        acc = (preds == y).float().mean()\n        self.log(\"test_acc\", acc)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if __name__ == '__main__':\n    datamodule = DataModule(path_label=path_label)\n    datamodule.setup()","metadata":{"execution":{"iopub.status.busy":"2025-10-30T04:27:54.558022Z","iopub.status.idle":"2025-10-30T04:27:54.55852Z","shell.execute_reply.started":"2025-10-30T04:27:54.558274Z","shell.execute_reply":"2025-10-30T04:27:54.558298Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"    model = ConvolutionalClassifier()\n\n    trainer = L.Trainer(\n        accelerator=\"tpu\",      ###########\n        devices=8,              ###########\n        precision=\"bf16-true\",  ###########\n        max_epochs=1000, \n    )\n\n\n    trainer.fit(model, datamodule)","metadata":{"execution":{"iopub.status.busy":"2025-10-30T04:27:54.559984Z","iopub.status.idle":"2025-10-30T04:27:54.560278Z","shell.execute_reply.started":"2025-10-30T04:27:54.560157Z","shell.execute_reply":"2025-10-30T04:27:54.560169Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"    datamodule.setup(stage='test')\n    test_loader = datamodule.test_dataloader()\n    trainer.test(dataloaders=test_loader)","metadata":{"papermill":{"duration":959.542695,"end_time":"2023-06-30T09:55:32.61869","exception":false,"start_time":"2023-06-30T09:39:33.075995","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-10-30T04:27:54.561394Z","iopub.status.idle":"2025-10-30T04:27:54.561881Z","shell.execute_reply.started":"2025-10-30T04:27:54.561724Z","shell.execute_reply":"2025-10-30T04:27:54.561738Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for images, labels in datamodule.train_dataloader():\n    break\nim=make_grid(images,nrow=16)\n\nplt.figure(figsize=(12,12))\nplt.imshow(np.transpose(im.numpy(),(1,2,0)))\n\ninv_normalize=transforms.Normalize(mean=[-0.485/0.229,-0.456/0.224,-0.406/0.225],\n                                   std=[1/0.229,1/0.224,1/0.225])\nim=inv_normalize(im)\n\nplt.figure(figsize=(12,12))\nplt.imshow(np.transpose(im.numpy(),(1,2,0)))","metadata":{"papermill":{"duration":1.112338,"end_time":"2023-06-30T09:55:33.735591","exception":false,"start_time":"2023-06-30T09:55:32.623253","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2025-10-30T04:27:54.56313Z","iopub.status.idle":"2025-10-30T04:27:54.563574Z","shell.execute_reply.started":"2025-10-30T04:27:54.563378Z","shell.execute_reply":"2025-10-30T04:27:54.563399Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cpu\")\nmodel.eval()\ny_true = []\ny_pred = []\n\nwith torch.no_grad():\n    for test_data in datamodule.test_dataloader():\n        test_images, test_labels = test_data[0].to(device), test_data[1].to(device)\n        pred = model(test_images)\n        \n        # Get predicted class indices\n        pred_classes = torch.argmax(pred, dim=1)\n        \n        for i in range(len(pred_classes)):\n            y_true.append(test_labels[i].item())\n            y_pred.append(pred_classes[i].item())","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"min(y_pred),max(y_pred)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-30T04:27:54.567336Z","iopub.status.idle":"2025-10-30T04:27:54.567607Z","shell.execute_reply.started":"2025-10-30T04:27:54.567466Z","shell.execute_reply":"2025-10-30T04:27:54.567476Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import classification_report\n\nprint(classification_report(y_true, y_pred, target_names=names, digits=4))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"papermill":{"duration":0.009079,"end_time":"2023-06-30T09:55:39.150422","exception":false,"start_time":"2023-06-30T09:55:39.141343","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null}]}