{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","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":31041,"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\nimport numpy as np # linear algebra\nimport 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\nimport os\nfor 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":{"iopub.status.busy":"2025-07-18T08:18:50.808330Z","iopub.execute_input":"2025-07-18T08:18:50.809390Z","iopub.status.idle":"2025-07-18T08:18:51.229484Z","shell.execute_reply.started":"2025-07-18T08:18:50.809355Z","shell.execute_reply":"2025-07-18T08:18:51.228750Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## モジュールのインポート","metadata":{}},{"cell_type":"code","source":"import os, glob, copy, zipfile\nfrom PIL import Image\nfrom tqdm import tqdm\nfrom sklearn.model_selection import train_test_split\nimport torch\nimport torch.nn as nn\nimport torch.utils.data as data\nimport torch.nn.functional as F\nfrom torchvision import models, transforms","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:18:51.230750Z","iopub.execute_input":"2025-07-18T08:18:51.231178Z","iopub.status.idle":"2025-07-18T08:18:54.957872Z","shell.execute_reply.started":"2025-07-18T08:18:51.231154Z","shell.execute_reply":"2025-07-18T08:18:54.957104Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## パラメータ設定","metadata":{}},{"cell_type":"code","source":"mean = (0.485, 0.456, 0.406) #ImageNetデータセットの平均値\nstd = (0.229, 0.224, 0.225)  #ImageNetデータセットの標準偏差\nbatch_size = 32  #バッチサイズ\nlr = 0.001  #学習率\nepochs = 3 #エポック数(この値を小さくすると実行時間を短縮できます)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:18:54.958667Z","iopub.execute_input":"2025-07-18T08:18:54.959109Z","iopub.status.idle":"2025-07-18T08:18:54.963659Z","shell.execute_reply.started":"2025-07-18T08:18:54.959084Z","shell.execute_reply":"2025-07-18T08:18:54.962976Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## データの用意","metadata":{}},{"cell_type":"code","source":"# データを初期化\n!rm -rf ../data","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:18:54.965070Z","iopub.execute_input":"2025-07-18T08:18:54.965362Z","iopub.status.idle":"2025-07-18T08:18:55.120818Z","shell.execute_reply.started":"2025-07-18T08:18:54.965345Z","shell.execute_reply":"2025-07-18T08:18:55.119997Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# zipファイルのディレクトリ\ndir_zip = '../input/dogs-vs-cats-redux-kernels-edition'\n# データを格納するディレクトリを作成\nos.makedirs('../data', exist_ok=True)\n\n# 学習データのzipファイルを展開\nwith zipfile.ZipFile(os.path.join(dir_zip, 'train.zip')) as train_zip:\n    train_zip.extractall('../data')\n    \n# 推論データのzipファイルを展開\nwith zipfile.ZipFile(os.path.join(dir_zip, 'test.zip')) as test_zip:\n    test_zip.extractall('../data')\n\n# 展開されたディレクトリから画像データのリストを作成\ntrain_list = glob.glob(os.path.join('../data/train', '*.jpg'))\ntest_list = glob.glob(os.path.join('../data/test', '*.jpg'))\n\n# 学習データから検証データを分割(8:2)\ntrain_list, val_list = train_test_split(train_list, test_size=0.2)\n\nprint(f'Train Data:{len(train_list)}')\nprint(f'Validation Data:{len(val_list)}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:18:55.121857Z","iopub.execute_input":"2025-07-18T08:18:55.122174Z","iopub.status.idle":"2025-07-18T08:19:11.768714Z","shell.execute_reply.started":"2025-07-18T08:18:55.122142Z","shell.execute_reply":"2025-07-18T08:19:11.767960Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# データの前処理の機能\n\n# 学習データ（画像サイズ変換、ランダム反転、テンソル変換、正規化）\ntrain_transforms = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomVerticalFlip(),\n    transforms.ToTensor(),\n    transforms.Normalize(mean, std)\n])\n\n# 推論データ（画像サイズ変換、テンソル変換、正規化）\nval_transforms = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean, std)\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:19:11.769523Z","iopub.execute_input":"2025-07-18T08:19:11.769803Z","iopub.status.idle":"2025-07-18T08:19:11.776164Z","shell.execute_reply.started":"2025-07-18T08:19:11.769781Z","shell.execute_reply":"2025-07-18T08:19:11.775276Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 画像データにラベル(0,1)を付ける機能\n\nclass CatDogDataset(data.Dataset):\n    # 初期化\n    def __init__(self, file_list, transform=None):\n        self.file_list = file_list\n        self.transform = transform\n    \n    # データセットのサイズ\n    def __len__(self):\n        return len(self.file_list)\n    \n    # ラベル付け\n    def __getitem__(self, idx):\n        img_path = self.file_list[idx]\n        img = Image.open(img_path)\n        img_transformed = self.transform(img)\n        \n        # ファイル名からラベルを取得\n        label = img_path.split('/')[-1].split('.')[0]\n        if label == 'dog': #ファイル名がdogであれば 1\n            label = 1\n        elif label == 'cat':#ファイル名がcatであれば 0\n            label = 0\n\n        return img_transformed, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:19:11.777877Z","iopub.execute_input":"2025-07-18T08:19:11.778391Z","iopub.status.idle":"2025-07-18T08:19:11.948493Z","shell.execute_reply.started":"2025-07-18T08:19:11.778368Z","shell.execute_reply":"2025-07-18T08:19:11.947628Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# データの前処理\ntrain_dataset = CatDogDataset(train_list, transform=train_transforms)\nval_dataset = CatDogDataset(val_list, transform=val_transforms)\n\n# データの読み込み\ntrain_loader = data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True)\nval_loader = data.DataLoader(val_dataset, batch_size=batch_size, shuffle=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:19:11.949395Z","iopub.execute_input":"2025-07-18T08:19:11.949717Z","iopub.status.idle":"2025-07-18T08:19:11.967407Z","shell.execute_reply.started":"2025-07-18T08:19:11.949696Z","shell.execute_reply":"2025-07-18T08:19:11.966744Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## モデルの用意","metadata":{}},{"cell_type":"code","source":"# ResNet-18の事前学習済みモデル\nweights = models.ResNet18_Weights.DEFAULT\nmodel = models.resnet18(weights=weights)\n    \n# モデルの全結合層を追加して2クラス分類に変更\nnum_classes = 2\nmodel.fc = nn.Linear(model.fc.in_features, num_classes)\n\n# パラメータを更新する/固定する層を決定\nupdate_params = 'layer4'  #更新したい層の名前（タプルで複数指定しても可）\nfor name, param in model.named_parameters():\n    if name.startswith(update_params):\n        param.requires_grad = True\n    else:\n        param.requires_grad = False\n\n# GPU\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel = model.to(device)\n\n# 損失関数\ncriterion = nn.CrossEntropyLoss() #クロスエントロピー\n# 最適化手法\nparam_list = list(filter(lambda p: p.requires_grad, model.parameters()))\noptimizer = torch.optim.Adam(param_list, lr=lr) #更新する層だけ指定","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:19:11.968330Z","iopub.execute_input":"2025-07-18T08:19:11.968575Z","iopub.status.idle":"2025-07-18T08:19:12.751337Z","shell.execute_reply.started":"2025-07-18T08:19:11.968557Z","shell.execute_reply":"2025-07-18T08:19:12.750551Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 学習","metadata":{}},{"cell_type":"code","source":"#学習曲線描画のため、ログを取得\ntrain_acc_list = [0.70, 0.80, 0.88]   \nval_acc_list =   [0.65, 0.75, 0.78]   \ntrain_loss_list = [0.90, 0.60, 0.35]  \nval_loss_list =   [0.95, 0.70, 0.68]  \n\n# 最良モデルと精度の初期化\nbest_model = copy.deepcopy(model.state_dict())\nbest_accuracy = 0.0\n\n# 学習\nfor epoch in range(epochs):\n    model.train()\n    epoch_loss = 0\n    epoch_accuracy = 0\n\n    for data, label in tqdm(train_loader):\n        \n        data = data.to(device)\n        label = label.to(device)\n\n        output = model(data)\n        loss = criterion(output, label)\n\n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n\n        acc = (output.argmax(dim=1) == label).float().mean()\n        epoch_accuracy  += acc\n        epoch_loss      += loss.item()\n    epoch_accuracy  /= len(train_loader)\n    epoch_loss      /= len(train_loader)\n\n    with torch.no_grad():\n        model.eval()\n        epoch_val_accuracy = 0\n        epoch_val_loss = 0\n\n        for data, label in val_loader:\n            data = data.to(device)\n            label = label.to(device)\n\n            val_output = model(data)\n            val_loss = criterion(val_output, label)\n\n            acc = (val_output.argmax(dim=1) == label).float().mean()\n            epoch_val_accuracy  += acc\n            epoch_val_loss      += val_loss.item()\n        epoch_val_accuracy  /= len(val_loader)\n        epoch_val_loss      /= len(val_loader)\n    \n    print(\n        f\"Epoch : {epoch+1} - loss : {epoch_loss:.4f} - acc: {epoch_accuracy:.4f} - val_loss : {epoch_val_loss:.4f} - val_acc: {epoch_val_accuracy:.4f}\\n\"\n    )\n    # 精度が最も良いならモデルを保存\n    if epoch_val_accuracy > best_accuracy:\n        best_accuracy = epoch_val_accuracy\n        best_model = copy.deepcopy(model.state_dict())\n\n    train_acc_list.append(epoch_accuracy)\n    val_acc_list.append(epoch_val_accuracy)\n    train_loss_list.append(epoch_loss)\n    val_loss_list.append(epoch_val_loss)\n\ntorch.save(best_model, '../working/model.pth')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:19:12.753944Z","iopub.execute_input":"2025-07-18T08:19:12.754564Z","iopub.status.idle":"2025-07-18T08:24:06.872590Z","shell.execute_reply.started":"2025-07-18T08:19:12.754544Z","shell.execute_reply":"2025-07-18T08:24:06.871881Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# 空リストを用意\ntrain_acc_list = []\nval_acc_list = []\ntrain_loss_list = []\nval_loss_list = []\n\n# 仮のデータを追加（本来は学習時に.appendで追加する）\ntrain_acc_list = [0.70, 0.80, 0.88]\nval_acc_list = [0.65, 0.75, 0.78]\ntrain_loss_list = [0.90, 0.60, 0.35]\nval_loss_list = [0.95, 0.70, 0.68]\n\n# グラフ描画\nepochs = range(1, len(train_acc_list) + 1)\n\nplt.figure(figsize=(12, 5))\n\n# Loss\nplt.subplot(1, 2, 1)\nplt.plot(epochs, train_loss_list, 'bo-', label='Train Loss')\nplt.plot(epochs, val_loss_list, 'ro-', label='Validation Loss')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.title('Loss over Epochs')\nplt.legend()\n\n# Accuracy\nplt.subplot(1, 2, 2)\nplt.plot(epochs, train_acc_list, 'bo-', label='Train Accuracy')\nplt.plot(epochs, val_acc_list, 'ro-', label='Validation Accuracy')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.title('Accuracy over Epochs')\nplt.legend()\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:24:06.873284Z","iopub.execute_input":"2025-07-18T08:24:06.873476Z","iopub.status.idle":"2025-07-18T08:24:07.255994Z","shell.execute_reply.started":"2025-07-18T08:24:06.873461Z","shell.execute_reply":"2025-07-18T08:24:07.255249Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport matplotlib.pyplot as plt\n\n# 仮の学習データを定義（Tensorのリスト）\n# 本番では、ここを学習ループから得られる実データに置き換えてください\ntrain_loss_list = [torch.tensor(1.0), torch.tensor(0.8), torch.tensor(0.6), torch.tensor(0.5)]\nval_loss_list = [torch.tensor(1.2), torch.tensor(1.0), torch.tensor(0.7), torch.tensor(0.6)]\ntrain_acc_list = [torch.tensor(0.4), torch.tensor(0.6), torch.tensor(0.7), torch.tensor(0.8)]\nval_acc_list = [torch.tensor(0.3), torch.tensor(0.5), torch.tensor(0.65), torch.tensor(0.75)]\n\n# Tensor → NumPy へ変換（CPU上のテンソル前提）\ntrain_loss_np = [t.cpu().numpy() for t in train_loss_list]\nval_loss_np = [t.cpu().numpy() for t in val_loss_list]\ntrain_acc_np = [t.cpu().numpy() for t in train_acc_list]\nval_acc_np = [t.cpu().numpy() for t in val_acc_list]\n\n# エポック数\nepochs = range(1, len(train_loss_np) + 1)\n\nimport torch\nimport matplotlib.pyplot as plt\n\n# 仮の学習データを定義（Tensorのリスト）\n# 本番では、ここを学習ループから得られる実データに置き換えてください\ntrain_loss_list = [torch.tensor(1.0), torch.tensor(0.8), torch.tensor(0.6), torch.tensor(0.5)]\nval_loss_list = [torch.tensor(1.2), torch.tensor(1.0), torch.tensor(0.7), torch.tensor(0.6)]\ntrain_acc_list = [torch.tensor(0.4), torch.tensor(0.6), torch.tensor(0.7), torch.tensor(0.8)]\nval_acc_list = [torch.tensor(0.3), torch.tensor(0.5), torch.tensor(0.65), torch.tensor(0.75)]\n\n# Tensor → NumPy へ変換（CPU上のテンソル前提）\ntrain_loss_np = [t.cpu().numpy() for t in train_loss_list]\nval_loss_np = [t.cpu().numpy() for t in val_loss_list]\ntrain_acc_np = [t.cpu().numpy() for t in train_acc_list]\nval_acc_np = [t.cpu().numpy() for t in val_acc_list]\n\n# エポック数\nepochs = range(1, len(train_loss_np) + 1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:24:07.256846Z","iopub.execute_input":"2025-07-18T08:24:07.257074Z","iopub.status.idle":"2025-07-18T08:24:07.267456Z","shell.execute_reply.started":"2025-07-18T08:24:07.257057Z","shell.execute_reply":"2025-07-18T08:24:07.266950Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 推論","metadata":{}},{"cell_type":"code","source":"param = torch.load('../working/model.pth')\nmodel.load_state_dict(param)\nmodel = model.eval()\n\n# IDと予測値の格納用リスト\nid_list = []\npred_list = []\n\nwith torch.no_grad():\n    for test_path in tqdm(test_list):\n        # 画像とIDの取得\n        img = Image.open(test_path)\n        id_number = int(test_path.split('/')[-1].split('.')[0])\n        \n        # 推論用に画像データを変換\n        img = val_transforms(img)\n        img = img.unsqueeze(0)\n        img = img.to(device)\n\n        outputs = model(img)\n        preds = F.softmax(outputs, dim=1)[:, 1].tolist() #dogの確率\n\n        # IDと予測値をリストに追加\n        id_list.append(id_number)\n        pred_list.append(preds[0])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:24:07.268141Z","iopub.execute_input":"2025-07-18T08:24:07.268467Z","iopub.status.idle":"2025-07-18T08:25:24.631616Z","shell.execute_reply.started":"2025-07-18T08:24:07.268445Z","shell.execute_reply":"2025-07-18T08:25:24.630697Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport matplotlib.pyplot as plt\n\n# 仮の学習データを定義（Tensorのリスト）\n# 本番では、ここを学習ループから得られる実データに置き換えてください\ntrain_loss_list = [torch.tensor(1.0), torch.tensor(0.8), torch.tensor(0.6), torch.tensor(0.5)]\nval_loss_list = [torch.tensor(1.2), torch.tensor(1.0), torch.tensor(0.7), torch.tensor(0.6)]\ntrain_acc_list = [torch.tensor(0.4), torch.tensor(0.6), torch.tensor(0.7), torch.tensor(0.8)]\nval_acc_list = [torch.tensor(0.3), torch.tensor(0.5), torch.tensor(0.65), torch.tensor(0.75)]\n\n# Tensor → NumPy へ変換（CPU上のテンソル前提）\ntrain_loss_np = [t.cpu().numpy() for t in train_loss_list]\nval_loss_np = [t.cpu().numpy() for t in val_loss_list]\ntrain_acc_np = [t.cpu().numpy() for t in train_acc_list]\nval_acc_np = [t.cpu().numpy() for t in val_acc_list]\n\n# エポック数\nepochs = range(1, len(train_loss_np) + 1)\n\n# グラフ描画\nplt.figure(figsize=(12, 5))\n\n# Loss の描画\nplt.subplot(1, 2, 1)\nplt.plot(epochs, train_loss_np, 'bo-', label='Train Loss')\nplt.plot(epochs, val_loss_np, 'ro-', label='Validation Loss')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.title('Loss over Epochs')\nplt.legend()\n\n# Accuracy の描画\nplt.subplot(1, 2, 2)\nplt.plot(epochs, train_acc_np, 'bo-', label='Train Accuracy')\nplt.plot(epochs, val_acc_np, 'ro-', label='Validation Accuracy')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.title('Accuracy over Epochs')\nplt.legend()\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:25:24.632491Z","iopub.execute_input":"2025-07-18T08:25:24.633765Z","iopub.status.idle":"2025-07-18T08:25:25.020406Z","shell.execute_reply.started":"2025-07-18T08:25:24.633738Z","shell.execute_reply":"2025-07-18T08:25:25.019642Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 提出用csvファイルの作成","metadata":{}},{"cell_type":"code","source":"# サンプルファイルを利用\nsubmit = pd.read_csv(dir_zip + '/sample_submission.csv')\n\nsubmit.set_index('id', inplace=True)\nsubmit.loc[id_list, 'label'] = pred_list\nsubmit.reset_index(inplace=True)\n\nsubmit.to_csv('/kaggle/working/submission.csv', index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:25:25.021199Z","iopub.execute_input":"2025-07-18T08:25:25.021383Z","iopub.status.idle":"2025-07-18T08:25:25.088220Z","shell.execute_reply.started":"2025-07-18T08:25:25.021369Z","shell.execute_reply":"2025-07-18T08:25:25.087362Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(submit)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:25:25.089126Z","iopub.execute_input":"2025-07-18T08:25:25.089328Z","iopub.status.idle":"2025-07-18T08:25:25.101260Z","shell.execute_reply.started":"2025-07-18T08:25:25.089312Z","shell.execute_reply":"2025-07-18T08:25:25.100412Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"weights = models.ResNet18_Weights.DEFAULT\nmodel = models.resnet18(weights=weights)\nprint(model)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:25:25.102059Z","iopub.execute_input":"2025-07-18T08:25:25.102258Z","iopub.status.idle":"2025-07-18T08:25:25.298303Z","shell.execute_reply.started":"2025-07-18T08:25:25.102243Z","shell.execute_reply":"2025-07-18T08:25:25.297526Z"}},"outputs":[],"execution_count":null}]}