{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"### Transfer Learning - EfficientNet\n\nThe goal of this competition is to develop a model capable of accurately predicting the flower depicted in an image.\n\n","metadata":{}},{"cell_type":"markdown","source":"\n   ## Contents:\n* <h1 style=\"padding: 1rem;\n          color:black;\n          text-align:left;\n          margin:0 auto;\n          font-size:1.5rem;\"><a href=\"#library\">Libraries</a></h1>\n* <h1 style=\"padding: 1rem;\n          color:black;\n          text-align:left;\n          margin:0 auto;\n          font-size:1.5rem;\"><a href=\"#transform\">Dataset Transform and Dataloader</a></h1>\n* <h1 style=\"padding: 1rem;\n          color:black;\n          text-align:left;\n          margin:0 auto;\n          font-size:1.5rem;\"><a href=\"#efficient\">Transfer Learning On EfficientNet Model</a></h1>\n               \n* <h1 style=\"padding: 1rem;\n          color:black;\n          text-align:left;\n          margin:0 auto;\n          font-size:1.5rem;\"><a href=\"#training\">Configs and Training Loop</a></h1>\n\n* <h1 style=\"padding: 1rem;\n          color:black;\n          text-align:left;\n          margin:0 auto;\n          font-size:1.5rem;\"><a href=\"#submission\">Inference and Submission</a></h1>","metadata":{}},{"cell_type":"markdown","source":"<div class=\"alert alert-warning\" role=\"alert\">\n<a id=\"library\"><h1 style=\"padding: 2rem;\n          color:black;\n          text-align:center;\n          margin:0 auto;\n          font-size:2rem;\">\n Libraries\n</h1></a>\n</div>","metadata":{}},{"cell_type":"code","source":"!pip install tfrecord\n\nimport io\nimport pandas as pd\nimport tensorflow as tf\nimport timm\nimport torch\nfrom PIL import Image\nfrom torch.utils.data import Dataset, DataLoader\nfrom tqdm.auto import tqdm\nimport pandas as pd\nimport tfrecord\nimport os\nimport cv2\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom torch import nn\nimport torchvision\nfrom torchinfo import summary\nfrom PIL import Image\nimport warnings\nwarnings.simplefilter('ignore')\n%matplotlib inline\nprint(f\"Make sure that the PyTorch version is the same as yours.\")\nprint(f\"PyTorch version: {torch.__version__}\\ntorchvision version: {torchvision.__version__}\")","metadata":{"execution":{"iopub.status.busy":"2023-09-13T21:16:22.536919Z","iopub.execute_input":"2023-09-13T21:16:22.537254Z","iopub.status.idle":"2023-09-13T21:16:51.804255Z","shell.execute_reply.started":"2023-09-13T21:16:22.537214Z","shell.execute_reply":"2023-09-13T21:16:51.802231Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"FLOWER_NAMES = ['pink primrose',    'hard-leaved pocket orchid', 'canterbury bells', 'sweet pea',     'wild geranium',     'tiger lily',           'moon orchid',              'bird of paradise', 'monkshood',        'globe thistle',         # 00 - 09\n           'snapdragon',       \"colt's foot\",               'king protea',      'spear thistle', 'yellow iris',       'globe-flower',         'purple coneflower',        'peruvian lily',    'balloon flower',   'giant white arum lily', # 10 - 19\n           'fire lily',        'pincushion flower',         'fritillary',       'red ginger',    'grape hyacinth',    'corn poppy',           'prince of wales feathers', 'stemless gentian', 'artichoke',        'sweet william',         # 20 - 29\n           'carnation',        'garden phlox',              'love in the mist', 'cosmos',        'alpine sea holly',  'ruby-lipped cattleya', 'cape flower',              'great masterwort', 'siam tulip',       'lenten rose',           # 30 - 39\n           'barberton daisy',  'daffodil',                  'sword lily',       'poinsettia',    'bolero deep blue',  'wallflower',           'marigold',                 'buttercup',        'daisy',            'common dandelion',      # 40 - 49\n           'petunia',          'wild pansy',                'primula',          'sunflower',     'lilac hibiscus',    'bishop of llandaff',   'gaura',                    'geranium',         'orange dahlia',    'pink-yellow dahlia',    # 50 - 59\n           'cautleya spicata', 'japanese anemone',          'black-eyed susan', 'silverbush',    'californian poppy', 'osteospermum',         'spring crocus',            'iris',             'windflower',       'tree poppy',            # 60 - 69\n           'gazania',          'azalea',                    'water lily',       'rose',          'thorn apple',       'morning glory',        'passion flower',           'lotus',            'toad lily',        'anthurium',             # 70 - 79\n           'frangipani',       'clematis',                  'hibiscus',         'columbine',     'desert-rose',       'tree mallow',          'magnolia',                 'cyclamen ',        'watercress',       'canna lily',            # 80 - 89\n           'hippeastrum ',     'bee balm',                  'pink quill',       'foxglove',      'bougainvillea',     'camellia',             'mallow',                   'mexican petunia',  'bromelia',         'blanket flower',        # 90 - 99\n           'trumpet creeper',  'blackberry lily',           'common tulip',     'wild rose']                                                                                                                                               # 100 - 102","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\nif not device:\n    print('CPU mode is on...')\nelse:\n    print('GPU mode is on...')","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-warning\" role=\"alert\">\n<a id=\"transform\"><h1 style=\"padding: 2rem;\n          color:black;\n          text-align:center;\n          margin:0 auto;\n          font-size:2rem;\">\n Dataset Transform\n</h1></a>\n</div>","metadata":{}},{"cell_type":"code","source":"DATASET_PATH = \"/kaggle/input/tpu-getting-started/tfrecords-jpeg-224x224\"\n\ndef transform_tf_to_df(subset_data):\n    df = pd.DataFrame({\"id\": pd.Series(dtype=\"str\"), \n                       \"class\": pd.Series(dtype=\"int\"), \n                       \"img\": pd.Series(dtype=\"object\")})    \n    tf_files = []\n    \n    for subdir, dirs, files in os.walk(DATASET_PATH):\n        if subdir.split(\"/\")[-1] == subset_data:\n            for file in files:\n                filepath = subdir + os.sep + file\n                tf_files.append(filepath)\n                \n    for tf_file in tf_files:\n        if subset_data == \"test\":\n            loader = tfrecord.tfrecord_loader(tf_file, None, {\"id\": \"byte\", \"image\": \"byte\"})\n        else:\n            loader = tfrecord.tfrecord_loader(tf_file, None, {\"id\": \"byte\",\"image\": \"byte\", \"class\": \"int\"})\n        \n        for record in loader:\n            id_label = record[\"id\"].decode('utf-8')\n            label = record[\"class\"][0].item() if subset_data != \"test\" else None\n            img_bytes = np.frombuffer(record[\"image\"], dtype=np.uint8)\n            img = cv2.imdecode(img_bytes, cv2.IMREAD_COLOR)\n            img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n            df.loc[len(df.index)] = [id_label, label, img]\n    return df\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_validation = transform_tf_to_df('val')\ndf_train = transform_tf_to_df('train')\ndf_test = transform_tf_to_df('test')","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"CLASSES_NUM = len(FLOWER_NAMES)\nCLASSES_NUM","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(df_train.dtypes, df_train.shape)\nplt.imshow(df_train.iloc[2]['img'])","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"weights = torchvision.models.EfficientNet_B0_Weights.DEFAULT\nauto_transforms = weights.transforms()\nauto_transforms","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class FlowersDataset(Dataset):\n    def __init__(self, data, transform=None) -> None:\n        self.data = data\n        self.transform = transform\n        \n    def __getitem__(self, idx):\n        \"Iterable function which applies to each row\"\n        img_id = self.data.iloc[idx, 0]\n        label = self.data.iloc[idx, 1]\n        image = self.data.iloc[idx, 2]\n        image = Image.fromarray(image)\n        if self.transform:\n            image = self.transform(image)\n        y = np.zeros(CLASSES_NUM, dtype=np.float32)\n        y[label] = int(1)\n        return img_id, y, image\n    \n    def __len__(self) -> int:\n        \"Returns the total number of samples.\"\n        return len(self.data)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_data = FlowersDataset(df_train, transform=auto_transforms)\nvalidation_data = FlowersDataset(df_validation, transform=auto_transforms)\ntest_data = FlowersDataset(df_test, transform=auto_transforms)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dataloader = DataLoader(dataset=train_data,\n                              batch_size=64,\n                              num_workers=2)\n\nvalidation_dataloader = DataLoader(dataset=validation_data, \n                              batch_size=64,\n                              num_workers=1)\n\ntest_dataloader = DataLoader(dataset=test_data, \n                             batch_size=64, \n                             num_workers=1, \n                             shuffle=False)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(f\"Length of validation dataloader: {len(validation_dataloader)} batches of {64}\")\nimage_id, label, image  = next(iter(validation_dataloader))","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image.shape, label.shape","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(f\"Length of test_dataloader dataloader: {len(test_dataloader)} batches of {64}\")\nimage_id_test, label_test, image_test  = next(iter(test_dataloader))\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-warning\" role=\"alert\">\n<a id=\"efficient\"><h1 style=\"padding: 2rem;\n          color:black;\n          text-align:center;\n          margin:0 auto;\n          font-size:2rem;\">\n Transfer Learning On EfficientNet Model\n</h1></a>\n</div>","metadata":{}},{"cell_type":"code","source":"model_effnet = torchvision.models.efficientnet_b0(weights=weights).to(device)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"summary(model=model_effnet, \n        input_size=(64, 3, 224, 224),\n        col_names=[\"input_size\", \"output_size\", \"num_params\", \"trainable\"],\n        col_width=10,\n        row_settings=[\"var_names\"])","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Ensure that all base layers within the 'features' section of the model (which acts as the feature extractor) are frozen by assigning requires_grad=False.\"\nfor param in model_effnet.features.parameters():\n    param.requires_grad = False","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Recreate the classifier layer\nmodel_effnet.classifier = torch.nn.Sequential(\n    torch.nn.Dropout(p=0.2, inplace=True), \n    torch.nn.Linear(in_features=1280, \n                    out_features=CLASSES_NUM, # same number of output units as our number of classes\n                    bias=True)).to(device)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Generate a summary following the freezing of the features and the modification of the output classifier layer\nsummary(model_effnet, \n        input_size=(64, 3, 224, 224),\n        verbose=0,\n        col_names=[\"input_size\", \"output_size\", \"num_params\", \"trainable\"],\n        col_width=9,\n        row_settings=[\"var_names\"]\n)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-warning\" role=\"alert\">\n<a id=\"training\"><h1 style=\"padding: 2rem;\n          color:black;\n          text-align:center;\n          margin:0 auto;\n          font-size:2rem;\">\n Configs and Training Loop\n</h1></a>\n</div>","metadata":{}},{"cell_type":"code","source":"training_loss_count, validation_loss_count  = [], []\n\ndef accuracy_step(y_true, y_pred):\n    correct = torch.eq(y_true, y_pred).sum().item()\n    acc = (correct / len(y_pred)) * 100\n    return acc\n\ndef train_step(model: torch.nn.Module,\n               data_loader: torch.utils.data.DataLoader,\n               loss_fn: torch.nn.Module,\n               optimizer: torch.optim.Optimizer,\n               accuracy_step,\n               device: torch.device = device):\n    \n    train_loss, train_acc = 0, 0\n    model.to(device)\n    \n    for img_ids, y, X in data_loader:\n        X, y = X.to(device), y.to(device)\n        #forward\n        y_pred = model(X)\n        #calculate loss\n        loss = loss_fn(y_pred, y)\n        train_loss += loss\n        train_acc += accuracy_step(y_true=y.argmax(dim=1), y_pred=y_pred.argmax(dim=1))\n        #zerograd\n        optimizer.zero_grad()\n        #backward\n        loss.backward()\n        #optimizer\n        optimizer.step()\n    train_loss /= len(data_loader)\n    training_loss_count.append(train_loss.cpu().item())\n    train_acc /= len(data_loader)\n    print(f\"Train loss: {train_loss:.5f} | Train accuracy: {train_acc:.2f}%\")\n    \n\ndef validation_step(data_loader: torch.utils.data.DataLoader,\n              model: torch.nn.Module,\n              loss_fn: torch.nn.Module,\n              accuracy_step,\n              device: torch.device = device):\n    valid_loss, valid_acc = 0, 0\n    model.to(device)\n    model.eval()\n    \n    with torch.inference_mode(): \n        for img_ids, y, X in data_loader:\n            X, y = X.to(device), y.to(device)\n            valid_pred = model(X)\n            valid_loss += loss_fn(valid_pred, y)\n            valid_acc += accuracy_step(y_true=y.argmax(dim=1), y_pred=valid_pred.argmax(dim=1))\n        valid_loss /= len(data_loader)\n        validation_loss_count.append(valid_loss.cpu().item())\n        valid_acc /= len(data_loader)\n        print(f\"Validation loss: {valid_loss:.5f} | Validation accuracy: {valid_acc:.2f}%\\n\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"EPOCHS = 10\n\nloss_fn = nn.CrossEntropyLoss()\noptimizer = torch.optim.Adam(model_effnet.parameters(), lr=0.001)\n\nfor epoch in tqdm(range(EPOCHS)):\n    print(f\"Epoch: {epoch}\\n---------\")\n    train_step(data_loader=train_dataloader, \n        model=model_effnet, \n        loss_fn=loss_fn,\n        optimizer=optimizer,\n        accuracy_step=accuracy_step,\n        device=device)\n    \n    validation_step(data_loader=validation_dataloader,\n        model=model_effnet,\n        loss_fn=loss_fn,\n        accuracy_step=accuracy_step,\n        device=device)\n    ","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"raw","source":"","metadata":{}},{"cell_type":"code","source":"plt.plot(training_loss_count, label='Training Loss')\nplt.plot(validation_loss_count, label='Validation Loss')\nplt.legend(frameon=False)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class=\"alert alert-warning\" role=\"alert\">\n<a id=\"submission\"><h1 style=\"padding: 2rem;\n          color:black;\n          text-align:center;\n          margin:0 auto;\n          font-size:2rem;\">\n Inference and Submission\n</h1></a>\n</div>","metadata":{}},{"cell_type":"code","source":"submission_data = []\n\nwith torch.inference_mode():\n    for img_ids, y, X in test_dataloader:\n        X = X.to(device)\n        y_preds = model_effnet(X)\n        y_preds = y_preds.argmax(dim=1)\n        for img_id, y_pred in zip(img_ids, y_preds.cpu()):\n            submission_data.append({'id': img_id, 'label': y_pred.item()})\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission_df = pd.DataFrame(submission_data)\nsubmission_df.to_csv('submission.csv', index=False)        \n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Define the number of columns for the subplot grid\nnum_cols = len(submission_df.head(7))\nfig, axes = plt.subplots(1, num_cols, figsize=(15, 5))\n\nfor i, (index, row) in enumerate(submission_df.head(7).iterrows()):\n    name = FLOWER_NAMES[row['label']]\n    selected_img = df_test.loc[df_test['id'] == row['id'], 'img'].squeeze()\n    ax = axes[i]\n    ax.imshow(selected_img)\n    ax.set_title(name)\n    ax.axis('off')\n    \nplt.subplots_adjust(wspace=0.1)\nplt.show()","metadata":{},"execution_count":null,"outputs":[]}]}