{"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":"code","source":"TEMP_DIR = '/kaggle/working/dogs-vs-cats'","metadata":{"execution":{"iopub.status.busy":"2022-03-12T15:09:43.421161Z","iopub.execute_input":"2022-03-12T15:09:43.422342Z","iopub.status.idle":"2022-03-12T15:09:43.428114Z","shell.execute_reply.started":"2022-03-12T15:09:43.422295Z","shell.execute_reply":"2022-03-12T15:09:43.426995Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Dependencies\nimport PIL\nimport numpy as np\nimport pandas as pd\nimport random\nimport torch\nimport torchvision\nimport math\n\nimport matplotlib.pyplot as plt\n\nimport os\nimport zipfile\nfrom copy import deepcopy\n\n# progress bar\nfrom tqdm import tqdm\nfrom tqdm.notebook import tqdm as tqdm_nb\n\nfrom sklearn.model_selection import KFold, StratifiedKFold","metadata":{"execution":{"iopub.status.busy":"2022-03-12T15:09:43.430553Z","iopub.execute_input":"2022-03-12T15:09:43.431390Z","iopub.status.idle":"2022-03-12T15:09:43.442361Z","shell.execute_reply.started":"2022-03-12T15:09:43.431349Z","shell.execute_reply":"2022-03-12T15:09:43.441462Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"데이터셋의 이미지를 다루기 위한 간단한 함수들","metadata":{}},{"cell_type":"code","source":"from torchvision import transforms\n\nmean = torch.tensor([0.485, 0.456, 0.406], dtype=torch.float32)\nstd = torch.tensor([0.229, 0.224, 0.225], dtype=torch.float32)\nnormalize = transforms.Normalize(mean.tolist(), std.tolist())\nunnormalize = transforms.Normalize((-mean / std).tolist(), (1.0 / std).tolist())\n\ndef get_image(path):\n    return PIL.Image.open(path)\n\ndef image_to_tensor(image):\n    tf = transforms.Compose([\n        transforms.Resize((256,256)),\n        transforms.ToTensor(),\n        normalize\n    ])\n    return tf(image)","metadata":{"execution":{"iopub.status.busy":"2022-03-12T15:09:43.444585Z","iopub.execute_input":"2022-03-12T15:09:43.445362Z","iopub.status.idle":"2022-03-12T15:09:43.456244Z","shell.execute_reply.started":"2022-03-12T15:09:43.445318Z","shell.execute_reply":"2022-03-12T15:09:43.455295Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"input에 있는 이미지를 가져오기 위해 zip 파일을 읽고 임시 디렉토리에 압축을 푼다.","metadata":{}},{"cell_type":"code","source":"import re\n\n# bad data-sets\nbad_dog_ids = [5604, 6413, 8736, 8898, 9188, \n               9517, 10161, 10190, 10237, 10401, 10797, 11186]\nbad_cat_ids = [2939, 3216, 4688, 4833, 5418, \n               6215, 7377, 8456, 8470, 11565, 12272]\nbad_ids = bad_dog_ids + bad_cat_ids\n\n\ndef mkdir(path):\n    l = path.split('/')\n    for i in range(len(l)):\n        p = os.path.join('/', *l[:i+1])\n        if p and not os.path.exists(p):\n            os.mkdir(p)\n\ndef abs_list_dir(path):\n    ret = []\n    for d, _, files in os.walk(path):\n        ret += [os.path.join(d, f) for f in files]\n    return ret\n\n# extract zips and remove bads\nfor zipname in ['test1', 'train']:\n    zipfilename = f'/kaggle/input/dogs-vs-cats/{zipname}.zip'\n    zipdirname = os.path.join(TEMP_DIR, zipname) # tempTEMP\n    print(f'extract zip file: {zipdirname}')\n    # extract if not exists\n    if not os.path.isdir(zipdirname):\n        with zipfile.ZipFile(zipfilename, 'r') as zip:\n            zip.extractall(TEMP_DIR)\n    # remove bad files\n    files = abs_list_dir(zipdirname)\n    for url in tqdm(files, desc=f'Remove bad images ({zipdirname})'):\n        if os.path.isdir(url):\n            continue\n        image_id = re.findall(r'\\d+', url)[0]\n        if int(image_id) in bad_ids:\n            os.remove(url)\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-03-12T15:09:43.458332Z","iopub.execute_input":"2022-03-12T15:09:43.458997Z","iopub.status.idle":"2022-03-12T15:09:43.788582Z","shell.execute_reply.started":"2022-03-12T15:09:43.458900Z","shell.execute_reply":"2022-03-12T15:09:43.787458Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"`ImageLoader`에서 읽을 수 있도록 아래의 형태로 맞춰준다.\n\n```\ntrain/\n|- dog/\n|- |- dog.XXXX.jpg\n|- cat/\n|- |- cat.XXXX.jpg\ntest/\n|- dog/\n|- |- dog.XXXX.jpg\n|- cat/\n|- |- cat.XXXX.jpg\n\n```\n\n이 노트북에 사용된 Dataset은 Competition이라 test set에 label이 존재하지 않는다. 따라서 검증을 위해 train set을 쪼개어 활용해야한다.\n\n- `train` -> `train` / `test`\n- `test` -> `submit`\n\n`train`은 다시 K Fold를 사용하여, epoch 내에서 valid set을 활용할 것이다.\n\n`submit`은 직접 읽고 실행하여 예측한 label을 저장할 것이므로, 따로 포맷을 맞추지 않을 것이다.\n\n```\nsubmit/\n|- XXXX.jpg\n```","metadata":{}},{"cell_type":"code","source":"# directory to arrange images for generic data loader\nmkdir(TEMP_DIR + '/train/dog')\nmkdir(TEMP_DIR + '/train/cat')\nmkdir(TEMP_DIR + '/test/dog')\nmkdir(TEMP_DIR + '/test/cat')\nmkdir(TEMP_DIR + '/submit')\n\ndef organize_files(files, dst, desc=''):\n    for file in tqdm(files, desc=desc):\n        if not os.path.isdir(file):\n            basename = os.path.basename(file)\n            label, imgid, ext = basename.split('.')\n            os.rename(file, os.path.join(dst, label, basename))\n\ndef organize_submit_files(files, dst, desc=''):\n    for file in tqdm(files, desc=desc):\n        if not os.path.isdir(file):\n            basename = os.path.basename(file)\n            imgid, ext = basename.split('.')\n            os.rename(file, os.path.join(dst, basename))\n            \n# Get absolute path list of image files\ntrain_files = abs_list_dir(TEMP_DIR + '/train') #[:8000] # uncomment for debug with small size\nsubmit_files = abs_list_dir(TEMP_DIR + '/test1')\n\n# Reorganize images\n# train : test = 8 : 2\ntrain_size = len(train_files)\ntrain_split_size = train_size * 8 // 10\norganize_files(train_files[:train_split_size], TEMP_DIR + '/train', desc='Organize train files')\norganize_files(train_files[train_split_size:], TEMP_DIR + '/test', desc='Organize test files')\norganize_submit_files(submit_files, TEMP_DIR + '/submit', desc='Organize submit files')","metadata":{"execution":{"iopub.status.busy":"2022-03-12T15:09:43.793448Z","iopub.execute_input":"2022-03-12T15:09:43.794563Z","iopub.status.idle":"2022-03-12T15:09:44.592365Z","shell.execute_reply.started":"2022-03-12T15:09:43.794516Z","shell.execute_reply":"2022-03-12T15:09:44.590526Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_set = torchvision.datasets.ImageFolder(TEMP_DIR + '/train', transform=image_to_tensor)\ntest_set = torchvision.datasets.ImageFolder(TEMP_DIR + '/test', transform=image_to_tensor)\ntrain_loader = torch.utils.data.DataLoader(train_set, batch_size=64, shuffle=True)\ntest_loader = torch.utils.data.DataLoader(test_set, batch_size=16, shuffle=False)\nprint(train_set, test_set)","metadata":{"execution":{"iopub.status.busy":"2022-03-12T15:09:44.596095Z","iopub.execute_input":"2022-03-12T15:09:44.597700Z","iopub.status.idle":"2022-03-12T15:09:44.914210Z","shell.execute_reply.started":"2022-03-12T15:09:44.597653Z","shell.execute_reply":"2022-03-12T15:09:44.913072Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"이미지 로드가 잘 되었는지 배치 하나만 읽어서 확인해본다.","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(16, 5))\nplt.suptitle(\"Train data/label\", fontsize=24)\nimages, labels = next(iter(train_loader))\nlabel_to_title = {0: 'cat(0)', 1: 'dog(1)'}\nfor index in range(16): # batch size\n    plt.subplot(2, 8, index + 1)\n    plt.title(label_to_title[labels[index].numpy().item()])\n    image = unnormalize(images[index])\n    plt.imshow(image.permute(1,2,0).numpy())\n    plt.axis(\"off\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-03-12T15:09:44.918795Z","iopub.execute_input":"2022-03-12T15:09:44.919410Z","iopub.status.idle":"2022-03-12T15:09:46.395769Z","shell.execute_reply.started":"2022-03-12T15:09:44.919368Z","shell.execute_reply":"2022-03-12T15:09:46.394848Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nclass MLPModel(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.layer = nn.Sequential(\n            nn.Conv2d(3, 32, 5),\n            nn.ReLU(),\n            nn.MaxPool2d(2, 2),\n            \n            nn.Conv2d(32, 64, 3),\n            nn.ReLU(),\n            nn.MaxPool2d(2, 2),\n            \n            nn.Conv2d(64, 128, 5),\n            nn.ReLU(),\n            nn.MaxPool2d(2, 2),\n            \n            nn.Conv2d(128, 256, 5),\n            nn.ReLU(),\n        )\n        self.classifier = nn.Sequential(\n            nn.Dropout(0.4),\n            nn.Linear(160000, 512),\n            nn.ReLU(),\n            nn.Dropout(0.2),\n            nn.Linear(512, 128),\n            nn.Linear(128, 2),\n            nn.LogSoftmax(dim=1)\n        )\n\n    def forward(self, x):\n        x = self.layer(x)\n        x = x.view(x.size(0), -1)\n        x = self.classifier(x)\n        return x","metadata":{"execution":{"iopub.status.busy":"2022-03-12T15:09:46.397669Z","iopub.execute_input":"2022-03-12T15:09:46.398244Z","iopub.status.idle":"2022-03-12T15:09:46.413530Z","shell.execute_reply.started":"2022-03-12T15:09:46.398200Z","shell.execute_reply":"2022-03-12T15:09:46.412066Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# CPU/GPU\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint('device:', device)","metadata":{"execution":{"iopub.status.busy":"2022-03-12T15:09:46.417191Z","iopub.execute_input":"2022-03-12T15:09:46.417510Z","iopub.status.idle":"2022-03-12T15:09:46.427960Z","shell.execute_reply.started":"2022-03-12T15:09:46.417479Z","shell.execute_reply":"2022-03-12T15:09:46.426491Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 모델 선택하기","metadata":{}},{"cell_type":"markdown","source":"## 1. 직접 구현한 CNN Model","metadata":{}},{"cell_type":"code","source":"model = MLPModel().to(device)\n\ncriterion = nn.NLLLoss()\noptimizer = torch.optim.Adam(model.parameters(), lr=1e-3)\n\nprint(model)","metadata":{"execution":{"iopub.status.busy":"2022-03-12T15:09:46.430456Z","iopub.execute_input":"2022-03-12T15:09:46.430840Z","iopub.status.idle":"2022-03-12T15:09:47.225582Z","shell.execute_reply.started":"2022-03-12T15:09:46.430779Z","shell.execute_reply":"2022-03-12T15:09:47.224599Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2. VGG16 모델","metadata":{}},{"cell_type":"code","source":"import collections\n\nmodel = torchvision.models.vgg16_bn(pretrained=True)\n\n# Freeze our feature parameters as we don't wanna retrain them to the new data\nfor param in model.parameters():\n  param.requires_grad = False\n\n# # 1. Override flassifier layer\n# model.classifier = nn.Sequential(\n#     nn.Linear(25088, 500),\n#     nn.ReLU(),\n#     nn.Dropout(0.3),\n#     nn.Linear(500, 2)\n# )\n# 2. Append flassifier layer\nmy_classifier = nn.Sequential(\n    nn.Linear(1000, 500),\n    nn.ReLU(),\n    nn.Dropout(0.3),\n    nn.Linear(500, 2)\n)\nmodel = nn.Sequential(collections.OrderedDict([\n    ('net', model),\n    ('classifier', my_classifier)\n]))\n\n# Gather the parameters to be optimized/updated in this run. If we are\n#  finetuning we will be updating all parameters. However, if we are\n#  doing feature extract method, we will only update the parameters\n#  that we have just initialized, i.e. the parameters with requires_grad\n#  is True.\nprint(\"Params to learn:\")\nparams_to_update = []\nfor name, param in model.named_parameters():\n    if param.requires_grad == True:\n        params_to_update.append(param)\n        print(\"\\t\",name)\n\ncriterion = nn.CrossEntropyLoss()\noptimizer = torch.optim.Adam(params_to_update, lr=2*1e-3)\n\ntrain_set = torchvision.datasets.ImageFolder(TEMP_DIR + '/train', transform=transforms.Compose([\n    transforms.Resize(256),\n    transforms.CenterCrop(224),\n    transforms.ToTensor(),\n    normalize\n]))\n\nprint(model)","metadata":{"execution":{"iopub.status.busy":"2022-03-12T15:09:47.227255Z","iopub.execute_input":"2022-03-12T15:09:47.227717Z","iopub.status.idle":"2022-03-12T15:09:49.035026Z","shell.execute_reply.started":"2022-03-12T15:09:47.227673Z","shell.execute_reply":"2022-03-12T15:09:49.033931Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 모델 학습하기","metadata":{}},{"cell_type":"markdown","source":"모델의 순전파/역전파 시작","metadata":{}},{"cell_type":"code","source":"train_losses = {}\nvalid_losses = {}\nvalid_accs = {}\nbest = (0, -1, 1e9, {}) # (fold, valid_idx, loss, model.state)\nEPOCHS = 30\n\nmodel.to(device)\n\nfold_count = 3\nkfold = KFold(n_splits=fold_count)\nbatch_size = 64\n\n# append result by fold -> by epoch\ndef add_fold_result(epoch, d, loss):\n    if epoch in d:\n        d[epoch].append(loss)\n    else:\n        d[epoch] = [loss]\n    return d\n\ndef avg_group_by_fold(losses):\n    return [np.array(losses[f]).mean() for f in losses]\n\n\n# for tracking by fold\ntrain_losses_by_fold = [[] for _ in range(fold_count)]\nvalid_losses_by_fold = [[] for _ in range(fold_count)]\nvalid_acc_by_fold = [[] for _ in range(fold_count)]\n\n# Run apoch\nfor epoch in range(EPOCHS):\n    early_stop = None\n    # K-Fold cross validation\n    for fold, (train_idx, valid_idx) in enumerate(kfold.split(train_set)):\n        # Split dataset and loader\n        train_subsampler = torch.utils.data.SubsetRandomSampler(train_idx)\n        valid_subsampler = torch.utils.data.SubsetRandomSampler(valid_idx)\n        # Use as train/valid set from train data set by k-fold\n        train_loader = torch.utils.data.DataLoader(train_set, batch_size=batch_size, sampler=train_subsampler)\n        valid_loader = torch.utils.data.DataLoader(train_set, batch_size=batch_size, sampler=valid_subsampler)\n\n        running_loss = 0.0\n        \n        # Train\n        model.train()\n        for inputs, labels in tqdm(train_loader, desc=f'train model ({epoch+1}/{EPOCHS} epoch, fold={fold})'):\n            inputs, labels = inputs.to(device), labels.to(device)\n\n            outputs = model.forward(inputs) # 예측값 산출 \n            loss = criterion(outputs, labels) # 손실함수 계산\n            \n            optimizer.zero_grad()\n            loss.backward() # 손실함수 기준으로 역전파 선언\n            optimizer.step() # 가중치 최적화\n\n            running_loss += loss.item()\n\n        # train loss (average)\n        train_loss = running_loss / len(train_loader)\n        train_losses_by_fold[fold].append(train_loss)\n        train_losses = add_fold_result(epoch, train_losses, train_loss)\n        \n        model_state = deepcopy(model.state_dict())\n    \n        # valid loss (just for check)\n        model.eval()\n        with torch.no_grad():\n            valid_loss_sum = 0\n            acc_sum = 0\n            test_var = False\n            for images, labels in valid_loader:\n                images, labels = images.to(device), labels.to(device)\n                outputs = model(images).to(device)\n                _, pred = torch.max(outputs.data, 1)\n                if test_var == False:\n                    print('outputs', outputs.data)\n                    print('pred', pred)\n                    print('labels', labels)\n                    print(pred == labels)\n                    test_var = True\n                valid_loss_sum += criterion(outputs, labels).item()\n                acc_sum += (pred == labels).sum().item() / labels.size(0)\n            print('acc_sum', acc_sum, 'len(valid_loader)', len(valid_loader))\n            acc = acc_sum / len(valid_loader) * 100.0\n            valid_loss = valid_loss_sum / len(valid_loader)\n            valid_losses_by_fold[fold].append(valid_loss)\n            valid_losses = add_fold_result(epoch, valid_losses, valid_loss)\n        \n        print('[%d] fold=%d, train loss: %.6f, valid loss: %.6f, valid acc: %.3f%%' % (epoch + 1, fold, train_loss, valid_loss, acc))\n        \n        valid_accs = add_fold_result(epoch, valid_accs, acc)\n\n        # get best\n        if valid_loss < best[2]:\n            best = (epoch, valid_idx, valid_loss, model_state)\n\n        tl = np.array(train_losses_by_fold[fold]).mean()\n        vl = np.array(valid_losses_by_fold[fold]).mean()\n\n        # Early stop - when valid loss is 3 times higher than train loss\n        # But, Do not stop early before 5 epoch because too short\n        if epoch >= 5 and len(train_losses_by_fold[fold]) > 1 and tl * 3 < vl:\n            early_stop = 'valid loss is 3 times higher than train loss'\n            break\n        # Early stop - trained well enough (average loss is under 0.1 (means 90% accuracy))\n        if np.array([tl, vl]).mean() < 0.1:\n            early_stop = 'trained well enough'\n            break\n    if early_stop != None:\n        print('Early stop - caused by', early_stop)\n        break\n\n# save model\nSAVE_BEST_PATH = '/kaggle/working/dogs-vs-cats/best_parameters.pth'\ntorch.save(best[-1], SAVE_BEST_PATH)\nprint(best[:-1])\n\n# Draw chart\nplt.figure(figsize=(20, 4))\n\nplt.subplot(1, 2, 1)\ny_train, y_valid = avg_group_by_fold(train_losses), avg_group_by_fold(valid_losses)\nplt.plot(y_train, label=\"train loss\")\nplt.plot(y_valid, label=\"valid loss\")\nplt.title(\"Training Loss\")\nplt.xlabel(\"epoch\")\nplt.ylim(top=min(10, max(y_train + y_valid) + 0.1), bottom=-0.01)\nplt.axvline(best[0], color='red')\nplt.legend()\n\nplt.subplot(1, 2, 2)\nplt.plot(avg_group_by_fold(valid_accs), label=\"accuracy\")\nplt.title(\"Valid accuracy\")\nplt.xlabel(\"epoch\")\nplt.legend()\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-03-12T15:09:49.037062Z","iopub.execute_input":"2022-03-12T15:09:49.037395Z","iopub.status.idle":"2022-03-12T15:14:16.003830Z","shell.execute_reply.started":"2022-03-12T15:09:49.037354Z","shell.execute_reply":"2022-03-12T15:14:16.002815Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"각 Fold별 train/valid loss 그래프를 그려본다. (특정 fold에서 학습의 문제가 있었나 확인해보기 위해)","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(25, 5))\nfor fold in range(fold_count):\n    y_train, y_valid = train_losses_by_fold[fold], valid_losses_by_fold[fold]\n    if len(y_train) < 1: continue\n    plt.subplot(1, fold_count, fold + 1)\n    plt.plot(y_train, label=\"train loss\")\n    plt.plot(y_valid, label=\"valid loss\")\n    plt.title(f'Loss (fold={fold})')\n    plt.xlabel(\"epoch\")\n    plt.ylim(top=min(10, max(y_train + y_valid) + 0.1), bottom=-0.01)\n    plt.axvline(best[0], color='red')\n    plt.legend()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-03-12T15:14:16.005466Z","iopub.execute_input":"2022-03-12T15:14:16.007089Z","iopub.status.idle":"2022-03-12T15:14:16.421326Z","shell.execute_reply.started":"2022-03-12T15:14:16.007041Z","shell.execute_reply":"2022-03-12T15:14:16.420294Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_accuracy(model, loader):\n    model.to(device)\n    model.eval()\n    with torch.no_grad():\n        acc = 0\n        once = False\n        for images, labels in tqdm(loader):\n            outputs = model(images.to(device))\n            _, pred = torch.max(outputs.data, 1)\n            acc += torch.sum(pred.to(device) == labels.to(device)).to('cpu')\n            if once == False:\n                print(outputs.data)\n                print(pred)\n                print(labels)\n                print(pred.to(device) == labels.to(device))\n                once = True\n        return acc / len(loader.dataset)\n\n\n# model = MLPModel()\nmodel = torchvision.models.vgg16_bn(pretrained=True)\n\n# VGG 네트워크로 저장한 것이 아니기 때문에, 같은 구조로 맞춘 후 읽어야한다.\nmodel = nn.Sequential(collections.OrderedDict([\n    ('net', model),\n    ('classifier', my_classifier)\n]))\nmodel.load_state_dict(torch.load(SAVE_BEST_PATH), strict=False)\n\n# epoch 도중에 사용한 이름과 겹쳐서, 다시 재정의한다.\ntrain_loader = torch.utils.data.DataLoader(train_set, batch_size=64, shuffle=True)\ntest_loader = torch.utils.data.DataLoader(test_set, batch_size=16, shuffle=False)\n\n# Get accuracy\ntrain_acc = get_accuracy(model, train_loader)\nprint('train accuracy:', '{:.5f} %'.format(train_acc * 100))\n\ntest_acc = get_accuracy(model, test_loader)\nprint(' test accuracy:', '{:.5f} %'.format(test_acc * 100))","metadata":{"execution":{"iopub.status.busy":"2022-03-12T15:14:16.425516Z","iopub.execute_input":"2022-03-12T15:14:16.426044Z","iopub.status.idle":"2022-03-12T15:17:59.670730Z","shell.execute_reply.started":"2022-03-12T15:14:16.425997Z","shell.execute_reply":"2022-03-12T15:17:59.669601Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# `test1` 데이터를 제출을 위해 csv 포맷으로 저장","metadata":{}},{"cell_type":"code","source":"class SubmitImageFolder(torch.utils.data.Dataset):\n    def __init__(self, root, transform):\n        self.root = root\n        self.files = filter(lambda s: s.endswith('.jpg'), os.listdir(root))\n        self.files = list(map(lambda s: os.path.join(root, s), self.files))\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.files)\n\n    def __getitem__(self, index):\n        path = self.files[index]\n        image = self.transform(get_image(path))\n        id_ = os.path.basename(path).split('.')[0]\n        return (int(id_), image)\n\n\nsubmit_set = SubmitImageFolder(TEMP_DIR + '/submit', transform=image_to_tensor)\nsubmit_loader = torch.utils.data.DataLoader(submit_set, batch_size=64)\n\nsample_pred_images = None\n\nmodel.to(device)\n\nwith torch.no_grad():\n    pred_ids = torch.empty(0)\n    pred_labels = torch.empty(0)\n    for ids, images in tqdm(submit_loader):\n        images = images.to(device)\n        outputs = model(images)\n        _, pred = torch.max(outputs.data, 1)\n        # append list\n        pred_ids = torch.cat([pred_ids, ids])\n        pred_labels = torch.cat([pred_labels, pred.to('cpu')])\n        # save image to show\n        if sample_pred_images is None:\n            sample_pred_images = (images, pred)\n\n    pred_df = pd.DataFrame({'id': pred_ids, 'label': pred_labels})\n    pred_df['id'] = pred_df['id'].astype('int')\n    pred_df['label'] = pred_df['label'].astype('int')\n\npred_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-03-12T15:18:46.959985Z","iopub.execute_input":"2022-03-12T15:18:46.960311Z","iopub.status.idle":"2022-03-12T15:20:51.437587Z","shell.execute_reply.started":"2022-03-12T15:18:46.960270Z","shell.execute_reply":"2022-03-12T15:20:51.436555Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"일부 데이터셋의 결과를 눈으로 확인해보자","metadata":{}},{"cell_type":"code","source":"sample_images, sample_preds = sample_pred_images\nplt.figure(figsize=(10, 10))\nfor index in range(5 * 5):\n    plt.subplot(5, 5, index + 1)\n    image = unnormalize(sample_images[index])\n    image = image.to('cpu').permute(1,2,0).numpy()\n    guess = sample_preds[index].to('cpu').numpy().item()\n    plt.title(label_to_title[guess])\n    plt.imshow(image)\n    plt.axis(\"off\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-03-12T15:21:42.788253Z","iopub.execute_input":"2022-03-12T15:21:42.788613Z","iopub.status.idle":"2022-03-12T15:21:44.129509Z","shell.execute_reply.started":"2022-03-12T15:21:42.788583Z","shell.execute_reply":"2022-03-12T15:21:44.128668Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred_df.to_csv('result.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2022-03-12T15:21:44.131394Z","iopub.execute_input":"2022-03-12T15:21:44.131829Z","iopub.status.idle":"2022-03-12T15:21:44.166431Z","shell.execute_reply.started":"2022-03-12T15:21:44.131786Z","shell.execute_reply":"2022-03-12T15:21:44.165498Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"현재 데이터셋에는 test에 라벨이 없어서, valid로 확인할 수 밖에 없다.","metadata":{}}]}