{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":33679,"databundleVersionId":3212216,"sourceType":"competition"},{"sourceId":8683138,"sourceType":"datasetVersion","datasetId":5205651}],"dockerImageVersionId":30733,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install python-docx\n!pip install albumentations\n!pip install pytorch-lightning\n!pip install timm\n!pip install torchmetrics","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-06-14T10:02:36.744147Z","iopub.execute_input":"2024-06-14T10:02:36.744522Z","iopub.status.idle":"2024-06-14T10:03:43.205079Z","shell.execute_reply.started":"2024-06-14T10:02:36.744479Z","shell.execute_reply":"2024-06-14T10:03:43.203972Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport numpy as np \nimport matplotlib.pyplot as plt \nimport pandas as pd \nimport json\nimport cv2\nimport seaborn as sns\nfrom skimage import io\nfrom docx import Document ","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:03:43.207065Z","iopub.execute_input":"2024-06-14T10:03:43.20735Z","iopub.status.idle":"2024-06-14T10:03:44.911614Z","shell.execute_reply.started":"2024-06-14T10:03:43.207322Z","shell.execute_reply":"2024-06-14T10:03:44.910754Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Function to read scientific names from a docx file\ndef read_scientific_names(docx_path):\n    doc = Document(docx_path)\n    scientific_names = []\n    for para in doc.paragraphs:\n        scientific_names.append(para.text.strip())\n    return scientific_names\n\n# Read scientific names from the provided docx file\nscientific_names = read_scientific_names(\"/kaggle/input/medical-plant/medical plant.docx\")\nscientific_names","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:03:44.912815Z","iopub.execute_input":"2024-06-14T10:03:44.913304Z","iopub.status.idle":"2024-06-14T10:03:44.986928Z","shell.execute_reply.started":"2024-06-14T10:03:44.913276Z","shell.execute_reply":"2024-06-14T10:03:44.98593Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# EDA","metadata":{}},{"cell_type":"code","source":"TRAIN_DIR = \"../input/herbarium-2022-fgvc9/train_images/\"\n'''TEST_DIR = \"../input/herbarium-2022-fgvc9/test_images/\"'''\n\nwith open(\"../input/herbarium-2022-fgvc9/train_metadata.json\") as json_file:\n    train_meta = json.load(json_file)\n'''with open(\"../input/herbarium-2022-fgvc9/test_metadata.json\") as json_file:\n    test_meta = json.load(json_file)'''\n    \nimage_ids = [image[\"image_id\"] for image in train_meta[\"images\"]]\nimage_dirs = [TRAIN_DIR + image[\"file_name\"] for image in train_meta[\"images\"]]\ncategory_ids = [annot[\"category_id\"] for annot in train_meta[\"annotations\"]]\ngenus_ids = [annot[\"genus_id\"] for annot in train_meta[\"annotations\"] ]\n'''test_ids = [image[\"image_id\"] for image in test_meta]\ntest_dirs = [TEST_DIR + image[\"file_name\"] for image in test_meta ]'''\n\ntrain = pd.DataFrame(data =np.array([image_ids , image_dirs, genus_ids, category_ids ]).T, \n                     columns = [\"image_id\", \"directory\",\"genus_id\", \"category\",])\n'''test = pd.DataFrame(data =np.array([test_ids  , test_dirs ]).T, \n                    columns = [\"image_id\", \"directory\",])'''\n\ncategories = pd.DataFrame(train_meta[\"categories\"])\ncategories = categories[categories[\"scientificName\"].isin(scientific_names)]\n\n# Ensure the data types of the columns to be merged match\ntrain[\"category\"] = train[\"category\"].astype(int)\ncategories[\"category_id\"] = categories[\"category_id\"].astype(int)\n# Merge the test data with the filtered categories data\ntrain = train.merge(categories, left_on=\"category\", right_on=\"category_id\", how=\"inner\")\n\n'''# Merge the test data with the filtered categories data\ntest[\"image_id\"] = test[\"image_id\"].astype(int)\ntest = test.merge(categories, left_on=\"image_id\", right_on=\"category_id\", how=\"inner\")\n'''\n\n# Save the filtered data to CSV\ntrain.to_csv(\"train.csv\", index=False)\n'''test.to_csv(\"test.csv\", index=False)'''","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:03:44.989236Z","iopub.execute_input":"2024-06-14T10:03:44.989566Z","iopub.status.idle":"2024-06-14T10:04:05.447797Z","shell.execute_reply.started":"2024-06-14T10:03:44.989542Z","shell.execute_reply":"2024-06-14T10:04:05.446852Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train = pd.read_csv('train.csv')\ngenera = pd.DataFrame(train_meta['genera'])\ngenera  = df_train.merge(genera,on='genus_id', how='inner')\ndf_train.shape","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:05.449295Z","iopub.execute_input":"2024-06-14T10:04:05.449968Z","iopub.status.idle":"2024-06-14T10:04:05.523458Z","shell.execute_reply.started":"2024-06-14T10:04:05.449931Z","shell.execute_reply":"2024-06-14T10:04:05.522386Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"'''df_test = pd.read_csv('test.csv')\ndf_test.head()'''","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:05.524711Z","iopub.execute_input":"2024-06-14T10:04:05.525046Z","iopub.status.idle":"2024-06-14T10:04:05.531892Z","shell.execute_reply.started":"2024-06-14T10:04:05.524998Z","shell.execute_reply":"2024-06-14T10:04:05.530873Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Count the number of images per scientific name\nimage_counts = df_train['scientificName'].value_counts()\n\n# Filter the image counts to only include scientific names with at least 50 images\nimage_counts = image_counts[image_counts >= 50]\n\n# Create a DataFrame from the filtered image counts\nimage_counts = image_counts.reset_index()\nimage_counts.columns = ['scientificName', 'imageCount']\n\n# Display the DataFrame\npd.set_option('display.max_rows', None)\nprint(image_counts)\n\n# Save the DataFrame to a CSV file\nimage_counts.to_csv('filtered_image_counts_per_scientific_name.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:05.53312Z","iopub.execute_input":"2024-06-14T10:04:05.533421Z","iopub.status.idle":"2024-06-14T10:04:05.560719Z","shell.execute_reply.started":"2024-06-14T10:04:05.533393Z","shell.execute_reply":"2024-06-14T10:04:05.559656Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"'''# Count the number of images per scientific name\nimage_counts_test = df_test['scientificName'].value_counts()\n\n\n# Create a DataFrame from the filtered image counts\nimage_counts_test = image_counts_test.reset_index()\nimage_counts_test.columns = ['scientificName', 'imageCount']\n\n# Display the DataFrame\npd.set_option('display.max_rows', None)\nprint(image_counts_test)\n\n# Save the DataFrame to a CSV file\nimage_counts_test.to_csv('filtered_image_counts_test_per_scientific_name.csv', index=False)'''","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:05.561996Z","iopub.execute_input":"2024-06-14T10:04:05.562315Z","iopub.status.idle":"2024-06-14T10:04:05.568862Z","shell.execute_reply.started":"2024-06-14T10:04:05.562289Z","shell.execute_reply":"2024-06-14T10:04:05.567895Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Filter df_train to only include rows with scientific names that have at least 50 images\ndf_train = df_train[df_train['scientificName'].isin(image_counts['scientificName'])]\n\n# Save the filtered train data to CSV\ndf_train.to_csv('filtered_train.csv', index=False)\n\n'''# Filter df_test to only include rows with scientific names that have at least 50 images\ndf_test = df_test[df_test['scientificName'].isin(image_counts['scientificName'])]\n\n# Save the filtered test data to CSV\ndf_test.to_csv('filtered_test.csv', index=False)'''","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:05.570109Z","iopub.execute_input":"2024-06-14T10:04:05.570553Z","iopub.status.idle":"2024-06-14T10:04:05.725713Z","shell.execute_reply.started":"2024-06-14T10:04:05.570512Z","shell.execute_reply":"2024-06-14T10:04:05.724762Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Sample Images","metadata":{}},{"cell_type":"code","source":"from PIL import Image\ndef display_images(scienName):\n    images = df_train.loc[df_train['scientificName'] == scienName]['directory'][:12]\n    i = 1\n    fig = plt.figure(figsize = (10, 10))\n    plt.suptitle(scienName, fontsize = '35')\n    for image in images:\n        ax = fig.add_subplot(3, 4, i)\n        image = Image.open(image).convert('RGB')\n        ax.imshow(image)\n        i+=1\n    fig.tight_layout()\n    plt.show()\n\ndisplay_images('Abutilon indicum (L.) Sweet')","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:05.730374Z","iopub.execute_input":"2024-06-14T10:04:05.730677Z","iopub.status.idle":"2024-06-14T10:04:09.449023Z","shell.execute_reply.started":"2024-06-14T10:04:05.730651Z","shell.execute_reply":"2024-06-14T10:04:09.448018Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Encode Class Labels","metadata":{}},{"cell_type":"code","source":"from sklearn import preprocessing\n\nle = preprocessing.LabelEncoder()\nle.fit(df_train['category'])\ndf_train['category'] = le.transform(df_train['category'])\ndf_train.category.nunique()","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:09.450151Z","iopub.execute_input":"2024-06-14T10:04:09.450442Z","iopub.status.idle":"2024-06-14T10:04:09.528407Z","shell.execute_reply.started":"2024-06-14T10:04:09.450417Z","shell.execute_reply":"2024-06-14T10:04:09.527399Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train.head()","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:09.529525Z","iopub.execute_input":"2024-06-14T10:04:09.529819Z","iopub.status.idle":"2024-06-14T10:04:09.546219Z","shell.execute_reply.started":"2024-06-14T10:04:09.529793Z","shell.execute_reply":"2024-06-14T10:04:09.545074Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Config","metadata":{}},{"cell_type":"code","source":"import timm\nimport torch\nimport torch.nn as nn\nimport torch.functional as F\nfrom torchvision import datasets, transforms,models\nfrom torch.utils.data import Dataset,DataLoader\n\nfrom pytorch_lightning import Trainer, seed_everything\nfrom pytorch_lightning import Callback\nfrom pytorch_lightning.loggers import CSVLogger\nfrom pytorch_lightning.callbacks import ModelCheckpoint, EarlyStopping\nimport torchmetrics\n\nfrom sklearn.metrics import classification_report\n\n\nimport albumentations as A\nfrom albumentations.core.composition import Compose\nfrom albumentations.pytorch import ToTensorV2\n\nimport pytorch_lightning as pl\n%matplotlib inline","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:09.547682Z","iopub.execute_input":"2024-06-14T10:04:09.548042Z","iopub.status.idle":"2024-06-14T10:04:19.599432Z","shell.execute_reply.started":"2024-06-14T10:04:09.54799Z","shell.execute_reply":"2024-06-14T10:04:19.598527Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class CFG:\n    seed = 42\n    model_name = 'tf_efficientnetv2_m_in21k'\n    pretrained = True\n    img_size = 384\n    num_classes = df_train.category.nunique() \n    lr = 5e-4\n    min_lr = 1e-6\n    t_max = 20\n    num_epochs = 50\n    batch_size = 128\n    accum = 1\n    precision = 16\n    dropout_rate = 0.5\n    weight_decay = 1e-4","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:19.600774Z","iopub.execute_input":"2024-06-14T10:04:19.601644Z","iopub.status.idle":"2024-06-14T10:04:19.608699Z","shell.execute_reply.started":"2024-06-14T10:04:19.601605Z","shell.execute_reply":"2024-06-14T10:04:19.607278Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"seed_everything(CFG.seed)\n","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:19.610078Z","iopub.execute_input":"2024-06-14T10:04:19.611266Z","iopub.status.idle":"2024-06-14T10:04:19.62933Z","shell.execute_reply.started":"2024-06-14T10:04:19.611228Z","shell.execute_reply":"2024-06-14T10:04:19.628276Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train.head()\n","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:19.630845Z","iopub.execute_input":"2024-06-14T10:04:19.631151Z","iopub.status.idle":"2024-06-14T10:04:19.647428Z","shell.execute_reply.started":"2024-06-14T10:04:19.631127Z","shell.execute_reply":"2024-06-14T10:04:19.646417Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Custom Dataset","metadata":{}},{"cell_type":"code","source":"class VietNamHerbariumDataset(Dataset):\n    def __init__(self, data, transform=None):\n        self.annotations = data\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.annotations)\n\n    def __getitem__(self, index):\n        img_path = self.annotations.iloc[index, 1]\n        image = io.imread(img_path)\n        y_label = torch.tensor(int(self.annotations.iloc[index, 3]))\n\n        if self.transform:\n            transformed = self.transform(image=image)\n            image = transformed['image']\n            \n        return (image, y_label)","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:19.648791Z","iopub.execute_input":"2024-06-14T10:04:19.649395Z","iopub.status.idle":"2024-06-14T10:04:19.658035Z","shell.execute_reply.started":"2024-06-14T10:04:19.649368Z","shell.execute_reply":"2024-06-14T10:04:19.656919Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Image Augmentation","metadata":{}},{"cell_type":"code","source":"def Transform(phase: str):\n    resize_and_crop_transforms = [\n        A.Compose([\n            A.Resize(size, size),\n            A.CenterCrop(384, 384)\n        ]) for size in [400, 416, 448, 480, 512]\n    ]\n    \n    if phase == 'train':\n        return A.Compose([\n            A.OneOf(resize_and_crop_transforms, p=1),\n            A.HorizontalFlip(p=0.5),\n            A.VerticalFlip(p=0.5),\n            A.ShiftScaleRotate(p=0.5),\n            A.RandomBrightnessContrast(brightness_limit=0.2, contrast_limit=0.2, p=0.5),\n            A.CoarseDropout(max_holes=8, max_height=32, max_width=32, p=0.5),\n            A.Normalize(),\n            ToTensorV2(),\n        ])\n    else:\n        return A.Compose([\n            A.OneOf(resize_and_crop_transforms, p=1),\n            A.Normalize(),\n            ToTensorV2(),\n        ])","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:19.659911Z","iopub.execute_input":"2024-06-14T10:04:19.660585Z","iopub.status.idle":"2024-06-14T10:04:19.672645Z","shell.execute_reply.started":"2024-06-14T10:04:19.660547Z","shell.execute_reply":"2024-06-14T10:04:19.671831Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Create Dataloader","metadata":{}},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\n# Split the data\ntrain_data, temp_data = train_test_split(df_train, test_size=0.3, random_state=CFG.seed, stratify=df_train['category'])\nvalid_data, test_data = train_test_split(temp_data, test_size=0.5, random_state=CFG.seed, stratify=temp_data['category'])\n\n# Verify the splits\nprint(f\"Training data shape: {train_data.shape}\")\nprint(f\"Valid data shape: {valid_data.shape}\")\nprint(f\"Test data shape: {test_data.shape}\")","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:19.673857Z","iopub.execute_input":"2024-06-14T10:04:19.674536Z","iopub.status.idle":"2024-06-14T10:04:19.709552Z","shell.execute_reply.started":"2024-06-14T10:04:19.674508Z","shell.execute_reply":"2024-06-14T10:04:19.70851Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dataset = VietNamHerbariumDataset(train_data,transform=Transform('train'))\nvalid_dataset = VietNamHerbariumDataset(valid_data,transform=Transform('valid'))\n\ntrain_loader = DataLoader(train_dataset, batch_size=CFG.batch_size, shuffle=True, num_workers=2, pin_memory=True, drop_last=True)\nvalid_loader = DataLoader(valid_dataset, batch_size=CFG.batch_size, shuffle=False, num_workers=2)","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:19.710767Z","iopub.execute_input":"2024-06-14T10:04:19.711097Z","iopub.status.idle":"2024-06-14T10:04:19.718104Z","shell.execute_reply.started":"2024-06-14T10:04:19.711071Z","shell.execute_reply":"2024-06-14T10:04:19.717023Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for batch in train_loader:\n    print(type(batch))\n    print(type(batch[0]))\n    print(type(batch[1]))\n    break","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:19.719458Z","iopub.execute_input":"2024-06-14T10:04:19.719765Z","iopub.status.idle":"2024-06-14T10:04:27.337669Z","shell.execute_reply.started":"2024-06-14T10:04:19.71974Z","shell.execute_reply":"2024-06-14T10:04:27.336093Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Transformed Image","metadata":{}},{"cell_type":"code","source":"dataiter = iter(train_loader)\nimages, labels = next(dataiter)\nprint(images[1].shape)\nprint(labels[1].shape)\nplt.imshow(images[3].squeeze().permute(1,2,0), cmap='Greys_r')","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:27.339376Z","iopub.execute_input":"2024-06-14T10:04:27.339787Z","iopub.status.idle":"2024-06-14T10:04:31.850137Z","shell.execute_reply.started":"2024-06-14T10:04:27.339746Z","shell.execute_reply":"2024-06-14T10:04:31.84899Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model","metadata":{}},{"cell_type":"code","source":"class CustomEfficientNet(nn.Module):\n    def __init__(self, model_name='tf_efficientnetv2_m_in21k', pretrained=True, fine_tune=False,dropout_rate=0.2):\n        super().__init__()\n        self.model = timm.create_model(model_name, pretrained=pretrained)\n        in_features = self.model.get_classifier().in_features\n        \n        # Adjust the classifier to the number of classes in your dataset\n        self.model.classifier = nn.Sequential(\n            nn.Dropout(dropout_rate),  # Add Dropout layer\n            nn.Linear(in_features, CFG.num_classes)\n        )\n    \n        if fine_tune:\n            # Freeze all layers first\n            for param in self.model.parameters():\n                param.requires_grad = False\n            \n            # Unfreeze the last few layers\n            for param in self.model.classifier.parameters():\n                param.requires_grad = True\n\n    def forward(self, x):\n        x = self.model(x)\n        return x","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:31.853314Z","iopub.execute_input":"2024-06-14T10:04:31.854337Z","iopub.status.idle":"2024-06-14T10:04:31.866665Z","shell.execute_reply.started":"2024-06-14T10:04:31.854278Z","shell.execute_reply":"2024-06-14T10:04:31.865568Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class LitHerbarium(pl.LightningModule):\n    def __init__(self, model):\n        super(LitHerbarium, self).__init__()\n        self.model = model \n        self.metric = torchmetrics.F1Score(num_classes=CFG.num_classes, task='multiclass') # F1 Score\n        self.criterion = nn.CrossEntropyLoss() # Cross Entropy loss\n        self.lr = CFG.lr # Learning Rate\n\n    def forward(self, x, *args, **kwargs):\n        return self.model(x)\n\n    def configure_optimizers(self):\n        \"\"\"Choose what optimizers and learning-rate schedulers to use in your optimization\"\"\"\n        self.optimizer = torch.optim.Adam(self.model.parameters(), lr=self.lr)\n        self.scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(self.optimizer, T_max=CFG.t_max, eta_min=CFG.min_lr)\n\n        return {'optimizer': self.optimizer, 'lr_scheduler': self.scheduler}\n\n    def training_step(self, batch, batch_idx):\n        \"\"\"Here you compute and return the training loss and some additional metrics for e.g. the progress bar or logger\"\"\"\n        image,target = batch\n        output = self.model(image)\n        loss = self.criterion(output, target)\n        score = self.metric(output.argmax(1), target)\n        logs = {'train_loss': loss, 'train_f1': score, 'lr': self.optimizer.param_groups[0]['lr']}\n        self.log_dict(\n            logs,\n            on_step=False, on_epoch=True, prog_bar=True, logger=True\n        )\n        return loss\n\n    def validation_step(self, batch, batch_idx):\n        \"\"\"Operates on a single batch of data from the validation set .calculate anything of interest like accuracy.\"\"\"\n        image,target = batch\n        output = self.model(image)\n        loss = self.criterion(output, target)\n        score = self.metric(output.argmax(1), target)\n        logs = {'valid_loss': loss, 'valid_f1': score}\n        self.log_dict(\n            logs,\n            on_step=False, on_epoch=True, prog_bar=True, logger=True\n        )\n        return loss","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:31.868505Z","iopub.execute_input":"2024-06-14T10:04:31.868805Z","iopub.status.idle":"2024-06-14T10:04:31.890085Z","shell.execute_reply.started":"2024-06-14T10:04:31.868781Z","shell.execute_reply":"2024-06-14T10:04:31.8884Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = CustomEfficientNet(model_name=CFG.model_name, pretrained=CFG.pretrained, fine_tune=True, dropout_rate=CFG.dropout_rate)\nlit_model = LitHerbarium(model)\nlit_model","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:31.892178Z","iopub.execute_input":"2024-06-14T10:04:31.892529Z","iopub.status.idle":"2024-06-14T10:04:36.775965Z","shell.execute_reply.started":"2024-06-14T10:04:31.892489Z","shell.execute_reply":"2024-06-14T10:04:36.774845Z"},"collapsed":true,"jupyter":{"outputs_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Trainer","metadata":{}},{"cell_type":"code","source":"logger = CSVLogger(save_dir='logs/', name=CFG.model_name)\nlogger.log_hyperparams(CFG.__dict__)\ncheckpoint_callback = ModelCheckpoint(monitor='valid_loss',\n                                      save_top_k=1,\n                                      save_last=True,\n                                      save_weights_only=True,\n                                      filename='checkpoint/{epoch:02d}-{valid_loss:.4f}-{valid_f1:.4f}',\n                                      verbose=False,\n                                      mode='min')\n\n# Initialize early stopping callback\nearly_stopping_callback = EarlyStopping(\n    monitor='valid_loss', \n    patience=3, \n    mode='min'\n)\n\n# Initialize the trainer\ntrainer = pl.Trainer(\n    max_epochs=CFG.num_epochs,\n    devices=1,  \n    accelerator='gpu', \n    accumulate_grad_batches=CFG.accum,\n    precision=CFG.precision,\n    callbacks=[checkpoint_callback, early_stopping_callback],\n    enable_checkpointing=True,\n    logger=logger,\n)","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:36.777372Z","iopub.execute_input":"2024-06-14T10:04:36.777722Z","iopub.status.idle":"2024-06-14T10:04:38.26957Z","shell.execute_reply.started":"2024-06-14T10:04:36.777678Z","shell.execute_reply":"2024-06-14T10:04:38.268226Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"trainer.fit(lit_model, train_dataloaders=train_loader, val_dataloaders=valid_loader)\n","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:04:38.271101Z","iopub.execute_input":"2024-06-14T10:04:38.272286Z","iopub.status.idle":"2024-06-14T10:51:11.801137Z","shell.execute_reply.started":"2024-06-14T10:04:38.272251Z","shell.execute_reply":"2024-06-14T10:51:11.800227Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"torch.save(lit_model,'best_model.pth')\n","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:51:11.808875Z","iopub.execute_input":"2024-06-14T10:51:11.809264Z","iopub.status.idle":"2024-06-14T10:51:12.685551Z","shell.execute_reply.started":"2024-06-14T10:51:11.809227Z","shell.execute_reply":"2024-06-14T10:51:12.684453Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Plot Metrics","metadata":{}},{"cell_type":"code","source":"metrics = pd.read_csv(f'{trainer.logger.log_dir}/metrics.csv')\n\ntrain_acc = metrics['train_f1'].dropna().reset_index(drop=True)\nvalid_acc = metrics['valid_f1'].dropna().reset_index(drop=True)\n    \nfig = plt.figure(figsize=(7, 6))\n\nplt.plot(train_acc, color=\"r\", label='train/f1')\nplt.plot(valid_acc, color=\"b\", label='valid/f1')\nplt.ylabel('F1', fontsize=24)\nplt.xlabel('Epoch', fontsize=24)\nplt.legend(loc='lower right', fontsize=18)\nplt.savefig(f'{trainer.logger.log_dir}/f1.png')\n\ntrain_loss = metrics['train_loss'].dropna().reset_index(drop=True)\nvalid_loss = metrics['valid_loss'].dropna().reset_index(drop=True)\n\nfig = plt.figure(figsize=(7, 6))\nplt.plot(train_loss, color=\"r\", label='train/loss')\nplt.plot(valid_loss, color=\"b\", label='valid/loss')\nplt.ylabel('Loss', fontsize=24)\nplt.xlabel('Epoch', fontsize=24)\nplt.legend(loc='upper right', fontsize=18)\nplt.savefig(f'{trainer.logger.log_dir}/loss.png')","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:51:12.687251Z","iopub.execute_input":"2024-06-14T10:51:12.687656Z","iopub.status.idle":"2024-06-14T10:51:13.532575Z","shell.execute_reply.started":"2024-06-14T10:51:12.687622Z","shell.execute_reply":"2024-06-14T10:51:13.531661Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Inference\n\n","metadata":{}},{"cell_type":"code","source":"class VietNamHerbariumTestDataset(Dataset):\n    def __init__(self, data, transform=None):\n        self.annotations = data\n        self.transform = transform\n        \n    def __len__(self):\n        return len(self.annotations)\n    \n    def __getitem__(self, index):\n        img_path = self.annotations.iloc[index, 1]\n        image = io.imread(img_path)\n        \n        if self.transform:\n            image = self.transform(image=np.array(image))\n        \n        return image","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:51:13.534198Z","iopub.execute_input":"2024-06-14T10:51:13.534658Z","iopub.status.idle":"2024-06-14T10:51:13.541591Z","shell.execute_reply.started":"2024-06-14T10:51:13.534623Z","shell.execute_reply":"2024-06-14T10:51:13.540575Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Load the trained model\nmodel = torch.load('/kaggle/working/best_model.pth')\nmodel.eval()\n\n# Define the transformation for inference\ndef inference_transform():\n    return A.Compose([\n        A.Resize(height=CFG.img_size, width=CFG.img_size),\n        A.Normalize(),\n        ToTensorV2(),\n    ])\n\n# Function to perform inference on the test dataset\ndef predict_test(model, test_loader):\n    model.eval()\n    predictions = []\n    device = 'cuda' if torch.cuda.is_available() else 'cpu'\n    model.to(device)\n    with torch.no_grad():\n        for images in test_loader:\n            images = images['image'].to(device)  # Send images to the same device as the model\n            outputs = model(images)\n            predicted_classes = torch.argmax(outputs, dim=1)\n            predictions.extend(predicted_classes.cpu().numpy())\n    return predictions","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:51:13.542971Z","iopub.execute_input":"2024-06-14T10:51:13.543329Z","iopub.status.idle":"2024-06-14T10:51:13.801413Z","shell.execute_reply.started":"2024-06-14T10:51:13.543302Z","shell.execute_reply":"2024-06-14T10:51:13.800583Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create Test Dataloader\ntest_dataset = VietNamHerbariumTestDataset(test_data, transform=inference_transform())\ntest_loader = DataLoader(test_dataset, batch_size=CFG.batch_size, shuffle=False, num_workers=2)\n","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:51:13.80256Z","iopub.execute_input":"2024-06-14T10:51:13.802859Z","iopub.status.idle":"2024-06-14T10:51:13.808535Z","shell.execute_reply.started":"2024-06-14T10:51:13.802832Z","shell.execute_reply":"2024-06-14T10:51:13.807505Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Load the trained model\nmodel = torch.load('/kaggle/working/best_model.pth')\nmodel.eval()","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:51:13.809824Z","iopub.execute_input":"2024-06-14T10:51:13.810218Z","iopub.status.idle":"2024-06-14T10:51:14.104739Z","shell.execute_reply.started":"2024-06-14T10:51:13.810187Z","shell.execute_reply":"2024-06-14T10:51:14.103717Z"},"collapsed":true,"jupyter":{"outputs_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Perform inference on the test dataset\npredictions = predict_test(model, test_loader)\n\n# Save the predictions to a CSV file\ntest_data['predicted_category'] = predictions\ntest_data.to_csv('test_predictions.csv', index=False)\n\nprint(\"Predictions saved to test_predictions.csv\")","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:51:14.105958Z","iopub.execute_input":"2024-06-14T10:51:14.106281Z","iopub.status.idle":"2024-06-14T10:51:41.401367Z","shell.execute_reply.started":"2024-06-14T10:51:14.106253Z","shell.execute_reply":"2024-06-14T10:51:41.400201Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Plot the first 10 images along with their predicted categories\nplt.figure(figsize=(15, 6))\nfor i in range(10):\n    image_path = test_data.iloc[i]['directory']  # Assuming 'directory' contains the file path of images\n    predicted_category = test_data.iloc[i]['predicted_category']\n    \n    # Load and plot the image\n    image = plt.imread(image_path)\n    plt.subplot(2, 5, i + 1)\n    plt.imshow(image)\n    plt.axis('off')\n    plt.title(f'Predicted: {predicted_category}')\n\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:51:41.402975Z","iopub.execute_input":"2024-06-14T10:51:41.403378Z","iopub.status.idle":"2024-06-14T10:51:43.479731Z","shell.execute_reply.started":"2024-06-14T10:51:41.403336Z","shell.execute_reply":"2024-06-14T10:51:43.47873Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\n\n# Đọc dữ liệu từ file CSV\ndata = pd.read_csv('/kaggle/working/test_predictions.csv')\n\n# Tính độ chính xác\naccuracy = (data['category'] == data['predicted_category']).mean()\nprint(f\"Accuracy: {accuracy * 100:.2f}%\")","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:51:43.481242Z","iopub.execute_input":"2024-06-14T10:51:43.48157Z","iopub.status.idle":"2024-06-14T10:51:43.501175Z","shell.execute_reply.started":"2024-06-14T10:51:43.481543Z","shell.execute_reply":"2024-06-14T10:51:43.499991Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"accuracy_per_class = test_data.groupby('category').apply(\n    lambda x: (x['category'] == x['predicted_category']).sum() / len(x) * 100\n).reset_index(name='accuracy')\n\n# Hiển thị kết quả\nprint(accuracy_per_class)\n\n# Lưu kết quả vào file CSV (tuỳ chọn)\naccuracy_per_class.to_csv('accuracy_per_class.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2024-06-14T10:51:43.502495Z","iopub.execute_input":"2024-06-14T10:51:43.502803Z","iopub.status.idle":"2024-06-14T10:51:43.590971Z","shell.execute_reply.started":"2024-06-14T10:51:43.502777Z","shell.execute_reply":"2024-06-14T10:51:43.590071Z"},"trusted":true},"execution_count":null,"outputs":[]}]}