{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":5441,"databundleVersionId":38425}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"데이터 경로 확인 및 내부 폴더 구조 확인","metadata":{}},{"cell_type":"code","source":"import os\n\nprint(os.listdir(\"/kaggle/input/competitions/dogs-vs-cats-redux-kernels-edition\"))","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-04-21T09:19:21.755181Z","iopub.execute_input":"2026-04-21T09:19:21.755719Z","iopub.status.idle":"2026-04-21T09:19:21.761362Z","shell.execute_reply.started":"2026-04-21T09:19:21.755687Z","shell.execute_reply":"2026-04-21T09:19:21.760698Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"데이터 압축 풀기","metadata":{}},{"cell_type":"code","source":"import zipfile\n\nwith zipfile.ZipFile(\"/kaggle/input/competitions/dogs-vs-cats-redux-kernels-edition/train.zip\", \"r\") as z:\n    z.extractall(\"/kaggle/working\")\n\nprint(\"finish\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T09:19:21.765480Z","iopub.execute_input":"2026-04-21T09:19:21.765703Z","iopub.status.idle":"2026-04-21T09:19:28.974650Z","shell.execute_reply.started":"2026-04-21T09:19:21.765681Z","shell.execute_reply":"2026-04-21T09:19:28.973790Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"폴더 안 파일 확인하기 ","metadata":{}},{"cell_type":"code","source":"import os \n\nfiles = os.listdir(\"/kaggle/working/train/\")\nprint(f\"전체 파일 수: {len(files)}\")\nprint(f\"처음 5개: {files[:5]}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T09:19:28.976549Z","iopub.execute_input":"2026-04-21T09:19:28.977070Z","iopub.status.idle":"2026-04-21T09:19:28.995202Z","shell.execute_reply.started":"2026-04-21T09:19:28.977044Z","shell.execute_reply":"2026-04-21T09:19:28.994571Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"각 데이터를 실제로 확인해보기 ","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nfrom PIL import Image\n\nTRAIN_DIR = \"/kaggle/working/train/\"\n\nfig, axes = plt.subplots(2, 5, figsize=(15,6))\n\nfor i, ax in enumerate(axes.flatten()):\n    img = Image.open(TRAIN_DIR + files[i])\n    ax.imshow(img)\n    ax.set_title(files[i].split(\".\")[0])\n    ax.axis(\"off\")\n\nplt.tight_layout()\nplt.show","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T09:19:28.995991Z","iopub.execute_input":"2026-04-21T09:19:28.996288Z","iopub.status.idle":"2026-04-21T09:19:29.976899Z","shell.execute_reply.started":"2026-04-21T09:19:28.996265Z","shell.execute_reply":"2026-04-21T09:19:29.975825Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"데이터 변환하기","metadata":{}},{"cell_type":"code","source":"from torchvision import transforms\n\ntransform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),             # 0~255 -> 0.0~1.0 정규화 \n    transforms.Normalize(              # 불러올 모델의 학습 데이터의 평균과 표준편차로 표준화 \n        mean=[0.485, 0.456, 0.406],    # RGB 각 채널의 평균(ImageNet 기준)\n        std=[0.229, 0.224, 0.225]      # RGB 각 채널의 표준 편차(ImageNet 기준)\n    )\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T09:19:29.978085Z","iopub.execute_input":"2026-04-21T09:19:29.978367Z","iopub.status.idle":"2026-04-21T09:19:34.868765Z","shell.execute_reply.started":"2026-04-21T09:19:29.978342Z","shell.execute_reply":"2026-04-21T09:19:34.868220Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Dataset 정의. cat은 0 dog는 1로 ","metadata":{}},{"cell_type":"code","source":"from torch.utils.data import Dataset\n\nclass DogsVsCatsDataset(Dataset):\n    def __init__(self, img_dir, transform=None):\n        self.img_dir = img_dir\n        self.transform = transform\n        self.files = os.listdir(img_dir)\n\n\n    def __len__(self):\n        return len(self.files)\n\n    def __getitem__(self, idx):\n        filename = self.files[idx]\n        img = Image.open(self.img_dir + filename)\n        label = 0 if filename.split(\".\")[0] == \"cat\" else 1\n        if self.transform:\n            img = self.transform(img)\n\n        return img, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T09:19:34.870523Z","iopub.execute_input":"2026-04-21T09:19:34.870908Z","iopub.status.idle":"2026-04-21T09:19:34.877546Z","shell.execute_reply.started":"2026-04-21T09:19:34.870882Z","shell.execute_reply":"2026-04-21T09:19:34.876779Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dataset = DogsVsCatsDataset(\n    img_dir = \"/kaggle/working/train/\",\n    transform=transform\n)\n\nprint(f\"전체 데이터 수: {len(dataset)}\")  \nimg, label = dataset[0]                \nprint(f\"이미지 shape: {img.shape}\")      \nprint(f\"레이블: {label}\")                ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T09:19:34.878412Z","iopub.execute_input":"2026-04-21T09:19:34.878742Z","iopub.status.idle":"2026-04-21T09:19:35.079155Z","shell.execute_reply.started":"2026-04-21T09:19:34.878703Z","shell.execute_reply":"2026-04-21T09:19:35.078333Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"DataLoader 정의","metadata":{}},{"cell_type":"code","source":"from torch.utils.data import DataLoader\n\ndataloader = DataLoader(\n    dataset,\n    batch_size = 32,\n    shuffle=True,\n    num_workers=2,\n)\n\nimages, labels = next(iter(dataloader))\nprint(f\"batch image shape: {images.shape}\")\nprint(f\"batch label shape: {labels.shape}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T09:19:35.080189Z","iopub.execute_input":"2026-04-21T09:19:35.080452Z","iopub.status.idle":"2026-04-21T09:19:35.463027Z","shell.execute_reply.started":"2026-04-21T09:19:35.080426Z","shell.execute_reply":"2026-04-21T09:19:35.462220Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"모델 불러오기","metadata":{}},{"cell_type":"code","source":"from torchvision import models\nimport torch\n\nmodel = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V2)\n\n# 모든 가중치 freeze\nfor param in model.parameters():\n    param.requires_grad = False\n\n# 마지막 FC층 교체 \nin_features = model.fc.in_features\nmodel.fc = torch.nn.Linear(in_features, 1)\n\nprint(model.fc)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T09:19:35.464447Z","iopub.execute_input":"2026-04-21T09:19:35.464714Z","iopub.status.idle":"2026-04-21T09:19:36.606034Z","shell.execute_reply.started":"2026-04-21T09:19:35.464684Z","shell.execute_reply":"2026-04-21T09:19:36.605272Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"cuda 설정, loss_func, Optimizer 정의하기 ","metadata":{}},{"cell_type":"code","source":"import torch.nn as nn\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"device: {device}\")\n\nmodel = model.to(device)\n\n# Loss_func 정의 \ncriterion = nn.BCEWithLogitsLoss()\n\n# Optimizer 정의\noptimizer = torch.optim.Adam(\n    model.fc.parameters(),\n    lr=1e-3\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T09:26:06.045534Z","iopub.execute_input":"2026-04-21T09:26:06.046505Z","iopub.status.idle":"2026-04-21T09:26:06.416526Z","shell.execute_reply.started":"2026-04-21T09:26:06.046471Z","shell.execute_reply":"2026-04-21T09:26:06.415920Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"모델 학습","metadata":{}},{"cell_type":"code","source":"EPOCHS = 3\n\nfor epoch in range(EPOCHS):\n    model.train()                    # 학습 모드로 전환\n    total_loss = 0\n    correct = 0\n\n    for images, labels in dataloader:\n        images = images.to(device)   # 이미지를 GPU로 이동 \n        labels = labels.float().unsqueeze(1).to(device)  # 열벡터로 변환 \n\n        optimizer.zero_grad()\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n\n        total_loss += loss.item()\n        preds = (torch.sigmoid(outputs) > 0.5).float()\n\n        correct += (preds == labels).sum().item()\n\n    acc = correct / len(dataset)\n    print(f\"Epoch {epoch+1}/{EPOCHS} | Loss: {total_loss:.4f} | Acc: {acc:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T09:42:08.154051Z","iopub.execute_input":"2026-04-21T09:42:08.154637Z","iopub.status.idle":"2026-04-21T09:46:15.272590Z","shell.execute_reply.started":"2026-04-21T09:42:08.154607Z","shell.execute_reply":"2026-04-21T09:46:15.271773Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Train / Valid 데이터 분할","metadata":{}},{"cell_type":"code","source":"from torch.utils.data import random_split\n\n# 8:2 분할\ntrain_size = int(0.8 * len(dataset))\nval_size = len(dataset) - train_size\n\ntrain_dataset, val_dataset = random_split(\n    dataset,\n    [train_size, val_size],\n    generator=torch.Generator().manual_seed(42)\n)\n\nprint(f\"Train: {len(train_dataset)}\")\nprint(f\"Validation: {len(val_dataset)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T09:56:09.483460Z","iopub.execute_input":"2026-04-21T09:56:09.483814Z","iopub.status.idle":"2026-04-21T09:56:09.491021Z","shell.execute_reply.started":"2026-04-21T09:56:09.483779Z","shell.execute_reply":"2026-04-21T09:56:09.490308Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Train/Val 용 dataloader 정의","metadata":{}},{"cell_type":"code","source":"train_loader = DataLoader(\n    train_dataset,\n    batch_size=32,\n    shuffle=True,\n    num_workers=2,\n)\n\nval_loader = DataLoader(\n    val_dataset,\n    batch_size=32,\n    shuffle=False,\n    num_workers=2\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T10:06:41.824507Z","iopub.execute_input":"2026-04-21T10:06:41.825257Z","iopub.status.idle":"2026-04-21T10:06:41.829228Z","shell.execute_reply.started":"2026-04-21T10:06:41.825226Z","shell.execute_reply":"2026-04-21T10:06:41.828472Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"모델 학습 후 검증","metadata":{}},{"cell_type":"code","source":"EPOCHS = 3\n \nfor epoch in range(EPOCHS):\n    # 학습 단계\n    model.train()\n    train_loss, train_correct = 0, 0\n\n    for images, labels in train_loader:\n        images = images.to(device)\n        labels = labels.float().unsqueeze(1).to(device)\n\n        optimizer.zero_grad()\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n\n        train_loss += loss.item()\n        preds = (torch.sigmoid(outputs) > 0.5).float()\n        train_correct += (preds == labels).sum().item()\n\n\n    # 검증 단계\n    model.eval()\n    val_loss, val_correct = 0, 0\n\n    with torch.no_grad():\n        for images, labels in val_loader:\n            images = images.to(device)\n            labels = labels.float().unsqueeze(1).to(device)\n\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n\n            val_loss += loss.item()\n            preds = (torch.sigmoid(outputs) > 0.5).float()\n            val_correct += (preds == labels).sum().item()\n\n    train_acc = train_correct / len(train_dataset)\n    val_acc = val_correct / len(val_dataset)\n    print(f\"Epoch {epoch+1}/{EPOCHS} | \"\n           f\"Train Acc: {train_acc:.4f} | Val Acc: {val_acc:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T10:25:13.883808Z","iopub.execute_input":"2026-04-21T10:25:13.884700Z","iopub.status.idle":"2026-04-21T10:29:16.662477Z","shell.execute_reply.started":"2026-04-21T10:25:13.884661Z","shell.execute_reply":"2026-04-21T10:29:16.661471Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"kaggle 제출용 파일 만들기","metadata":{}},{"cell_type":"code","source":"with zipfile.ZipFile(\"/kaggle/input/competitions/dogs-vs-cats-redux-kernels-edition/test.zip\", \"r\") as z:\n    z.extractall(\"/kaggle/working/\")\n\nprint(os.listdir(\"/kaggle/working/test/\")[:5])\nprint(f\"test 폴더 이미지 수: {len(os.listdir('/kaggle/working/test/'))}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T11:06:37.726257Z","iopub.execute_input":"2026-04-21T11:06:37.726567Z","iopub.status.idle":"2026-04-21T11:06:41.046553Z","shell.execute_reply.started":"2026-04-21T11:06:37.726536Z","shell.execute_reply":"2026-04-21T11:06:41.045830Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"제출할 데이터 형식 확인하기","metadata":{}},{"cell_type":"code","source":"import pandas as pd\n\nsample = pd.read_csv(\"/kaggle/input/competitions/dogs-vs-cats-redux-kernels-edition/sample_submission.csv\")\nprint(sample.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T10:41:21.484861Z","iopub.execute_input":"2026-04-21T10:41:21.485190Z","iopub.status.idle":"2026-04-21T10:41:21.531608Z","shell.execute_reply.started":"2026-04-21T10:41:21.485162Z","shell.execute_reply":"2026-04-21T10:41:21.531032Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"test Dataset 정의하기","metadata":{}},{"cell_type":"code","source":"class TestDataset(Dataset):\n    def __init__(self, img_dir, transform=None):\n        self.img_dir = img_dir\n        self.transform = transform\n        self.files = sorted(os.listdir(img_dir), key=lambda x: int(x.split(\".\")[0]))\n\n    def __len__(self):\n        return len(self.files)\n\n    def __getitem__(self, idx):\n        filename = self.files[idx]\n        img = Image.open(self.img_dir + filename)\n        img_id = int(filename.split(\".\")[0])\n        if self.transform:\n            img = self.transform(img)\n\n        return img, img_id\n        ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T10:51:46.821801Z","iopub.execute_input":"2026-04-21T10:51:46.822178Z","iopub.status.idle":"2026-04-21T10:51:46.827861Z","shell.execute_reply.started":"2026-04-21T10:51:46.822148Z","shell.execute_reply":"2026-04-21T10:51:46.827252Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"예측값 구하기","metadata":{}},{"cell_type":"code","source":"test_dataset = TestDataset(\n    img_dir=\"/kaggle/working/test/\",\n    transform=transform,\n)\n\ntest_loader = DataLoader(\n    test_dataset,\n    batch_size=32,\n    shuffle=False,\n    num_workers=2,\n)\n\nmodel.eval()\nresults = []\n\nwith torch.no_grad():\n    for images, img_ids in test_loader:\n        images = images.to(device)\n        outputs = model(images)\n        probs = torch.sigmoid(outputs).squeeze(1)\n\n        for img_id, probs in zip(img_ids, probs):\n            results.append((img_id.item(), probs.item()))\n\nprint(f\"예측 완료 총{len(results)}개\")\nprint(f\"sample: {results[:3]}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T11:12:41.133419Z","iopub.execute_input":"2026-04-21T11:12:41.133860Z","iopub.status.idle":"2026-04-21T11:13:20.122636Z","shell.execute_reply.started":"2026-04-21T11:12:41.133829Z","shell.execute_reply":"2026-04-21T11:13:20.121921Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"예측값을 csv 파일로 저장하기","metadata":{}},{"cell_type":"code","source":"submission = pd.DataFrame(results, columns=[\"id\", \"label\"])\nsubmission = submission.sort_values(\"id\").reset_index(drop=True)\n\nprint(submission.head())\nprint(f\"총 행 수: {len(submission)}\")\n\nsubmission.to_csv(\"/kaggle/working/submission.csv\", index=False)\nprint(\"저장 완료\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-21T11:18:15.743858Z","iopub.execute_input":"2026-04-21T11:18:15.744594Z","iopub.status.idle":"2026-04-21T11:18:15.802056Z","shell.execute_reply.started":"2026-04-21T11:18:15.744562Z","shell.execute_reply":"2026-04-21T11:18:15.801379Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}