{"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":"# 1. Data Exploration\n- Let's take a look at the data.\n- 일단 데이터를 살펴봅시다.\n\n## 1.1 CSV","metadata":{}},{"cell_type":"code","source":"import pandas as pd\ninput_path = \"/kaggle/input/histopathologic-cancer-detection\"\ntrain_data = pd.read_csv(f\"{input_path}/train_labels.csv\")\ntrain_data.head()\n\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"- It consists of an image name (id) and a classification value (label).\n- 이미지 이름(id)와 분류값(label)로 구성되어있네요.","metadata":{}},{"cell_type":"markdown","source":"## 1.2 Img\n- The image has a tif extension and cannot be loaded through OpenCV. You need to utilize the PIL library.\n- 이미지가 tif 확장자로 되어있어 OpenCV를 통해서 불러올 수 없습니다. PIL 라이브러리를 활용해야합니다.","metadata":{}},{"cell_type":"code","source":"from PIL import Image\nfrom matplotlib import pyplot as plt\nimport numpy as np\nid = train_data.loc[0][\"id\"]\nim = np.array(Image.open(f'{input_path}/train/{id}.tif'))\nplt.title(f\"{id} \\n {im.shape}\")\nplt.imshow(im)\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 2. Data Loader\n- Let's configure the dataset and data loader.\n- 데이터셋과 데이터로더를 구성해봅시다.\n- Ref : https://tutorials.pytorch.kr/beginner/data_loading_tutorial.html","metadata":{}},{"cell_type":"markdown","source":"## 2.1 Dataset","metadata":{}},{"cell_type":"code","source":"import torch\nclass Dataset(torch.utils.data.Dataset):\n    def __init__(self, transform=None):\n        self.input_path = \"/kaggle/input/histopathologic-cancer-detection\"\n        self.dataset = pd.read_csv(f\"{input_path}/train_labels.csv\")\n        self.img_path = f\"{self.input_path}/train/\"\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.dataset)\n\n    def __getitem__(self, idx):\n        if torch.is_tensor(idx):\n            idx = idx.tolist()\n        image = Image.open(f'{self.input_path}/train/{self.dataset.iloc[idx][\"id\"]}.tif')\n        label = self.dataset.iloc[idx][\"label\"]\n        \n        if self.transform:\n            image = self.transform(image)\n        #label = np.zeros([1 if label == i else 0 for i in range(2)])\n        return image, label","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2.2 Dataloader & Transform\n- 데이터 증강을 위한 트랜스폼을 구성합니다.\n- Construct a transform for data augmentation.","metadata":{}},{"cell_type":"code","source":"import torchvision\ndata_transform = torchvision.transforms.Compose([\n    torchvision.transforms.RandomRotation(30),\n    torchvision.transforms.Resize((256, 256)), \n    torchvision.transforms.RandomResizedCrop(224),\n    torchvision.transforms.RandomHorizontalFlip(),\n    torchvision.transforms.RandomVerticalFlip(),\n    torchvision.transforms.ToTensor(),\n    torchvision.transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]),\n])\n\nbatch_size = 128\ndataset = Dataset(transform=data_transform)\ntrainset, validset = torch.utils.data.random_split(dataset, [int(len(dataset)*0.7), len(dataset) - int(len(dataset)*0.7)], generator=torch.Generator().manual_seed(42))\ndataloaders = {\n    \"train\" : torch.utils.data.DataLoader(trainset, batch_size=batch_size, shuffle=True, num_workers=4),\n    \"valid\" : torch.utils.data.DataLoader(validset, batch_size=batch_size, shuffle=True, num_workers=4),\n}\ndataset_sizes = {\n    \"train\" : int(len(dataset)*0.7),\n    \"valid\" : len(dataset) - int(len(dataset)*0.7),\n}\ngenerator = iter(dataloaders[\"train\"])\nimage, label = next(generator)\nfor i in range(3):\n    plt.title(label[i])\n    plt.imshow(image[i].permute(1, 2, 0))\n    plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 3. Modeling\n- Let's do transfer learning using ResNet.\n- ResNet을 이용해서 전이학습 시켜봅시다.\nhttps://tutorials.pytorch.kr/beginner/transfer_learning_tutorial.html","metadata":{}},{"cell_type":"markdown","source":"## 3.1 Import Model\n- Load the ResNet to be used as the backbone model, and adjust the Output Layer to 2 (number of classes in our problem).\n- 백본 모델로 사용할 ResNet을 불러오고, Output Layer를 2개(우리 문제의 클래스 수)로 조정합니다. ","metadata":{}},{"cell_type":"code","source":"backbone = torchvision.models.resnet50(pretrained=True)\nnum = backbone.fc.in_features\nbackbone.fc = torch.nn.Linear(num, 2)\n\ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\nmodel = backbone.to(device)\nprint(device)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 3.2 Set Functions\n- SGD and Adam are mainly used as optimizers. But here we will use Adamp (ICLR 2021).\n- Optimizer로는 SGD, Adam을 주로 사용합니다. 하지만 여기서는 Adamp(ICLR 2021)를 사용해보겠습니다.\n- Ref : https://github.com/clovaai/AdamP","metadata":{}},{"cell_type":"code","source":"!pip3 install adamp\nimport adamp","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"criterion = torch.nn.CrossEntropyLoss()\n#optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)\n#optimizer = torch.optim.Adam(model.parameters(), lr=0.001)\noptimizer = adamp.AdamP(model.parameters(), lr=0.001, betas=(0.9, 0.999), weight_decay=1e-2)\nscheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 3.3 Training","metadata":{}},{"cell_type":"code","source":"import time\nimport copy\ndef train_model(model, criterion, optimizer, scheduler, num_epochs=25):\n    since = time.time()\n\n    best_model_wts = copy.deepcopy(model.state_dict())\n    best_acc = 0.0\n\n    for epoch in range(num_epochs):\n        print('Epoch {}/{}'.format(epoch, num_epochs - 1))\n\n        for phase in ['train', 'valid']:\n            if phase == 'train':\n                model.train() \n            else:\n                model.eval() \n\n            running_loss = 0.0\n            running_corrects = 0\n\n            for inputs, labels in dataloaders[phase]:\n                inputs = inputs.to(device)\n                labels = labels.to(device)\n\n                optimizer.zero_grad()\n\n                with torch.set_grad_enabled(phase == 'train'):\n                    outputs = model(inputs)\n                    _, preds = torch.max(outputs, 1)\n                    loss = criterion(outputs, labels)\n\n                    if phase == 'train':\n                        loss.backward()\n                        optimizer.step()\n\n                running_loss += loss.item() * inputs.size(0)\n                running_corrects += torch.sum(preds == labels.data)\n            if phase == 'train':\n                scheduler.step()\n\n            epoch_loss = running_loss / dataset_sizes[phase]\n            epoch_acc = running_corrects.double() / dataset_sizes[phase]\n\n            print('{} Loss: {:.4f} Acc: {:.4f}'.format(\n                phase, epoch_loss, epoch_acc))\n\n            if phase == 'valid' and epoch_acc > best_acc:\n                best_acc = epoch_acc\n                best_model_wts = copy.deepcopy(model.state_dict())\n\n        print()\n\n    time_elapsed = time.time() - since\n    print('Training complete in {:.0f}m {:.0f}s'.format(\n        time_elapsed // 60, time_elapsed % 60))\n    print('Best val Acc: {:4f}'.format(best_acc))\n\n    model.load_state_dict(best_model_wts)\n    return model","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = train_model(model, criterion, optimizer, scheduler, num_epochs=20)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 4. Test\n","metadata":{}},{"cell_type":"code","source":"input_path = \"/kaggle/input/histopathologic-cancer-detection\"\ntest_data = pd.read_csv(f\"{input_path}/sample_submission.csv\")\nprint(len(test_data))\ntest_data.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Testset(torch.utils.data.Dataset):\n    def __init__(self, transform):\n        self.input_path = \"/kaggle/input/histopathologic-cancer-detection\"\n        self.dataset = pd.read_csv(f\"{input_path}/sample_submission.csv\")\n        self.img_path = f\"{self.input_path}/test/\"\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.dataset)\n\n    def __getitem__(self, idx):\n        if torch.is_tensor(idx):\n            idx = idx.tolist()\n        image = Image.open(f'{self.input_path}/test/{self.dataset.iloc[idx][\"id\"]}.tif')\n        label = self.dataset.iloc[idx][\"label\"]\n        \n        if self.transform:\n            image = self.transform(image)\n       \n        return image","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_transform = torchvision.transforms.Compose([\n    torchvision.transforms.Resize((224, 224)), \n    torchvision.transforms.ToTensor(),\n    torchvision.transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]),\n])\ntestset = Testset(transform= data_transform)\ntest_loader = torch.utils.data.DataLoader(testset, batch_size=1, shuffle=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nfor i, image in enumerate(test_loader):\n    if i % 10000 == 0:\n        print(i, \"/\", len(test_loader))\n    outputs = model(image.to(device))\n    test_data.loc[i, 'label'] = int(torch.argmax(outputs))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_data.to_csv('submission.csv', index=False)\ntest_data","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}