{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df = pd.read_csv(\"../input/ranzcr-clip-catheter-line-classification/train.csv\")\n\nLABELS = [\n    'ETT - Abnormal', 'ETT - Borderline', 'ETT - Normal',\n    'NGT - Abnormal', 'NGT - Borderline', 'NGT - Incompletely Imaged', 'NGT - Normal', \n    'CVC - Abnormal', 'CVC - Borderline', 'CVC - Normal',\n    'Swan Ganz Catheter Present'\n]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df = pd.read_csv(\"../input/ranzcr-clip-catheter-line-classification/train.csv\")\ndf.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"DEBUG = True\nif DEBUG:\n    df = df.sample(frac = 0.01).reset_index(drop = True)\n    print(df.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.model_selection import train_test_split\ntrain, valid =train_test_split(df ,test_size =0.1)\ntrain.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(train.shape,valid.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"path= train.iloc[0 ,0]\npath","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"path = \"../input/ranzcr-clip-catheter-line-classification/train\" + \"/\" + path + \".jpg\"\npath","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import cv2 \nimage= cv2.imread(path)\nimage.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import matplotlib.pyplot as plt\nplt.imshow(image)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image = cv2.cvtColor(image,cv2.COLOR_BGR2RGB)\nplt.imshow(image)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"path = train.iloc[1, 0]\npath = \"../input/ranzcr-clip-catheter-line-classification/train\" + \"/\" + path + \".jpg\"\nimage2 = cv2.imread(path)\nimage2.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.imshow(image2)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from albumentations import Resize\ndummy = Resize(width = 300, height = 300)(image = image)\ndummy","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image = Resize(width = 300, height = 300)(image = image)[\"image\"]\nimage.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from albumentations.pytorch import ToTensorV2\nimage = ToTensorV2()(image = image)[\"image\"]\nimage.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\n\nfrom torch.utils.data import Dataset\n\nclass TrainDataset(Dataset):\n    def __init__(self, df):\n        self.df = df\n        self.studyuid = df[\"StudyInstanceUID\"].values\n        self.labels = df[LABELS].values\n        \n    def __len__(self):\n        return self.df.shape[0]\n    \n    def __getitem__(self, idx):\n        path = self.studyuid[idx]\n        path = \"../input/ranzcr-clip-catheter-line-classification/train\" + \"/\" + path + \".jpg\"\n        image = cv2.imread(path)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n        image = Resize(300, 300)(image = image)[\"image\"]\n        image = ToTensorV2()(image = image)[\"image\"]\n        labels = self.labels[idx]\n        return image, labels\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"\nThe dataset is created in the class.\n\ninit: Initialization condition. The argument is a data frame such as train. Since self is essential, let's write it for the time being.\n\nlen: Required to define the data size. It is basically the number of rows of data passed at initialization.\n\ngetitem: Required when retrieving data. index is the argument.\n\nWhen retrieving data, the index becomes an argument, so for example, when 0 is entered, the first path of studyuid will be the target.\n\nAfter that, the same process as before is executed and the image data is output as image and the corresponding label (correct answer) is output as labels.****"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_dataset = TrainDataset(train)\ntrain_dataset[0]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"\nWhen you create a dataset, you pass in a pandas dataframe. This is the init argument.\n\nLet's actually pass 0 and see the first data.\n\nImage data is output first, and label data is output next."},{"metadata":{"trusted":true},"cell_type":"code","source":"image, label = train_dataset[0]\nplt.imshow(image.permute(1, 2, 0))\nplt.show()\nprint(label)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"\nIn this way, we have created a system that retrieves images and labels using only indexes."},{"metadata":{},"cell_type":"markdown","source":"4. DataLoader\n"},{"metadata":{},"cell_type":"markdown","source":"Put the created dataset in the data loader."},{"metadata":{"trusted":true},"cell_type":"code","source":"from torch.utils.data import DataLoader","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Import the data loader"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_loader = DataLoader(train_dataset, batch_size = 8, shuffle = True)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"\nbatch_size: How many sheets to take out at one time. The more it is, the faster it learns, but it uses memory. The smaller it is, the more memory is suppressed, but it takes longer to learn, and it is greatly affected by the characteristics of one sheet.\n\nshuffle: Take out in random order.\n\nThere are other things such as drop_last, so please check them if you want to learn in earnest."},{"metadata":{"trusted":true},"cell_type":"code","source":"for batch in train_loader:\n    print(batch[0].shape)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"All data can be retrieved with the for statement.\n\nSince batch_size is set to 8, 8 sheets of data are output at once."},{"metadata":{"trusted":true},"cell_type":"code","source":"valid_dataset = TrainDataset(valid)\nvalid_loader = DataLoader(valid_dataset, batch_size = 16, shuffle = False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"5. Modeling"},{"metadata":{},"cell_type":"markdown","source":"The model uses EfficientNet."},{"metadata":{"trusted":true},"cell_type":"code","source":"import sys\nsys.path.append('../input/pytorch-image-models/pytorch-image-models-master')\nimport timm\nfrom pprint import pprint\npprint(timm.list_models(pretrained = True))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"There are several ways to use EfficientNet.\n\nThis time I used a set of image classification models called timm. Since it is uploaded to the Dataset, let's put it in the input from \"+ Add data\".\n\nThere is also a way to install it with pip install, but in this competition you can not use it because you can not connect to the net at the time of submission."},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch.nn as nn\n\nclass Effnet(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.effnet = timm.create_model(model_name = \"tf_efficientnet_b0\", pretrained = False)\n        n_features = self.effnet.classifier.in_features\n        self.effnet.classifier = nn.Linear(n_features, len(LABELS))\n    \n    def forward(self, x):\n        x = self.effnet(x)\n        return x\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Create a class using Module in torch.nn.\n\nSince super and init are fixed phrases, let's write them without worrying about them.\n\nCreate EfficientNet with timing.create_model. Select the model name to specify from the list output earlier.\n\nEfficientNet has B0 to B7, and this time it is B0.\n\nIf pretrained = True, it will be a trained model, but it cannot be used with net OFF because parameters need to be downloaded from the net.\n\nI want to change the final output format, so replace the .classifier part with Linear (fully connected layer).\n\nSince the input size at this time is required, let's get it as n_features. The output size is the number of LABELS you want to predict.\n\nforward is a function for actually learning (predicting). Returns the result of passing through EfficientNet with the input as x."},{"metadata":{"trusted":true},"cell_type":"code","source":"model = Effnet()\nmodel","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch\nDEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Specify whether to use CPU or GPU for calculation.\n\nYou can turn on the GPU from the \"setting\" on the far right. (Currently 43 hours a week free)\n\nIf it is ON, torch.cuda.is_available will be True, so DEVICE will be cuda (GPU type). If False, it remains the CPU."},{"metadata":{"trusted":true},"cell_type":"code","source":"model = model.to(DEVICE)\nprint(DEVICE)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"6. Learning\nThe steps to learn are as follows.\n\n・ Determine the loss function\n\n・ Determine the optimizer\n\n・ Train with train_loader\n\n-Check the performance with valid_loader"},{"metadata":{"trusted":true},"cell_type":"code","source":"criterion = nn.BCEWithLogitsLoss()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"It is a loss function. After scaling the output result to the range of 0 to 1 (sigmoid function), the error from the prediction is calculated."},{"metadata":{"trusted":true},"cell_type":"code","source":"optimizer = torch.optim.Adam(model.parameters())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"It is an optimization method. There are various things, but I chose Adam, which is a major one.\n\nLet's pass the parameters of the model we made earlier"},{"metadata":{"trusted":true},"cell_type":"code","source":"model.train()\nfor X, y in train_loader:\n    optimizer.zero_grad()\n    X = X.float().to(DEVICE)\n    y = y.float().to(DEVICE)\n    pred = model(X)\n    loss = criterion(pred, y)\n    loss.backward()\n    optimizer.step()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"First, put it in learning mode with .train. I'm not sure what I'm doing.\n\nReset the optimizer once with .zero_grad before predicting.\n\nThe for statement pulls data from train_loader. I took it out as X and y.\n\nWhen learning pytorch, it is necessary to make it a float type, so let's convert it with .float.\n\nIn addition, it is necessary to set CPU or GPU with to (DEVICE) even for the data to be included in the model. This is also easy to forget.\n\nIf you put X in model, it will be output as a prediction label through EfficientNet, so let's pass it to the loss function."},{"metadata":{"trusted":true},"cell_type":"code","source":"model.eval()\nvalid_loss = 0\nwith torch.no_grad():\n    for X, y in valid_loader:\n        X = X.float().to(DEVICE)\n        y = y.float().to(DEVICE)\n        pred = model(X)\n        loss = criterion(pred, y)\n        valid_loss += loss.item()\nvalid_loss /= len(valid_loader)\nprint(\"Loss:\", valid_loss)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Check the performance with the evaluation data.\n\nFirst change to evaluation mode with .eval. I don't know what this is doing either. .. ..\n\nAt the time of evaluation, I do not want to change the parameters of the model, so lock it with torch.no_grad.\n\nLet's take out Xy and make it a float type and predict it in the same way as when learning.\n\nNext we calculate the loss function, but this time we don't need to backward the error to the model.\n\nLet's average the error in all batches. This is the performance in the first learning."},{"metadata":{"trusted":true},"cell_type":"code","source":"model = Effnet().to(DEVICE)\n\ncriterion = nn.BCEWithLogitsLoss()\noptimizer = torch.optim.Adam(model.parameters())\n\nbest_loss = np.inf\nfor epoch in range(10):\n    model.train()\n    for X, y in train_loader:\n        optimizer.zero_grad()\n        X = X.float().to(DEVICE)\n        y = y.float().to(DEVICE)\n        pred = model(X)\n        loss = criterion(pred, y)\n        loss.backward()\n        optimizer.step()\n    model.eval()\n    valid_loss = 0\n    with torch.no_grad():\n        for X, y in valid_loader:\n            X = X.float().to(DEVICE)\n            y = y.float().to(DEVICE)\n            pred = model(X)\n            loss = criterion(pred, y)\n            valid_loss += loss.item()\n    valid_loss /= len(valid_loader)\n    print(f\"EPOCH:{epoch}, Loss:{valid_loss}\")\n    if valid_loss < best_loss:\n        best_loss = valid_loss\n        torch.save(model.state_dict(), \"effnet.pth\")\n        print(\"saved...\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"I want to recreate the model once, so I summarized what I have done so far.\n\nDefine the minimum error as best_loss. The start is endless.\n\nIf the error in the evaluation data is smaller than the minimum error so far, update it. Then save the model.\n\nBy doing this, the model with the smallest error will be overwritten by the end of all training.\n\nThis is the end of learning. What I introduced this time is at least what is necessary to build a model"},{"metadata":{},"cell_type":"markdown","source":"7. Forecast"},{"metadata":{"trusted":true},"cell_type":"code","source":"class TestDataset(Dataset):\n    def __init__(self, df):\n        self.df = df\n        self.studyuid = df[\"StudyInstanceUID\"].values\n        \n    def __len__(self):\n        return self.df.shape[0]\n\n    def __getitem__(self, idx):\n        path = self.studyuid[idx]\n        path = \"../input/ranzcr-clip-catheter-line-classification/test\" + \"/\" + path + \".jpg\"\n        image = cv2.imread(path)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n        image = Resize(300, 300)(image = image)[\"image\"]\n        image = ToTensorV2()(image = image)[\"image\"]\n        return image","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Create a dataset for test. Almost the same as for learning.\n\nPlease note that the path is the path of test.\n\nAlso, since it does not have a correct label, the output is only image."},{"metadata":{"trusted":true},"cell_type":"code","source":"test = pd.read_csv(\"../input/ranzcr-clip-catheter-line-classification/sample_submission.csv\")\ntest_dataset = TestDataset(test)\ntest_loader = DataLoader(test_dataset, batch_size = 32, shuffle = False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"You have defined a dataset and a data loader.\n\nMainly doing the same as valid_loader"},{"metadata":{"trusted":true},"cell_type":"code","source":"model = Effnet().to(DEVICE)\nmodel.load_state_dict(torch.load(\"./effnet.pth\"))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submit_preds = []\n\nmodel.eval()\nwith torch.no_grad():\n    for X in test_loader:\n        X = X.float().to(DEVICE)\n        submit_preds.append(model(X).sigmoid().to(\"cpu\"))\n    submit_preds = np.concatenate([p.numpy() for p in submit_preds], axis = 0)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Load the data from test_loader and pass it to the model.\n\nLet's scale the output value from 0 to 1 with .sigmoid.\n\nIf you do not make the data correspond to cpu, an error will occur later, so add to (\"cpu\").\n\nPut the prediction result of each batch in the list (submit_preds), and finally join it in the row direction (axis = 0) with .concatenate of numpy.\n\nYou now have a forecast for submission."},{"metadata":{"trusted":true},"cell_type":"code","source":"submit = pd.DataFrame(submit_preds, columns = LABELS)\nsubmit.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submit[\"StudyInstanceUID\"] = test[\"StudyInstanceUID\"]\nsubmit = pd.concat([submit.iloc[:, -1], submit.iloc[:, :-1]], axis = 1)\nsubmit.to_csv(\"submission.csv\", index = False)","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}