{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.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":5441,"databundleVersionId":38425,"sourceType":"competition"}],"dockerImageVersionId":31089,"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-25T07:19:44.393139Z","iopub.execute_input":"2025-07-25T07:19:44.393452Z","iopub.status.idle":"2025-07-25T07:19:45.226583Z","shell.execute_reply.started":"2025-07-25T07:19:44.393422Z","shell.execute_reply":"2025-07-25T07:19:45.225747Z"}},"outputs":[],"execution_count":null},{"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":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T07:19:45.227567Z","iopub.execute_input":"2025-07-25T07:19:45.227966Z","iopub.status.idle":"2025-07-25T07:19:45.234465Z","shell.execute_reply.started":"2025-07-25T07:19:45.227940Z","shell.execute_reply":"2025-07-25T07:19:45.234027Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install -q timm\n\nimport os, zipfile, random, gc, warnings\nimport numpy as np\nimport pandas as pd\nimport torch\nimport timm\nfrom torch import nn\nfrom torch.utils.data import Dataset, DataLoader\nfrom torch.optim import AdamW\nfrom torch.cuda.amp import autocast, GradScaler\nfrom sklearn.model_selection import StratifiedKFold\nfrom PIL import Image\nimport torchvision.transforms as transforms\n\nwarnings.filterwarnings(\"ignore\")# 警告の無効化","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T07:19:45.236402Z","iopub.execute_input":"2025-07-25T07:19:45.237028Z","iopub.status.idle":"2025-07-25T07:21:15.452413Z","shell.execute_reply.started":"2025-07-25T07:19:45.237002Z","shell.execute_reply":"2025-07-25T07:21:15.451464Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 再現性を確保するために乱数シードを固定\nSEED = 42\nrandom.seed(SEED)\nnp.random.seed(SEED)\ntorch.manual_seed(SEED)\nif torch.cuda.is_available():\n    torch.cuda.manual_seed_all(SEED)\n    torch.backends.cudnn.benchmark = True\n\n# ハイパーパラメータ設定(画像サイズやバッチサイズなど)\nIMG_SIZE = 256            \nBATCH_SIZE = 32           \nEPOCHS = 5                \nFOLDS = 3                     \nLR = 2e-4                  \nN_TTA = 3               \nDEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T07:21:15.453473Z","iopub.execute_input":"2025-07-25T07:21:15.453964Z","iopub.status.idle":"2025-07-25T07:21:15.541523Z","shell.execute_reply.started":"2025-07-25T07:21:15.453937Z","shell.execute_reply":"2025-07-25T07:21:15.540704Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# zipファイルの解凍\nDATA_DIR = \"/kaggle/input/dogs-vs-cats-redux-kernels-edition\"\nfor z in [\"train.zip\", \"test.zip\"]:\n    with zipfile.ZipFile(f\"{DATA_DIR}/{z}\") as f:\n        f.extractall(\"/kaggle/working\")\n\nTRN_DIR = \"/kaggle/working/train\"\nTST_DIR = \"/kaggle/working/test\"\n\n# データフレーム作成\ntrain_files = os.listdir(TRN_DIR)\ndf = pd.DataFrame({\n    \"filepath\": [f\"{TRN_DIR}/{f}\" for f in train_files],\n    \"label\": [1 if f.startswith(\"dog\") else 0 for f in train_files]\n})","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T07:21:15.542519Z","iopub.execute_input":"2025-07-25T07:21:15.542750Z","iopub.status.idle":"2025-07-25T07:21:29.037314Z","shell.execute_reply.started":"2025-07-25T07:21:15.542731Z","shell.execute_reply":"2025-07-25T07:21:29.036652Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# データ拡張の定義\ndef get_transforms(mode):\n    if mode == \"train\":\n        # 学習時のデータ拡張を定義\n        return transforms.Compose([\n            transforms.Resize((IMG_SIZE, IMG_SIZE)),\n            transforms.RandomHorizontalFlip(),\n            transforms.RandomVerticalFlip(0.2),\n            transforms.RandomRotation(15),\n            transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1),\n            transforms.ToTensor(),\n            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n        ])\n    else:\n\n         # 検証・推論時のデータ拡張を定義\n        return transforms.Compose([\n            transforms.Resize((IMG_SIZE, IMG_SIZE)),\n            transforms.ToTensor(),\n            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n        ])\n\n# 犬・猫データセットクラスの定義\nclass DogCatDataset(Dataset):\n    # コンストラクタ(データ拡張など)\n    def __init__(self, df, transform, is_test=False):\n        self.df = df.reset_index(drop=True)\n        self.transform = transform\n        self.is_test = is_test\n\n    # データ総数を返す\n    def __len__(self):\n        return len(self.df)\n\n# 指定のデータを取得\n    def __getitem__(self, idx):\n        img_path = self.df.filepath[idx]\n        img = Image.open(img_path).convert(\"RGB\")\n        img = self.transform(img)\n        \n        if self.is_test:\n            img_id = os.path.basename(img_path).split(\".\")[0]\n            return img, img_id\n        else:\n            label = self.df.label[idx]\n            return img, torch.tensor([label], dtype=torch.float32)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T07:21:29.038191Z","iopub.execute_input":"2025-07-25T07:21:29.038445Z","iopub.status.idle":"2025-07-25T07:21:29.049412Z","shell.execute_reply.started":"2025-07-25T07:21:29.038418Z","shell.execute_reply":"2025-07-25T07:21:29.048728Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class DogCatModel(nn.Module):\n    def __init__(self, model_name=\"resnet34\", pretrained=True):# 事前学習済みresnet34を使用\n        super().__init__()\n        \n        # 事前学習済みモデルの読み込み\n        self.backbone = timm.create_model(\n            model_name, \n            pretrained=pretrained, \n            num_classes=0  # 分類層は使用しない\n        )\n        \n        # 出力される特徴ベクトルの次元数を取得\n        in_features = self.backbone.num_features\n        \n        # 分類用の全結合層を定義\n        self.head = nn.Sequential(\n            nn.Dropout(0.3),             \n            nn.Linear(in_features, 1)\n        )\n    # 入力→特徴量→スコア\n    def forward(self, x):\n        features = self.backbone(x)\n        return self.head(features)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T07:21:29.050547Z","iopub.execute_input":"2025-07-25T07:21:29.050813Z","iopub.status.idle":"2025-07-25T07:21:29.583954Z","shell.execute_reply.started":"2025-07-25T07:21:29.050794Z","shell.execute_reply":"2025-07-25T07:21:29.582869Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_model(model, train_loader, valid_loader):\n\n    # 損失関数と最適化手法を定義\n    criterion = nn.BCEWithLogitsLoss()\n    optimizer = AdamW(model.parameters(), lr=LR, weight_decay=1e-2)\n    scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=EPOCHS)\n    scaler = GradScaler() \n\n    best_acc = 0\n    best_weights = None\n\n    for epoch in range(1, EPOCHS + 1):\n        model.train()  # 学習モードに切り替え\n        train_loss = 0\n\n        for images, labels in train_loader:\n            images, labels = images.to(DEVICE), labels.to(DEVICE)\n            optimizer.zero_grad(set_to_none=True)\n    # 順伝播と損失計算\n            with autocast():\n                outputs = model(images)\n                loss = criterion(outputs, labels)\n\n            # 逆伝播とパラメータ更新\n            scaler.scale(loss).backward()\n            scaler.step(optimizer)\n            scaler.update()\n\n            train_loss += loss.item()\n\n        scheduler.step()  # 学習率更新\n # 検証\n        model.eval()\n        correct = 0\n        total = 0\n        valid_loss = 0\n\n        with torch.no_grad():\n            for images, labels in valid_loader:\n                images, labels = images.to(DEVICE), labels.to(DEVICE)\n                with autocast():\n                    outputs = model(images)\n                    loss = criterion(outputs, labels)\n                    valid_loss += loss.item()\n# 出力にシグモイドをかける。0.5以上を犬とする\n                predictions = (torch.sigmoid(outputs) > 0.5).float()\n                correct += (predictions == labels).sum().item()\n                total += labels.size(0)\n\n        # 精度と損失の平均を計算\n        accuracy = correct / total\n        train_loss /= len(train_loader)\n        valid_loss /= len(valid_loader)\n\n # 結果を表示\n        print(f\"Epoch {epoch}/{EPOCHS} - Train Loss: {train_loss:.4f}, Valid Loss: {valid_loss:.4f}, Accuracy: {accuracy:.4f}\")\n\n# 現時点で最も精度が高いモデルの重みを保存\n        if accuracy > best_acc:\n            best_acc = accuracy\n            best_weights = model.state_dict().copy()\n\n    # 最も性能の良かった重みに戻す\n    model.load_state_dict(best_weights)\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T07:21:29.585327Z","iopub.execute_input":"2025-07-25T07:21:29.585617Z","iopub.status.idle":"2025-07-25T07:21:30.275144Z","shell.execute_reply.started":"2025-07-25T07:21:29.585590Z","shell.execute_reply":"2025-07-25T07:21:30.274320Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_with_folds():\n    \n    # 学習済みモデルのパスを保存するリスト\n    trained_models = []\n    \n    # ラベルの比率を保ったまま分割(Stratified K-Fold)\n    skf = StratifiedKFold(n_splits=FOLDS, shuffle=True, random_state=SEED)\n    \n    # 各Foldごとに学習・検証を実行\n    for fold, (train_idx, valid_idx) in enumerate(skf.split(df, df.label)):\n        print(f\"\\n=== Fold {fold+1}/{FOLDS} ===\")\n        \n        # 学習用データローダの作成\n        train_loader = DataLoader(\n            DogCatDataset(df.iloc[train_idx], get_transforms(\"train\")),\n            batch_size=BATCH_SIZE,\n            shuffle=True,\n            num_workers=2,\n            pin_memory=True\n        )\n\n# 検証用データローダの作成\n        valid_loader = DataLoader(\n            DogCatDataset(df.iloc[valid_idx], get_transforms(\"valid\")),\n            batch_size=BATCH_SIZE * 2,\n            shuffle=False,\n            num_workers=2,\n            pin_memory=True\n        )\n        \n        # モデルの作成と、学習の実行\n        model = DogCatModel().to(DEVICE)\n        model = train_model(model, train_loader, valid_loader)\n        \n        # 学習済みモデルを保存\n        model_path = f\"dogcat_model_fold{fold}.pt\"\n        torch.save(model.state_dict(), model_path)\n        trained_models.append(model_path)\n        \n        # メモリを解放\n        del model, train_loader, valid_loader\n        gc.collect()\n        torch.cuda.empty_cache()\n\n# 全Foldの学習済みモデルのパスを返す\n    return trained_models\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T07:21:30.277503Z","iopub.execute_input":"2025-07-25T07:21:30.277835Z","iopub.status.idle":"2025-07-25T07:21:30.292730Z","shell.execute_reply.started":"2025-07-25T07:21:30.277816Z","shell.execute_reply":"2025-07-25T07:21:30.291597Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def predict(model_paths):\n    \"\"\"テストデータに対して予測を行い、提出ファイルを作成する関数\"\"\"\n    \n    # ID順にソートしてデータフレームを作成\n    test_files = sorted(os.listdir(TST_DIR), key=lambda x: int(x.split('.')[0]))\n    test_df = pd.DataFrame({\"filepath\": [f\"{TST_DIR}/{f}\" for f in test_files]})\n    \n    # 全モデルの予測結果を格納するリスト\n    all_predictions = []\n    \n    for model_path in model_paths:\n        print(f\"Predicting with model: {model_path}\")\n        \n        # モデルの読み込み\n        model = DogCatModel().to(DEVICE)\n        model.load_state_dict(torch.load(model_path))\n        model.eval()\n\n# テスト時のTTAごとの予測を格納\n        tta_predictions = []\n        \n        for _ in range(N_TTA):\n            # データローダを作成\n            test_loader = DataLoader(\n                DogCatDataset(test_df, get_transforms(\"valid\"), is_test=True),\n                batch_size=BATCH_SIZE * 2,\n                shuffle=False,\n                num_workers=2\n            )\n\n # 予測結果を保存するリスト\n            predictions = []\n            with torch.no_grad():\n                for images, _ in test_loader:\n                    images = images.to(DEVICE)\n                    with autocast():\n                        outputs = model(images)\n                        outputs = torch.sigmoid(outputs).squeeze().cpu().numpy()  # 確率に変換\n                    predictions.extend(outputs)\n            \n            # 1回分のTTA結果を保存\n            tta_predictions.append(np.array(predictions))\n        \n        # TTA結果の平均を計算\n        avg_predictions = np.mean(tta_predictions, axis=0)\n        all_predictions.append(avg_predictions)\n\n# メモリ解放\n        del model\n        gc.collect()\n        torch.cuda.empty_cache()\n    \n    # アンサンブル学習(全モデルの平均予測値を計算)\n    final_predictions = np.mean(all_predictions, axis=0)\n    \n    # 予測値を0.01〜0.99の範囲にする\n    final_predictions = np.clip(final_predictions, 0.01, 0.99)\n    \n    # 提出用のデータフレームを作成\n    submission = pd.DataFrame({\n        \"id\": [f.split('.')[0] for f in test_files],\n        \"label\": final_predictions\n    })\n\n# CSVファイルとして保存\n    submission.to_csv(\"submission.csv\", index=False)\n    print(\"Submission file created!\")\n    \n    return submission","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T07:21:30.296644Z","iopub.execute_input":"2025-07-25T07:21:30.296940Z","iopub.status.idle":"2025-07-25T07:21:30.309441Z","shell.execute_reply.started":"2025-07-25T07:21:30.296913Z","shell.execute_reply":"2025-07-25T07:21:30.308634Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if __name__ == \"__main__\":\n    # モデルの学習\n    trained_model_paths = train_with_folds()\n    \n    # テストデータに対して予測を行い、提出用ファイルを作成\n    submission = predict(trained_model_paths)\n    \n    # 先頭5行だけ表示して確認\n    print(\"\\nSubmission Preview:\")\n    print(submission.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T07:21:30.310190Z","iopub.execute_input":"2025-07-25T07:21:30.310390Z","execution_failed":"2025-07-25T07:44:19.337Z"}},"outputs":[],"execution_count":null}]}