{"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":"# Clipping","metadata":{}},{"cell_type":"markdown","source":"## Imports","metadata":{}},{"cell_type":"code","source":"import os, json, shutil, zipfile\nfrom pathlib import Path\nfrom datetime import datetime\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom PIL import Image\n\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)}\")\n    print(f\"Memory: {torch.cuda.get_device_properties(0).total_memory / 1e9:.1f} GB\")\nprint(f\"torch version: {torch.__version__}\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-10-16T10:43:31.833956Z","iopub.execute_input":"2026-10-16T10:43:31.836451Z","iopub.status.idle":"2026-10-16T10:43:50.413144Z","shell.execute_reply.started":"2026-10-16T10:43:31.836405Z","shell.execute_reply":"2026-10-16T10:43:50.412178Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DATASET_DIR = Path('/kaggle/input/dvc-fastai-experiments')\nLOG_PATH = Path('/kaggle/working/experiments.csv')\nRUNS_DIR = Path('/kaggle/working/runs')\n\nif DATASET_DIR.exists():\n    if (DATASET_DIR / 'experiments.csv').exists() and not LOG_PATH.exists():\n        shutil.copy(DATASET_DIR / 'experiments.csv', LOG_PATH)\n        print(\"Restored experiments.csv from dataset\")\n\n    if (DATASET_DIR / 'runs.zip').exists() and not RUNS_DIR.exists():\n        shutil.unpack_archive(DATASET_DIR / 'runs.zip', '/kaggle/working/')\n        n = len(list(RUNS_DIR.iterdir())) if RUNS_DIR.exists() else 0\n        print(f\"Restored {n} prior runs from dataset\")\nelse:\n    print(\"No dataset attached — starting fresh log\")\n\nif LOG_PATH.exists():\n    print(\"\\nExisting experiments:\")\n    display(pd.read_csv(LOG_PATH))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-16T10:43:50.414256Z","iopub.execute_input":"2026-10-16T10:43:50.414815Z","iopub.status.idle":"2026-10-16T10:43:50.421832Z","shell.execute_reply.started":"2026-10-16T10:43:50.414784Z","shell.execute_reply":"2026-10-16T10:43:50.420734Z"}},"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 images: {len(list((WORK_DIR/'train').glob('*.jpg')))}\")\nprint(f\"Test images:  {len(list((WORK_DIR/'test').glob('*.jpg')))}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-16T10:43:50.42357Z","iopub.execute_input":"2026-10-16T10:43:50.424407Z","iopub.status.idle":"2026-10-16T10:44:06.343221Z","shell.execute_reply.started":"2026-10-16T10:43:50.424358Z","shell.execute_reply":"2026-10-16T10:44:06.34249Z"}},"outputs":[],"execution_count":null},{"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-16T10:44:06.344228Z","iopub.execute_input":"2026-10-16T10:44:06.344643Z","iopub.status.idle":"2026-10-16T10:44:08.158073Z","shell.execute_reply.started":"2026-10-16T10:44:06.344613Z","shell.execute_reply":"2026-10-16T10:44:08.15719Z"}},"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 shape: {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-16T10:44:08.159097Z","iopub.execute_input":"2026-10-16T10:44:08.159406Z","iopub.status.idle":"2026-10-16T10:44:09.86953Z","shell.execute_reply.started":"2026-10-16T10:44:08.159379Z","shell.execute_reply":"2026-10-16T10:44:09.868665Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Experiment Logging","metadata":{}},{"cell_type":"code","source":"RUNS_DIR = Path('/kaggle/working/runs')\nRUNS_DIR.mkdir(exist_ok=True)\nLOG_PATH = Path('/kaggle/working/experiments.csv')\n\ndef log_run(**kwargs):\n    row = {'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M'), **kwargs}\n    if LOG_PATH.exists():\n        df = pd.read_csv(LOG_PATH)\n        df = pd.concat([df, pd.DataFrame([row])], ignore_index=True)\n    else:\n        df = pd.DataFrame([row])\n    df.to_csv(LOG_PATH, index=False)\n    print(f\"Logged run. Total rows: {len(df)}\")\n    return df\n\ndef save_run_artifacts(learn, run_id, config):\n    run_dir = RUNS_DIR / run_id\n    run_dir.mkdir(parents=True, exist_ok=True)\n\n    try:\n        learn.recorder.plot_loss()\n        plt.savefig(run_dir / 'loss_curve.png', dpi=100, bbox_inches='tight')\n        plt.close()\n    except Exception as e:\n        print(f\"  loss_curve failed: {e}\")\n\n    try:\n        interp = ClassificationInterpretation.from_learner(learn)\n        interp.plot_confusion_matrix()\n        plt.savefig(run_dir / 'confusion_matrix.png', dpi=100, bbox_inches='tight')\n        plt.close()\n\n        interp.plot_top_losses(9, nrows=3)\n        plt.savefig(run_dir / 'top_losses.png', dpi=100, bbox_inches='tight')\n        plt.close()\n    except Exception as e:\n        print(f\"  interpretation failed: {e}\")\n\n    with open(run_dir / 'config.json', 'w') as f:\n        json.dump(config, f, indent=2, default=str)\n\n    print(f\"Artifacts saved to {run_dir}:\")\n    for p in sorted(run_dir.iterdir()):\n        print(f\"  {p.name}\")\n\nprint(\"Tracking setup ready.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-16T10:44:09.870915Z","iopub.execute_input":"2026-10-16T10:44:09.871461Z","iopub.status.idle":"2026-10-16T10:44:09.882216Z","shell.execute_reply.started":"2026-10-16T10:44:09.871417Z","shell.execute_reply":"2026-10-16T10:44:09.881398Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Config","metadata":{}},{"cell_type":"code","source":"run_id = 'dvc_003'\n\nconfig = {\n    'run_id': run_id,\n    'description': 'dvc_002 + prediction clipping [0.005, 0.995]',\n    'arch': 'resnet34',\n    'img_size': 224,\n    'batch_size': 64,\n    'augmentation': 'aug_transforms (default)',\n    'lr': 'default (fine_tune)',\n    'epochs': 3,\n    'notes': 'one change from dvc_002: clip probs before submission',\n}\n\nprint(json.dumps(config, indent=2))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-16T10:44:09.886483Z","iopub.execute_input":"2026-10-16T10:44:09.887732Z","iopub.status.idle":"2026-10-16T10:44:09.905644Z","shell.execute_reply.started":"2026-10-16T10:44:09.887673Z","shell.execute_reply":"2026-10-16T10:44:09.904775Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Model","metadata":{}},{"cell_type":"code","source":"learn = vision_learner(\n    dls,\n    resnet34,\n    metrics=[accuracy, error_rate],\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-16T10:44:09.907942Z","iopub.execute_input":"2026-10-16T10:44:09.908486Z","iopub.status.idle":"2026-10-16T10:44:11.034119Z","shell.execute_reply.started":"2026-10-16T10:44:09.908459Z","shell.execute_reply":"2026-10-16T10:44:11.033508Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"learn.lr_find()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-16T10:44:11.034956Z","iopub.execute_input":"2026-10-16T10:44:11.035293Z","iopub.status.idle":"2026-10-16T10:44:33.681755Z","shell.execute_reply.started":"2026-10-16T10:44:11.035249Z","shell.execute_reply":"2026-10-16T10:44:33.680817Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"learn.fine_tune(3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-16T10:47:34.712915Z","iopub.execute_input":"2026-10-16T10:47:34.71342Z","iopub.status.idle":"2026-10-16T10:55:26.264379Z","shell.execute_reply.started":"2026-10-16T10:47:34.713386Z","shell.execute_reply":"2026-10-16T10:55:26.263415Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Save Logs","metadata":{}},{"cell_type":"code","source":"val_loss, val_acc, val_err = learn.validate()\nprint(f\"val_loss={val_loss:.4f}  val_acc={val_acc:.4f}  val_err={val_err:.4f}\")\n\nsave_run_artifacts(learn, run_id, config)\n\nlog_run(\n    **{k: v for k, v in config.items() if k != 'run_id'},\n    run_id=run_id,\n    val_loss=round(float(val_loss), 4),\n    val_acc=round(float(val_acc), 4),\n    lb_score=None,\n)\n\ndisplay(pd.read_csv(LOG_PATH))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-16T10:55:56.702401Z","iopub.execute_input":"2026-10-16T10:55:56.702996Z","iopub.status.idle":"2026-10-16T10:56:28.77837Z","shell.execute_reply.started":"2026-10-16T10:55:56.702962Z","shell.execute_reply":"2026-10-16T10:56:28.777411Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Submission with clipping","metadata":{}},{"cell_type":"code","source":"test_files = get_image_files(WORK_DIR / 'test').sorted()\ntest_dl = dls.test_dl(test_files, with_labels=False)\n\npreds, _ = learn.get_preds(dl=test_dl)\nraw_probs = preds[:, 1].numpy()\n\n# --- Clipping ---\nCLIP_LO, CLIP_HI = 0.005, 0.995\nprobs = np.clip(raw_probs, CLIP_LO, CLIP_HI)\n\nprint(f\"Raw prob range:   [{raw_probs.min():.6f}, {raw_probs.max():.6f}]\")\nprint(f\"Clipped range:    [{probs.min():.6f}, {probs.max():.6f}]\")\nn_clipped = ((raw_probs < CLIP_LO) | (raw_probs > CLIP_HI)).sum()\nprint(f\"Values clipped:   {n_clipped} / {len(raw_probs)} ({100*n_clipped/len(raw_probs):.2f}%)\")\n\nsubmission = pd.read_csv(COMP_DIR / 'sample_submission.csv')\nsubmission['label'] = probs\nsubmission.to_csv('/kaggle/working/submission_dvc003_clipped.csv', index=False)\n\n# Also save unclipped for comparison\nsubmission_raw = pd.read_csv(COMP_DIR / 'sample_submission.csv')\nsubmission_raw['label'] = raw_probs\nsubmission_raw.to_csv('/kaggle/working/submission_dvc003_raw.csv', index=False)\n\nprint(f\"\\nMean prob (raw):     {raw_probs.mean():.4f}\")\nprint(f\"Mean prob (clipped): {probs.mean():.4f}\")\nprint(submission.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-16T10:56:28.780192Z","iopub.execute_input":"2026-10-16T10:56:28.78056Z","iopub.status.idle":"2026-10-16T10:56:53.54968Z","shell.execute_reply.started":"2026-10-16T10:56:28.780529Z","shell.execute_reply":"2026-10-16T10:56:53.548958Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}