{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":5441,"databundleVersionId":38425,"sourceType":"competition"}],"dockerImageVersionId":30748,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## GPUの動作確認","metadata":{}},{"cell_type":"code","source":"# GPUが使用可能かを確認\nimport torch\ndevice = 'cuda' if torch.cuda.is_available() else 'cpu'\nprint(\"device:\", device)","metadata":{"execution":{"iopub.status.busy":"2024-07-19T04:03:44.424161Z","iopub.execute_input":"2024-07-19T04:03:44.425406Z","iopub.status.idle":"2024-07-19T04:03:48.265577Z","shell.execute_reply.started":"2024-07-19T04:03:44.425380Z","shell.execute_reply":"2024-07-19T04:03:48.264631Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Kaggle課題のデータ読み込み\n/kaggle/inputのデータを、/kaggle/workingに読み込む。","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\n\n# /kaggle/input/ に入っているファイルを確認\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))","metadata":{"execution":{"iopub.status.busy":"2024-07-19T04:03:48.266848Z","iopub.execute_input":"2024-07-19T04:03:48.267260Z","iopub.status.idle":"2024-07-19T04:03:48.274573Z","shell.execute_reply.started":"2024-07-19T04:03:48.267233Z","shell.execute_reply":"2024-07-19T04:03:48.273137Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import shutil\n# /kaggle/input/ のcsvファイルを/kaggle/workingにコピーする\nshutil.copyfile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/sample_submission.csv', '/kaggle/working/sample_submission.csv')","metadata":{"execution":{"iopub.status.busy":"2024-07-19T04:03:48.277054Z","iopub.execute_input":"2024-07-19T04:03:48.277781Z","iopub.status.idle":"2024-07-19T04:03:48.323828Z","shell.execute_reply.started":"2024-07-19T04:03:48.277754Z","shell.execute_reply":"2024-07-19T04:03:48.322978Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import zipfile\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()","metadata":{"execution":{"iopub.status.busy":"2024-07-19T04:03:48.325294Z","iopub.execute_input":"2024-07-19T04:03:48.325664Z","iopub.status.idle":"2024-07-19T04:04:03.554595Z","shell.execute_reply.started":"2024-07-19T04:03:48.325634Z","shell.execute_reply":"2024-07-19T04:04:03.553818Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import glob\n# '/kaggle/working/' に入っているファイルを確認\nfiles = glob.glob('/kaggle/working/*')\nfor file in files: \n    print(file)","metadata":{"execution":{"iopub.status.busy":"2024-07-19T04:04:03.555669Z","iopub.execute_input":"2024-07-19T04:04:03.555991Z","iopub.status.idle":"2024-07-19T04:04:03.561719Z","shell.execute_reply.started":"2024-07-19T04:04:03.555966Z","shell.execute_reply":"2024-07-19T04:04:03.560880Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 犬・猫の画像を別々のフォルダに保存する\ntrainフォルダの下に、dogフォルダ・catフォルダを作成する。\n犬・猫の画像が、それぞれのクラスのフォルダに所属するよう移動させる。","metadata":{}},{"cell_type":"code","source":"# 訓練用フォルダ内に作成する、dogフォルダ・catフォルダのパス\nd_dir = '/kaggle/working/train/dog/'\nc_dir = '/kaggle/working/train/cat/'\n\n# dogフォルダ・catフォルダが存在しない場合は、新たに作成する\nif os.path.isdir(d_dir) == False:\n    os.mkdir(d_dir)\nif os.path.isdir(c_dir) == False:\n    os.mkdir(c_dir)","metadata":{"execution":{"iopub.status.busy":"2024-07-19T04:04:03.562882Z","iopub.execute_input":"2024-07-19T04:04:03.563192Z","iopub.status.idle":"2024-07-19T04:04:03.574928Z","shell.execute_reply.started":"2024-07-19T04:04:03.563164Z","shell.execute_reply":"2024-07-19T04:04:03.574091Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# trainフォルダの全.jpgパスを取得\nfiles = glob.glob('/kaggle/working/train/*.jpg') \n\n# 犬・猫の画像をそれぞれのフォルダに移動する\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":{"execution":{"iopub.status.busy":"2024-07-19T04:04:03.576204Z","iopub.execute_input":"2024-07-19T04:04:03.576524Z","iopub.status.idle":"2024-07-19T04:04:04.422058Z","shell.execute_reply.started":"2024-07-19T04:04:03.576496Z","shell.execute_reply":"2024-07-19T04:04:04.421285Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## trainフォルダ内の犬・猫の画像を2500枚ずつ検証用フォルダに移動\n「訓練データ：検証データ = 8 : 2」の割合となるように、犬・猫の画像を移動させる。","metadata":{}},{"cell_type":"code","source":"# 検証用フォルダの作成\nvalid = '/kaggle/working/valid/'\nd_valid = '/kaggle/working/valid/dog/'\nc_valid = '/kaggle/working/valid/cat/'\n\n# 検証用フォルダが存在しない場合は、新たに作成する\nif os.path.isdir(valid) == False:\n    os.mkdir(valid)\nif os.path.isdir(d_valid) == False:\n    os.mkdir(d_valid)\nif os.path.isdir(c_valid) == False:\n    os.mkdir(c_valid)","metadata":{"execution":{"iopub.status.busy":"2024-07-19T04:04:04.423307Z","iopub.execute_input":"2024-07-19T04:04:04.423748Z","iopub.status.idle":"2024-07-19T04:04:04.429993Z","shell.execute_reply.started":"2024-07-19T04:04:04.423715Z","shell.execute_reply":"2024-07-19T04:04:04.429111Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n# /train/dog/フォルダ内から，検証用フォルダに移動する2500枚を選ぶ\nd_files = glob.glob('/kaggle/working/train/dog/*.jpg')\nprint(len(d_files))\nd_train, d_valid = train_test_split(d_files, test_size = 0.2, random_state = 100)\nprint(len(d_valid))\ncount = 0\n# 選んだ2500枚を検証用フォルダに移動する\nfor file in d_valid:\n    count += 1 \n    d_name = os.path.basename(file)\n    name = d_name.split('/')[0]\n    shutil.move(file, '/kaggle/working/valid/dog/' + str(name))\n\nprint(count)","metadata":{"execution":{"iopub.status.busy":"2024-07-19T04:04:04.433430Z","iopub.execute_input":"2024-07-19T04:04:04.433753Z","iopub.status.idle":"2024-07-19T04:04:05.343988Z","shell.execute_reply.started":"2024-07-19T04:04:04.433723Z","shell.execute_reply":"2024-07-19T04:04:05.343111Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# /train/cat/フォルダ内から，検証用フォルダに移動する2500枚を選ぶ\nc_files = glob.glob('/kaggle/working/train/cat/*.jpg')\nprint(len(c_files))\nc_train, c_valid = train_test_split(c_files, test_size = 0.2, random_state = 100)\nprint(len(c_valid))\ncount = 0\n# 選んだ2500枚を検証用フォルダに移動する\nfor file in c_valid:\n    count += 1 \n    c_name = os.path.basename(file)\n    name = c_name.split('/')[0]\n    shutil.move(file, '/kaggle/working/valid/cat/' + str(name))\n\nprint(count)","metadata":{"execution":{"iopub.status.busy":"2024-07-19T04:04:05.345123Z","iopub.execute_input":"2024-07-19T04:04:05.345398Z","iopub.status.idle":"2024-07-19T04:04:05.478312Z","shell.execute_reply.started":"2024-07-19T04:04:05.345375Z","shell.execute_reply":"2024-07-19T04:04:05.477507Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 学習モデルを構築する\n今回使用するVGG16モデルを構築する。","metadata":{}},{"cell_type":"code","source":"# パッケージをインポートする\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nimport torch.nn as nn\nimport time","metadata":{"execution":{"iopub.status.busy":"2024-07-19T04:04:05.479254Z","iopub.execute_input":"2024-07-19T04:04:05.479517Z","iopub.status.idle":"2024-07-19T04:04:07.076451Z","shell.execute_reply.started":"2024-07-19T04:04:05.479495Z","shell.execute_reply":"2024-07-19T04:04:07.075605Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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":{"execution":{"iopub.status.busy":"2024-07-19T04:04:07.077571Z","iopub.execute_input":"2024-07-19T04:04:07.077980Z","iopub.status.idle":"2024-07-19T04:04:07.091668Z","shell.execute_reply.started":"2024-07-19T04:04:07.077954Z","shell.execute_reply":"2024-07-19T04:04:07.090646Z"},"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":{"execution":{"iopub.status.busy":"2024-07-19T04:04:07.092768Z","iopub.execute_input":"2024-07-19T04:04:07.093072Z","iopub.status.idle":"2024-07-19T04:04:07.163346Z","shell.execute_reply.started":"2024-07-19T04:04:07.093049Z","shell.execute_reply":"2024-07-19T04:04:07.162658Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 訓練データの前処理\nPyTorchのDataset・DataLoaderを使用する。","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":{"execution":{"iopub.status.busy":"2024-07-19T04:04:07.164596Z","iopub.execute_input":"2024-07-19T04:04:07.165206Z","iopub.status.idle":"2024-07-19T04:04:07.171501Z","shell.execute_reply.started":"2024-07-19T04:04:07.165173Z","shell.execute_reply":"2024-07-19T04:04:07.170515Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"d_train = glob.glob('/kaggle/working/train/dog/*.jpg')\nc_train = glob.glob('/kaggle/working/train/cat/*.jpg')\nd_valid = glob.glob('/kaggle/working/valid/dog/*.jpg')\nc_valid = glob.glob('/kaggle/working/valid/cat/*.jpg')\n\ntrain_data = Dataset(d_train+c_train, transform=transforms.Compose([transforms.Resize([256, 256]), transforms.ToTensor()]))\nvalid_data = Dataset(d_valid+c_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":{"execution":{"iopub.status.busy":"2024-07-19T04:04:07.172537Z","iopub.execute_input":"2024-07-19T04:04:07.172874Z","iopub.status.idle":"2024-07-19T04:04:07.271002Z","shell.execute_reply.started":"2024-07-19T04:04:07.172852Z","shell.execute_reply":"2024-07-19T04:04:07.270337Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 深層学習モデルの学習\n訓練データと検証データを用いて、犬・猫分類に最適なモデルになるように学習を行う。","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        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} train_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":"2024-07-19T04:04:07.271936Z","iopub.execute_input":"2024-07-19T04:04:07.272191Z","iopub.status.idle":"2024-07-19T04:04:07.285705Z","shell.execute_reply.started":"2024-07-19T04:04:07.272169Z","shell.execute_reply":"2024-07-19T04:04:07.284721Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from PIL import Image\n\nepochs = 100\nhistory = train(cnn, epochs, train_dl, valid_dl)\ntorch.save(cnn, 'model_weight.pth')","metadata":{"execution":{"iopub.status.busy":"2024-07-19T04:04:07.286746Z","iopub.execute_input":"2024-07-19T04:04:07.287040Z","iopub.status.idle":"2024-07-19T06:48:01.446002Z","shell.execute_reply.started":"2024-07-19T04:04:07.287018Z","shell.execute_reply":"2024-07-19T06:48:01.444904Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 深層学習モデルの学習結果\n訓練データと検証データのAccuracy・ロスのグラフを表示する。","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\ndpi = 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":{"execution":{"iopub.status.busy":"2024-07-19T06:48:01.447804Z","iopub.execute_input":"2024-07-19T06:48:01.448730Z","iopub.status.idle":"2024-07-19T06:48:02.057045Z","shell.execute_reply.started":"2024-07-19T06:48:01.448671Z","shell.execute_reply":"2024-07-19T06:48:02.056084Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"結果のグラフを見ると、検証データのAccuracy・ロスの値がともに収束していることがわかる。\nこのことから、VGG16モデルの学習は上手くいったといえる。","metadata":{}},{"cell_type":"markdown","source":"## テストデータの前処理\n訓練データ・検証データで行った前処理と同じものを、テストデータに対しても行う。","metadata":{}},{"cell_type":"code","source":"test_id = []\nt_files = glob.glob('/kaggle/working/test/*.jpg')\nfor no in range(len(t_files)):\n    t_id = int(t_files[no].split('/')[-1].split('.')[0])\n    test_id.append(t_id)\n\ntest_data = Dataset(t_files, 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)","metadata":{"execution":{"iopub.status.busy":"2024-07-19T06:48:02.058393Z","iopub.execute_input":"2024-07-19T06:48:02.058787Z","iopub.status.idle":"2024-07-19T06:48:02.123920Z","shell.execute_reply.started":"2024-07-19T06:48:02.058754Z","shell.execute_reply":"2024-07-19T06:48:02.123164Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## テストデータの推論\n学習したVGG16モデルを使用して、テストデータを推論する。\n推論した結果は、「画像名についている数字」と「その画像が犬である確率」の2つを、.csvファイルに保存する。","metadata":{}},{"cell_type":"code","source":"import csv\nVGG16 = torch.load('model_weight.pth')\n\nVGG16.eval()\n\npredictions = []\nwith torch.no_grad():\n    for x_batch, _ in test_dl:\n        x_batch = x_batch.to(device)\n        pred = VGG16(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        write_id = test_id[i]\n        writer.writerow([write_id, pred])","metadata":{"execution":{"iopub.status.busy":"2024-07-19T06:54:42.902736Z","iopub.execute_input":"2024-07-19T06:54:42.903443Z","iopub.status.idle":"2024-07-19T06:55:31.466779Z","shell.execute_reply.started":"2024-07-19T06:54:42.903407Z","shell.execute_reply":"2024-07-19T06:55:31.465540Z"},"trusted":true},"execution_count":null,"outputs":[]}]}