{"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":"markdown","source":"## Setup","metadata":{}},{"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 *\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-16T11:32:23.232147Z","iopub.execute_input":"2026-10-16T11:32:23.232649Z","iopub.status.idle":"2026-10-16T11:32:41.881428Z","shell.execute_reply.started":"2026-10-16T11:32:23.232624Z","shell.execute_reply":"2026-10-16T11:32:41.88049Z"}},"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-16T11:33:28.533143Z","iopub.execute_input":"2026-10-16T11:33:28.533575Z","iopub.status.idle":"2026-10-16T11:33:39.932467Z","shell.execute_reply.started":"2026-10-16T11:33:28.53354Z","shell.execute_reply":"2026-10-16T11:33:39.931687Z"}},"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-16T11:33:39.934112Z","iopub.execute_input":"2026-10-16T11:33:39.934436Z","iopub.status.idle":"2026-10-16T11:33:41.676702Z","shell.execute_reply.started":"2026-10-16T11:33:39.934407Z","shell.execute_reply":"2026-10-16T11:33:41.676063Z"}},"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-16T11:33:41.677749Z","iopub.execute_input":"2026-10-16T11:33:41.678178Z","iopub.status.idle":"2026-10-16T11:33:42.884577Z","shell.execute_reply.started":"2026-10-16T11:33:41.678145Z","shell.execute_reply":"2026-10-16T11:33:42.883886Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Model","metadata":{}},{"cell_type":"code","source":"learn = vision_learner(dls, resnet34, metrics=[accuracy, error_rate])\n\nlearn.lr_find()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-16T11:33:48.214866Z","iopub.execute_input":"2026-10-16T11:33:48.215331Z","iopub.status.idle":"2026-10-16T11:34:11.729806Z","shell.execute_reply.started":"2026-10-16T11:33:48.215291Z","shell.execute_reply":"2026-10-16T11:34:11.729077Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"learn.fine_tune(3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-16T11:34:11.731535Z","iopub.execute_input":"2026-10-16T11:34:11.731902Z","iopub.status.idle":"2026-10-16T11:41:20.010699Z","shell.execute_reply.started":"2026-10-16T11:34:11.731873Z","shell.execute_reply":"2026-10-16T11:41:20.010038Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Loss-Curve","metadata":{}},{"cell_type":"code","source":"learn.recorder.plot_loss()\nplt.title('dvc_004 — train vs validation loss')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-16T11:41:20.011912Z","iopub.execute_input":"2026-10-16T11:41:20.012206Z","iopub.status.idle":"2026-10-16T11:41:20.17104Z","shell.execute_reply.started":"2026-10-16T11:41:20.012175Z","shell.execute_reply":"2026-10-16T11:41:20.170251Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Prediction distribution","metadata":{}},{"cell_type":"code","source":"test_files = get_image_files(WORK_DIR / 'test').sorted()\n\n# Sanity check the sort — this is the bug that tanked dvc_003\nprint(\"First 5 files:\", [p.name for p in test_files[:5]])\nprint(\"Last 5 files: \", [p.name for p in test_files[-5:]])\n\ntest_dl = dls.test_dl(test_files, with_labels=False)\npreds, _ = learn.get_preds(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\nfig, 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)')\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()\n\nn = len(raw_probs)\nprint(f\"Raw 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-16T11:41:31.049392Z","iopub.execute_input":"2026-10-16T11:41:31.049833Z","iopub.status.idle":"2026-10-16T11:41:54.70871Z","shell.execute_reply.started":"2026-10-16T11:41:31.049792Z","shell.execute_reply":"2026-10-16T11:41:54.708065Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Submission","metadata":{}},{"cell_type":"code","source":"# Map each test file to its integer id (the filename stem) and pair with its prediction\nid_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 — check filename parsing\"\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_dvc004.csv', index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-16T11:41:54.710252Z","iopub.execute_input":"2026-10-16T11:41:54.710568Z","iopub.status.idle":"2026-10-16T11:41:54.822849Z","shell.execute_reply.started":"2026-10-16T11:41:54.710541Z","shell.execute_reply":"2026-10-16T11:41:54.822067Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}