{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.13.15","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import zipfile\nfrom pathlib import Path\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport torch\nfrom fastai.vision.all import *\nfrom fastai.callback.mixup import MixUp\nfrom fastai.losses import LabelSmoothingCrossEntropy\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint(f\"Device: {device}\")\nif device.type == 'cuda':\n    print(f\"GPU: {torch.cuda.get_device_name(0)}\")\nprint(f\"torch: {torch.__version__}\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-10-17T12:50:10.098498Z","iopub.execute_input":"2026-10-17T12:50:10.099194Z","iopub.status.idle":"2026-10-17T12:50:28.305361Z","shell.execute_reply.started":"2026-10-17T12:50:10.099161Z","shell.execute_reply":"2026-10-17T12:50:28.304558Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Data","metadata":{}},{"cell_type":"code","source":"COMP_DIR = Path('/kaggle/input/competitions/dogs-vs-cats-redux-kernels-edition')\nWORK_DIR = Path('/kaggle/working/dogscats')\nWORK_DIR.mkdir(exist_ok=True)\n\nif not (WORK_DIR / 'train').exists():\n    with zipfile.ZipFile(COMP_DIR / 'train.zip', 'r') as z:\n        z.extractall(WORK_DIR / 'train')\nif not (WORK_DIR / 'test').exists():\n    with zipfile.ZipFile(COMP_DIR / 'test.zip', 'r') as z:\n        z.extractall(WORK_DIR / 'test')\n\nprint(f\"Train: {len(list((WORK_DIR/'train').glob('*.jpg')))}\")\nprint(f\"Test:  {len(list((WORK_DIR/'test').glob('*.jpg')))}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-17T12:50:28.306951Z","iopub.execute_input":"2026-10-17T12:50:28.307409Z","iopub.status.idle":"2026-10-17T12:50:39.611721Z","shell.execute_reply.started":"2026-10-17T12:50:28.307379Z","shell.execute_reply":"2026-10-17T12:50:39.611004Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## DataBlock","metadata":{}},{"cell_type":"code","source":"def label_func(x):\n    return 'cat' if x.name.startswith('cat') else 'dog'\n\ndblock = DataBlock(\n    blocks=(ImageBlock, CategoryBlock),\n    get_items=get_image_files,\n    splitter=RandomSplitter(valid_pct=0.2, seed=42),\n    get_y=label_func,\n    item_tfms=Resize(224),\n    batch_tfms=[\n        *aug_transforms(size=224),\n        Normalize.from_stats(*imagenet_stats),\n    ],\n)\n\ndls = dblock.dataloaders(WORK_DIR / 'train', bs=64)\nprint(f\"Train batches: {len(dls.train)} | Val batches: {len(dls.valid)}\")\nprint(f\"Classes: {dls.vocab}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-17T12:50:39.615997Z","iopub.execute_input":"2026-10-17T12:50:39.616217Z","iopub.status.idle":"2026-10-17T12:50:41.358573Z","shell.execute_reply.started":"2026-10-17T12:50:39.61619Z","shell.execute_reply":"2026-10-17T12:50:41.357904Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dls.show_batch(max_n=6, nrows=2)\n\nxb, yb = dls.one_batch()\nprint(f\"Batch: {xb.shape}  Labels: {yb.shape}\")\nprint(f\"Pixel range: [{xb.min().item():.2f}, {xb.max().item():.2f}]\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-17T12:50:41.35969Z","iopub.execute_input":"2026-10-17T12:50:41.360016Z","iopub.status.idle":"2026-10-17T12:50:42.517207Z","shell.execute_reply.started":"2026-10-17T12:50:41.359985Z","shell.execute_reply":"2026-10-17T12:50:42.516049Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Model","metadata":{}},{"cell_type":"code","source":"learn = vision_learner(\n    dls,\n    resnet34,\n    loss_func=LabelSmoothingCrossEntropy(),\n    metrics=[accuracy, error_rate],\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-17T12:50:42.51804Z","iopub.execute_input":"2026-10-17T12:50:42.518324Z","iopub.status.idle":"2026-10-17T12:50:43.566694Z","shell.execute_reply.started":"2026-10-17T12:50:42.518295Z","shell.execute_reply":"2026-10-17T12:50:43.566052Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"learn.lr_find()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-17T12:50:43.567475Z","iopub.execute_input":"2026-10-17T12:50:43.567749Z","iopub.status.idle":"2026-10-17T12:51:07.601578Z","shell.execute_reply.started":"2026-10-17T12:50:43.567721Z","shell.execute_reply":"2026-10-17T12:51:07.600888Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Train with MixUp","metadata":{}},{"cell_type":"code","source":"learn.fine_tune(5, cbs=MixUp(alpha=0.4))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-17T12:53:15.025521Z","iopub.execute_input":"2026-10-17T12:53:15.026027Z","iopub.status.idle":"2026-10-17T13:05:31.290215Z","shell.execute_reply.started":"2026-10-17T12:53:15.025993Z","shell.execute_reply":"2026-10-17T13:05:31.289513Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Loss Curve","metadata":{}},{"cell_type":"code","source":"learn.recorder.plot_loss()\nplt.title('dvc_005 — train vs validation loss (MixUp + label smoothing)')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-17T13:06:49.549666Z","iopub.execute_input":"2026-10-17T13:06:49.550556Z","iopub.status.idle":"2026-10-17T13:06:49.720866Z","shell.execute_reply.started":"2026-10-17T13:06:49.550505Z","shell.execute_reply":"2026-10-17T13:06:49.720088Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Predictions with clipping","metadata":{}},{"cell_type":"code","source":"test_files = get_image_files(WORK_DIR / 'test').sorted()\n\n# Sanity check — confirm lexicographic sort (we'll map by id anyway)\nprint(\"First 5 files:\", [p.name for p in test_files[:5]])\n\ntest_dl = dls.test_dl(test_files, with_labels=False)\n\n# TTA — averages predictions over 4 augmented views of each test image\npreds, _ = learn.tta(dl=test_dl)\nraw_probs = preds[:, 1].numpy()\n\nCLIP_LO, CLIP_HI = 0.005, 0.995\nclipped_probs = np.clip(raw_probs, CLIP_LO, CLIP_HI)\n\nprint(f\"\\nRaw range:     [{raw_probs.min():.6f}, {raw_probs.max():.6f}]\")\nprint(f\"Clipped range: [{clipped_probs.min():.6f}, {clipped_probs.max():.6f}]\")\nprint(f\"Mean (raw):    {raw_probs.mean():.4f}\")\nprint(f\"Saturated < 0.01:      {(raw_probs < 0.01).sum():>5} ({100*(raw_probs<0.01).mean():.2f}%)\")\nprint(f\"Saturated > 0.99:      {(raw_probs > 0.99).sum():>5} ({100*(raw_probs>0.99).mean():.2f}%)\")\nprint(f\"In middle [0.01,0.99]: {((raw_probs>=0.01)&(raw_probs<=0.99)).sum():>5} \"\n      f\"({100*((raw_probs>=0.01)&(raw_probs<=0.99)).mean():.2f}%)\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-17T13:06:57.35851Z","iopub.execute_input":"2026-10-17T13:06:57.359276Z","iopub.status.idle":"2026-10-17T13:09:09.137007Z","shell.execute_reply.started":"2026-10-17T13:06:57.359244Z","shell.execute_reply":"2026-10-17T13:09:09.136173Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Prediction Plot","metadata":{}},{"cell_type":"code","source":"fig, axes = plt.subplots(1, 2, figsize=(14, 4))\naxes[0].hist(clipped_probs, bins=50, edgecolor='black')\naxes[0].set_xlabel('P(dog)')\naxes[0].set_ylabel('Count')\naxes[0].set_title('Prediction distribution (clipped, TTA)')\n\naxes[1].hist(clipped_probs, bins=100, edgecolor='black')\naxes[1].set_yscale('log')\naxes[1].set_xlabel('P(dog)')\naxes[1].set_ylabel('Count (log)')\naxes[1].set_title('Same, log scale')\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-17T13:09:09.13894Z","iopub.execute_input":"2026-10-17T13:09:09.139338Z","iopub.status.idle":"2026-10-17T13:09:09.714043Z","shell.execute_reply.started":"2026-10-17T13:09:09.139287Z","shell.execute_reply":"2026-10-17T13:09:09.713396Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Submission","metadata":{}},{"cell_type":"code","source":"id_to_prob = {int(p.stem): float(prob) for p, prob in zip(test_files, clipped_probs)}\n\nsubmission = pd.read_csv(COMP_DIR / 'sample_submission.csv')\nsubmission['label'] = submission['id'].map(id_to_prob)\n\nassert submission['label'].notna().all(), \"Some ids have no prediction\"\nassert len(submission) == len(raw_probs), \"Row count mismatch\"\n\nprint(submission.head())\nprint(f\"Rows: {len(submission)}\")\nprint(f\"Prob range: [{submission['label'].min():.6f}, {submission['label'].max():.6f}]\")\nprint(f\"Mean prob:  {submission['label'].mean():.4f}\")\n\nsubmission.to_csv('/kaggle/working/submission_dvc005.csv', index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-17T13:09:15.93537Z","iopub.execute_input":"2026-10-17T13:09:15.935981Z","iopub.status.idle":"2026-10-17T13:09:16.053596Z","shell.execute_reply.started":"2026-10-17T13:09:15.935945Z","shell.execute_reply":"2026-10-17T13:09:16.052951Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}