{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":87323,"databundleVersionId":9934427,"sourceType":"competition"}],"dockerImageVersionId":30840,"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-01-16T01:04:55.216218Z","iopub.execute_input":"2025-01-16T01:04:55.216512Z","iopub.status.idle":"2025-01-16T01:04:57.230433Z","shell.execute_reply.started":"2025-01-16T01:04:55.216482Z","shell.execute_reply":"2025-01-16T01:04:57.229527Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import warnings\nwarnings.simplefilter(action='ignore')\n\nimport os\nimport random\nimport numpy as np\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torch.utils.data\nimport torchvision\nimport torchvision.transforms as transforms\nfrom torchvision.models import convnext_large, ConvNeXt_Large_Weights\nfrom PIL import Image\nfrom tqdm import tqdm\nfrom torch.optim.lr_scheduler import CosineAnnealingLR\nimport torch.backends.cudnn as cudnn\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import confusion_matrix\nimport seaborn as sns\nimport matplotlib.pyplot as plt\n\n# 시드 고정\nseed = 42\nprint(\"Random Seed: \", seed)\nrandom.seed(seed)\nnp.random.seed(seed)\ntorch.manual_seed(seed)\ntorch.cuda.manual_seed(seed)\ntorch.cuda.manual_seed_all(seed)\ncudnn.deterministic = True\ncudnn.benchmark = False\n\n# GPU 설정\nngpu = torch.cuda.device_count()\ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\nprint(\"Number of GPUs Available: \", ngpu)\nprint(\"Device being used: \", device)\n\n# 하이퍼파라미터 (필요에 따라 조정)\nbatch_size = 8 * ngpu  # 더 큰 배치 사이즈\nnum_epochs = 25  # 에폭 증가\nlearning_rate = 1e-4 # 더 작은 학습률\nimage_size = 224\naccumulation_steps = 4  # Gradient Accumulation steps\nweight_decay = 1e-4 # weight decay 증가\nclip_grad_norm = 1 # Gradient Clipping 추가\n\n# 데이터셋 경로 (Kaggle Dataset 기준)\ntrain_dir = '/kaggle/input/cidaut-ai-fake-scene-classification-2024/Train'\ntest_dir = '/kaggle/input/cidaut-ai-fake-scene-classification-2024/Test'\ntrain_csv_path = '/kaggle/input/cidaut-ai-fake-scene-classification-2024/train.csv'\nsample_submission_path = '/kaggle/input/cidaut-ai-fake-scene-classification-2024/sample_submission.csv'\n\n# 데이터셋 transform\ntransform = transforms.Compose([\n    transforms.RandomResizedCrop(image_size),\n    transforms.RandomRotation(20),\n    transforms.RandomHorizontalFlip(p=0.5),\n    transforms.RandomVerticalFlip(p=0.5), # 수직 뒤집기 추가\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\n# 데이터셋 로드\nclass CustomDataset(torch.utils.data.Dataset):\n    def __init__(self, data_dir, dataframe, transform=None):\n        self.data_dir = data_dir\n        self.data_info = dataframe\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.data_info)\n\n    def __getitem__(self, idx):\n        img_name = self.data_info.iloc[idx, 0]\n        img_path = os.path.join(self.data_dir, img_name)\n        image = Image.open(img_path).convert(\"RGB\")\n        label_str = self.data_info.iloc[idx, 1]\n        if label_str == 'editada':\n            label = 1\n        elif label_str == 'real':\n            label = 0\n        else:\n            raise ValueError(f\"Unknown label: {label_str}\")\n\n        if self.transform:\n            image = self.transform(image)\n        return image, torch.tensor(label, dtype=torch.float32)\n\n# 학습 데이터셋과 검증 데이터셋 분리\ntrain_df = pd.read_csv(train_csv_path)\ntrain_df, val_df = train_test_split(train_df, test_size=0.2, random_state=seed)\n\ntrain_dataset = CustomDataset(train_dir, train_df, transform=transform)\nval_dataset = CustomDataset(train_dir, val_df, transform=transform)\n\ntrain_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=4, pin_memory=True)\nval_loader = torch.utils.data.DataLoader(val_dataset, batch_size=batch_size, shuffle=False, num_workers=4, pin_memory=True)\n\n# 테스트 데이터셋 로드 (라벨 없음)\nclass TestDataset(torch.utils.data.Dataset):\n    def __init__(self, data_dir, transform=None):\n        self.data_dir = data_dir\n        self.img_names = os.listdir(data_dir)\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.img_names)\n\n    def __getitem__(self, idx):\n        img_name = self.img_names[idx]\n        img_path = os.path.join(self.data_dir, img_name)\n        image = Image.open(img_path).convert(\"RGB\")\n        if self.transform:\n            image = self.transform(image)\n        return img_name, image\n\ntest_dataset = TestDataset(test_dir, transform=transform)\ntest_loader = torch.utils.data.DataLoader(test_dataset, batch_size=batch_size, shuffle=False, num_workers=4, pin_memory=True)\n\n# ConvNeXt 모델 정의\nclass ConvNextModel(nn.Module):\n    def __init__(self, num_classes=1):\n        super().__init__()\n        self.convnext = convnext_large(weights=ConvNeXt_Large_Weights.DEFAULT)\n        self.convnext.classifier[-1] = nn.Linear(self.convnext.classifier[-1].in_features, num_classes)\n\n    def forward(self, x):\n        return self.convnext(x)\n\n# 모델 초기화 및 GPU 설정\nmodel = ConvNextModel().float()\nif ngpu > 1:\n    model = nn.DataParallel(model, device_ids=list(range(ngpu))).to(device)\nelse:\n    model.to(device)\n\n# 손실 함수 및 옵티마이저\ncriterion = nn.BCEWithLogitsLoss()\noptimizer = optim.AdamW(model.parameters(), lr=learning_rate, weight_decay=weight_decay)\n\n# Learning Rate Scheduler\nscheduler = CosineAnnealingLR(optimizer, T_max=num_epochs, eta_min=0)\n\n# 학습\nprint(\"Starting Training...\")\nbest_val_loss = float('inf')\nfor epoch in range(num_epochs):\n    model.train()\n    running_loss = 0.0\n    optimizer.zero_grad()\n    for i, (inputs, labels) in enumerate(tqdm(train_loader, desc=f\"Epoch {epoch+1}/{num_epochs} (Train)\")):\n        inputs = inputs.to(device)\n        labels = labels.to(device).unsqueeze(1)\n\n        outputs = model(inputs)\n        loss = criterion(outputs, labels)\n\n        # Gradient Accumulation\n        loss = loss / accumulation_steps\n        loss.backward()\n\n        # Gradient Clipping\n        torch.nn.utils.clip_grad_norm_(model.parameters(), clip_grad_norm)\n\n        if (i + 1) % accumulation_steps == 0 or (i + 1) == len(train_loader):\n            optimizer.step()\n            optimizer.zero_grad()\n\n        running_loss += loss.item() * accumulation_steps\n\n    train_loss = running_loss / len(train_loader)\n    print(f\"Epoch {epoch+1}/{num_epochs} - Train Loss: {train_loss:.4f}\")\n\n    # 검증\n    model.eval()\n    running_loss = 0.0\n    all_preds = []\n    all_labels = []\n    with torch.no_grad():\n        for i, (inputs, labels) in enumerate(tqdm(val_loader, desc=f\"Epoch {epoch+1}/{num_epochs} (Val)\")):\n            inputs = inputs.to(device)\n            labels = labels.to(device).unsqueeze(1)\n\n            outputs = model(inputs)\n            loss = criterion(outputs, labels)\n\n            running_loss += loss.item()\n            all_preds.extend(outputs.sigmoid().cpu().numpy())\n            all_labels.extend(labels.cpu().numpy())\n\n    val_loss = running_loss / len(val_loader)\n    print(f\"Epoch {epoch+1}/{num_epochs} - Val Loss: {val_loss:.4f}\")\n\n    # 검증 손실이 가장 낮을 때 모델 저장\n    if val_loss < best_val_loss:\n        best_val_loss = val_loss\n        torch.save(model.state_dict(), f\"/kaggle/working/best_model.pth\")\n        print(\"Best model saved!\")\n\n    # 에폭마다 스케줄러 업데이트\n    scheduler.step()\n\n# Confusion Matrix 시각화\ncm = confusion_matrix(np.array(all_labels).flatten(), np.array(all_preds).flatten() > 0.5)\nplt.figure(figsize=(8, 6))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=['Real', 'Fake'], yticklabels=['Real', 'Fake'])\nplt.xlabel('Predicted')\nplt.ylabel('True')\nplt.title('Confusion Matrix')\nplt.show()\n\n# 제출 파일 로드\nfsc_submission = pd.read_csv(sample_submission_path, index_col=\"image\")\n\n# 추론\nmodel.load_state_dict(torch.load(\"/kaggle/working/best_model.pth\")) # 가장 좋은 모델 로드\nmodel.eval()\npredictions = {}\nwith torch.no_grad():\n    for img_name, img in tqdm(test_loader, desc=\"Inference\"):\n        img = img.to(device)\n        outputs = model(img)\n        outputs = outputs.sigmoid().cpu()\n        for i, img_name in enumerate(img_name):\n            predictions[img_name] = outputs[i].item()\n\n# 결과 저장\nsubmission = pd.DataFrame(columns=['image', 'label'])\nsubmission['image'] = list(predictions.keys())\nsubmission['label'] = list(predictions.values())\nsubmission['label'] = 1 - submission['label'] # Label Inversion\nsubmission.to_csv(f\"/kaggle/working/submission.csv\", index=False)\nprint(submission)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-16T06:12:40.395483Z","iopub.execute_input":"2025-01-16T06:12:40.395699Z","iopub.status.idle":"2025-01-16T06:32:23.0252Z","shell.execute_reply.started":"2025-01-16T06:12:40.395678Z","shell.execute_reply":"2025-01-16T06:32:23.024077Z"}},"outputs":[],"execution_count":null}]}