Skip to content

Commit 81c8b4b

Browse files
JuliaEdomclaude
andcommitted
fix(autotune): resume interrupted Kaggle run from rescued checkpoint
v8 hit the 12h session cut at step 336/500 (steady ~127 s/step); a cancelled kernel keeps no attachable output, but ListKernelSessionOutput still serves the session files — checkpoint-300 rescued that way and re-uploaded as the private dataset nlsql-autotune-prev. Kaggle flattens the uploaded zip, so the notebook now rebuilds checkpoint-<global_step>/ around any trainer_state.json (+optimizer.pt guard) found under /kaggle/input, failing loudly when resume is requested but no checkpoint is present. Smoke off for v9: the full path was already proven on v8. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent b529b08 commit 81c8b4b

2 files changed

Lines changed: 4 additions & 100 deletions

File tree

‎scripts/autotune/kaggle/kaggle_autotune.ipynb‎

Lines changed: 3 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -20,111 +20,15 @@
2020
"id": "acae54e37e7d407bbb7b55eff062a284",
2121
"metadata": {},
2222
"outputs": [],
23-
"source": [
24-
"MODE = \"train\" # \"train\" | \"serve\"\n",
25-
"\n",
26-
"BASE_MODEL = \"Qwen/Qwen2.5-Coder-7B-Instruct\"\n",
27-
"DATA_DIR = \"/kaggle/input/nlsql-autotune-data\" # train.jsonl, val.jsonl, train_qlora.py\n",
28-
"OUT = \"/kaggle/working/qlora_out\"\n",
29-
"\n",
30-
"# train mode\n",
31-
"EPOCHS = 1.0\n",
32-
"MAX_SEQ = 4096\n",
33-
"SMOKE_STEPS = 5 # >0: run this many steps end-to-end first, then the real run (0 = straight in)\n",
34-
"RESUME_INPUT = \"\" # e.g. \"/kaggle/input/nlsql-autotune-prev\" (previous run's output) or \"\"\n",
35-
"\n",
36-
"# serve mode\n",
37-
"ADAPTER_DIR = \"/kaggle/input/nlsql-adapter/adapter\" # absent path => serve plain base model\n",
38-
"SERVE_HOURS = 5.0\n",
39-
"PORT = 8000"
40-
]
23+
"source": "MODE = \"train\" # \"train\" | \"serve\"\n\nBASE_MODEL = \"Qwen/Qwen2.5-Coder-7B-Instruct\"\nDATA_DIR = \"/kaggle/input/nlsql-autotune-data\" # train.jsonl, val.jsonl, train_qlora.py\nOUT = \"/kaggle/working/qlora_out\"\n\n# train mode\nEPOCHS = 1.0\nMAX_SEQ = 4096\nSMOKE_STEPS = 0 # >0: run this many steps end-to-end first, then the real run (0 = straight in)\nRESUME_INPUT = \"auto\" # non-empty: rglob /kaggle/input for checkpoint-* and resume; \"\" = fresh\n\n# serve mode\nADAPTER_DIR = \"/kaggle/input/nlsql-adapter/adapter\" # absent path => serve plain base model\nSERVE_HOURS = 5.0\nPORT = 8000"
4124
},
4225
{
4326
"cell_type": "code",
4427
"execution_count": null,
4528
"id": "9a63283cbaf04dbcab1f6479b197f3a8",
4629
"metadata": {},
4730
"outputs": [],
48-
"source": [
49-
"import shutil\n",
50-
"import subprocess\n",
51-
"import sys\n",
52-
"from pathlib import Path\n",
53-
"\n",
54-
"if MODE == \"train\":\n",
55-
" tree = subprocess.run(\n",
56-
" [\"find\", \"/kaggle/input\", \"-maxdepth\", \"4\"], capture_output=True, text=True\n",
57-
" ).stdout\n",
58-
" print(tree, flush=True)\n",
59-
" hits = sorted(Path(\"/kaggle/input\").rglob(\"train_qlora.py\"))\n",
60-
" if not hits:\n",
61-
" raise RuntimeError(\"train_qlora.py not found anywhere under /kaggle/input\")\n",
62-
" data_dir = hits[0].parent\n",
63-
" print(f\"data_dir resolved: {data_dir}\", flush=True)\n",
64-
" subprocess.run([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", \"unsloth\"], check=True)\n",
65-
" subprocess.run(\n",
66-
" [\n",
67-
" sys.executable,\n",
68-
" \"-m\",\n",
69-
" \"pip\",\n",
70-
" \"install\",\n",
71-
" \"-q\",\n",
72-
" \"datasets>=3.0\",\n",
73-
" \"trl>=0.12\",\n",
74-
" \"peft>=0.13\",\n",
75-
" \"bitsandbytes>=0.44\",\n",
76-
" \"accelerate>=1.0\",\n",
77-
" ],\n",
78-
" check=True,\n",
79-
" )\n",
80-
"\n",
81-
" def train_cmd(out, extra):\n",
82-
" return [\n",
83-
" sys.executable,\n",
84-
" str(data_dir / \"train_qlora.py\"),\n",
85-
" \"--model\",\n",
86-
" BASE_MODEL,\n",
87-
" \"--train\",\n",
88-
" str(data_dir / \"train.jsonl\"),\n",
89-
" \"--val\",\n",
90-
" str(data_dir / \"val.jsonl\"),\n",
91-
" \"--out\",\n",
92-
" out,\n",
93-
" \"--max-seq-len\",\n",
94-
" str(MAX_SEQ),\n",
95-
" \"--batch-size\",\n",
96-
" \"1\",\n",
97-
" \"--grad-accum\",\n",
98-
" \"16\",\n",
99-
" \"--no-merge\",\n",
100-
" *extra,\n",
101-
" ]\n",
102-
"\n",
103-
" if SMOKE_STEPS:\n",
104-
" # Both earlier sessions died in their first minutes on a trl kwarg rename,\n",
105-
" # and each failure cost a day because nobody was watching. Prove the whole\n",
106-
" # path -- config, trainer, a real step, evaluate, adapter save -- on a few\n",
107-
" # steps before committing the 6-12h run.\n",
108-
" smoke_out = \"/kaggle/working/smoke_out\"\n",
109-
" smoke = train_cmd(smoke_out, [\"--epochs\", \"1\", \"--max-steps\", str(SMOKE_STEPS)])\n",
110-
" print(\" \".join(smoke), flush=True)\n",
111-
" subprocess.run(smoke, check=True)\n",
112-
" if not Path(smoke_out, \"adapter\", \"adapter_config.json\").exists():\n",
113-
" raise RuntimeError(\"smoke run finished but saved no adapter\")\n",
114-
" shutil.rmtree(smoke_out)\n",
115-
" print(\"SMOKE OK -- starting the real run\", flush=True)\n",
116-
"\n",
117-
" ckpt_dst = Path(OUT) / \"checkpoints\"\n",
118-
" prev = Path(RESUME_INPUT) / \"qlora_out\" / \"checkpoints\" if RESUME_INPUT else None\n",
119-
" if prev and prev.is_dir() and not ckpt_dst.exists():\n",
120-
" shutil.copytree(prev, ckpt_dst)\n",
121-
" print(f\"resume: copied checkpoints from {prev}\")\n",
122-
" cmd = train_cmd(OUT, [\"--epochs\", str(EPOCHS), \"--resume\"])\n",
123-
" print(\" \".join(cmd), flush=True)\n",
124-
" subprocess.run(cmd, check=True)\n",
125-
" shutil.make_archive(\"/kaggle/working/adapter\", \"zip\", OUT, \"adapter\")\n",
126-
" print(\"DONE: /kaggle/working/adapter.zip\")"
127-
]
31+
"source": "import json\nimport shutil\nimport subprocess\nimport sys\nfrom pathlib import Path\n\nif MODE == \"train\":\n tree = subprocess.run(\n [\"find\", \"/kaggle/input\", \"-maxdepth\", \"4\"], capture_output=True, text=True\n ).stdout\n print(tree, flush=True)\n hits = sorted(Path(\"/kaggle/input\").rglob(\"train_qlora.py\"))\n if not hits:\n raise RuntimeError(\"train_qlora.py not found anywhere under /kaggle/input\")\n data_dir = hits[0].parent\n print(f\"data_dir resolved: {data_dir}\", flush=True)\n subprocess.run([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", \"unsloth\"], check=True)\n subprocess.run(\n [\n sys.executable,\n \"-m\",\n \"pip\",\n \"install\",\n \"-q\",\n \"datasets>=3.0\",\n \"trl>=0.12\",\n \"peft>=0.13\",\n \"bitsandbytes>=0.44\",\n \"accelerate>=1.0\",\n ],\n check=True,\n )\n\n def train_cmd(out, extra):\n return [\n sys.executable,\n str(data_dir / \"train_qlora.py\"),\n \"--model\",\n BASE_MODEL,\n \"--train\",\n str(data_dir / \"train.jsonl\"),\n \"--val\",\n str(data_dir / \"val.jsonl\"),\n \"--out\",\n out,\n \"--max-seq-len\",\n str(MAX_SEQ),\n \"--batch-size\",\n \"1\",\n \"--grad-accum\",\n \"16\",\n \"--no-merge\",\n *extra,\n ]\n\n if SMOKE_STEPS:\n # Both earlier sessions died in their first minutes on a trl kwarg rename,\n # and each failure cost a day because nobody was watching. Prove the whole\n # path -- config, trainer, a real step, evaluate, adapter save -- on a few\n # steps before committing the 6-12h run.\n smoke_out = \"/kaggle/working/smoke_out\"\n smoke = train_cmd(smoke_out, [\"--epochs\", \"1\", \"--max-steps\", str(SMOKE_STEPS)])\n print(\" \".join(smoke), flush=True)\n subprocess.run(smoke, check=True)\n if not Path(smoke_out, \"adapter\", \"adapter_config.json\").exists():\n raise RuntimeError(\"smoke run finished but saved no adapter\")\n shutil.rmtree(smoke_out)\n print(\"SMOKE OK -- starting the real run\", flush=True)\n\n ckpt_dst = Path(OUT) / \"checkpoints\"\n if RESUME_INPUT and not ckpt_dst.exists():\n # A cancelled session leaves no kernel output to attach, so checkpoints\n # ride in via a dataset -- and Kaggle flattens the uploaded zip, dropping\n # the checkpoint-N/ folder. Locate trainer_state.json anywhere under\n # /kaggle/input and rebuild checkpoint-<global_step>/ around it (HF\n # resume needs that exact dir-name pattern).\n states = [\n p\n for p in Path(\"/kaggle/input\").rglob(\"trainer_state.json\")\n if (p.parent / \"optimizer.pt\").exists()\n ]\n if not states:\n raise RuntimeError(\n f\"RESUME_INPUT={RESUME_INPUT!r} but no resumable checkpoint \"\n \"(trainer_state.json + optimizer.pt) under /kaggle/input \"\n \"-- refusing to silently train from scratch\"\n )\n ckpt_dst.mkdir(parents=True)\n for state in states:\n step = json.loads(state.read_text())[\"global_step\"]\n dst = ckpt_dst / f\"checkpoint-{step}\"\n shutil.copytree(state.parent, dst)\n print(f\"resume: {state.parent} -> {dst}\", flush=True)\n cmd = train_cmd(OUT, [\"--epochs\", str(EPOCHS), \"--resume\"])\n print(\" \".join(cmd), flush=True)\n subprocess.run(cmd, check=True)\n shutil.make_archive(\"/kaggle/working/adapter\", \"zip\", OUT, \"adapter\")\n print(\"DONE: /kaggle/working/adapter.zip\")"
12832
},
12933
{
13034
"cell_type": "code",
@@ -230,4 +134,4 @@
230134
},
231135
"nbformat": 4,
232136
"nbformat_minor": 5
233-
}
137+
}

‎scripts/autotune/kaggle/kernel-metadata.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"enable_gpu": true,
99
"machine_shape": "NvidiaTeslaT4",
1010
"enable_internet": true,
11-
"dataset_sources": ["liovinajo/nlsql-autotune-data"],
11+
"dataset_sources": ["liovinajo/nlsql-autotune-data", "liovinajo/nlsql-autotune-prev"],
1212
"competition_sources": [],
1313
"kernel_sources": []
1414
}

0 commit comments

Comments
 (0)