{"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":"none","dataSources":[{"sourceId":13578,"databundleVersionId":588368,"sourceType":"competition"}],"dockerImageVersionId":31153,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# import zipfile\n# from glob import glob\n# all_zip_files = glob(\"/kaggle/input/kuzushiji-recognition/*.zip\")\n# for f in all_zip_files:\n#     with zipfile.ZipFile(f, \"r\") as zip_file:\n#         zip_file.extractall(path='/kaggle/working/')\n\nimport zipfile\nwith zipfile.ZipFile(\"/kaggle/input/kuzushiji-recognition/test_images.zip\", \"r\") as zip_file:\n    zip_file.extractall(path='/kaggle/working/test_images/')\n\nwith zipfile.ZipFile(\"/kaggle/input/kuzushiji-recognition/train_images.zip\", \"r\") as zip_file:\n    zip_file.extractall(path='/kaggle/working/train_images/')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-13T14:59:46.477561Z","iopub.execute_input":"2025-10-13T14:59:46.477894Z","iopub.status.idle":"2025-10-13T15:00:33.302675Z","shell.execute_reply.started":"2025-10-13T14:59:46.477864Z","shell.execute_reply":"2025-10-13T15:00:33.301576Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nimport torchvision.transforms as T\nfrom transformers import DetrForObjectDetection, DetrFeatureExtractor\nfrom PIL import Image\nimport numpy as np\nfrom sklearn.model_selection import KFold\nfrom tqdm import tqdm\n\n# Enable Hugging Face hub to work in offline mode\nimport os\n\nos.environ[\"HF_HUB_OFFLINE\"] = \"0\"\n\n# Load data\ntrain_df = pd.read_csv(\"/kaggle/input/kuzushiji-recognition/train.csv\")#train_df = pd.read_csv(\"./data/train.csv\")\nunicode_translation_df = pd.read_csv(\"/kaggle/input/kuzushiji-recognition/unicode_translation.csv\")#unicode_translation_df = pd.read_csv(\"./data/unicode_translation.csv\")\n\n# Create a dictionary mapping Unicode to character\nunicode_to_char = unicode_translation_df.set_index(\"Unicode\")[\"char\"].to_dict()\nunique_labels = sorted(set(unicode_to_char.values()))\nlabel_to_class_idx = {label: idx for idx, label in enumerate(unique_labels)}\n\n\nclass KuzushijiDataset(Dataset):\n    def __init__(self, df, image_dir, feature_extractor):\n        self.df = df\n        self.image_dir = image_dir\n        self.feature_extractor = feature_extractor\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        image_id = self.df.iloc[idx][\"image_id\"]\n        image_path = os.path.join(self.image_dir, f\"{image_id}.jpg\")\n        image = Image.open(image_path).convert(\"RGB\")\n\n        labels = self.df.iloc[idx][\"labels\"]\n        boxes = []\n        labels_list = []\n        for i in range(0, len(labels.split()), 5):\n            label, x, y, w, h = labels.split()[i : i + 5]\n            boxes.append([int(x), int(y), int(x) + int(w), int(y) + int(h)])\n            # labels_list.append(ord(unicode_to_char[label]))\n            label_char = unicode_to_char[label]\n            class_idx = label_to_class_idx[label_char]\n            labels_list.append(class_idx)\n\n        encoding = self.feature_extractor(images=image, return_tensors=\"pt\", size={\"height\":800, \"width\":800})\n        encoding[\"labels\"] = torch.tensor(labels_list, dtype=torch.int64)\n        encoding[\"boxes\"] = torch.tensor(boxes, dtype=torch.float32)\n\n        return encoding\n\n\ndef collate_fn(batch):\n    return {\n        \"pixel_values\": torch.cat([b[\"pixel_values\"] for b in batch], dim=0),\n        \"labels\": [b[\"labels\"] for b in batch],\n        \"boxes\": [b[\"boxes\"] for b in batch],\n    }\n\n\n# Set device\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\ntry:\n    # Define model and feature extractor\n    feature_extractor = DetrFeatureExtractor.from_pretrained(\"facebook/detr-resnet-50\")\n    model = DetrForObjectDetection.from_pretrained(\"facebook/detr-resnet-50\")\n    model.config.num_labels = len(unique_labels)\n    model.class_labels_classifier = torch.nn.Linear(model.config.d_model, model.config.num_labels)\n    model.to(device)\nexcept EnvironmentError as e:\n    print(f\"Error loading model: {e}\")\n    print(\n        \"Please ensure the model is cached locally or adjust the code to download it manually.\"\n    )\n\n\n# Train function\ndef train(model, device, loader, optimizer):\n    model.train()\n    total_loss = 0\n    for batch in tqdm(loader):\n        pixel_values = batch[\"pixel_values\"].to(device)\n        labels = [\n            {\"class_labels\": labels, \"boxes\": boxes}\n            for labels, boxes in zip(batch[\"labels\"], batch[\"boxes\"])\n        ]\n        outputs = model(pixel_values=pixel_values, labels=labels)\n        loss = outputs.loss\n        total_loss += loss.item()\n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n    return total_loss / len(loader)\n\n\n# Evaluate function\ndef evaluate(model, device, loader):\n    model.eval()\n    total_loss = 0\n    with torch.no_grad():\n        for batch in tqdm(loader):\n            pixel_values = batch[\"pixel_values\"].to(device)\n            labels = [\n                {\"class_labels\": labels, \"boxes\": boxes}\n                for labels, boxes in zip(batch[\"labels\"], batch[\"boxes\"])\n            ]\n            outputs = model(pixel_values=pixel_values, labels=labels)\n            loss = outputs.loss\n            total_loss += loss.item()\n    return total_loss / len(loader)\n\n\n# 5-fold cross-validation\nkf = KFold(n_splits=5, shuffle=True, random_state=42)\nscores = []\nfor fold, (train_idx, val_idx) in enumerate(kf.split(train_df)):\n    # train_dataset = KuzushijiDataset(\n    #     train_df.iloc[train_idx], \"./data/train_images\", feature_extractor\n    # )\n    # val_dataset = KuzushijiDataset(\n    #     train_df.iloc[val_idx], \"./data/train_images\", feature_extractor\n    # )\n    train_dataset = KuzushijiDataset(\n        train_df.iloc[train_idx], \"/kaggle/working/train_images\", feature_extractor\n    )\n    val_dataset = KuzushijiDataset(\n        train_df.iloc[val_idx], \"/kaggle/working/train_images\", feature_extractor\n    )\n    train_loader = DataLoader(\n        train_dataset, batch_size=2, shuffle=True, num_workers=4, collate_fn=collate_fn\n    )\n    val_loader = DataLoader(\n        val_dataset, batch_size=2, shuffle=False, num_workers=4, collate_fn=collate_fn\n    )\n\n    optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4)\n\n    for epoch in range(5):\n        train(model, device, train_loader, optimizer)\n        evaluate(model, device, val_loader)\n\n    scores.append(evaluate(model, device, val_loader))\n\nprint(f\"5-fold cross-validation score: {np.mean(scores)}\")\n\n# Make predictions on test set\n# test_dataset = KuzushijiDataset(\n#     pd.read_csv(\"./data/sample_submission.csv\"),\n#     \"./data/test_images\",\n#     feature_extractor,\n# )\ntest_dataset = KuzushijiDataset(\n    pd.read_csv(\"/kaggle/input/kuzushiji-recognition/sample_submission.csv\"),\n    \"/kaggle/working/test_images\",\n    feature_extractor,\n)\ntest_loader = DataLoader(\n    test_dataset, batch_size=2, shuffle=False, num_workers=4, collate_fn=collate_fn\n)\n\nmodel.eval()\ntest_preds = []\nwith torch.no_grad():\n    for batch in tqdm(test_loader):\n        pixel_values = batch[\"pixel_values\"].to(device)\n        outputs = model(pixel_values=pixel_values)\n        for i, output in enumerate(outputs):\n            scores = output[\"scores\"].cpu().numpy()\n            boxes = output[\"pred_boxes\"].cpu().numpy()\n            labels = output[\"labels\"].cpu().numpy()\n            for score, box, label in zip(scores, boxes, labels):\n                if score > 0.5:\n                    test_preds.append(\n                        {\n                            \"image_id\": test_dataset.df.iloc[i][\"image_id\"],\n                            \"label\": chr(label),\n                            \"x\": (box[0] * 512 + box[2] * 512) / 2,\n                            \"y\": (box[1] * 512 + box[3] * 512) / 2,\n                        }\n                    )\n\n# Save predictions to submission.csv\nsubmission_df = pd.DataFrame(test_preds)\nsubmission_df = (\n    submission_df.groupby(\"image_id\")\n    .apply(\n        lambda x: \" \".join(\n            f\"{label} {x:.2f} {y:.2f}\"\n            for label, x, y in zip(x[\"label\"], x[\"x\"], x[\"y\"])\n        )\n    )\n    .reset_index()\n)\nsubmission_df.columns = [\"image_id\", \"labels\"]\nsubmission_df.to_csv(\"submission.csv\", index=False)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-13T15:00:33.304322Z","iopub.execute_input":"2025-10-13T15:00:33.304715Z","iopub.status.idle":"2025-10-13T15:00:42.140955Z","shell.execute_reply.started":"2025-10-13T15:00:33.304682Z","shell.execute_reply":"2025-10-13T15:00:42.139033Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# len(unique_labels)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-12T11:45:58.333624Z","iopub.execute_input":"2025-10-12T11:45:58.334006Z","iopub.status.idle":"2025-10-12T11:45:58.338687Z","shell.execute_reply.started":"2025-10-12T11:45:58.333979Z","shell.execute_reply":"2025-10-12T11:45:58.337303Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}