{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Starter for beginners-Pytorch\n### This notebook is written by a beginner for beginners. Classes implemented in this notebook:\n#### * RetrievalData ---> Class for loading dataset.\n#### * CheckPoint    ---> Class for EarlyStopping and saving models/results.\n#### * Hashtag       ---> Class for training, testing.\n\n#### * Train-Test split using sklearn train_test_split.\n#### * Computes Loss, Accuracy and ROC-AUC for both training and testing.\n\n\n\n#### Suggestion/feedback would be highly appreciated. Thanks\n\n# Haasha Bin Atif"},{"metadata":{"trusted":true},"cell_type":"code","source":"#torch & torchvision\nimport torch\nimport torch.nn as nn\nimport torchvision\nimport torchvision.transforms as transforms\nfrom torch.utils.data import Dataset,DataLoader\n\n#sklearn for splitting dataset and scoring functions\nfrom sklearn.metrics import accuracy_score,roc_auc_score\nfrom sklearn.model_selection import train_test_split\n\n\n#Other\nimport matplotlib.pyplot as plt\nfrom time import perf_counter\nfrom scipy import stats\nfrom PIL import Image\nimport pandas as pd\nimport numpy as np\nimport random\nimport pickle\nimport json\nimport os","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"#HyperParameters and Paths for Train & Test Dataset.\nBATCH_SIZE = 64\nNUM_WORKERS = 4\nGPU = torch.cuda.is_available()\nDEVICE = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\nTRAIN_DIR = '../input/cassava-leaf-disease-classification/train_images/'\nTEST_DIR = '../input/cassava-leaf-disease-classification/test_images/'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#helper functions\ndef imshow(img):\n    #img = img / 2 + 0.5     # unnormalize\n    npimg = img.numpy()\n    plt.imshow(np.transpose(npimg, (1, 2, 0)))\n    plt.show()\n\ndef display_images(images, title=None): \n    f, ax = plt.subplots(5,5, figsize=(18,22))\n    if title:\n        f.suptitle(title, fontsize = 30)\n\n    for i, (image_id,label) in enumerate(images):\n        image_path = os.path.join(TRAIN_DIR,image_id)\n        image = Image.open(image_path)\n        \n        ax[i//5, i%5].imshow(image) \n        image.close()       \n        ax[i//5, i%5].axis('off')\n        ax[i//5, i%5].set_title(labels[str(label)], fontsize=\"10\")\n        \n    plt.show()\ndef getModel(Name, OutFeatures):\n    if Name == \"AlexNet\":\n        model = torchvision.models.alexnet()\n        model.classifier[6] = nn.Linear(4096,OutFeatures,bias=True)\n    elif Name==\"VGG16\":\n        model = torchvision.models.vgg16()\n        model.classifier[6] = nn.Linear(4096,OutFeatures,bias=True)\n    elif Name==\"resnet152\":\n        model = torchvision.models.resnet152()\n        model.fc = nn.Linear(2048,OutFeatures,bias=True)\n    return model\n\ndef display(Type,epochNum,totalEpochs,Results):\n    my_formatter = \"{0:.6f}\"\n    print( ''.join([Type,\" Epoch#\",str(epochNum+1).zfill(3),'/',str(totalEpochs).zfill(3)]) ,end='' )\n    for key in Results.keys():\n        print(''.join([\" \",key,my_formatter.format(Results[key])]),end='')\n    print()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#reading train.csv and submission.csv and loading labels\ntrain = pd.read_csv('../input/cassava-leaf-disease-classification/train.csv')\nsubmission = pd.read_csv('../input/cassava-leaf-disease-classification/sample_submission.csv')\nprint(\"Train File:\\n\",train.head())\nprint(\"\\nSubmission File:\\n\",submission.head())\nlabels = json.load(open(\"../input/cassava-leaf-disease-classification/label_num_to_disease_map.json\"))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"Images_Per_Class = np.unique(train['label'].tolist(),return_counts=True)\nprint(\"Description of Dataset\")\nprint(\"Number of Classes:\",len(train.label.unique()))\nprint(\"#OfImages Per Cassava Disease Class\")\nprint(\"     Min:\",min(Images_Per_Class[1]))\nprint(\"     Max:\",max(Images_Per_Class[1]))\nprint(\"     Mean:\",np.mean(Images_Per_Class[1]))\nprint(\"     Median:\",np.median(Images_Per_Class[1]))\nprint(\"     Mode:\",stats.mode(Images_Per_Class[1])[0][0])\n\nprint(\"\\n\")\nprint(train.head())\n\nprint(\"\\nCLASS ---->       LABEL\")\nfor i in labels:\n    print(i,'    ---->',labels[i])\n\nfig = plt.figure(figsize = (10, 5))\nplt.bar(labels.keys(), Images_Per_Class[1], color ='blue', width = 0.8) \nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"Displaying some Images randomly...\")\nsamples = train.sample(25)\ndisplay_images( zip(samples.image_id.values,samples.label.values))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class RetrievalData(Dataset):\n    def __init__(self, Directory, FileNames, CorrectLabels,Transform, labels):\n        self.directory = Directory\n        self.filenames = FileNames\n        self.transform = Transform\n        self.correctlabels = CorrectLabels\n        self.labels = labels\n    def __len__(self):\n        return len(self.filenames)\n\n    def __getitem__(self,index):\n        x = Image.open(os.path.join(self.directory,self.filenames[index]))\n        if \"train\" in self.directory:\n            if self.transform is not None:\n                return self.transform(x),self.correctlabels[index]\n            return x,self.correctlabels[index]\n        elif \"test\" in self.directory:\n            if self.transform is not None:\n                return self.transform(x),self.filenames[index]\n            return x,self.filenames[index]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class CheckPoint():\n    def __init__(self,Parameters):\n        self.Count = 0\n        self.BestLoss = float('inf')\n        self.BestEpoch = -1\n        \n        self.Patience = Parameters[\"Patience\"]\n        self.Path = Parameters[\"SavePath\"]\n        self.earlyStopping=Parameters[\"earlyStopping\"]\n\n    def check(self,epoch,loss):\n        torch.save({\"Model\":self.Model.state_dict(),\"Optimizer\":self.Optimizer.state_dict()},self.Path+\"/Model.pth\")\n        if loss>self.BestLoss:\n            self.Count+=1\n        else:\n            self.Count=0\n            self.BestLoss = loss\n            self.BestEpoch = epoch\n            torch.save({\"Model\":self.Model.state_dict(),\"Optimizer\":self.Optimizer.state_dict()},self.Path+\"/BestModel.pth\")\n        with open(self.Path+\"/Results.txt\", 'wb') as file:\n            pickle.dump(self.Results,file)\n        if self.earlyStopping:\n            if self.Count==self.Patience:\n                print(\"\\nEarly Stopping!\")\n                print(\"Model didn't improved for\",self.Patience,\"epochs.\")\n                return False\n            return True\n        else:\n            return True","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class Hashtag(CheckPoint):\n    def __init__(self, Parameters):\n        CheckPoint.__init__(self,Parameters[\"CheckPoint\"])\n        self.Model = Parameters[\"Model\"]\n        self.Criterion = Parameters[\"Criterion\"]\n        self.Optimizer = Parameters[\"Optimizer\"]\n        self.TrainLoader = Parameters[\"TrainLoader\"]\n        self.ValidateLoader = Parameters[\"ValidateLoader\"]\n        self.Labels = Parameters[\"Labels\"]\n        self.Device = Parameters[\"Device\"]\n        self.Results = Parameters[\"Results\"]\n        self.Softmax = nn.Softmax(dim=1)\n        self.ontoDevice()\n\n        \n    def ontoDevice(self):\n        self.Model.to(DEVICE)\n        self.Criterion.to(DEVICE)\n\n    def train(self):\n        self.Model.train()\n        running_loss = 0.0\n        Predictions = []\n        TrueLabels = []\n        PredictionProb = []\n        for batch,data in enumerate(self.TrainLoader):\n            images,labels = data\n            TrueLabels.extend(labels)\n            images = images.to(DEVICE)\n            labels = labels.to(DEVICE)\n            self.Optimizer.zero_grad()\n            pred = self.Model(images)\n            loss = self.Criterion(pred,labels)\n            running_loss+=loss.item()\n            loss.backward()\n            self.Optimizer.step()\n            TempX = self.Softmax(pred).detach().cpu()\n            PredictionProb.extend(TempX.numpy().tolist())\n            Predictions.extend(torch.argmax(TempX,dim=1).numpy().tolist())\n        trainResults = {\n                        \"Loss:\":running_loss/len(self.TrainLoader),\n                        \"Accuracy:\":accuracy_score(TrueLabels,Predictions),\n                        \"ROC-AUC:\":roc_auc_score(TrueLabels,PredictionProb,multi_class=\"ovr\")\n                        }\n        self.Results[-1].append(trainResults)\n\n    def validate(self):\n        self.Model.eval()\n        running_loss = 0.0\n        Predictions = []\n        TrueLabels = []\n        PredictionProb = []\n        with torch.no_grad():\n            for batch,data in enumerate(self.ValidateLoader):\n                images,labels = data\n                TrueLabels.extend(labels)\n                images = images.to(DEVICE)\n                labels = labels.to(DEVICE)\n                pred = self.Model(images)\n                loss = self.Criterion(pred,labels)\n                running_loss+=loss.item()\n                TempX = self.Softmax(pred).detach().cpu()\n                PredictionProb.extend(TempX.numpy().tolist())\n                Predictions.extend(torch.argmax(TempX,dim=1).numpy().tolist())\n                \n        validateResults = {\n                        \"Loss:\":running_loss/len(self.ValidateLoader),\n                        \"Accuracy:\":accuracy_score(TrueLabels,Predictions),\n                        \"ROC-AUC:\":roc_auc_score(TrueLabels,PredictionProb,multi_class=\"ovr\")\n                        }\n\n        self.Results[-1].append(validateResults)\n\n    def fit(self,epochs):\n        for epoch in range(epochs):\n            self.Results.append([])\n            self.train()\n            display(\"Train\",epoch,epochs,self.Results[-1][-1])\n            self.validate()\n            display(\"Test \",epoch,epochs,self.Results[-1][-1])\n\n            self.check(epoch,self.Results[-1][-1][\"Loss:\"])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"if __name__ == '__main__':\n    \n    Start = perf_counter()\n    print(\"Started...\")\n    Transform = transformations = transforms.Compose([\n                                        transforms.Resize((256, 256)),\n                                        transforms.ToTensor(),\n                                     ])\n\n    if not os.path.exists(\"./Results\"):\n        os.mkdir(\"./Results\")\n    \n    X,Y = train.image_id.values,train.label.values\n    X_Train,X_Test,Y_Train,Y_Test = train_test_split(X,Y,test_size=0.2,shuffle=True)\n    TrainSet = RetrievalData(TRAIN_DIR, X_Train,Y_Train, Transform, Images_Per_Class)\n    TrainLoader=DataLoader(TrainSet, batch_size=BATCH_SIZE, shuffle=True, num_workers=NUM_WORKERS,pin_memory=True)\n    \n    ValidationSet = RetrievalData(TRAIN_DIR, X_Test,Y_Test, Transform, Images_Per_Class)\n    ValidationLoader=DataLoader(ValidationSet, batch_size=BATCH_SIZE, shuffle=False, num_workers=NUM_WORKERS,pin_memory=True)\n    print(\"Minibatches in TrainLoader:\",len(TrainLoader))\n    print(\"Minibatches in ValidationLoader:\",len(ValidationLoader))\n    #Code for Training, Validation, Inference + Submission\n    model = getModel(\"resnet152\",len(labels))\n    criterion = nn.CrossEntropyLoss()\n    optimizer = torch.optim.Adam(model.parameters(), lr = 0.000001)\n    Params = {\n        \"Model\":model,\n        \"Criterion\":criterion,\n        \"Optimizer\":optimizer,\n        \"TrainLoader\":TrainLoader,\n        \"ValidateLoader\":ValidationLoader,\n        \"Labels\":None,\n        \"Device\":DEVICE,\n        \"Results\":[],\n        \"CheckPoint\":{\n                    \"Patience\":2,\n                    \"SavePath\":\"./Results\",\n                    \"earlyStopping\":False\n                    }\n             }\n    hashtag = Hashtag(Params)\n    hashtag.fit(50)\n    Finish = perf_counter()\n    print(\"Ended.\")\n    print(\"Time Taken:\",Finish-Start, \"seconds\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","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}