{"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":"# CNNを用いた犬猫分類","metadata":{}},{"cell_type":"markdown","source":"## 画像の前処理","metadata":{}},{"cell_type":"markdown","source":"zipファイルの展開","metadata":{}},{"cell_type":"code","source":"import os\nimport shutil\nimport torch\nimport random\nimport numpy as np\nfrom torchvision import transforms\nfrom torch.utils.data import Dataset\nfrom sklearn.model_selection import train_test_split\nfrom torch.utils.data import DataLoader\nimport torch.nn as nn\nimport time\nfrom PIL import Image\nimport matplotlib.pyplot as plt","metadata":{"execution":{"iopub.status.busy":"2023-07-30T16:13:41.465511Z","iopub.execute_input":"2023-07-30T16:13:41.465868Z","iopub.status.idle":"2023-07-30T16:13:47.419153Z","shell.execute_reply.started":"2023-07-30T16:13:41.465836Z","shell.execute_reply":"2023-07-30T16:13:47.417686Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#import os\n#import shutil\n\nbase_dir = '../input/dogs-vs-cats-redux-kernels-edition'\ntrain_dir = '../data/train'\ntest_dir = '../data/test'\n\nshutil.unpack_archive(os.path.join(base_dir, 'train.zip'), '../data')\nshutil.unpack_archive(os.path.join(base_dir, 'test.zip'), '../data')\n\ntrain_list = [os.path.join(train_dir, filename) for filename in os.listdir(train_dir) if filename.endswith('.jpg')]\ntest_list = [os.path.join(test_dir, filename) for filename in os.listdir(test_dir) if filename.endswith('.jpg')]","metadata":{"execution":{"iopub.status.busy":"2023-07-30T16:13:47.421666Z","iopub.execute_input":"2023-07-30T16:13:47.422392Z","iopub.status.idle":"2023-07-30T16:14:03.770241Z","shell.execute_reply.started":"2023-07-30T16:13:47.422348Z","shell.execute_reply":"2023-07-30T16:14:03.769216Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"GPUの使用と再現性の確保（なぜかできてない）","metadata":{}},{"cell_type":"code","source":"#import torch\n#import random\n#import numpy as np\n\nSEED = 1\n\ndevice = 'cuda' if torch.cuda.is_available() else 'cpu'\n\ntorch.manual_seed(SEED)\nif device =='cuda':\n    torch.cuda.manual_seed(SEED)\n\nrandom.seed(SEED)\nnp.random.seed(SEED)\ntorch.backends.cudnn.deterministic = True\ntorch.backends.cudnn.benchmark = False\n\nprint(\"device:\", device)","metadata":{"execution":{"iopub.status.busy":"2023-07-30T16:14:03.772338Z","iopub.execute_input":"2023-07-30T16:14:03.773079Z","iopub.status.idle":"2023-07-30T16:14:03.819190Z","shell.execute_reply.started":"2023-07-30T16:14:03.773042Z","shell.execute_reply":"2023-07-30T16:14:03.818025Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"データ拡張","metadata":{}},{"cell_type":"code","source":"#from torchvision import transforms\n\ndata_transforms = {\n    'train': transforms.Compose([\n        transforms.RandomResizedCrop([512,512]),\n        transforms.RandomHorizontalFlip(),\n        transforms.Resize([256,256]),\n        transforms.ToTensor(),\n    ]),\n    'test': transforms.Compose([\n        transforms.Resize([256,256]),\n        transforms.ToTensor()\n    ])\n}","metadata":{"execution":{"iopub.status.busy":"2023-07-30T16:14:03.826184Z","iopub.execute_input":"2023-07-30T16:14:03.827258Z","iopub.status.idle":"2023-07-30T16:14:04.041129Z","shell.execute_reply.started":"2023-07-30T16:14:03.827217Z","shell.execute_reply":"2023-07-30T16:14:04.040027Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"ファイル名からラベルデータを作成できるdatasetクラス","metadata":{}},{"cell_type":"code","source":"#from torch.utils.data import Dataset\n\nclass Dataset(Dataset):\n    def __init__(self, file_list, transform=None):\n        self.file_list = file_list\n        self.transform = transform\n        \n    def __len__(self):\n        return len(self.file_list)\n    \n    def __getitem__(self, idx):\n        img_path = self.file_list[idx]\n        img = Image.open(img_path)\n        if self.transform is not None:\n            img = self.transform(img)\n        label = img_path.split('/')[-1].split('.')[0]\n        label = 0 if label == 'cat' else 1\n            \n        return img, label","metadata":{"execution":{"iopub.status.busy":"2023-07-30T16:14:04.042903Z","iopub.execute_input":"2023-07-30T16:14:04.043295Z","iopub.status.idle":"2023-07-30T16:14:04.053503Z","shell.execute_reply.started":"2023-07-30T16:14:04.043258Z","shell.execute_reply":"2023-07-30T16:14:04.052414Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"datasetの作成","metadata":{}},{"cell_type":"code","source":"#from sklearn.model_selection import train_test_split\n#from torch.utils.data import DataLoader\n\ntrlist, valist = train_test_split(train_list, test_size=0.1, random_state=SEED)\n\ntr_ds = Dataset(trlist, transform=data_transforms['train'])\nva_ds = Dataset(valist, transform=data_transforms['test'])\nte_ds = Dataset(test_list, transform=data_transforms['test'])\n\nbatch_size = 128\ntorch.manual_seed(SEED)\nnum_workers = os.cpu_count()\n\ntr_dl = DataLoader(tr_ds, batch_size=batch_size, shuffle=True, num_workers=num_workers, pin_memory=True)\nva_dl = DataLoader(va_ds, batch_size=batch_size, shuffle=False, num_workers=num_workers, pin_memory=True)","metadata":{"execution":{"iopub.status.busy":"2023-07-30T16:14:04.055394Z","iopub.execute_input":"2023-07-30T16:14:04.056161Z","iopub.status.idle":"2023-07-30T16:14:04.075671Z","shell.execute_reply.started":"2023-07-30T16:14:04.056124Z","shell.execute_reply":"2023-07-30T16:14:04.074609Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## CNNモデルの作成","metadata":{}},{"cell_type":"code","source":"#import torch.nn as nn\n\nclass Model(nn.Module):\n    def __init__(self, kernel_size=3, dropout_rate=0.5):\n        super(Model, self).__init__()\n        \n        self.kernel_size = kernel_size\n        self.padding = (kernel_size - 1) // 2\n        self.dropout_rate = dropout_rate\n        \n        self.layer1 = nn.Sequential(\n            nn.Conv2d(in_channels = 3, out_channels = 32, kernel_size = self.kernel_size, padding = self.padding),\n            nn.BatchNorm2d(32),\n            nn.ReLU(),\n            nn.MaxPool2d(kernel_size = 2),\n            nn.Dropout(p = self.dropout_rate)\n        )\n        self.layer2 = nn.Sequential(\n            nn.Conv2d(in_channels = 32, out_channels = 64, kernel_size = self.kernel_size, padding = self.padding),\n            nn.BatchNorm2d(64),\n            nn.ReLU(),\n            nn.MaxPool2d(kernel_size = 2),\n            nn.Dropout(p = self.dropout_rate)\n        )\n        self.layer3 = nn.Sequential(\n            nn.Conv2d(in_channels = 64, out_channels = 128, kernel_size = self.kernel_size, padding = self.padding),\n            nn.BatchNorm2d(128),\n            nn.ReLU(),\n            nn.MaxPool2d(kernel_size = 2),\n            nn.Dropout(p = self.dropout_rate)\n        )\n        self.layer4 = nn.Sequential(\n            nn.Conv2d(in_channels = 128, out_channels = 256, kernel_size = self.kernel_size, padding = self.padding),\n            nn.BatchNorm2d(256),\n            nn.ReLU(),\n            nn.MaxPool2d(kernel_size = 2),\n            nn.Dropout(p = self.dropout_rate)\n        )\n        self.layer5 = nn.Sequential(\n            nn.Conv2d(in_channels = 256, out_channels = 512, kernel_size = self.kernel_size, padding = self.padding),\n            nn.BatchNorm2d(512),\n            nn.ReLU(),\n            nn.AvgPool2d(kernel_size = 16),\n            nn.Dropout(p = self.dropout_rate),\n            nn.Flatten(),\n            nn.Linear(512,1),\n            nn.Sigmoid()\n        )\n        \n    def forward(self,x):\n        x = self.layer1(x)\n        x = self.layer2(x)\n        x = self.layer3(x)\n        x = self.layer4(x)\n        x = self.layer5(x)\n\n        return x","metadata":{"execution":{"iopub.status.busy":"2023-07-30T16:14:04.077167Z","iopub.execute_input":"2023-07-30T16:14:04.077803Z","iopub.status.idle":"2023-07-30T16:14:04.095408Z","shell.execute_reply.started":"2023-07-30T16:14:04.077674Z","shell.execute_reply":"2023-07-30T16:14:04.094373Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 損失関数と最適化アルゴリズムの選択","metadata":{}},{"cell_type":"code","source":"CNN = Model()\nopt = torch.optim.Adam(CNN.parameters(), lr=0.001)\nloss_fn = nn.BCELoss()","metadata":{"execution":{"iopub.status.busy":"2023-07-30T16:14:04.097036Z","iopub.execute_input":"2023-07-30T16:14:04.097647Z","iopub.status.idle":"2023-07-30T16:14:04.163998Z","shell.execute_reply.started":"2023-07-30T16:14:04.097606Z","shell.execute_reply":"2023-07-30T16:14:04.163023Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Modelの学習","metadata":{}},{"cell_type":"code","source":"def train(model, num_epochs, train_dl, valid_dl):\n    model.to(device)\n    loss_hist_train = np.zeros(num_epochs)\n    accuracy_hist_train = [0] * num_epochs\n    loss_hist_valid = np.zeros(num_epochs)\n    accuracy_hist_valid = [0] * num_epochs\n    start_time = time.time() \n    for epoch in range(num_epochs):\n        model.train()\n        train_loss = 0.0\n        train_correct = 0\n        train_total = 0\n        \n        for batch_idx, (x_batch, y_batch) in enumerate(train_dl):\n            x_batch = x_batch.to(device)\n            y_batch = y_batch.to(device)\n            pred = model(x_batch)[:, 0]\n            loss = loss_fn(pred, y_batch.float())\n            opt.zero_grad()\n            loss.backward()\n            opt.step()\n            train_loss += loss.item() * y_batch.size(0)\n            train_correct += ((pred >= 0.5).float() == y_batch).sum()\n            train_total += y_batch.size(0)\n            \n            if batch_idx % 50 == 0:\n                print ('Epoch: %03d/%03d | Batch %03d/%03d | Loss: %.4f' \n                       %(epoch+1, num_epochs, batch_idx, \n                         len(train_dl), loss.item()))\n        \n        loss_hist_train[epoch] = train_loss / train_total\n        accuracy_hist_train[epoch] = train_correct / train_total\n        \n        model.eval()\n        valid_loss = 0.0\n        valid_correct = 0\n        valid_total = 0\n        \n        with torch.no_grad():\n            for x_batch, y_batch in valid_dl:\n                x_batch = x_batch.to(device)\n                y_batch = y_batch.to(device)\n                pred = model(x_batch)[:, 0]\n                loss = loss_fn(pred, y_batch.float())\n                valid_loss += loss.item() * y_batch.size(0)\n                valid_correct += ((pred >= 0.5).float() == y_batch).sum()\n                valid_total += y_batch.size(0)\n        \n        loss_hist_valid[epoch] = valid_loss / valid_total\n        accuracy_hist_valid[epoch] = valid_correct / valid_total\n        \n        print(f'Epoch {epoch+1} accuracy: {accuracy_hist_train[epoch]:.4f} val_accuracy: {accuracy_hist_valid[epoch]:.4f}')\n        print('Time elapsed: %.2f min' % ((time.time() - start_time) / 60))\n    \n    print('Total Training Time: %.2f min' % ((time.time() - start_time) / 60))\n    \n    return loss_hist_train, loss_hist_valid, accuracy_hist_train, accuracy_hist_valid","metadata":{"execution":{"iopub.status.busy":"2023-07-30T16:14:04.168066Z","iopub.execute_input":"2023-07-30T16:14:04.168396Z","iopub.status.idle":"2023-07-30T16:14:04.184763Z","shell.execute_reply.started":"2023-07-30T16:14:04.168366Z","shell.execute_reply":"2023-07-30T16:14:04.183623Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#import time\n#from PIL import Image\n\ntorch.manual_seed(SEED)\n\nnum_epochs = 100\nhistory = train(CNN, num_epochs, tr_dl, va_dl)\n\ntorch.save(CNN, 'model_weight.pth')","metadata":{"execution":{"iopub.status.busy":"2023-07-30T16:14:04.188821Z","iopub.execute_input":"2023-07-30T16:14:04.189553Z","iopub.status.idle":"2023-07-30T21:46:07.343000Z","shell.execute_reply.started":"2023-07-30T16:14:04.189512Z","shell.execute_reply":"2023-07-30T21:46:07.341710Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## グラフの出力","metadata":{}},{"cell_type":"code","source":"#import matplotlib.pyplot as plt\n\nmy_dpi = 100\nfig, ax = plt.subplots(nrows=2, ncols=1, figsize=(5.4, 7.2), dpi=my_dpi)\nax[0].plot(history[0], lw=2, label='Train loss')\nax[0].plot(history[1], lw=2, label='Validation loss')\nax[0].legend(fontsize=12)\nax[0].set_xlabel('Epochs', fontsize=12)\n\nax[1].plot([i.cpu() for i in history[2]], lw=2, label='Train acc.')\nax[1].plot([i.cpu() for i in history[3]], lw=2, label='Validation acc.')\nax[1].legend(fontsize=12)\nax[1].set_xlabel('Epochs', fontsize=12)\n\nplt.savefig('model_train_history.png', dpi=my_dpi)","metadata":{"execution":{"iopub.status.busy":"2023-07-30T21:46:07.344869Z","iopub.execute_input":"2023-07-30T21:46:07.345777Z","iopub.status.idle":"2023-07-30T21:46:08.042489Z","shell.execute_reply.started":"2023-07-30T21:46:07.345730Z","shell.execute_reply":"2023-07-30T21:46:08.041386Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 予測結果の出力","metadata":{}},{"cell_type":"code","source":"CNN = torch.load('/kaggle/working/model_weight.pth')\n\nCNN.eval()\nte_dl = DataLoader(te_ds, batch_size=batch_size, shuffle=False, num_workers=num_workers, pin_memory=True)\n\npredictions = []\nwith torch.no_grad():\n    for x_batch, _ in te_dl:\n        x_batch = x_batch.to(device)\n        pred = CNN(x_batch)[:, 0]\n        predictions.extend(pred.tolist())\n\nimport csv\n\nsubmission_file = 'submission.csv'\nwith open(submission_file, 'w', newline='') as file:\n    writer = csv.writer(file)\n    writer.writerow(['id', 'label'])\n    for i, pred in enumerate(predictions):\n        writer.writerow([i+1, pred])","metadata":{"execution":{"iopub.status.busy":"2023-07-31T00:52:54.788998Z","iopub.execute_input":"2023-07-31T00:52:54.789435Z","iopub.status.idle":"2023-07-31T00:52:54.836193Z","shell.execute_reply.started":"2023-07-31T00:52:54.789396Z","shell.execute_reply":"2023-07-31T00:52:54.834830Z"},"trusted":true},"execution_count":null,"outputs":[]}]}