{"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.12.12"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":132732,"databundleVersionId":16583342,"isSourceIdPinned":false}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## Import Libraries","metadata":{"_uuid":"dea9fb84-1173-4e92-9582-b165902cb441","_cell_guid":"736d4e5b-b464-48cd-92e5-dbed93b7b8cd","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"import os\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nimport pandas as pd\nimport numpy as np\nimport cv2\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nfrom tqdm import tqdm\nfrom sklearn.linear_model import RidgeClassifier\nimport warnings\n\nwarnings.filterwarnings(\"ignore\")","metadata":{"_uuid":"99900f22-df34-4f4b-9da4-94f711bcc64c","_cell_guid":"9b427fea-8c43-4f18-b018-5a14f2b7c1b0","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Load CSV Data","metadata":{"_uuid":"8326c776-34c3-4c21-83b0-3c3cbed04778","_cell_guid":"a259c067-5fcf-4e8a-bb29-ce5368901e91","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"BASE_DIR = \"/kaggle/input/competitions/dlmmdd-workshop-synthetic-source-attribution-challenge/Data/Data\"\n\ntrain_df = pd.read_csv(os.path.join(BASE_DIR, \"training.csv\"))\ntest_df = pd.read_csv(os.path.join(BASE_DIR, \"test.csv\"))\n\ntrain_df","metadata":{"_uuid":"319db77e-6488-41a4-8205-259091d1d01f","_cell_guid":"2d746c4a-095d-4654-bfdd-c18f24774e4f","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Dataset Class","metadata":{"_uuid":"c3320029-3a39-429d-914e-60a595f6900f","_cell_guid":"4f3e87d0-dc3b-4733-b1f5-ca64681f0784","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"class SyntheticImageDataset(Dataset):\n    def __init__(self, df, transform=None):\n        self.df = df\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n\n        relative_path = str(row[\"path\"]).replace(\"Data/\", \"\", 1).lstrip(\"/\")\n        img_path = os.path.join(BASE_DIR, relative_path)\n\n        image = cv2.imread(img_path)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n\n        if self.transform:\n            augmented = self.transform(image=image)\n            image = augmented[\"image\"]\n\n        label = row.get(\"y\", -1)\n        return image, label, row[\"ID\"]","metadata":{"_uuid":"c853e178-4d1b-4811-b94a-b211d3dc9b66","_cell_guid":"8602c27d-7e2d-45b6-8f24-3071f7da5076","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Mean & STD","metadata":{"_uuid":"22764697-15ba-41f9-aa59-aa0824103d1b","_cell_guid":"59c2ff45-be42-42b8-8815-987854758595","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"stat_transform = A.Compose([\n    A.Resize(224, 224),\n    ToTensorV2(),\n])\n\nstat_dataset = SyntheticImageDataset(train_df, transform=stat_transform)\nstat_loader = DataLoader(stat_dataset, batch_size=256, shuffle=False, num_workers=4)\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\npixel_count = 0\nchannel_sum = torch.zeros(3).to(device)\nchannel_sum_squared = torch.zeros(3).to(device)\n\nwith torch.no_grad():\n    for images, _, _ in tqdm(stat_loader):\n        images = images.to(device).float() / 255.0 \n        \n        b, c, h, w = images.shape\n        pixel_count += (b * h * w)\n        \n        channel_sum += torch.sum(images, dim=[0, 2, 3])\n        channel_sum_squared += torch.sum(images**2, dim=[0, 2, 3])\n\n# Calculate E[X] and sqrt(E[X^2] - (E[X])^2) \ncomputed_mean = channel_sum / pixel_count\ncomputed_std = torch.sqrt((channel_sum_squared / pixel_count) - (computed_mean ** 2))\n\nprint(f\"Mean: {computed_mean.cpu().tolist()}\")\nprint(f\"Std:  {computed_std.cpu().tolist()}\")","metadata":{"_uuid":"bc1135b2-ecb8-4856-a5b8-b732567d1840","_cell_guid":"3bdb171f-77d5-47a0-a323-e86c6d6a2b4d","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Augmentations","metadata":{"_uuid":"13d461ba-2cd9-4392-a678-04de50edb01e","_cell_guid":"a93c7e75-4ae1-44ab-b413-759a9afc8e8e","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"train_transform = A.Compose(\n    [\n        A.ImageCompression(quality_range=(60, 100), p=0.5),\n        A.Affine(\n            scale=(0.9, 1.1), translate_percent=(-0.05, 0.05), rotate=(-15, 15), p=0.5\n        ),\n        A.GaussianBlur(blur_limit=(3, 7), p=0.5),\n        A.RandomBrightnessContrast(p=0.5),\n        A.Resize(224, 224),\n        A.Normalize(mean=computed_mean, std=computed_std),\n        ToTensorV2(),\n    ]\n)\n\ntest_transform = A.Compose(\n    [\n        A.Resize(224, 224),\n        A.Normalize(mean=computed_mean, std=computed_std),\n        ToTensorV2(),\n    ]\n)\n\ntest_transform_flipped = A.Compose(\n    [\n        A.HorizontalFlip(p=1.0),  # Force a flip on every image\n        A.Resize(224, 224),\n        A.Normalize(mean=computed_mean, std=computed_std),\n        ToTensorV2(),\n    ]\n)","metadata":{"_uuid":"a6f0353e-66d4-4da3-a329-65cc5733c8e8","_cell_guid":"fb7a5114-6140-44d5-b872-5307c90b0371","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Data Loaders","metadata":{"_uuid":"1b151837-c492-499d-b291-c51d257a756e","_cell_guid":"91ca29a2-a0c3-4689-a253-57766fc67b3f","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"batch_size = 128\n\ntrain_dataset = SyntheticImageDataset(train_df, transform=train_transform)\ntest_dataset = SyntheticImageDataset(test_df, transform=test_transform)\ntest_dataset_flipped = SyntheticImageDataset(test_df, transform=test_transform_flipped)\n\ntrain_loader = DataLoader(\n    train_dataset,\n    batch_size=batch_size,\n    shuffle=False,\n    num_workers=4\n)\ntest_loader = DataLoader(\n    test_dataset,\n    batch_size=batch_size,\n    shuffle=False,\n    num_workers=4\n)\ntest_loader_flipped = DataLoader(\n    test_dataset_flipped,\n    batch_size=128,\n    shuffle=False,\n    num_workers=4\n)","metadata":{"_uuid":"78235be0-d126-4cba-8e9b-1c4e58cf6e83","_cell_guid":"26ed69c3-fdc3-444d-b0f1-c57fe225278d","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Feature Extraction","metadata":{"_uuid":"1c8991ec-cfc5-4651-8049-25eb151bfc40","_cell_guid":"0af149a4-b302-4d44-baf9-0841a852f924","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"model = torch.hub.load(\"facebookresearch/dinov2\", \"dinov2_vitb14\")\nmodel = nn.DataParallel(model)\nmodel = model.cuda()\nmodel.eval()\n\n\ndef extract_features(loader):\n    features = []\n    labels = []\n    ids = []\n\n    with torch.no_grad():\n        for images, targets, img_ids in tqdm(loader, desc=\"Extracting\"):\n            images = images.cuda()\n            embeddings = model(images)\n\n            features.append(embeddings.cpu().numpy())\n            labels.append(targets.numpy())\n            ids.extend(img_ids)\n\n    return np.vstack(features), np.concatenate(labels), ids\n\n\n# Extract Train Features\nX_train, y_train, train_ids = extract_features(train_loader)\n\n# Extracting Test Features\nX_test, _, _ = extract_features(test_loader)\nX_test_flipped, _, _ = extract_features(test_loader_flipped)\n\nnp.save(\"X_train.npy\", X_train)\nnp.save(\"y_train.npy\", y_train)\nnp.save(\"X_test.npy\", X_test)\nprint(f\"Extraction complete! X_train shape: {X_train.shape}\")","metadata":{"_uuid":"c2071320-108b-423a-b76c-48e4241e6488","_cell_guid":"0581acbd-f464-4531-991e-6baa2f6416af","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Model Training","metadata":{"_uuid":"dd801936-a51a-4c73-9f71-dd7638187056","_cell_guid":"86a549fc-1463-4881-bc9a-417b10028b90","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"X_train = np.load(\"X_train.npy\")\ny_train = np.load(\"y_train.npy\")\nX_test = np.load(\"X_test.npy\")\n\nridge_clf = RidgeClassifier(\n    alpha=724.554709063955,\n    tol=1.0087032099321284e-05,\n    solver=\"svd\",\n    random_state=42\n)\n\nridge_clf.fit(X_train, y_train)","metadata":{"_uuid":"d8961c01-3e19-450b-abca-50b17a26b66d","_cell_guid":"82645f14-746c-4d63-b1ae-858c6a422132","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Submission","metadata":{"_uuid":"dfe68633-ee15-48a2-8bb8-fcaf3b15ecbd","_cell_guid":"c222c136-a0dd-4711-bc32-65a590fc2c31","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"scores_original = ridge_clf.decision_function(X_test)\nscores_flipped = ridge_clf.decision_function(X_test_flipped)\n\naveraged_scores = (scores_original + scores_flipped) / 2\nfinal_tta_preds = np.argmax(averaged_scores, axis=1)\n\nsubmission = pd.DataFrame({\n    \"ID\": test_df[\"ID\"],\n    \"TARGET\": final_tta_preds\n})\n\nsubmission.to_csv(\"submission.csv\", index=False)\nprint(\"Saved submission.csv successfully!\")","metadata":{"_uuid":"26817887-f7a5-44fc-bcde-d58d8bc3b4d8","_cell_guid":"e57e96d2-78b3-44fe-8a96-8c61b03fb9a0","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pd.read_csv(\"/kaggle/working/submission.csv\")","metadata":{"_uuid":"d72d08b7-fb96-46ab-919b-ae82d0b29c5a","_cell_guid":"16859379-d1b9-43d1-8091-f52ca295e545","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"_uuid":"0165c26c-0ed7-4c03-9033-737e0fcb38d2","_cell_guid":"443604c4-14f2-4379-8ac5-8a7d8a2e6834","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null}]}