{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.11.11"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":5441,"databundleVersionId":38425,"sourceType":"competition"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false},"papermill":{"default_parameters":{},"duration":5937.853598,"end_time":"2025-07-09T08:52:49.650899","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2025-07-09T07:13:51.797301","version":"2.6.0"}},"nbformat_minor":4,"nbformat":4,"cells":[{"id":"9499625d","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":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","execution":{"iopub.execute_input":"2025-07-09T07:13:57.238952Z","iopub.status.busy":"2025-07-09T07:13:57.238662Z","iopub.status.idle":"2025-07-09T07:13:59.186601Z","shell.execute_reply":"2025-07-09T07:13:59.185710Z"},"papermill":{"duration":1.955533,"end_time":"2025-07-09T07:13:59.188223","exception":false,"start_time":"2025-07-09T07:13:57.232690","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"9e8dd4aa","cell_type":"code","source":"import glob\nimport os\nimport random\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torch.utils.data as data\nimport torchvision.transforms as transforms\nimport torchvision.models as models\nfrom PIL import Image\nfrom sklearn.model_selection import train_test_split\nimport time\nimport copy\nfrom tqdm import tqdm\nimport matplotlib.pyplot as plt\n\n# シード固定（再現性確保）\ndef set_seed(seed=42):\n    random.seed(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed_all(seed)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = False\n\nset_seed()\n","metadata":{"execution":{"iopub.execute_input":"2025-07-09T07:13:59.198820Z","iopub.status.busy":"2025-07-09T07:13:59.197893Z","iopub.status.idle":"2025-07-09T07:14:12.955896Z","shell.execute_reply":"2025-07-09T07:14:12.955074Z"},"papermill":{"duration":13.764714,"end_time":"2025-07-09T07:14:12.957462","exception":false,"start_time":"2025-07-09T07:13:59.192748","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"138354c9","cell_type":"code","source":"size = 224\nmean = (0.485, 0.456, 0.406)\nstd = (0.229, 0.224, 0.225)\nbatch_size = 64\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nnum_epochs = 8\n","metadata":{"execution":{"iopub.execute_input":"2025-07-09T07:14:12.967689Z","iopub.status.busy":"2025-07-09T07:14:12.966806Z","iopub.status.idle":"2025-07-09T07:14:12.974100Z","shell.execute_reply":"2025-07-09T07:14:12.973395Z"},"papermill":{"duration":0.013566,"end_time":"2025-07-09T07:14:12.975471","exception":false,"start_time":"2025-07-09T07:14:12.961905","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"566ebd5c","cell_type":"code","source":"# データを初期化\n!rm -rf ../data","metadata":{"execution":{"iopub.execute_input":"2025-07-09T07:14:12.985220Z","iopub.status.busy":"2025-07-09T07:14:12.984342Z","iopub.status.idle":"2025-07-09T07:14:13.119239Z","shell.execute_reply":"2025-07-09T07:14:13.117949Z"},"papermill":{"duration":0.141708,"end_time":"2025-07-09T07:14:13.121190","exception":false,"start_time":"2025-07-09T07:14:12.979482","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"271135c4","cell_type":"code","source":"import zipfile\n\n# データの解凍\nbase_dir = '../input/dogs-vs-cats-redux-kernels-edition'\ntrain_dir = '../data/train'\ntest_dir = '../data/test'\n\nos.makedirs('../data', exist_ok=True)\n\n# 学習データの解凍\nwith zipfile.ZipFile(os.path.join(base_dir, 'train.zip')) as train_zip:\n    train_zip.extractall('../data')\n    \n#推論データの解凍\nwith zipfile.ZipFile(os.path.join(base_dir, 'test.zip')) as test_zip:\n    test_zip.extractall('../data')\n\n\ntrain_list = glob.glob(os.path.join(train_dir, '*.jpg'))\ntest_list = glob.glob(os.path.join(test_dir, '*.jpg'))\n\n# 学習データから検証データを分割(8:2)\ntrain_list, val_list = train_test_split(train_list, test_size=0.2)\n# ★画像数を制限して高速化したい場合（例：学習800枚、検証200枚）\ntrain_list = train_list[:800]\nval_list = val_list[:200]\n","metadata":{"execution":{"iopub.execute_input":"2025-07-09T07:14:13.130963Z","iopub.status.busy":"2025-07-09T07:14:13.130618Z","iopub.status.idle":"2025-07-09T07:14:29.011292Z","shell.execute_reply":"2025-07-09T07:14:29.010461Z"},"papermill":{"duration":15.887592,"end_time":"2025-07-09T07:14:29.012974","exception":false,"start_time":"2025-07-09T07:14:13.125382","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"026bafac","cell_type":"code","source":"class ImageTransform:\n    def __init__(self, resize, mean, std):\n        self.transforms_dict = {\n            'train': transforms.Compose([\n                transforms.RandomHorizontalFlip(p=0.5),\n                transforms.RandomVerticalFlip(p=0.2),\n                transforms.RandomRotation(degrees=10),\n                transforms.Resize((resize, resize)),\n                transforms.ToTensor(),\n                transforms.Normalize(mean, std),\n            ]),\n            'val': transforms.Compose([\n                transforms.Resize((resize, resize)),\n                transforms.ToTensor(),\n                transforms.Normalize(mean, std),\n            ])\n        }\n\n    def __call__(self, img, phase):\n        return self.transforms_dict[phase](img)","metadata":{"execution":{"iopub.execute_input":"2025-07-09T07:14:29.022493Z","iopub.status.busy":"2025-07-09T07:14:29.022196Z","iopub.status.idle":"2025-07-09T07:14:29.028500Z","shell.execute_reply":"2025-07-09T07:14:29.027642Z"},"papermill":{"duration":0.012963,"end_time":"2025-07-09T07:14:29.030181","exception":false,"start_time":"2025-07-09T07:14:29.017218","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"f2ac07aa","cell_type":"code","source":"class ImageDataset(data.Dataset):\n    def __init__(self, file_list, transform=None, phase='train'):\n        self.file_list = file_list\n        self.transform = transform\n        self.phase = phase\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).convert('RGB')  # RGB変換を追加\n        img = self.transform(img, self.phase)\n\n        label_str = img_path.split('/')[-1].split('.')[0]\n        label = 1 if label_str == 'dog' else 0\n\n        return img, label","metadata":{"execution":{"iopub.execute_input":"2025-07-09T07:14:29.039345Z","iopub.status.busy":"2025-07-09T07:14:29.039062Z","iopub.status.idle":"2025-07-09T07:14:29.045059Z","shell.execute_reply":"2025-07-09T07:14:29.043996Z"},"papermill":{"duration":0.012215,"end_time":"2025-07-09T07:14:29.046471","exception":false,"start_time":"2025-07-09T07:14:29.034256","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"afd20ada","cell_type":"code","source":"# データの前処理\ntrain_dataset = ImageDataset(train_list, transform=ImageTransform(size, mean, std), phase='train')\nval_dataset = ImageDataset(val_list, transform=ImageTransform(size, mean, std), phase='val')\n\n# データの読み込み\ntrain_dataloader = data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True)\nval_dataloader = data.DataLoader(val_dataset, batch_size=batch_size, shuffle=False)\n\ndataloader_dict = {'train': train_dataloader, 'val': val_dataloader}","metadata":{"execution":{"iopub.execute_input":"2025-07-09T07:14:29.056516Z","iopub.status.busy":"2025-07-09T07:14:29.056188Z","iopub.status.idle":"2025-07-09T07:14:29.063315Z","shell.execute_reply":"2025-07-09T07:14:29.062522Z"},"papermill":{"duration":0.013848,"end_time":"2025-07-09T07:14:29.064814","exception":false,"start_time":"2025-07-09T07:14:29.050966","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"4a0a9ac1","cell_type":"code","source":"use_pretrained = True\n# 事前学習済みのvgg16\nnet = models.vgg16(pretrained=True)\n\n# 全パラメータ更新停止\nfor param in net.parameters():\n    param.requires_grad = False\n\n# classifierの5層目以降は微調整可能に\nfor param in net.classifier[5:].parameters():\n    param.requires_grad = True\n\n# 出力層のクラス数変更（2クラス）\nnet.classifier[6] = nn.Linear(4096, 2)\n\nparams_to_update = [p for p in net.parameters() if p.requires_grad]\nupdate_params_name = ['classifier.6.weight', 'classifier.6.bias']\n\nfor name, param in net.named_parameters():\n    if name in update_params_name: #更新するパラメータの場合\n        param.requires_grad = True #更新する\n        params_to_update.append(param) #更新するパラメータの追加\n    else: #更新しないパラメータの場合\n        param.requires_grad = False #更新しない\n","metadata":{"execution":{"iopub.execute_input":"2025-07-09T07:14:29.075384Z","iopub.status.busy":"2025-07-09T07:14:29.074385Z","iopub.status.idle":"2025-07-09T07:14:34.124449Z","shell.execute_reply":"2025-07-09T07:14:34.123491Z"},"papermill":{"duration":5.057153,"end_time":"2025-07-09T07:14:34.126160","exception":false,"start_time":"2025-07-09T07:14:29.069007","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"3075bad5","cell_type":"code","source":"criterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(params_to_update, lr=1e-4)\n\n# 検証精度が改善しなければ学習率を下げる\nscheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='max', factor=0.5, patience=2, verbose=True)\n\ndef train_model(net, dataloader_dict, criterion, optimizer, num_epoch):\n    \n    since = time.time() #学習開始時刻を記録\n    best_model_wts = copy.deepcopy(net.state_dict()) #最良モデルの初期化\n    best_acc = 0.0 #最良精度の初期化\n    \n     # 損失と精度の履歴\n    history = {'train_loss': [], 'val_loss': [], 'train_acc': [], 'val_acc': []}\n    \n    net = net.to(device) #モデルをGPUに転送\n    print('training started')\n    for epoch in range(num_epoch): #各エポックの進捗\n        print('Epoch {}/{}'.format(epoch + 1, num_epoch))\n        print('-'*20)\n        \n        for phase in ['train', 'val']:\n            #学習と検証を交互に行う\n            if phase == 'train':\n                net.train() #学習フェーズ\n            else:\n                net.eval() #検証フェーズ\n                \n            epoch_loss = 0.0\n            epoch_corrects = 0\n            \n            for inputs, labels in tqdm(dataloader_dict[phase]):\n                inputs = inputs.to(device)\n                labels = labels.to(device)\n                optimizer.zero_grad()\n                \n                with torch.set_grad_enabled(phase == 'train'):\n                    outputs = net(inputs) #出力を算出\n                    _, preds = torch.max(outputs, 1) #予測値の取得\n                    loss = criterion(outputs, labels) #損失の計算\n                    \n                    if phase == 'train':\n                        loss.backward() #逆伝播で学習\n                        optimizer.step()\n                        \n                    #バッチごとの損失と精度を累積    \n                    epoch_loss += loss.item() * inputs.size(0) \n                    epoch_corrects += torch.sum(preds == labels.data)\n            \n            #エポックごとの損失と精度の計算\n            epoch_loss = epoch_loss / len(dataloader_dict[phase].dataset)\n            epoch_acc = epoch_corrects.double() / len(dataloader_dict[phase].dataset)\n            \n            print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc))\n            \n            # 履歴に保存\n            history[f'{phase}_loss'].append(epoch_loss)\n            history[f'{phase}_acc'].append(epoch_acc.cpu().numpy())\n            \n            # 最良モデルの保存\n            if phase == 'val' and epoch_acc > best_acc:\n                best_acc = epoch_acc\n                best_model_wts = copy.deepcopy(net.state_dict())\n                \n    time_elapsed = time.time() - since #学習の総時間を計算\n    print('Training complete in {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))\n    print('Best val Acc: {:4f}'.format(best_acc))\n\n    # 最良の重みを読み込む\n    net.load_state_dict(best_model_wts)\n    return net, history","metadata":{"execution":{"iopub.execute_input":"2025-07-09T07:14:34.139888Z","iopub.status.busy":"2025-07-09T07:14:34.139216Z","iopub.status.idle":"2025-07-09T07:14:34.153930Z","shell.execute_reply":"2025-07-09T07:14:34.152943Z"},"papermill":{"duration":0.023333,"end_time":"2025-07-09T07:14:34.155559","exception":false,"start_time":"2025-07-09T07:14:34.132226","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"fc36f637","cell_type":"code","source":"transform = ImageTransform(size, mean, std)\n\n# train_list, val_list は事前に用意されている想定\ntrain_dataset = ImageDataset(train_list, transform=transform, phase='train')\nval_dataset = ImageDataset(val_list, transform=transform, phase='val')\n\ntrain_loader = data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=2)\nval_loader = data.DataLoader(val_dataset, batch_size=batch_size, shuffle=False, num_workers=2)\n\ndataloaders = {'train': train_loader, 'val': val_loader}\n","metadata":{"execution":{"iopub.execute_input":"2025-07-09T07:14:34.168358Z","iopub.status.busy":"2025-07-09T07:14:34.168063Z","iopub.status.idle":"2025-07-09T07:14:34.174062Z","shell.execute_reply":"2025-07-09T07:14:34.173247Z"},"papermill":{"duration":0.013987,"end_time":"2025-07-09T07:14:34.175363","exception":false,"start_time":"2025-07-09T07:14:34.161376","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"328e5b62","cell_type":"code","source":"def train_model(net, dataloaders, criterion, optimizer, scheduler, num_epochs, device):\n    best_acc = 0.0\n    best_weights = copy.deepcopy(net.state_dict())\n    history = {'train_loss': [], 'val_loss': [], 'train_acc': [], 'val_acc': []}\n    net.to(device)\n\n    for epoch in range(num_epochs):\n        print(f'Epoch {epoch+1}/{num_epochs}')\n        for phase in ['train', 'val']:\n            net.train() if phase == 'train' else net.eval()\n\n            running_loss = 0.0\n            running_corrects = 0\n\n            for inputs, labels in tqdm(dataloaders[phase]):\n                inputs = inputs.to(device)\n                labels = labels.to(device)\n\n                optimizer.zero_grad()\n\n                with torch.set_grad_enabled(phase == 'train'):\n                    outputs = net(inputs)\n                    loss = criterion(outputs, labels)\n                    preds = torch.argmax(outputs, dim=1)\n\n                    if phase == 'train':\n                        loss.backward()\n                        optimizer.step()\n\n                running_loss += loss.item() * inputs.size(0)\n                running_corrects += torch.sum(preds == labels.data)\n\n            epoch_loss = running_loss / len(dataloaders[phase].dataset)\n            epoch_acc = running_corrects.double() / len(dataloaders[phase].dataset)\n\n            print(f'{phase} Loss: {epoch_loss:.4f} Acc: {epoch_acc:.4f}')\n\n            history[f'{phase}_loss'].append(epoch_loss)\n            history[f'{phase}_acc'].append(epoch_acc.cpu().numpy())\n\n            if phase == 'val':\n                scheduler.step(epoch_acc)\n\n            if phase == 'val' and epoch_acc > best_acc:\n                best_acc = epoch_acc\n                best_weights = copy.deepcopy(net.state_dict())\n\n        # 5エポック以降でval_accが改善しなければ早期終了\n        if epoch > 5 and all(history['val_acc'][-1] <= acc for acc in history['val_acc'][-4:-1]):\n            print(f\"Early stopping at epoch {epoch+1}\")\n            break\n\n    net.load_state_dict(best_weights)\n    return net, history\n","metadata":{"execution":{"iopub.execute_input":"2025-07-09T07:14:34.188901Z","iopub.status.busy":"2025-07-09T07:14:34.188296Z","iopub.status.idle":"2025-07-09T07:14:34.199480Z","shell.execute_reply":"2025-07-09T07:14:34.198636Z"},"papermill":{"duration":0.019801,"end_time":"2025-07-09T07:14:34.201029","exception":false,"start_time":"2025-07-09T07:14:34.181228","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"989040c9","cell_type":"code","source":"net, history = train_model(net, dataloaders, criterion, optimizer, scheduler, num_epochs, device)\n\ntorch.save(net.state_dict(), 'best_model_v2.pth')","metadata":{"execution":{"iopub.execute_input":"2025-07-09T07:14:34.213671Z","iopub.status.busy":"2025-07-09T07:14:34.213346Z","iopub.status.idle":"2025-07-09T07:53:28.081912Z","shell.execute_reply":"2025-07-09T07:53:28.080346Z"},"papermill":{"duration":2333.877439,"end_time":"2025-07-09T07:53:28.084305","exception":false,"start_time":"2025-07-09T07:14:34.206866","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"fd4daf49","cell_type":"code","source":"import matplotlib.pyplot as plt\n# 損失のプロット\nplt.figure(figsize=(12, 5))\nplt.subplot(1, 2, 1)\nplt.plot(history['train_loss'], label='Train Loss')\nplt.plot(history['val_loss'], label='Val Loss')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.title('Loss over epochs')\nplt.legend()\n\n# 精度のプロット\nplt.subplot(1, 2, 2)\nplt.plot(history['train_acc'], label='Train Accuracy')\nplt.plot(history['val_acc'], label='Val Accuracy')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.title('Accuracy over epochs')\nplt.legend()\n\nplt.show()","metadata":{"execution":{"iopub.execute_input":"2025-07-09T07:53:28.115693Z","iopub.status.busy":"2025-07-09T07:53:28.115250Z","iopub.status.idle":"2025-07-09T07:53:28.621155Z","shell.execute_reply":"2025-07-09T07:53:28.620095Z"},"papermill":{"duration":0.523841,"end_time":"2025-07-09T07:53:28.622973","exception":false,"start_time":"2025-07-09T07:53:28.099132","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"c2d37717","cell_type":"code","source":"# ハイパーパラメータの定義（推論・学習共通）\nsize = 224  # 入力画像のサイズ\nmean = (0.485, 0.456, 0.406)  # ImageNetの平均\nstd = (0.229, 0.224, 0.225)   # ImageNetの標準偏差\n","metadata":{"execution":{"iopub.execute_input":"2025-07-09T07:53:28.654902Z","iopub.status.busy":"2025-07-09T07:53:28.654502Z","iopub.status.idle":"2025-07-09T07:53:28.659619Z","shell.execute_reply":"2025-07-09T07:53:28.658670Z"},"papermill":{"duration":0.022831,"end_time":"2025-07-09T07:53:28.661230","exception":false,"start_time":"2025-07-09T07:53:28.638399","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"36b6c35e","cell_type":"code","source":"class ImageTransform():\n    def __init__(self, resize, mean, std):\n        self.data_transform = {\n            'train': transforms.Compose([\n                transforms.Resize((resize, resize)),\n                transforms.RandomHorizontalFlip(),\n                transforms.RandomVerticalFlip(),\n                transforms.ToTensor(),\n                transforms.Normalize(mean, std)\n            ]),\n            'val': transforms.Compose([\n                transforms.Resize((resize, resize)),\n                transforms.ToTensor(),\n                transforms.Normalize(mean, std)\n            ])\n        }\n\n    def __call__(self, img, phase):\n        return self.data_transform[phase](img)\n","metadata":{"execution":{"iopub.execute_input":"2025-07-09T07:53:28.692667Z","iopub.status.busy":"2025-07-09T07:53:28.692271Z","iopub.status.idle":"2025-07-09T07:53:28.698673Z","shell.execute_reply":"2025-07-09T07:53:28.697711Z"},"papermill":{"duration":0.024112,"end_time":"2025-07-09T07:53:28.700241","exception":false,"start_time":"2025-07-09T07:53:28.676129","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"029eebe1","cell_type":"code","source":"print(len(test_list))       # 画像の数を確認\nprint(test_list[:5])        # 最初の5件を表示（空なら [] になる）\n","metadata":{"execution":{"iopub.execute_input":"2025-07-09T07:53:28.731760Z","iopub.status.busy":"2025-07-09T07:53:28.731363Z","iopub.status.idle":"2025-07-09T07:53:28.736617Z","shell.execute_reply":"2025-07-09T07:53:28.735560Z"},"papermill":{"duration":0.022693,"end_time":"2025-07-09T07:53:28.738087","exception":false,"start_time":"2025-07-09T07:53:28.715394","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"7edf2351","cell_type":"code","source":"import glob\n\ntest_dir = '../data/test'  # 画像が入っているフォルダ\ntest_list = glob.glob(test_dir + '/*.jpg')  # 拡張子が .jpg の画像を取得\n\nprint(f\"画像数: {len(test_list)}\")\nprint(test_list[:5])\n","metadata":{"execution":{"iopub.execute_input":"2025-07-09T07:53:28.769676Z","iopub.status.busy":"2025-07-09T07:53:28.768899Z","iopub.status.idle":"2025-07-09T07:53:28.803658Z","shell.execute_reply":"2025-07-09T07:53:28.802651Z"},"papermill":{"duration":0.052216,"end_time":"2025-07-09T07:53:28.805315","exception":false,"start_time":"2025-07-09T07:53:28.753099","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"cb99b26b","cell_type":"code","source":"from PIL import Image\nimport torch.nn.functional as F\nimport pandas as pd\nfrom tqdm import tqdm\n\n# 推論前の準備（まだなら定義）\ntransform = ImageTransform(size, mean, std)\nnet.eval()\n\n# 結果を格納するリスト\nid_list = []\npred_list = []\n\n# 推論ループ\nwith torch.no_grad():\n    for test_path in tqdm(test_list):\n        img = Image.open(test_path)\n        _id = int(test_path.split('/')[-1].split('.')[0])  # 画像名から ID を取得\n\n        img = transform(img, phase='val')  # 前処理\n        img = img.unsqueeze(0).to(device)  # バッチ化してGPUへ\n\n        outputs = net(img)\n        preds = F.softmax(outputs, dim=1)[:, 1].tolist()  # 犬(1)の確率\n\n        id_list.append(_id)\n\n\n","metadata":{"execution":{"iopub.execute_input":"2025-07-09T07:53:28.838846Z","iopub.status.busy":"2025-07-09T07:53:28.838010Z","iopub.status.idle":"2025-07-09T08:52:45.925383Z","shell.execute_reply":"2025-07-09T08:52:45.924230Z"},"papermill":{"duration":3557.106076,"end_time":"2025-07-09T08:52:45.927127","exception":false,"start_time":"2025-07-09T07:53:28.821051","status":"completed"},"tags":[]},"outputs":[],"execution_count":null}]}