{"cells":[{"metadata":{},"cell_type":"markdown","source":"# IMPORTS"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os\nfrom os import path\nimport json\nimport collections\n\nimport numpy as np\nimport pandas as pd\nimport cv2\nfrom PIL import Image\nimport torch\nfrom torch.utils.data import DataLoader\nimport torch.nn as nn\nfrom torchvision.utils import make_grid\nfrom torchvision import datasets, transforms, models\nfrom torch.utils import data as torch_data\nfrom tqdm import tqdm\nfrom sklearn.model_selection import KFold\nimport matplotlib.pyplot as plt","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* The below statement will help in better error intepretation for CUDA."},{"metadata":{"trusted":true},"cell_type":"code","source":"CUDA_LAUNCH_BLOCKING=\"1\"","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Data Loading and Visualization\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"base_path = '../input/herbarium-2021-fgvc8'\ntrain_path = os.path.join(base_path, \"train/\")\ntrain_metadata_path = os.path.join(train_path, \"metadata.json\")\ntest_path = os.path.join(base_path, \"test/\")\ntest_metadata_path = os.path.join(test_path, \"metadata.json\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"with open(train_metadata_path) as json_file:\n    metadata = json.load(json_file)\n    \nmetadata.keys()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(metadata[\"annotations\"][0])\nprint(metadata[\"images\"][0])\nprint(metadata[\"categories\"][0])\nprint(metadata[\"licenses\"][0])\nprint(metadata[\"institutions\"][0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class Create_Image_Paths():\n    def __init__(self,datafile):\n        with open(datafile) as json_file:\n            self.metadata = json.load(json_file)\n    \n    def create_dataframe(self):\n        ids = []\n        categories = []\n        paths = []\n\n        for annotation, image in zip(metadata[\"annotations\"], metadata[\"images\"]):\n            assert annotation[\"image_id\"] == image[\"id\"]\n            ids.append(image[\"id\"])\n            categories.append(annotation[\"category_id\"])\n            paths.append(image[\"file_name\"])\n        \n        self.df = pd.DataFrame({\"id\": ids, \"category\": categories, \"path\": paths})\n        d_categories = {category[\"id\"]: category[\"name\"] for category in metadata[\"categories\"]}\n        d_families = {category[\"id\"]: category[\"family\"] for category in metadata[\"categories\"]}\n        d_orders = {category[\"id\"]: category[\"order\"] for category in metadata[\"categories\"]}\n        self.df[\"category_name\"] = self.df[\"category\"].map(d_categories)\n        self.df[\"family_name\"] = self.df[\"category\"].map(d_families)\n        self.df[\"order_name\"] = self.df[\"category\"].map(d_orders)\n        ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"Image_Paths_Obj = Create_Image_Paths(train_metadata_path)\nImage_Paths_Obj.create_dataframe()\nImage_Paths_Obj.df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"n_class = len(Image_Paths_Obj.df.groupby('category'))\nn_class","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"Image_Paths_Obj.df.path[0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class Data_Visualization:\n    def __init__(self,df):\n        self.dfx = df\n        \n    def visualize_by_id(self, _id=None):\n        tmp = self.dfx.sample(6)\n        if _id is not None:\n            tmp = self.dfx[self.dfx[\"category\"] == _id].sample(6)\n            \n        self.visualize_train_batch(\n            tmp[\"path\"].tolist(), \n            tmp[\"category_name\"].tolist(),\n            tmp[\"family_name\"].tolist(),\n            tmp[\"order_name\"].tolist())\n     \n    def visualize_train_batch(self,paths, categories, families, orders):\n        plt.figure(figsize=(16, 16))\n    \n        for ind, info in enumerate(zip(paths, categories, families, orders)):\n            path, category, family, order = info\n        \n            plt.subplot(2, 3, ind + 1)\n        \n            image = cv2.imread(os.path.join(train_path, path))\n            image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n\n            plt.imshow(image)\n        \n            plt.title(\n                f\"FAMILY: {family} ORDER: {order}\\n{category}\", \n                fontsize=10,\n            )\n            plt.axis(\"off\")\n    \n        plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"data_viz = Data_Visualization(Image_Paths_Obj.df)\ndata_viz.visualize_by_id()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Data Preparation for Model Training"},{"metadata":{"trusted":true},"cell_type":"code","source":"class DataRetriever(torch_data.Dataset):\n    def __init__(self, paths, categories=None,transforms=None,base_path=train_path):\n        self.paths = paths\n        self.categories = categories\n        self.transforms = transforms\n        self.base_path = base_path\n          \n    def __len__(self):\n        return len(self.paths)\n    \n    def __getitem__(self, index):\n        img = Image.open(os.path.join(self.base_path, self.paths[index]))\n        #img = cv2.resize(img, (224, 224))\n        #img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        \n        if self.transforms:\n            img = self.transforms(img)\n        \n        if self.categories is None:\n            return img\n        \n        y = self.categories[index] \n        return img, y\n    \n    \ndef get_transforms():\n    return transforms.Compose([\n        transforms.RandomRotation(10),      # rotate +/- 10 degrees\n        transforms.RandomHorizontalFlip(),  # reverse 50% of images\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            ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"tr_df = Image_Paths_Obj.df\ntmp_path = tr_df[\"path\"].tolist()\ntmp_category = tr_df[\"category\"].tolist()\nBATCH = 10\ntrain_data_retriever = DataRetriever(\n    tmp_path,\n    tmp_category,\n    transforms=get_transforms(),\n)\n\ntorch.manual_seed(42)\n#train_data_loader = DataLoader(train_data_retriever, batch_size=BATCH, shuffle=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"Base_Model1 = models.resnet34(pretrained=True)\nfor param in Base_Model1.parameters():\n    param.requires_grad = False\n\ntorch.manual_seed(42)\nBase_Model1.fc = nn.Linear(512, n_class, bias=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"DEVICE = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"DEVICE","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Model Training\n\nThe model will be trained in K-Fold cross validation in GPU. The learning rate scheduler that will be used is Cosine Annealing."},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\nEPOCHS = 10\n\ncriterion = nn.CrossEntropyLoss()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Define the K-fold Cross Validator\nk_folds = 5\nkfold = KFold(n_splits=k_folds, shuffle=True)\n# K-fold Cross Validation model evaluation\nfor fold, (train_ids, test_ids) in enumerate(kfold.split(train_data_retriever)):\n    # Print\n    print(f'FOLD {fold}')\n    print('--------------------------------')\n    \n    # Sample elements randomly from a given list of ids, no replacement.\n    train_subsampler = torch.utils.data.SubsetRandomSampler(train_ids)\n    test_subsampler = torch.utils.data.SubsetRandomSampler(test_ids)\n    \n    # Define data loaders for training and testing data in this fold\n    trainloader = torch.utils.data.DataLoader(\n                      train_data_retriever, \n                      batch_size=BATCH, sampler=train_subsampler)\n    testloader = torch.utils.data.DataLoader(\n                      train_data_retriever,\n                      batch_size=BATCH, sampler=test_subsampler)\n    \n    #configure the model for GPU training\n    model = Base_Model1.to(DEVICE)\n    epochs = 0\n    #set the optimizer\n    optimizer = torch.optim.Adam(Base_Model1.parameters(), lr=0.01)\n    #set the learning rate scheduler\n    step_size = 4*len(trainloader)\n    scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, step_size)\n        \n    \n    # Run the training loop for defined number of epochs\n    for epoch in range(epochs, EPOCHS):\n        # Print epoch\n        print(f'Starting epoch {epoch+1}')\n        \n        # Run the training batches\n        for b, (X_train, y_train) in enumerate(trainloader):\n            \n            b+=1\n            X_train = X_train.cuda()\n            y_train = y_train.cuda()\n            # Apply the model\n            y_pred = model.forward(X_train)\n            loss = criterion(y_pred, y_train)\n            \n            \n            # Update parameters\n            optimizer.zero_grad()\n            loss.backward()\n            scheduler.step()\n            optimizer.step()\n            \n            # Print interim results\n            if b%20 == 0:\n                print(f'epoch: {epoch:2}  batch: {b:4}  loss: {loss.item():10.8f}') \n                \n            \n    # Process is complete.\n    print('Training process has finished. Saving trained model.')\n    \n    # Print about testing\n    print('Starting testing')\n    \n    # Saving the model\n    save_path = f'model-fold-{fold}.pth'\n    torch.save(model.state_dict(), save_path)\n    \n    # Evaluation for this fold\n    correct, total = 0, 0\n    with torch.no_grad():\n        \n        # Iterate over the test data and generate predictions\n        for b, (X_test, y_test) in enumerate(testloader):\n            b+=1\n            X_test = X_test.cuda()\n            y_test = y_test.cuda()\n            \n            # Apply the model\n            y_val = model.forward(X_test)\n            # Tally the number of correct predictions\n            predicted = torch.max(y_val.data, 1)[1] \n            total += y_test.size(0)\n            correct += (predicted == y_test).sum().item()\n            \n        \n        # Print accuracy\n        print('Accuracy for fold %d: %d %%' % (fold, 100.0 * correct / total))\n        print('--------------------------------')\n        results[fold] = 100.0 * (correct / total)\n            \n            \n# Print fold results\nprint(f'K-FOLD CROSS VALIDATION RESULTS FOR {k_folds} FOLDS')\nprint('--------------------------------')\nsum = 0.0\nfor key, value in results.items():\n    print(f'Fold {key}: {value} %')\n    sum += value\nprint(f'Average: {sum/len(results.items())} %')                   ","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}