{"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":"# パッケージのインポート","metadata":{}},{"cell_type":"code","source":"import os\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport shutil\nimport glob\nimport zipfile\nimport time\nimport matplotlib.pyplot as plt\nimport csv\nfrom sklearn.model_selection import train_test_split\nfrom torchvision import transforms\nfrom torch.utils.data import Dataset, DataLoader\nfrom PIL import Image","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# GPUの検証","metadata":{}},{"cell_type":"code","source":"device = 'cuda' if torch.cuda.is_available() else 'cpu'\nprint(\"device:\", device)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# データの読み込み","metadata":{}},{"cell_type":"code","source":"# '/kaggle/input'に入っているファイルを確認\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# /kaggle/inputのzipファイルを/kaggle/workingに解凍\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/train.zip') as existing_zip:\n    existing_zip.extractall()\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/test.zip') as existing_zip:\n    existing_zip.extractall()\n\n# '/kaggle/working'に入っているファイルを確認\nfiles = glob.glob('/kaggle/working/*')\nfor file in files:\n    print(file)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 犬と猫の画像を，別々のフォルダに保存する","metadata":{}},{"cell_type":"code","source":"# 犬・猫の画像を，それぞれ保存するフォルダのパスを定義\ndog_dir = '/kaggle/working/train/dog'\ncat_dir = '/kaggle/working/train/cat'\n\n# フォルダの作成\nos.mkdir(dog_dir)\nos.mkdir(cat_dir)\n\n# 犬・猫の画像を，それぞれのフォルダに移動する\nfiles = glob.glob('/kaggle/working/train/*.jpg')# 訓練用フォルダ内の全ファイルパスを取得\nfor file in files:\n    file_name = os.path.basename(file)\n    if 'cat' in file:\n        shutil.move(file,'/kaggle/working/train/cat/' + file_name)\n    else:\n        shutil.move(file,'/kaggle/working/train/dog/' + file_name)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 検証用データに分割","metadata":{}},{"cell_type":"code","source":"# フォルダの作成\nos.mkdir('/kaggle/working/valid')\nos.mkdir('/kaggle/working/valid/dog')\nos.mkdir('/kaggle/working/valid/cat')\n\n# 訓練画像の一部を，バリデーションフォルダに移動する(犬)\ndog_files = glob.glob('/kaggle/working/train/dog/*.jpg')\ndog_train, dog_valid = train_test_split(dog_files, test_size=0.2, random_state=42)\n# 画像の一部をバリデーションフォルダに移動する\nfor file in dog_valid:\n    file_name = os.path.basename(file)\n    shutil.move(file,'/kaggle/working/valid/dog/' + file_name)\ndog_valid = glob.glob('/kaggle/working/valid/dog/*.jpg')\n\n# 訓練画像の一部を，バリデーションフォルダに移動する(猫)\ncat_files = glob.glob('/kaggle/working/train/cat/*.jpg')\ncat_train, cat_valid = train_test_split(cat_files, test_size=0.2, random_state=42)\n# 画像の一部をバリデーションフォルダに移動する\nfor file in cat_valid:\n    file_name = os.path.basename(file)\n    shutil.move(file,'/kaggle/working/valid/cat/' + file_name)\ncat_valid = glob.glob('/kaggle/working/valid/cat/*.jpg')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 学習モデルの定義","metadata":{}},{"cell_type":"code","source":"class 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":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cnn = Model()\nopt = torch.optim.Adam(cnn.parameters(), lr=0.001)\nloss_fn = nn.BCELoss()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 訓練データの前処理","metadata":{}},{"cell_type":"code","source":"class 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, index):\n        image_path = self.file_list[index]\n        image = Image.open(image_path)\n        if self.transform is not None:\n            image = self.transform(image)\n        if 'cat' in image_path:\n            label = 0\n        else:\n            label = 1\n            \n        return image, label","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_data = Dataset(dog_train+cat_train, transform=transforms.Compose([transforms.Resize([256, 256]), transforms.ToTensor()]))\nvalid_data = Dataset(dog_valid+cat_valid, transform=transforms.Compose([transforms.Resize([256, 256]), transforms.ToTensor()]))\n\ntrain_dl = DataLoader(train_data, batch_size=64, shuffle=True, num_workers=1, pin_memory=True)\nvalid_dl = DataLoader(valid_data, batch_size=64, shuffle=False, num_workers=1, pin_memory=True)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 学習","metadata":{}},{"cell_type":"code","source":"def train(model, epochs, train_dl, valid_dl):\n    model.to(device)\n    loss_hist_train = np.zeros(epochs)\n    accuracy_hist_train = [0] * epochs\n    loss_hist_valid = np.zeros(epochs)\n    accuracy_hist_valid = [0] * epochs\n    start_time = time.time() \n    \n    for epoch in range(epochs):\n        \n        #Train\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, epochs, batch_idx, 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        #Valid\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":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"epochs = 100\nhistory = train(cnn, epochs, train_dl, valid_dl)\ntorch.save(cnn, 'model_weight.pth')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 学習結果のグラフ","metadata":{}},{"cell_type":"code","source":"dpi = 100\nfig, ax = plt.subplots(nrows=2, ncols=1, figsize=(5.4, 7.2), dpi=dpi)\nax[0].plot(history[0], lw=2, label='Train loss')\nax[0].plot(history[1], lw=2, label='Valid 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='Valid acc.')\nax[1].legend(fontsize=12)\nax[1].set_xlabel('Epochs', fontsize=12)\n\nplt.savefig('history.png', dpi=dpi)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 予測","metadata":{}},{"cell_type":"code","source":"CNN = torch.load('model_weight.pth')\n\nCNN.eval()\ntest_data = Dataset(glob.glob('/kaggle/working/test/*.jpg'), transform=transforms.Compose([transforms.Resize([256, 256]), transforms.ToTensor()]))\ntest_dl = DataLoader(test_data, batch_size=64, shuffle=False, num_workers=1, pin_memory=True)\n\npredictions = []\nwith torch.no_grad():\n    for x_batch, _ in test_dl:\n        x_batch = x_batch.to(device)\n        pred = CNN(x_batch)[:, 0]\n        predictions.extend(pred.tolist())\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":{"trusted":true},"execution_count":null,"outputs":[]}]}