{"cells":[{"metadata":{},"cell_type":"markdown","source":"This is my first ever Image competition and I wanted to try my hands on ResNet50. This is a very basic model and I'm still trying to learn and improve on image based data. I felt I should share this with the kaggle crowd. Any comments and suggestions are most welcomed. Thanks\n\nTo get started with I have built the model validating it on a holdout dataset for max 10 epochs. I always have a nack of calling the entire train and test dataset as train_prod, test_prod and output of train_test_split() as train_local and test_local just incase if you guys are wondering."},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"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\n# import numpy as np # linear algebra\n# import 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\n# import os\n# for 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","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport pandas as pd\nimport numpy as np\nimport os\nimport glob\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import accuracy_score\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import DataLoader\nimport torch\n\nfrom torchvision.models import resnet18, resnet50\n\nimport random\nfrom tqdm import tqdm\n\nfrom PIL import Image\n\nfrom sklearn.metrics import confusion_matrix\n\n# from efficientnet_pytorch import EfficientNet\n\nimport seaborn as sns\nimport warnings\nwarnings.filterwarnings(action='ignore')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"kaggle = True\n\nif kaggle:\n    data_label_path = \"/kaggle/input/cassava-leaf-disease-classification/train.csv\"\n    train_images_path =  \"/kaggle/input/cassava-leaf-disease-classification/train_images/\"\n    test_images_path =  \"/kaggle/input/cassava-leaf-disease-classification/test_images/\"\n    \nelse:\n    data_label_path = \"../data/train.csv\"\n    train_images_path = \"../data/train_images\"\n    test_images_path = \"../data/test_images\"\n          \n        \nconfig = {'data_label_path': data_label_path,\n          'train_images_path': train_images_path,\n          'test_images_path': test_images_path,\n          'resize_length': 226,\n          'resize_breath': 226,\n          'train_batch_size': 8, \n          'test_batch_size': 16,\n          'num_workers':8, \n          'device':'cuda' if torch.cuda.is_available() else 'cpu', \n          'model_name':'ResNet50'}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Reading the training dataset\ntrain_df = pd.read_csv(config['data_label_path'])\nnum_targets = len(train_df.label.unique())\n\nif ~kaggle:\n    train_df = train_df.sample(len(train_df))\n    \n# Forming the test dataset\ntest_df = pd.DataFrame()\ntest_df['image_id'] = list(os.listdir(config['test_images_path']))\n\nprint(train_df.shape, test_df.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_local, test_local = train_test_split(train_df, test_size=0.2)\ntrain_local, test_local = train_local.reset_index(drop=True), test_local.reset_index(drop=True)\n\ntrain_local.shape, test_local.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class Dataset():\n    def __init__(self, dataframe, img_path, resize_length, resize_breath, data_type, do_resize, transpose):\n        self.dataframe = dataframe\n        self.img_path = img_path\n        self.length = resize_length\n        self.breath = resize_breath\n        self.do_resize = do_resize\n        self.data_type = data_type\n        self.transpose = transpose\n        \n    def __len__(self):\n        return len(self.dataframe)\n    \n    def __getitem__(self, idx):\n        image_path = os.path.join(self.img_path, self.dataframe['image_id'][idx])\n        image = Image.open(image_path)\n        \n        if self.do_resize:\n            image = image.resize((self.length, self.breath))\n            \n        image = np.array(image)\n        \n        if self.transpose:\n            image = image.transpose(2,0,1)\n        \n        if self.data_type  == 'test':\n            label = 1\n        else:          \n            label = self.dataframe['label'][idx]\n\n        \n        return {#'image': torch.tensor(image, dtype=torch.float).view(3, 128, -1),\n                'image': torch.tensor(image, dtype=torch.float),\n                'label': torch.tensor(label, dtype=torch.long)}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_local_images = Dataset(dataframe=train_local, \n                             img_path= config['train_images_path'],\n                             resize_length=config['resize_length'],\n                             resize_breath=config['resize_breath'], \n                             data_type='train',\n                             do_resize=True, \n                             transpose=True)\n\ntest_local_images = Dataset(dataframe=test_local,\n                            img_path= config['train_images_path'],\n                            resize_length=config['resize_length'],\n                            resize_breath=config['resize_breath'], \n                            data_type='train',\n                            do_resize=True, \n                            transpose=True)\n\ntrain_prod_images = Dataset(dataframe=train_df,\n                            img_path= config['train_images_path'],\n                            resize_length=config['resize_length'],\n                            resize_breath=config['resize_breath'], \n                            data_type='train',\n                            do_resize=True, \n                            transpose=True)\n\ntest_prod_images = Dataset(dataframe=test_df,\n                           img_path= config['test_images_path'],\n                           resize_length=config['resize_length'],\n                           resize_breath=config['resize_breath'], \n                           data_type='test',\n                           do_resize=True, \n                           transpose=True)\n\n# plt.imshow(train_local_images[10]['image'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_local_images_dataloader = DataLoader(train_local_images, \n                                           batch_size=config['train_batch_size'], \n                                           num_workers=config['num_workers'])\ntest_local_images_dataloader = DataLoader(test_local_images, \n                                           batch_size=config['test_batch_size'], \n                                           num_workers=config['num_workers'])\ntrain_prod_images_dataloader = DataLoader(train_prod_images, \n                                           batch_size=config['train_batch_size'], \n                                           num_workers=config['num_workers'])\ntest_prod_images_dataloader = DataLoader(test_prod_images, \n                                           batch_size=config['test_batch_size'], \n                                           num_workers=config['num_workers'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class ResNet50_classifier_for_cassava(nn.Module):\n    def __init__(self, num_labels):\n        super(ResNet50_classifier_for_cassava, self).__init__()\n        self.num_labels = num_labels\n        \n        ####################\n        # ResNet 18 model\n\n        #self.model = resnet50(pretrained=True)\n        \n        self.model = resnet50()\n        model_weights = torch.load(\"../input/resetnet50/resnet50-19c8e357.pth\")\n        self.model.load_state_dict(model_weights)\n\n        for param in self.model.parameters():\n            param.requires_grad = True\n        self.last_layer_size = self.model.fc.in_features #get the in feature of the last layer\n        self.model.fc= nn.Linear(self.last_layer_size, self.num_labels)         \n        \n    def forward(self, x):\n        \n        output = self.model(x)\n        \n        return output\n        ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"criterion = nn.CrossEntropyLoss()\n\ndef setting_seed(seed_no):\n    random.seed(seed_no)\n    np.random.seed(seed_no)\n    torch.manual_seed(seed_no)\n    torch.cuda.manual_seed_all(seed_no)    \n\n    \ndef model_saving(model):\n    \n    if kaggle:\n        model_name = './best_' + config['model_name'] + \".bin\"\n        torch.save(model, model_name)\n    else:\n        model_name = '../saved_models/best_' + config['model_name'] + \".bin\"\n        torch.save(model, model_name)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def train_fn(data_loader, model, optimizer, params):\n    \n    model.train()\n    setting_seed(seed_no = seed)\n    \n    train_loss  = 0\n    for index, dataset in tqdm(enumerate(data_loader), total = len(data_loader)):\n        image = dataset['image'].to(config['device'], dtype = torch.float)\n        target = dataset['label'].to(config['device'], dtype = torch.long)\n    \n        prediction = model(image)\n        \n        step_loss = criterion(prediction, target)\n        \n        step_loss.sum().backward()\n        optimizer.step()\n        #scheduler.step()\n        optimizer.zero_grad()\n        \n        #torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)\n        \n        train_loss += step_loss\n    \n    avg_train_loss = train_loss/len(data_loader)\n    \n    return avg_train_loss\n    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def eval_fn(data_loader, model):\n    \n    model.eval()\n    eval_loss = 0\n    \n    actual_output = torch.tensor([]).to(config['device'], dtype=torch.long)\n    predicted_prob = torch.tensor([]).to(config['device'], dtype=torch.float)\n    \n    with torch.no_grad():\n        for index, dataset in tqdm(enumerate(data_loader), total = len(data_loader)):\n            image = dataset['image'].to(config['device'], dtype = torch.float)\n            target = dataset['label'].to(config['device'], dtype = torch.long)\n            \n            prediction = model(image)\n            \n            step_loss = criterion(prediction, target)\n            eval_loss += step_loss\n            \n            actual_output = torch.cat((actual_output, target))\n            predicted_prob = torch.cat((predicted_prob, prediction))\n                        \n            #print(\"Prediction\", prediction.shape, prediction)\n            #print(\"predicted_output\", predicted_output.shape, predicted_output)\n            #print(\"###########################################\")\n            \n    actual_class = np.array(actual_output.detach().cpu())\n    predicted_class = np.argmax(np.array(predicted_prob.detach().cpu()), axis = 1)\n\n    conf_mat = confusion_matrix(actual_class, predicted_class)\n    print(conf_mat)\n    \n    avg_eval_loss = eval_loss/len(data_loader)        \n\n    return avg_eval_loss, actual_class, predicted_prob,  predicted_class\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def training_engine(EPOCHS, train_data, valid_data, patience):\n       \n    setting_seed(seed_no = seed)\n    model = ResNet50_classifier_for_cassava(num_labels=num_targets)\n    model = nn.DataParallel(model)\n    model.to(config['device'])\n    \n    optimizer_grouped_parameters = model.parameters() #params_2_tune(model)\n    optimizer = torch.optim.Adam(optimizer_grouped_parameters, lr = 0.0005)\n    \n    total_steps = len(train_data) * EPOCHS\n    \n    # Set up the learning rate scheduler\n    # scheduler = transformers.get_linear_schedule_with_warmup(optimizer,\n    #                                                         num_warmup_steps=0, # Default value\n    #                                                         num_training_steps=total_steps)\n\n    best_accuracy=0\n    counter=0\n    for epoch in range(EPOCHS):\n        # Training\n        avg_train_loss = train_fn(data_loader = train_data,\n                                  model = model,\n                                  optimizer = optimizer, \n                                  #scheduler = scheduler, \n                                  params = optimizer_grouped_parameters)\n\n        # Evaluation\n        avg_eval_loss, actual_class, predicted_prob, predicted_class = eval_fn(data_loader = valid_data,\n                                                                         model = model)\n        \n        acc = accuracy_score(actual_class, predicted_class)\n\n        print(f\"Epoch {epoch}/{EPOCHS} train_loss: {avg_train_loss}, eval_loss {avg_eval_loss}, Accuracy: {acc}\")\n\n        if (acc>best_accuracy):\n            best_accuracy = acc\n            counter = 0\n            \n            print('Saving the model')\n            model_saving(model)\n        \n        else:\n            counter+=1\n            print(f\"Accuracy did not improve from the best {best_accuracy}, patience is {counter}/{patience}\")\n            \n            if counter==patience:\n                print(f\"Maximum patience level {patience} reached so exiting the training\")\n                break            \n        \n    return model, actual_class, predicted_prob, predicted_class\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def prediction_fn(data_loader, model):\n    \n    model.eval()\n    \n    predicted_output = torch.tensor([]).to(config['device'])\n    \n    with torch.no_grad():\n        for index, dataset in tqdm(enumerate(data_loader), total = len(data_loader)):\n            image = dataset['image'].to(config['device'], dtype = torch.float)\n            \n            prediction = model(image)\n            \n            predicted_output = torch.cat((predicted_output, prediction))\n            predicted_class = np.argmax(np.array(predicted_output.detach().cpu()), axis = 1)\n                    \n        return predicted_class\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# # Training on the Local dataset\n# seed = 50\n# model, actual, predicted_prob, predicted_class = training_engine(EPOCHS=50, \n#                                                                  train_data=train_local_images_dataloader, \n#                                                                  valid_data=test_local_images_dataloader, \n#                                                                  patience=5)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Training on the entire dataset\n\nseed = 50\nmodel, actual, predicted_prob, predicted_class = training_engine(EPOCHS=10, \n                                                                 train_data=train_prod_images_dataloader, \n                                                                 valid_data=test_local_images_dataloader, \n                                                                 patience=5)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Loading the best model\n\nif kaggle:\n    model_name = './best_' + config['model_name'] + \".bin\"\n    model = torch.load(model_name)\nelse:\n    model_name = '../saved_models/best_' + config['model_name'] + \".bin\"\n    model = torch.load(model_name)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Making the prediction on the test data and Creating the submission file\n\nif kaggle:\n    predicted_output = prediction_fn(data_loader=test_prod_images_dataloader, \n                                     model=model)\n    \n    submission = test_df.copy()\n    submission['label'] = predicted_output\n    submission.to_csv('./submission.csv', index=False)","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}