{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":34426,"databundleVersionId":3218632,"sourceType":"competition"}],"dockerImageVersionId":30665,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# # This Python 3 environment comes with many helpful analytics libraries installed\n# # It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# # For example, here's several helpful packages to load\n\n# import numpy as np # linear algebra\n# import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# # Input data files are available in the read-only \"../input/\" directory\n# # For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\n# import os\n# for dirname, _, filenames in os.walk('/kaggle/input'):\n#     for filename in filenames:\n#         print(os.path.join(dirname, filename))\n\n# # You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# # You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import DataLoader, Dataset, random_split\nfrom torchvision.datasets.folder import default_loader\nfrom torchvision import datasets, models, transforms\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport pandas as pd\nimport os\n%matplotlib inline","metadata":{"execution":{"iopub.status.busy":"2024-03-19T10:12:42.045158Z","iopub.execute_input":"2024-03-19T10:12:42.045650Z","iopub.status.idle":"2024-03-19T10:12:42.369343Z","shell.execute_reply.started":"2024-03-19T10:12:42.045620Z","shell.execute_reply":"2024-03-19T10:12:42.368596Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 訓練データとテストデータのpath\ntrain_path = \"./../input/dog-vs-cat-classification/train/train\"\ntest_path = \"./../input/dog-vs-cat-classification/test/test\"\n\n# バッチサイズ\nbatch_size = 64","metadata":{"execution":{"iopub.status.busy":"2024-03-19T10:16:08.932232Z","iopub.execute_input":"2024-03-19T10:16:08.932902Z","iopub.status.idle":"2024-03-19T10:16:08.937136Z","shell.execute_reply.started":"2024-03-19T10:16:08.932868Z","shell.execute_reply":"2024-03-19T10:16:08.936115Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 今回はtorchvisionの学習済みのDenseNetを使うので、それに合わせて画像サイズの変更や標準化をする\ntransform = transforms.Compose([\n    # transforms.Resizeで短いほうの長さを256にする\n    transforms.Resize(256),\n    # 辺の長さが224の正方形を切り取る\n    transforms.CenterCrop(224),\n    transforms.ToTensor(),\n    # 標準化\n    # meanとstdの値は、学習済みのmodelに合わせた(公式ドキュメント記載)\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n])","metadata":{"execution":{"iopub.status.busy":"2024-03-19T10:14:13.000468Z","iopub.execute_input":"2024-03-19T10:14:13.001095Z","iopub.status.idle":"2024-03-19T10:14:13.006947Z","shell.execute_reply.started":"2024-03-19T10:14:13.001061Z","shell.execute_reply":"2024-03-19T10:14:13.005880Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# trainデータセットの作成\ndataset = datasets.ImageFolder(train_path, transform=transform)","metadata":{"execution":{"iopub.status.busy":"2024-03-19T10:15:29.479071Z","iopub.execute_input":"2024-03-19T10:15:29.480007Z","iopub.status.idle":"2024-03-19T10:15:59.867941Z","shell.execute_reply.started":"2024-03-19T10:15:29.479970Z","shell.execute_reply":"2024-03-19T10:15:59.867133Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# データセットを学習用と検証用に分割\ntrain_size = int(0.8 * len(dataset))  # 80%を学習用とする\nval_size = len(dataset) - train_size\ntrain_dataset, val_dataset = random_split(dataset, [train_size, val_size])\n\nprint(\"train images:\", len(train_dataset))\nprint(\"val images:\", len(val_dataset))","metadata":{"execution":{"iopub.status.busy":"2024-03-19T10:16:21.608843Z","iopub.execute_input":"2024-03-19T10:16:21.609221Z","iopub.status.idle":"2024-03-19T10:16:21.640077Z","shell.execute_reply.started":"2024-03-19T10:16:21.609189Z","shell.execute_reply":"2024-03-19T10:16:21.639194Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# ラベルに対応する名前\nclasses = dataset.classes\nprint(classes)","metadata":{"execution":{"iopub.status.busy":"2024-03-19T10:16:34.851883Z","iopub.execute_input":"2024-03-19T10:16:34.852230Z","iopub.status.idle":"2024-03-19T10:16:34.857064Z","shell.execute_reply.started":"2024-03-19T10:16:34.852203Z","shell.execute_reply":"2024-03-19T10:16:34.856116Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# データローダー\ntrain_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)\nval_loader = DataLoader(dataset=val_dataset, batch_size=batch_size, shuffle=False)","metadata":{"execution":{"iopub.status.busy":"2024-03-19T10:17:07.555854Z","iopub.execute_input":"2024-03-19T10:17:07.556203Z","iopub.status.idle":"2024-03-19T10:17:07.561793Z","shell.execute_reply.started":"2024-03-19T10:17:07.556174Z","shell.execute_reply":"2024-03-19T10:17:07.560529Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 画像の確認\ntrain_iter = iter(train_loader)\nimgs, labels = next(train_iter)\nprint(imgs.size())\nprint(labels)","metadata":{"execution":{"iopub.status.busy":"2024-03-19T10:17:21.204297Z","iopub.execute_input":"2024-03-19T10:17:21.204704Z","iopub.status.idle":"2024-03-19T10:17:22.045120Z","shell.execute_reply.started":"2024-03-19T10:17:21.204672Z","shell.execute_reply":"2024-03-19T10:17:22.044094Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 画像の確認\nplt.style.use(\"dark_background\")\nplt.figure(figsize=(15, 15))\nfor i in range(36):\n    ax = plt.subplot(6, 6, i+1, xticks=[], yticks=[])\n    # 範囲外のものを0or1にする\n    imgs[i] = np.clip(imgs[i], 0, 1)\n    # チャンネルラストに\n    ax.imshow(imgs[i].permute(1, 2, 0))\n    # 画像名の取得\n    label = classes[labels[i]]\n    ax.set_title(label, fontsize=12)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-03-19T10:17:40.831364Z","iopub.execute_input":"2024-03-19T10:17:40.831986Z","iopub.status.idle":"2024-03-19T10:17:43.207359Z","shell.execute_reply.started":"2024-03-19T10:17:40.831954Z","shell.execute_reply":"2024-03-19T10:17:43.206435Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"net = models.densenet121(weights=models.DenseNet121_Weights.DEFAULT)","metadata":{"execution":{"iopub.status.busy":"2024-03-19T10:18:11.582196Z","iopub.execute_input":"2024-03-19T10:18:11.583029Z","iopub.status.idle":"2024-03-19T10:18:12.349464Z","shell.execute_reply.started":"2024-03-19T10:18:11.582995Z","shell.execute_reply":"2024-03-19T10:18:12.348495Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 一旦すべての層を訓練不可にする\nfor param in net.parameters():\n    param.requires_grad = False","metadata":{"execution":{"iopub.status.busy":"2024-03-19T10:18:24.124903Z","iopub.execute_input":"2024-03-19T10:18:24.125658Z","iopub.status.idle":"2024-03-19T10:18:24.132332Z","shell.execute_reply.started":"2024-03-19T10:18:24.125626Z","shell.execute_reply":"2024-03-19T10:18:24.131382Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 最後のclassifier層のみ2クラス分類に対応するように入れ替える\nnet.classifier = nn.Linear(in_features=1024, out_features=2)\n# モデルをcudaに渡す\nnet.cuda()\nprint(net)","metadata":{"execution":{"iopub.status.busy":"2024-03-19T10:18:42.705577Z","iopub.execute_input":"2024-03-19T10:18:42.706438Z","iopub.status.idle":"2024-03-19T10:18:42.963943Z","shell.execute_reply.started":"2024-03-19T10:18:42.706406Z","shell.execute_reply":"2024-03-19T10:18:42.962986Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 損失関数\ncriterion = nn.CrossEntropyLoss()\n# 最適化アルゴリズム\noptimizer = optim.Adam(params=net.parameters(), lr=0.001)","metadata":{"execution":{"iopub.status.busy":"2024-03-19T10:19:03.081863Z","iopub.execute_input":"2024-03-19T10:19:03.082248Z","iopub.status.idle":"2024-03-19T10:19:03.090811Z","shell.execute_reply.started":"2024-03-19T10:19:03.082218Z","shell.execute_reply":"2024-03-19T10:19:03.089754Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 学習\nnum_epochs = 20\n\nlosses = []\naccs = []\nt_losses = []\nt_accs = []\n\nfor epoch in range(num_epochs):\n    # 訓練モード\n    net.train()\n    running_loss = 0.0\n    running_acc = 0.0\n    for i, (imgs, labels) in enumerate(train_loader):\n        # cudaにデータを渡す\n        imgs, labels = imgs.cuda(), labels.cuda()\n        # 最適化アルゴリズムの初期化\n        optimizer.zero_grad()\n        # 分類\n        outputs = net(imgs)\n        # 損失の計算\n        loss = criterion(outputs, labels)\n        running_loss += loss.item()\n        # 精度の計算\n        acc = torch.argmax(outputs, dim=1)\n        running_acc += torch.mean(acc.eq(labels).float())\n        # 誤差逆伝播\n        loss.backward()\n        # 重みの更新\n        optimizer.step()\n    # lossの平均化とリストに格納\n    running_loss /= i+1\n    losses.append(running_loss)\n    # accの平均化とリストに格納\n    running_acc /= i+1\n    accs.append(running_acc.cpu())\n\n    # 評価モード\n    net.eval()\n    t_running_loss = 0.0\n    t_running_acc = 0.0\n    with torch.no_grad():\n        for j, (t_imgs, t_labels) in enumerate(val_loader):\n            t_imgs, t_labels = t_imgs.cuda(), t_labels.cuda()\n            t_outputs = net(t_imgs)\n            t_loss = criterion(t_outputs, t_labels)\n            t_running_loss += t_loss.item()\n            t_acc = torch.argmax(t_outputs, dim=1)\n            t_running_acc += torch.mean(t_acc.eq(t_labels).float())\n        t_running_loss /= j+1\n        t_losses.append(t_running_loss)\n        t_running_acc /= j+1\n        t_accs.append(t_running_acc.cpu())\n    \n    # 損失や精度の表示\n    print(f\"epoch: {epoch}, train loss: {running_loss:.4f}, train acc: {running_acc:.4f}\")\n    print(f\"epoch: {epoch}, val loss: {t_running_loss:.4f}, val acc: {t_running_acc:.4f}\")\n    print(\"=\"*100)","metadata":{"execution":{"iopub.status.busy":"2024-03-19T10:19:46.084352Z","iopub.execute_input":"2024-03-19T10:19:46.085060Z","iopub.status.idle":"2024-03-19T11:27:53.788801Z","shell.execute_reply.started":"2024-03-19T10:19:46.085029Z","shell.execute_reply":"2024-03-19T11:27:53.787894Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 誤差の推移\nplt.style.use(\"ggplot\")\nplt.plot(losses, label=\"train\")\nplt.plot(t_losses, label=\"test\")\nplt.legend()\nplt.xlabel(\"epochs\")\nplt.ylabel(\"loss\")\nplt.title(\"Loss vs Epoch\")\nplt.xticks(torch.arange(0, 21, 1))\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-03-19T11:28:53.645430Z","iopub.execute_input":"2024-03-19T11:28:53.646109Z","iopub.status.idle":"2024-03-19T11:28:53.979062Z","shell.execute_reply.started":"2024-03-19T11:28:53.646073Z","shell.execute_reply":"2024-03-19T11:28:53.978021Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 精度の推移\nplt.plot(accs, label=\"train\")\nplt.plot(t_accs, label=\"test\")\nplt.legend()\nplt.xlabel(\"epochs\")\nplt.ylabel(\"acuracy\")\nplt.title(\"Accuracy vs Epoch\")\nplt.xticks(torch.arange(0, 21, 1))\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-03-19T11:28:57.483448Z","iopub.execute_input":"2024-03-19T11:28:57.483838Z","iopub.status.idle":"2024-03-19T11:28:57.788158Z","shell.execute_reply.started":"2024-03-19T11:28:57.483804Z","shell.execute_reply":"2024-03-19T11:28:57.787244Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# カスタムデータセットを作成する\nclass TestDataset(Dataset):\n    \n    def __init__(self, root, transforms=None):\n        self.transforms = transforms\n        # 指定されたpathにあるすべてのjpgファイルを取得してリストに保存する\n        # img_pathsはリスト\n        self.img_paths = sorted(os.path.join(root, x) for x in os.listdir(root) if x.endswith('.jpg'))\n\n        \n    def __len__(self):\n        # データセット内の画像の個数\n        return len(self.img_paths)\n    \n    # 画像に通し番号(idx)を振って、順番にアクセスして前処理する\n    def __getitem__(self, idx):\n        # img_pathに対応する画像ファイル対応するパスをimg_pathsから取得\n        img_path = self.img_paths[idx]\n        # default_loader関数はimg_pathから画像を読み込んでtensorで返す\n        img = default_loader(img_path)\n        # もし前処理が指定されていれば、前処理を行う\n        if self.transforms is not None:\n            img = self.transforms(img)\n        # 画像と画像ファイルの名前のタプルを返す\n        return img, img_path.split('/')[-1]","metadata":{"execution":{"iopub.status.busy":"2024-03-19T11:29:02.907783Z","iopub.execute_input":"2024-03-19T11:29:02.908150Z","iopub.status.idle":"2024-03-19T11:29:02.916375Z","shell.execute_reply.started":"2024-03-19T11:29:02.908121Z","shell.execute_reply":"2024-03-19T11:29:02.915114Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# データセット\ntest_dataset = TestDataset(root=test_path, transforms=transform)\n# データローダー\ntest_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False)","metadata":{"execution":{"iopub.status.busy":"2024-03-19T11:29:07.773463Z","iopub.execute_input":"2024-03-19T11:29:07.774303Z","iopub.status.idle":"2024-03-19T11:29:08.456683Z","shell.execute_reply.started":"2024-03-19T11:29:07.774268Z","shell.execute_reply":"2024-03-19T11:29:08.455680Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"net.eval()\nt_result_arr = np.array([])\nt_img_paths = np.array([])\n\nwith torch.no_grad():\n    for j, (t_imgs, t_img_path) in enumerate(test_loader):\n        # 画像をgpuに渡す\n        t_imgs = t_imgs.cuda()\n        # モデルにt_imgsを渡して推論結果を取得\n        t_outputs = net(t_imgs)\n        # モデルの出力(t_outputs)から予想されたlabelを取得\n        t_preds = torch.argmax(t_outputs, dim=1)\n        # 予測されたlabelをt_result_arrに格納\n        # その際にcpuに移して勾配を外して、numpyにする\n        t_result_arr = np.append(t_result_arr, t_preds.cpu().detach().numpy())\n        # テストデータの各画像のpathをt_img_pathsに格納\n        t_img_paths = np.append(t_img_paths, t_img_path)","metadata":{"execution":{"iopub.status.busy":"2024-03-19T11:29:10.987405Z","iopub.execute_input":"2024-03-19T11:29:10.987781Z","iopub.status.idle":"2024-03-19T11:31:14.190345Z","shell.execute_reply.started":"2024-03-19T11:29:10.987750Z","shell.execute_reply":"2024-03-19T11:31:14.189552Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.style.use(\"dark_background\")\nplt.figure(figsize=(15, 15))\nfor i in range(batch_size):\n    plt.subplot(8, 8, i+1)\n    t_img = plt.imread(os.path.join(test_path, t_img_paths[i]))\n    plt.imshow(t_img)\n    plt.axis(\"off\")\n    title = classes[int(t_result_arr[i])]\n    plt.title(f\"predict: {title}\")","metadata":{"execution":{"iopub.status.busy":"2024-03-19T11:31:25.468230Z","iopub.execute_input":"2024-03-19T11:31:25.468625Z","iopub.status.idle":"2024-03-19T11:31:31.748390Z","shell.execute_reply.started":"2024-03-19T11:31:25.468590Z","shell.execute_reply":"2024-03-19T11:31:31.747193Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}