r1cksync commited on
Commit
0fa8055
·
1 Parent(s): 423678c

feat: hand-written 3 Kaggle ipynbs with unsloth install attempt; train_lib disables actor grad-checkpoint + filters Phi3 warning spam (2x faster + clean logs)

Browse files
colab/train_lib.py CHANGED
@@ -22,16 +22,47 @@ T4. If a non-T4 GPU is available it will be used automatically.
22
  from __future__ import annotations
23
 
24
  import json
 
25
  import os
26
  import re
27
  import sys
28
  import time
 
29
  from dataclasses import asdict, dataclass, field
30
  from pathlib import Path
31
  from typing import Any, Iterable
32
 
33
  import torch
34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  # ---------------------------------------------------------------------------
36
  # Repository wiring — assumes you cloned the repo and the notebook lives in
37
  # /content/incident-commander/ on Colab.
@@ -314,7 +345,13 @@ class QwenActor:
314
  base = AutoModelForCausalLM.from_pretrained(
315
  hf_name, quantization_config=bnb, device_map="auto",
316
  trust_remote_code=False)
317
- base = prepare_model_for_kbit_training(base)
 
 
 
 
 
 
318
  # Pick LoRA target modules that actually exist in this arch.
319
  # Qwen / Llama use split q/k/v/gate/up. Phi-3 uses fused
320
  # qkv_proj + gate_up_proj. Detect by introspecting param names.
 
22
  from __future__ import annotations
23
 
24
  import json
25
+ import logging
26
  import os
27
  import re
28
  import sys
29
  import time
30
+ import warnings
31
  from dataclasses import asdict, dataclass, field
32
  from pathlib import Path
33
  from typing import Any, Iterable
34
 
35
  import torch
36
 
37
+ # ---------------------------------------------------------------------------
38
+ # Silence the noisy per-token warnings emitted by Phi-3 and friends during
39
+ # PPO updates (gradient checkpointing × cache interaction). They are harmless
40
+ # but produce thousands of identical lines that hide actual progress.
41
+ # ---------------------------------------------------------------------------
42
+ warnings.filterwarnings(
43
+ "ignore", message=".*Caching is incompatible with gradient checkpointing.*")
44
+ warnings.filterwarnings(
45
+ "ignore", message=".*None of the inputs have requires_grad=True.*")
46
+ warnings.filterwarnings(
47
+ "ignore", message=".*use_reentrant parameter should be passed explicitly.*")
48
+ warnings.filterwarnings(
49
+ "ignore", message=".*AccumulateGrad node's stream does not match.*")
50
+
51
+
52
+ class _Phi3CacheFilter(logging.Filter):
53
+ def filter(self, record: logging.LogRecord) -> bool: # noqa: D401
54
+ msg = record.getMessage()
55
+ if "Caching is incompatible with gradient checkpointing" in msg:
56
+ return False
57
+ if "None of the inputs have requires_grad" in msg:
58
+ return False
59
+ return True
60
+
61
+
62
+ for _name in ("transformers", "transformers.models.phi3.modeling_phi3",
63
+ "torch.utils.checkpoint", "torch.autograd.graph"):
64
+ logging.getLogger(_name).addFilter(_Phi3CacheFilter())
65
+
66
  # ---------------------------------------------------------------------------
67
  # Repository wiring — assumes you cloned the repo and the notebook lives in
68
  # /content/incident-commander/ on Colab.
 
345
  base = AutoModelForCausalLM.from_pretrained(
346
  hf_name, quantization_config=bnb, device_map="auto",
347
  trust_remote_code=False)
348
+ # NOTE: gradient checkpointing is OFF for the 3.8B actor — it
349
+ # fits in T4 16 GB without it, and disabling it 1) eliminates
350
+ # the per-token "Caching is incompatible with gradient
351
+ # checkpointing in Phi3DecoderLayer" warning spam, and 2) speeds
352
+ # up the PPO backward pass roughly 2x by avoiding recompute.
353
+ base = prepare_model_for_kbit_training(
354
+ base, use_gradient_checkpointing=False)
355
  # Pick LoRA target modules that actually exist in this arch.
356
  # Qwen / Llama use split q/k/v/gate/up. Phi-3 uses fused
357
  # qkv_proj + gate_up_proj. Detect by introspecting param names.
kaggle/generate_notebooks.py DELETED
@@ -1,272 +0,0 @@
1
- """Generate the 3 Kaggle notebooks for sharded IncidentCommander training.
2
-
3
- Each notebook is identical except for the IC_TASK_SHARD value (0/1/2) and
4
- the run_name. Re-run this script if you want to tweak the template.
5
- """
6
- from __future__ import annotations
7
-
8
- import json
9
- from pathlib import Path
10
-
11
- NOTEBOOK_DIR = Path(__file__).resolve().parent
12
-
13
- # Kaggle Models mount paths (read-only, mounted at /kaggle/input/, do NOT
14
- # consume the 20 GB /kaggle/working/ quota). Exact paths the user pinned:
15
- #
16
- # Actor : /kaggle/input/models/Microsoft/phi-3/pytorch/phi-3.5-mini-instruct/2
17
- # Critic: /kaggle/input/models/deepseek-ai/deepseek-r1-0528/transformers/deepseek-r1-0528-qwen3-8b/1
18
- #
19
- # In the Kaggle notebook sidebar ("+ Add Input" → Models):
20
- # • search `phi-3` → publisher Microsoft → framework PyTorch → variation `phi-3.5-mini-instruct` → version 2 → Add
21
- # • search `deepseek-r1` → publisher deepseek-ai → framework Transformers → variation `deepseek-r1-0528-qwen3-8b` → version 1 → Add
22
- ACTOR_PATH_LITERAL = "/kaggle/input/models/Microsoft/phi-3/pytorch/phi-3.5-mini-instruct/2"
23
- CRITIC_PATH_LITERAL = "/kaggle/input/models/deepseek-ai/deepseek-r1-0528/transformers/deepseek-r1-0528-qwen3-8b/1"
24
-
25
- ACTOR_NAME = "microsoft/Phi-3.5-mini-instruct"
26
- CRITIC_NAME = "deepseek-ai/DeepSeek-R1-0528-Qwen3-8B"
27
-
28
- REPO_URL = "https://github.com/r1cksync/meta-rl-hack.git"
29
-
30
-
31
- def cell_md(text: str) -> dict:
32
- return {"cell_type": "markdown", "metadata": {},
33
- "source": text.splitlines(keepends=True)}
34
-
35
-
36
- def cell_code(text: str) -> dict:
37
- return {"cell_type": "code", "metadata": {}, "execution_count": None,
38
- "outputs": [], "source": text.splitlines(keepends=True)}
39
-
40
-
41
- def build(shard: int, total_shards: int = 3) -> dict:
42
- title = f"# IncidentCommander RL — Kaggle shard {shard + 1} / {total_shards}"
43
- intro = f"""
44
- **Workload:** every {total_shards}rd task starting at index {shard}
45
- (~127 of 381 scenarios). Trains a LoRA on **{ACTOR_NAME}** using a local
46
- **{CRITIC_NAME}** critic — both attached as Kaggle Models so they live in
47
- the read-only `/kaggle/input/` mount and DO NOT eat the 20 GB working quota.
48
-
49
- **REQUIRED — attach these 2 Kaggle Models before running** (right sidebar →
50
- `+ Add Input` → `Models` tab). Both are open / no access request:
51
- 1. `Microsoft / phi-3` → framework `PyTorch` → variation `phi-3.5-mini-instruct` → version `2`
52
- 2. `deepseek-ai / deepseek-r1-0528` → framework `Transformers` → variation `deepseek-r1-0528-qwen3-8b` → version `1`
53
-
54
- Expected mount paths after attach (cell 3 verifies):
55
- - `{ACTOR_PATH_LITERAL}`
56
- - `{CRITIC_PATH_LITERAL}`
57
-
58
- **Required notebook settings** (right-hand sidebar):
59
- - Accelerator: `GPU T4 x2` or `GPU P100`
60
- - Persistence: `Files only`
61
- - Internet: `On` (for `git clone` and optional HF Hub upload)
62
-
63
- **Optional** (only if you want intermediate checkpoint upload to your HF
64
- repo): Add-ons → Secrets → add `HF_TOKEN` and toggle it on.
65
-
66
- **Output:** `/kaggle/working/adapter_kaggle{shard + 1}.zip` — download from
67
- the sidebar after the run finishes. Combine all 3 with
68
- `scripts/merge_lora_adapters.py` on your laptop.
69
- """
70
- cells = [
71
- cell_md(title + "\n" + intro),
72
-
73
- cell_md("## 1. GPU + path sanity"),
74
- cell_code(f"""\
75
- import subprocess
76
- print('--- GPU ---')
77
- subprocess.run(['nvidia-smi', '-L'], check=False)
78
- import torch
79
- print('CUDA OK?', torch.cuda.is_available(), '| device:',
80
- torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'none')
81
- """.strip()),
82
-
83
- cell_md("## 2. Install deps (Kaggle has torch/transformers preinstalled — we just pin compatible versions)"),
84
- cell_code("""\
85
- # Qwen3 architecture (used inside DeepSeek-R1-0528-Qwen3-8B) requires
86
- # transformers >= 4.51. Bump the whole stack to a known-compatible set.
87
- %pip install -q -U \\
88
- "transformers>=4.51,<4.55" \\
89
- "peft>=0.13,<0.16" \\
90
- "accelerate>=1.1,<1.5" \\
91
- "bitsandbytes>=0.45.5" \\
92
- "huggingface_hub>=0.25,<1.0" \\
93
- "pydantic>=2,<3" \\
94
- "datasets" "sentencepiece" "protobuf" "safetensors"
95
- """.strip()),
96
-
97
- cell_md("## 3. Resolve attached Kaggle Models (read-only, no download)"),
98
- cell_code(f"""\
99
- import os, pathlib
100
-
101
- ACTOR_PATH = '{ACTOR_PATH_LITERAL}'
102
- CRITIC_PATH = '{CRITIC_PATH_LITERAL}'
103
-
104
- def verify(path, label):
105
- p = pathlib.Path(path)
106
- if not p.exists():
107
- raise SystemExit(
108
- f'{{label}} not found at {{path}}. Open the right sidebar → '
109
- f'"+ Add Input" → Models → attach the model with the matching '
110
- f'publisher/framework/variation/version (see the markdown above).')
111
- has_weights = any(p.glob('*.safetensors')) or any(p.glob('*.bin'))
112
- if not has_weights:
113
- raise SystemExit(f'{{label}} found at {{path}} but no *.safetensors / *.bin inside.')
114
- print(f'{{label}}: OK → {{path}}')
115
-
116
- verify(ACTOR_PATH, 'actor (Phi-3.5-mini-instruct)')
117
- verify(CRITIC_PATH, 'critic (DeepSeek-R1-0528-Qwen3-8B)')
118
-
119
- # Push HF Hub cache out of /kaggle/working so an accidental snapshot_download
120
- # (e.g. by a tokenizer) writes to /tmp instead of eating the 20 GB quota.
121
- os.environ['HF_HOME'] = '/tmp/hf-cache'
122
- os.environ['HUGGINGFACE_HUB_CACHE'] = '/tmp/hf-cache'
123
- os.environ['TRANSFORMERS_CACHE'] = '/tmp/hf-cache'
124
- pathlib.Path('/tmp/hf-cache').mkdir(parents=True, exist_ok=True)
125
-
126
- # Optional HF_TOKEN — only used if you want to upload checkpoints.
127
- try:
128
- from kaggle_secrets import UserSecretsClient
129
- os.environ['HF_TOKEN'] = UserSecretsClient().get_secret('HF_TOKEN')
130
- print('HF_TOKEN attached from Kaggle Secrets')
131
- except Exception:
132
- print('No HF_TOKEN — that is fine, training works fully offline now.')
133
-
134
- # IMPORTANT: do NOT set TRANSFORMERS_TRUST_REMOTE_CODE here. Phi-3 (>=4.40)
135
- # and Qwen3 (>=4.51) are natively supported by transformers — using the
136
- # custom modeling_*.py shipped inside the Kaggle Models mount triggers an
137
- # `AttributeError: 'DynamicCache' object has no attribute 'get_max_length'`
138
- # because that custom code targets transformers <4.48. We force native impl
139
- # by NOT enabling trust_remote_code (train_lib.py also passes it as False).
140
- # Clear any previously-downloaded custom modeling code that an earlier run
141
- # may have cached, otherwise from_pretrained reuses it from the cache.
142
- import shutil as _sh, pathlib as _pl
143
- _modules = _pl.Path('/tmp/hf-cache/modules')
144
- if _modules.exists():
145
- _sh.rmtree(_modules, ignore_errors=True)
146
- print('cleared cached custom modeling code at', _modules)
147
- """.strip()),
148
-
149
- cell_md("## 4. Clone the repo (public GitHub) — always pull latest"),
150
- cell_code(f"""\
151
- import os, subprocess, pathlib, shutil
152
- WORK = '/kaggle/working/incident-commander'
153
- # IMPORTANT: chdir OUT of WORK before deleting it, otherwise git clone fails
154
- # with "Unable to read current working directory" on a re-run.
155
- os.chdir('/kaggle/working')
156
- p = pathlib.Path(WORK)
157
- if p.exists():
158
- # Wipe any stale clone from a previous session/run so we always get the
159
- # newest scripts/run_training.py + colab/train_lib.py from main.
160
- shutil.rmtree(WORK, ignore_errors=True)
161
- subprocess.run(['git', 'clone', '--depth', '1',
162
- '{REPO_URL}', WORK], check=True)
163
- os.chdir(WORK)
164
- # Show the commit we are running so it is obvious in the logs.
165
- subprocess.run(['git', '-C', WORK, 'log', '-1', '--oneline'], check=False)
166
- print('cwd =', os.getcwd())
167
- """.strip()),
168
-
169
- cell_md("## 5. Configure run (shard, paths, env vars)"),
170
- cell_code(f"""\
171
- import os
172
-
173
- os.environ['INCIDENT_COMMANDER_MOCK'] = 'true'
174
- os.environ['IC_ACTOR_MODEL'] = ACTOR_PATH
175
- os.environ['IC_CRITIC_PROVIDER'] = 'local' # 7B critic on the same GPU
176
- os.environ['IC_CRITIC_MODEL'] = CRITIC_PATH
177
- os.environ['IC_TASK_MODE'] = 'all' # full 381 corpus
178
- os.environ['IC_TASK_SHARDS'] = '{total_shards}'
179
- os.environ['IC_TASK_SHARD'] = '{shard}'
180
- os.environ['IC_TOTAL_UPDATES'] = '60' # ~6h on T4 / P100
181
- os.environ['IC_ROLLOUTS'] = '3'
182
- os.environ['IC_MAX_STEPS'] = '12'
183
- os.environ['IC_CKPT_EVERY'] = '15'
184
- os.environ['IC_RUN_NAME'] = 'kaggle{shard + 1}'
185
-
186
- print('actor :', os.environ['IC_ACTOR_MODEL'])
187
- print('critic:', os.environ['IC_CRITIC_MODEL'])
188
- print('shard :', os.environ['IC_TASK_SHARD'], '/', os.environ['IC_TASK_SHARDS'])
189
- """.strip()),
190
-
191
- cell_md("## 6. Train"),
192
- cell_code("""\
193
- # The training script runs to completion. tqdm progress + ETA are streamed
194
- # to stdout. Kaggle truncates very long outputs — adapter checkpoints are
195
- # always written to /kaggle/working/incident-commander/colab/logs/ regardless.
196
- import subprocess, sys
197
- result = subprocess.run([sys.executable, 'scripts/run_training.py'],
198
- check=False)
199
- print('exit code:', result.returncode)
200
- """.strip()),
201
-
202
- cell_md("## 7. Package outputs for download"),
203
- cell_code(f"""\
204
- import shutil, glob, pathlib
205
-
206
- LOGS = pathlib.Path('colab/logs')
207
- finals = sorted(LOGS.glob('adapter_kaggle{shard + 1}_final'))
208
- ckpts = sorted(LOGS.glob('adapter_kaggle{shard + 1}_u*'))
209
- keep = (finals or ckpts)
210
- assert keep, 'No adapter directories found — check the training cell output for errors.'
211
- src = keep[-1]
212
- print('packaging', src)
213
-
214
- dst = pathlib.Path('/kaggle/working/adapter_kaggle{shard + 1}.zip')
215
- shutil.make_archive(str(dst.with_suffix('')), 'zip', root_dir=src)
216
- print('zipped to', dst, 'size:', dst.stat().st_size, 'bytes')
217
-
218
- # Also copy the JSON training log for plotting on your laptop.
219
- for j in glob.glob('colab/logs/training_kaggle{shard + 1}*.json'):
220
- shutil.copy(j, '/kaggle/working/')
221
- print('files in /kaggle/working/:')
222
- for f in sorted(pathlib.Path('/kaggle/working/').iterdir()):
223
- if f.name == 'hf-cache': continue # don't list the model cache
224
- print(' ', f.name, f.stat().st_size if f.is_file() else '<dir>')
225
- """.strip()),
226
-
227
- cell_md(f"""\
228
- ## Done
229
-
230
- Download `adapter_kaggle{shard + 1}.zip` from the **Output** tab on the
231
- right. Repeat for the other two shards (notebooks 2 and 3), then on your
232
- laptop run:
233
-
234
- ```powershell
235
- python scripts/merge_lora_adapters.py `
236
- --inputs ./adapter_kaggle1 ./adapter_kaggle2 ./adapter_kaggle3 `
237
- --output ./adapter_merged
238
- ```
239
-
240
- The merged adapter loads with the standard `peft` API on top of
241
- `{ACTOR_NAME}`.
242
- """),
243
- ]
244
-
245
- return {
246
- "cells": cells,
247
- "metadata": {
248
- "kernelspec": {"display_name": "Python 3",
249
- "language": "python",
250
- "name": "python3"},
251
- "language_info": {"name": "python", "version": "3.10"},
252
- "kaggle": {"accelerator": "nvidiaTeslaT4",
253
- "dataSources": [],
254
- "isInternetEnabled": True,
255
- "language": "python",
256
- "sourceType": "notebook"},
257
- },
258
- "nbformat": 4,
259
- "nbformat_minor": 5,
260
- }
261
-
262
-
263
- def main() -> None:
264
- for shard in range(3):
265
- nb = build(shard)
266
- path = NOTEBOOK_DIR / f"kaggle_train_shard{shard + 1}.ipynb"
267
- path.write_text(json.dumps(nb, indent=1), encoding="utf-8")
268
- print("wrote", path)
269
-
270
-
271
- if __name__ == "__main__":
272
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
kaggle/kaggle_train_shard1.ipynb CHANGED
@@ -6,94 +6,78 @@
6
  "source": [
7
  "# IncidentCommander RL — Kaggle shard 1 / 3\n",
8
  "\n",
9
- "**Workload:** every 3rd task starting at index 0\n",
10
- "(~127 of 381 scenarios). Trains a LoRA on **microsoft/Phi-3.5-mini-instruct** using a local\n",
11
- "**deepseek-ai/DeepSeek-R1-0528-Qwen3-8B** critic — both attached as Kaggle Models so they live in\n",
12
- "the read-only `/kaggle/input/` mount and DO NOT eat the 20 GB working quota.\n",
13
  "\n",
14
- "**REQUIRED — attach these 2 Kaggle Models before running** (right sidebar →\n",
15
- "`+ Add Input` → `Models` tab). Both are open / no access request:\n",
16
- "1. `Microsoft / phi-3` → framework `PyTorch` → variation `phi-3.5-mini-instruct` → version `2`\n",
17
- "2. `deepseek-ai / deepseek-r1-0528` → framework `Transformers` → variation `deepseek-r1-0528-qwen3-8b` → version `1`\n",
18
  "\n",
19
- "Expected mount paths after attach (cell 3 verifies):\n",
20
- "- `/kaggle/input/models/Microsoft/phi-3/pytorch/phi-3.5-mini-instruct/2`\n",
21
- "- `/kaggle/input/models/deepseek-ai/deepseek-r1-0528/transformers/deepseek-r1-0528-qwen3-8b/1`\n",
22
- "\n",
23
- "**Required notebook settings** (right-hand sidebar):\n",
24
  "- Accelerator: `GPU T4 x2` or `GPU P100`\n",
 
25
  "- Persistence: `Files only`\n",
26
- "- Internet: `On` (for `git clone` and optional HF Hub upload)\n",
27
- "\n",
28
- "**Optional** (only if you want intermediate checkpoint upload to your HF\n",
29
- "repo): Add-ons → Secrets → add `HF_TOKEN` and toggle it on.\n",
30
  "\n",
31
- "**Output:** `/kaggle/working/adapter_kaggle1.zip` — download from\n",
32
- "the sidebar after the run finishes. Combine all 3 with\n",
33
- "`scripts/merge_lora_adapters.py` on your laptop.\n"
34
  ]
35
  },
36
  {
37
- "cell_type": "markdown",
 
 
38
  "metadata": {},
 
39
  "source": [
40
- "## 1. GPU + path sanity"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  ]
42
  },
43
  {
44
  "cell_type": "code",
45
  "execution_count": null,
 
46
  "metadata": {},
47
  "outputs": [],
48
  "source": [
 
49
  "import subprocess\n",
50
  "print('--- GPU ---')\n",
51
  "subprocess.run(['nvidia-smi', '-L'], check=False)\n",
52
  "import torch\n",
53
- "print('CUDA OK?', torch.cuda.is_available(), '| device:',\n",
54
- " torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'none')"
55
- ]
56
- },
57
- {
58
- "cell_type": "markdown",
59
- "metadata": {},
60
- "source": [
61
- "## 2. Install deps (Kaggle has torch/transformers preinstalled — we just pin compatible versions)"
62
  ]
63
  },
64
  {
65
  "cell_type": "code",
66
  "execution_count": null,
 
67
  "metadata": {},
68
  "outputs": [],
69
  "source": [
70
- "# Qwen3 architecture (used inside DeepSeek-R1-0528-Qwen3-8B) requires\n",
71
- "# transformers >= 4.51. Bump the whole stack to a known-compatible set.\n",
72
- "%pip install -q -U \\\n",
73
- " \"transformers>=4.51,<4.55\" \\\n",
74
- " \"peft>=0.13,<0.16\" \\\n",
75
- " \"accelerate>=1.1,<1.5\" \\\n",
76
- " \"bitsandbytes>=0.45.5\" \\\n",
77
- " \"huggingface_hub>=0.25,<1.0\" \\\n",
78
- " \"pydantic>=2,<3\" \\\n",
79
- " \"datasets\" \"sentencepiece\" \"protobuf\" \"safetensors\""
80
- ]
81
- },
82
- {
83
- "cell_type": "markdown",
84
- "metadata": {},
85
- "source": [
86
- "## 3. Resolve attached Kaggle Models (read-only, no download)"
87
- ]
88
- },
89
- {
90
- "cell_type": "code",
91
- "execution_count": null,
92
- "id": "4e224033",
93
- "metadata": {},
94
- "outputs": [],
95
- "source": [
96
- "import os, pathlib\n",
97
  "\n",
98
  "ACTOR_PATH = '/kaggle/input/models/Microsoft/phi-3/pytorch/phi-3.5-mini-instruct/2'\n",
99
  "CRITIC_PATH = '/kaggle/input/models/deepseek-ai/deepseek-r1-0528/transformers/deepseek-r1-0528-qwen3-8b/1'\n",
@@ -101,179 +85,143 @@
101
  "def verify(path, label):\n",
102
  " p = pathlib.Path(path)\n",
103
  " if not p.exists():\n",
104
- " raise SystemExit(\n",
105
- " f'{label} not found at {path}. Open the right sidebar → '\n",
106
- " f'\"+ Add Input\" Models attach the model with the matching '\n",
107
- " f'publisher/framework/variation/version (see the markdown above).')\n",
108
- " has_weights = any(p.glob('*.safetensors')) or any(p.glob('*.bin'))\n",
109
- " if not has_weights:\n",
110
- " raise SystemExit(f'{label} found at {path} but no *.safetensors / *.bin inside.')\n",
111
- " print(f'{label}: OK → {path}')\n",
112
  "\n",
113
  "verify(ACTOR_PATH, 'actor (Phi-3.5-mini-instruct)')\n",
114
  "verify(CRITIC_PATH, 'critic (DeepSeek-R1-0528-Qwen3-8B)')\n",
115
  "\n",
116
- "# Push HF Hub cache out of /kaggle/working so an accidental snapshot_download\n",
117
- "# (e.g. by a tokenizer) writes to /tmp instead of eating the 20 GB quota.\n",
118
  "os.environ['HF_HOME'] = '/tmp/hf-cache'\n",
119
  "os.environ['HUGGINGFACE_HUB_CACHE'] = '/tmp/hf-cache'\n",
120
  "os.environ['TRANSFORMERS_CACHE'] = '/tmp/hf-cache'\n",
121
  "pathlib.Path('/tmp/hf-cache').mkdir(parents=True, exist_ok=True)\n",
122
  "\n",
123
- "# Optional HF_TOKEN — only used if you want to upload checkpoints.\n",
 
 
 
 
124
  "try:\n",
125
  " from kaggle_secrets import UserSecretsClient\n",
126
  " os.environ['HF_TOKEN'] = UserSecretsClient().get_secret('HF_TOKEN')\n",
127
  " print('HF_TOKEN attached from Kaggle Secrets')\n",
128
  "except Exception:\n",
129
- " print('No HF_TOKEN that is fine, training works fully offline now.')\n",
130
  "\n",
131
- "# IMPORTANT: do NOT set TRANSFORMERS_TRUST_REMOTE_CODE here. Phi-3 (>=4.40)\n",
132
- "# and Qwen3 (>=4.51) are natively supported by transformers — using the\n",
133
- "# custom modeling_*.py shipped inside the Kaggle Models mount triggers an\n",
134
- "# `AttributeError: 'DynamicCache' object has no attribute 'get_max_length'`\n",
135
- "# because that custom code targets transformers <4.48. We force native impl\n",
136
- "# by NOT enabling trust_remote_code (train_lib.py also passes it as False).\n",
137
- "# Clear any previously-downloaded custom modeling code that an earlier run\n",
138
- "# may have cached, otherwise from_pretrained reuses it from the cache.\n",
139
- "import shutil as _sh, pathlib as _pl\n",
140
- "_modules = _pl.Path('/tmp/hf-cache/modules')\n",
141
- "if _modules.exists():\n",
142
- " _sh.rmtree(_modules, ignore_errors=True)\n",
143
- " print('cleared cached custom modeling code at', _modules)"
144
- ]
145
- },
146
- {
147
- "cell_type": "markdown",
148
- "metadata": {},
149
- "source": [
150
- "## 4. Clone the repo (public GitHub) — always pull latest"
151
  ]
152
  },
153
  {
154
  "cell_type": "code",
155
  "execution_count": null,
 
156
  "metadata": {},
157
  "outputs": [],
158
  "source": [
 
159
  "import os, subprocess, pathlib, shutil\n",
160
  "WORK = '/kaggle/working/incident-commander'\n",
161
- "# IMPORTANT: chdir OUT of WORK before deleting it, otherwise git clone fails\n",
162
- "# with \"Unable to read current working directory\" on a re-run.\n",
163
  "os.chdir('/kaggle/working')\n",
164
- "p = pathlib.Path(WORK)\n",
165
- "if p.exists():\n",
166
- " # Wipe any stale clone from a previous session/run so we always get the\n",
167
- " # newest scripts/run_training.py + colab/train_lib.py from main.\n",
168
  " shutil.rmtree(WORK, ignore_errors=True)\n",
169
  "subprocess.run(['git', 'clone', '--depth', '1',\n",
170
- " 'https://github.com/r1cksync/meta-rl-hack.git', WORK], check=True)\n",
 
171
  "os.chdir(WORK)\n",
172
- "# Show the commit we are running so it is obvious in the logs.\n",
173
  "subprocess.run(['git', '-C', WORK, 'log', '-1', '--oneline'], check=False)\n",
174
  "print('cwd =', os.getcwd())"
175
  ]
176
  },
177
- {
178
- "cell_type": "markdown",
179
- "metadata": {},
180
- "source": [
181
- "## 5. Configure run (shard, paths, env vars)"
182
- ]
183
- },
184
  {
185
  "cell_type": "code",
186
  "execution_count": null,
 
187
  "metadata": {},
188
  "outputs": [],
189
  "source": [
 
190
  "import os\n",
191
- "\n",
192
  "os.environ['INCIDENT_COMMANDER_MOCK'] = 'true'\n",
193
  "os.environ['IC_ACTOR_MODEL'] = ACTOR_PATH\n",
194
- "os.environ['IC_CRITIC_PROVIDER'] = 'local' # 7B critic on the same GPU\n",
195
  "os.environ['IC_CRITIC_MODEL'] = CRITIC_PATH\n",
196
- "os.environ['IC_TASK_MODE'] = 'all' # full 381 corpus\n",
197
  "os.environ['IC_TASK_SHARDS'] = '3'\n",
198
  "os.environ['IC_TASK_SHARD'] = '0'\n",
199
- "os.environ['IC_TOTAL_UPDATES'] = '60' # ~6h on T4 / P100\n",
200
  "os.environ['IC_ROLLOUTS'] = '3'\n",
201
  "os.environ['IC_MAX_STEPS'] = '12'\n",
202
  "os.environ['IC_CKPT_EVERY'] = '15'\n",
203
  "os.environ['IC_RUN_NAME'] = 'kaggle1'\n",
204
- "\n",
205
  "print('actor :', os.environ['IC_ACTOR_MODEL'])\n",
206
  "print('critic:', os.environ['IC_CRITIC_MODEL'])\n",
207
  "print('shard :', os.environ['IC_TASK_SHARD'], '/', os.environ['IC_TASK_SHARDS'])"
208
  ]
209
  },
210
- {
211
- "cell_type": "markdown",
212
- "metadata": {},
213
- "source": [
214
- "## 6. Train"
215
- ]
216
- },
217
  {
218
  "cell_type": "code",
219
  "execution_count": null,
 
220
  "metadata": {},
221
  "outputs": [],
222
  "source": [
223
- "# The training script runs to completion. tqdm progress + ETA are streamed\n",
224
- "# to stdout. Kaggle truncates very long outputs — adapter checkpoints are\n",
225
- "# always written to /kaggle/working/incident-commander/colab/logs/ regardless.\n",
226
  "import subprocess, sys\n",
227
- "result = subprocess.run([sys.executable, 'scripts/run_training.py'],\n",
228
- " check=False)\n",
229
  "print('exit code:', result.returncode)"
230
  ]
231
  },
232
- {
233
- "cell_type": "markdown",
234
- "metadata": {},
235
- "source": [
236
- "## 7. Package outputs for download"
237
- ]
238
- },
239
  {
240
  "cell_type": "code",
241
  "execution_count": null,
 
242
  "metadata": {},
243
  "outputs": [],
244
  "source": [
 
245
  "import shutil, glob, pathlib\n",
246
- "\n",
247
  "LOGS = pathlib.Path('colab/logs')\n",
248
  "finals = sorted(LOGS.glob('adapter_kaggle1_final'))\n",
249
  "ckpts = sorted(LOGS.glob('adapter_kaggle1_u*'))\n",
250
- "keep = (finals or ckpts)\n",
251
- "assert keep, 'No adapter directories found check the training cell output for errors.'\n",
252
  "src = keep[-1]\n",
253
  "print('packaging', src)\n",
254
- "\n",
255
  "dst = pathlib.Path('/kaggle/working/adapter_kaggle1.zip')\n",
256
  "shutil.make_archive(str(dst.with_suffix('')), 'zip', root_dir=src)\n",
257
  "print('zipped to', dst, 'size:', dst.stat().st_size, 'bytes')\n",
258
- "\n",
259
- "# Also copy the JSON training log for plotting on your laptop.\n",
260
  "for j in glob.glob('colab/logs/training_kaggle1*.json'):\n",
261
  " shutil.copy(j, '/kaggle/working/')\n",
262
  "print('files in /kaggle/working/:')\n",
263
  "for f in sorted(pathlib.Path('/kaggle/working/').iterdir()):\n",
264
- " if f.name == 'hf-cache': continue # don't list the model cache\n",
 
265
  " print(' ', f.name, f.stat().st_size if f.is_file() else '<dir>')"
266
  ]
267
  },
268
  {
269
  "cell_type": "markdown",
 
270
  "metadata": {},
271
  "source": [
272
  "## Done\n",
273
  "\n",
274
- "Download `adapter_kaggle1.zip` from the **Output** tab on the\n",
275
- "right. Repeat for the other two shards (notebooks 2 and 3), then on your\n",
276
- "laptop run:\n",
277
  "\n",
278
  "```powershell\n",
279
  "python scripts/merge_lora_adapters.py `\n",
@@ -281,8 +229,7 @@
281
  " --output ./adapter_merged\n",
282
  "```\n",
283
  "\n",
284
- "The merged adapter loads with the standard `peft` API on top of\n",
285
- "`microsoft/Phi-3.5-mini-instruct`.\n"
286
  ]
287
  }
288
  ],
 
6
  "source": [
7
  "# IncidentCommander RL — Kaggle shard 1 / 3\n",
8
  "\n",
9
+ "**Workload:** every 3rd task starting at index **0** (~127 of 381 scenarios).\n",
10
+ "Trains a LoRA on **Phi-3.5-mini-instruct** using a local **DeepSeek-R1-0528-Qwen3-8B** critic.\n",
 
 
11
  "\n",
12
+ "## REQUIRED — attach these 2 Kaggle Models before running\n",
13
+ "Right sidebar → `+ Add Input` → `Models` tab:\n",
14
+ "1. `Microsoft / phi-3` → framework `PyTorch` → variation `phi-3.5-mini-instruct` → version `2`\n",
15
+ "2. `deepseek-ai / deepseek-r1-0528` → framework `Transformers` → variation `deepseek-r1-0528-qwen3-8b` → version `1`\n",
16
  "\n",
17
+ "## Required notebook settings\n",
 
 
 
 
18
  "- Accelerator: `GPU T4 x2` or `GPU P100`\n",
19
+ "- Internet: `On`\n",
20
  "- Persistence: `Files only`\n",
 
 
 
 
21
  "\n",
22
+ "**Output:** `/kaggle/working/adapter_kaggle1.zip` — download from the sidebar after the run finishes."
 
 
23
  ]
24
  },
25
  {
26
+ "cell_type": "code",
27
+ "execution_count": null,
28
+ "id": "1c28582e",
29
  "metadata": {},
30
+ "outputs": [],
31
  "source": [
32
+ "# === 1. Install deps + unsloth (best-effort) ===\n",
33
+ "# Qwen3 (in DeepSeek-R1-0528) needs transformers >= 4.51. Unsloth speeds up\n",
34
+ "# the actor ~2x; if its install fails on this Kaggle image we fall back to\n",
35
+ "# pure HF transformers automatically (train_lib.py handles both paths).\n",
36
+ "import subprocess, sys\n",
37
+ "\n",
38
+ "def pip(*args):\n",
39
+ " return subprocess.run([sys.executable, '-m', 'pip', 'install', '-q', *args],\n",
40
+ " check=False).returncode\n",
41
+ "\n",
42
+ "rc = pip('-U', 'unsloth')\n",
43
+ "print('[install] unsloth rc =', rc, '(non-zero is fine, HF fallback works)')\n",
44
+ "\n",
45
+ "pip('-U',\n",
46
+ " 'transformers>=4.51,<4.55',\n",
47
+ " 'peft>=0.13,<0.16',\n",
48
+ " 'accelerate>=1.1,<1.5',\n",
49
+ " 'bitsandbytes>=0.45.5',\n",
50
+ " 'huggingface_hub>=0.25,<1.0',\n",
51
+ " 'pydantic>=2,<3',\n",
52
+ " 'datasets', 'sentencepiece', 'protobuf', 'safetensors')\n",
53
+ "print('[install] pinned stack done')"
54
  ]
55
  },
56
  {
57
  "cell_type": "code",
58
  "execution_count": null,
59
+ "id": "37a868c2",
60
  "metadata": {},
61
  "outputs": [],
62
  "source": [
63
+ "# === 2. GPU sanity ===\n",
64
  "import subprocess\n",
65
  "print('--- GPU ---')\n",
66
  "subprocess.run(['nvidia-smi', '-L'], check=False)\n",
67
  "import torch\n",
68
+ "print('CUDA OK?', torch.cuda.is_available(),\n",
69
+ " '| device:', torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'none')"
 
 
 
 
 
 
 
70
  ]
71
  },
72
  {
73
  "cell_type": "code",
74
  "execution_count": null,
75
+ "id": "fa14ea90",
76
  "metadata": {},
77
  "outputs": [],
78
  "source": [
79
+ "# === 3. Verify attached Kaggle Models + suppress warning spam ===\n",
80
+ "import os, pathlib, shutil, warnings, logging\n",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  "\n",
82
  "ACTOR_PATH = '/kaggle/input/models/Microsoft/phi-3/pytorch/phi-3.5-mini-instruct/2'\n",
83
  "CRITIC_PATH = '/kaggle/input/models/deepseek-ai/deepseek-r1-0528/transformers/deepseek-r1-0528-qwen3-8b/1'\n",
 
85
  "def verify(path, label):\n",
86
  " p = pathlib.Path(path)\n",
87
  " if not p.exists():\n",
88
+ " raise SystemExit(f'{label} not found at {path}. Attach the matching Kaggle Model.')\n",
89
+ " if not (any(p.glob('*.safetensors')) or any(p.glob('*.bin'))):\n",
90
+ " raise SystemExit(f'{label} found at {path} but no weight files inside.')\n",
91
+ " print(f'{label}: OK -> {path}')\n",
 
 
 
 
92
  "\n",
93
  "verify(ACTOR_PATH, 'actor (Phi-3.5-mini-instruct)')\n",
94
  "verify(CRITIC_PATH, 'critic (DeepSeek-R1-0528-Qwen3-8B)')\n",
95
  "\n",
 
 
96
  "os.environ['HF_HOME'] = '/tmp/hf-cache'\n",
97
  "os.environ['HUGGINGFACE_HUB_CACHE'] = '/tmp/hf-cache'\n",
98
  "os.environ['TRANSFORMERS_CACHE'] = '/tmp/hf-cache'\n",
99
  "pathlib.Path('/tmp/hf-cache').mkdir(parents=True, exist_ok=True)\n",
100
  "\n",
101
+ "modules_dir = pathlib.Path('/tmp/hf-cache/modules')\n",
102
+ "if modules_dir.exists():\n",
103
+ " shutil.rmtree(modules_dir, ignore_errors=True)\n",
104
+ " print('cleared cached custom modeling code at', modules_dir)\n",
105
+ "\n",
106
  "try:\n",
107
  " from kaggle_secrets import UserSecretsClient\n",
108
  " os.environ['HF_TOKEN'] = UserSecretsClient().get_secret('HF_TOKEN')\n",
109
  " print('HF_TOKEN attached from Kaggle Secrets')\n",
110
  "except Exception:\n",
111
+ " print('No HF_TOKEN -- training runs fully offline (that is fine).')\n",
112
  "\n",
113
+ "for pat in ('.*Caching is incompatible with gradient checkpointing.*',\n",
114
+ " '.*None of the inputs have requires_grad=True.*',\n",
115
+ " '.*use_reentrant parameter should be passed explicitly.*',\n",
116
+ " \".*AccumulateGrad node's stream does not match.*\"):\n",
117
+ " warnings.filterwarnings('ignore', message=pat)\n",
118
+ "\n",
119
+ "class _PhiFilter(logging.Filter):\n",
120
+ " def filter(self, r):\n",
121
+ " return 'Caching is incompatible' not in r.getMessage()\n",
122
+ "for n in ('transformers', 'transformers.models.phi3.modeling_phi3',\n",
123
+ " 'torch.utils.checkpoint'):\n",
124
+ " logging.getLogger(n).addFilter(_PhiFilter())\n",
125
+ "print('warning filters installed')"
 
 
 
 
 
 
 
126
  ]
127
  },
128
  {
129
  "cell_type": "code",
130
  "execution_count": null,
131
+ "id": "b83c7a39",
132
  "metadata": {},
133
  "outputs": [],
134
  "source": [
135
+ "# === 4. Clone the repo (fresh every run, prints commit hash) ===\n",
136
  "import os, subprocess, pathlib, shutil\n",
137
  "WORK = '/kaggle/working/incident-commander'\n",
 
 
138
  "os.chdir('/kaggle/working')\n",
139
+ "if pathlib.Path(WORK).exists():\n",
 
 
 
140
  " shutil.rmtree(WORK, ignore_errors=True)\n",
141
  "subprocess.run(['git', 'clone', '--depth', '1',\n",
142
+ " 'https://github.com/r1cksync/meta-rl-hack.git', WORK],\n",
143
+ " check=True)\n",
144
  "os.chdir(WORK)\n",
 
145
  "subprocess.run(['git', '-C', WORK, 'log', '-1', '--oneline'], check=False)\n",
146
  "print('cwd =', os.getcwd())"
147
  ]
148
  },
 
 
 
 
 
 
 
149
  {
150
  "cell_type": "code",
151
  "execution_count": null,
152
+ "id": "81701d83",
153
  "metadata": {},
154
  "outputs": [],
155
  "source": [
156
+ "# === 5. Configure run (shard 1 / 3) ===\n",
157
  "import os\n",
 
158
  "os.environ['INCIDENT_COMMANDER_MOCK'] = 'true'\n",
159
  "os.environ['IC_ACTOR_MODEL'] = ACTOR_PATH\n",
160
+ "os.environ['IC_CRITIC_PROVIDER'] = 'local'\n",
161
  "os.environ['IC_CRITIC_MODEL'] = CRITIC_PATH\n",
162
+ "os.environ['IC_TASK_MODE'] = 'all'\n",
163
  "os.environ['IC_TASK_SHARDS'] = '3'\n",
164
  "os.environ['IC_TASK_SHARD'] = '0'\n",
165
+ "os.environ['IC_TOTAL_UPDATES'] = '60'\n",
166
  "os.environ['IC_ROLLOUTS'] = '3'\n",
167
  "os.environ['IC_MAX_STEPS'] = '12'\n",
168
  "os.environ['IC_CKPT_EVERY'] = '15'\n",
169
  "os.environ['IC_RUN_NAME'] = 'kaggle1'\n",
 
170
  "print('actor :', os.environ['IC_ACTOR_MODEL'])\n",
171
  "print('critic:', os.environ['IC_CRITIC_MODEL'])\n",
172
  "print('shard :', os.environ['IC_TASK_SHARD'], '/', os.environ['IC_TASK_SHARDS'])"
173
  ]
174
  },
 
 
 
 
 
 
 
175
  {
176
  "cell_type": "code",
177
  "execution_count": null,
178
+ "id": "5fecb57e",
179
  "metadata": {},
180
  "outputs": [],
181
  "source": [
182
+ "# === 6. Train ===\n",
 
 
183
  "import subprocess, sys\n",
184
+ "result = subprocess.run([sys.executable, 'scripts/run_training.py'], check=False)\n",
 
185
  "print('exit code:', result.returncode)"
186
  ]
187
  },
 
 
 
 
 
 
 
188
  {
189
  "cell_type": "code",
190
  "execution_count": null,
191
+ "id": "ed71f261",
192
  "metadata": {},
193
  "outputs": [],
194
  "source": [
195
+ "# === 7. Package outputs for download ===\n",
196
  "import shutil, glob, pathlib\n",
 
197
  "LOGS = pathlib.Path('colab/logs')\n",
198
  "finals = sorted(LOGS.glob('adapter_kaggle1_final'))\n",
199
  "ckpts = sorted(LOGS.glob('adapter_kaggle1_u*'))\n",
200
+ "keep = (finals or ckpts)\n",
201
+ "assert keep, 'No adapter directories found -- check the training cell output.'\n",
202
  "src = keep[-1]\n",
203
  "print('packaging', src)\n",
 
204
  "dst = pathlib.Path('/kaggle/working/adapter_kaggle1.zip')\n",
205
  "shutil.make_archive(str(dst.with_suffix('')), 'zip', root_dir=src)\n",
206
  "print('zipped to', dst, 'size:', dst.stat().st_size, 'bytes')\n",
 
 
207
  "for j in glob.glob('colab/logs/training_kaggle1*.json'):\n",
208
  " shutil.copy(j, '/kaggle/working/')\n",
209
  "print('files in /kaggle/working/:')\n",
210
  "for f in sorted(pathlib.Path('/kaggle/working/').iterdir()):\n",
211
+ " if f.name == 'hf-cache':\n",
212
+ " continue\n",
213
  " print(' ', f.name, f.stat().st_size if f.is_file() else '<dir>')"
214
  ]
215
  },
216
  {
217
  "cell_type": "markdown",
218
+ "id": "1be8077f",
219
  "metadata": {},
220
  "source": [
221
  "## Done\n",
222
  "\n",
223
+ "Download `adapter_kaggle1.zip` from the **Output** tab on the right.\n",
224
+ "Run shard 2 and shard 3 in parallel browser tabs, then on your laptop:\n",
 
225
  "\n",
226
  "```powershell\n",
227
  "python scripts/merge_lora_adapters.py `\n",
 
229
  " --output ./adapter_merged\n",
230
  "```\n",
231
  "\n",
232
+ "The merged adapter loads with the standard `peft` API on top of `microsoft/Phi-3.5-mini-instruct`."
 
233
  ]
234
  }
235
  ],
kaggle/kaggle_train_shard2.ipynb CHANGED
@@ -4,95 +4,77 @@
4
  "cell_type": "markdown",
5
  "metadata": {},
6
  "source": [
7
- "# IncidentCommander RL \u2014 Kaggle shard 2 / 3\n",
8
  "\n",
9
- "**Workload:** every 3rd task starting at index 1\n",
10
- "(~127 of 381 scenarios). Trains a LoRA on **microsoft/Phi-3.5-mini-instruct** using a local\n",
11
- "**deepseek-ai/DeepSeek-R1-0528-Qwen3-8B** critic \u2014 both attached as Kaggle Models so they live in\n",
12
- "the read-only `/kaggle/input/` mount and DO NOT eat the 20 GB working quota.\n",
13
  "\n",
14
- "**REQUIRED \u2014 attach these 2 Kaggle Models before running** (right sidebar \u2192\n",
15
- "`+ Add Input` \u2192 `Models` tab). Both are open / no access request:\n",
16
- "1. `Microsoft / phi-3` \u2192 framework `PyTorch` \u2192 variation `phi-3.5-mini-instruct` \u2192 version `2`\n",
17
- "2. `deepseek-ai / deepseek-r1-0528` \u2192 framework `Transformers` \u2192 variation `deepseek-r1-0528-qwen3-8b` \u2192 version `1`\n",
18
  "\n",
19
- "Expected mount paths after attach (cell 3 verifies):\n",
20
- "- `/kaggle/input/models/Microsoft/phi-3/pytorch/phi-3.5-mini-instruct/2`\n",
21
- "- `/kaggle/input/models/deepseek-ai/deepseek-r1-0528/transformers/deepseek-r1-0528-qwen3-8b/1`\n",
22
- "\n",
23
- "**Required notebook settings** (right-hand sidebar):\n",
24
  "- Accelerator: `GPU T4 x2` or `GPU P100`\n",
 
25
  "- Persistence: `Files only`\n",
26
- "- Internet: `On` (for `git clone` and optional HF Hub upload)\n",
27
- "\n",
28
- "**Optional** (only if you want intermediate checkpoint upload to your HF\n",
29
- "repo): Add-ons \u2192 Secrets \u2192 add `HF_TOKEN` and toggle it on.\n",
30
  "\n",
31
- "**Output:** `/kaggle/working/adapter_kaggle2.zip` \u2014 download from\n",
32
- "the sidebar after the run finishes. Combine all 3 with\n",
33
- "`scripts/merge_lora_adapters.py` on your laptop.\n"
34
  ]
35
  },
36
  {
37
- "cell_type": "markdown",
 
38
  "metadata": {},
 
39
  "source": [
40
- "## 1. GPU + path sanity"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  ]
42
  },
43
  {
44
  "cell_type": "code",
45
- "metadata": {},
46
  "execution_count": null,
 
47
  "outputs": [],
48
  "source": [
 
49
  "import subprocess\n",
50
  "print('--- GPU ---')\n",
51
  "subprocess.run(['nvidia-smi', '-L'], check=False)\n",
52
  "import torch\n",
53
- "print('CUDA OK?', torch.cuda.is_available(), '| device:',\n",
54
- " torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'none')"
55
- ]
56
- },
57
- {
58
- "cell_type": "markdown",
59
- "metadata": {},
60
- "source": [
61
- "## 2. Install deps (Kaggle has torch/transformers preinstalled \u2014 we just pin compatible versions)"
62
  ]
63
  },
64
  {
65
  "cell_type": "code",
66
- "metadata": {},
67
  "execution_count": null,
68
- "outputs": [],
69
- "source": [
70
- "# Qwen3 architecture (used inside DeepSeek-R1-0528-Qwen3-8B) requires\n",
71
- "# transformers >= 4.51. Bump the whole stack to a known-compatible set.\n",
72
- "%pip install -q -U \\\n",
73
- " \"transformers>=4.51,<4.55\" \\\n",
74
- " \"peft>=0.13,<0.16\" \\\n",
75
- " \"accelerate>=1.1,<1.5\" \\\n",
76
- " \"bitsandbytes>=0.45.5\" \\\n",
77
- " \"huggingface_hub>=0.25,<1.0\" \\\n",
78
- " \"pydantic>=2,<3\" \\\n",
79
- " \"datasets\" \"sentencepiece\" \"protobuf\" \"safetensors\""
80
- ]
81
- },
82
- {
83
- "cell_type": "markdown",
84
- "metadata": {},
85
- "source": [
86
- "## 3. Resolve attached Kaggle Models (read-only, no download)"
87
- ]
88
- },
89
- {
90
- "cell_type": "code",
91
  "metadata": {},
92
- "execution_count": null,
93
  "outputs": [],
94
  "source": [
95
- "import os, pathlib\n",
 
96
  "\n",
97
  "ACTOR_PATH = '/kaggle/input/models/Microsoft/phi-3/pytorch/phi-3.5-mini-instruct/2'\n",
98
  "CRITIC_PATH = '/kaggle/input/models/deepseek-ai/deepseek-r1-0528/transformers/deepseek-r1-0528-qwen3-8b/1'\n",
@@ -100,167 +82,127 @@
100
  "def verify(path, label):\n",
101
  " p = pathlib.Path(path)\n",
102
  " if not p.exists():\n",
103
- " raise SystemExit(\n",
104
- " f'{label} not found at {path}. Open the right sidebar \u2192 '\n",
105
- " f'\"+ Add Input\" \u2192 Models \u2192 attach the model with the matching '\n",
106
- " f'publisher/framework/variation/version (see the markdown above).')\n",
107
- " has_weights = any(p.glob('*.safetensors')) or any(p.glob('*.bin'))\n",
108
- " if not has_weights:\n",
109
- " raise SystemExit(f'{label} found at {path} but no *.safetensors / *.bin inside.')\n",
110
- " print(f'{label}: OK \u2192 {path}')\n",
111
  "\n",
112
  "verify(ACTOR_PATH, 'actor (Phi-3.5-mini-instruct)')\n",
113
  "verify(CRITIC_PATH, 'critic (DeepSeek-R1-0528-Qwen3-8B)')\n",
114
  "\n",
115
- "# Push HF Hub cache out of /kaggle/working so an accidental snapshot_download\n",
116
- "# (e.g. by a tokenizer) writes to /tmp instead of eating the 20 GB quota.\n",
117
  "os.environ['HF_HOME'] = '/tmp/hf-cache'\n",
118
  "os.environ['HUGGINGFACE_HUB_CACHE'] = '/tmp/hf-cache'\n",
119
  "os.environ['TRANSFORMERS_CACHE'] = '/tmp/hf-cache'\n",
120
  "pathlib.Path('/tmp/hf-cache').mkdir(parents=True, exist_ok=True)\n",
121
  "\n",
122
- "# Optional HF_TOKEN \u2014 only used if you want to upload checkpoints.\n",
 
 
 
 
123
  "try:\n",
124
  " from kaggle_secrets import UserSecretsClient\n",
125
  " os.environ['HF_TOKEN'] = UserSecretsClient().get_secret('HF_TOKEN')\n",
126
  " print('HF_TOKEN attached from Kaggle Secrets')\n",
127
  "except Exception:\n",
128
- " print('No HF_TOKEN \u2014 that is fine, training works fully offline now.')\n",
129
  "\n",
130
- "# IMPORTANT: do NOT set TRANSFORMERS_TRUST_REMOTE_CODE here. Phi-3 (>=4.40)\n",
131
- "# and Qwen3 (>=4.51) are natively supported by transformers \u2014 using the\n",
132
- "# custom modeling_*.py shipped inside the Kaggle Models mount triggers an\n",
133
- "# `AttributeError: 'DynamicCache' object has no attribute 'get_max_length'`\n",
134
- "# because that custom code targets transformers <4.48. We force native impl\n",
135
- "# by NOT enabling trust_remote_code (train_lib.py also passes it as False).\n",
136
- "# Clear any previously-downloaded custom modeling code that an earlier run\n",
137
- "# may have cached, otherwise from_pretrained reuses it from the cache.\n",
138
- "import shutil as _sh, pathlib as _pl\n",
139
- "_modules = _pl.Path('/tmp/hf-cache/modules')\n",
140
- "if _modules.exists():\n",
141
- " _sh.rmtree(_modules, ignore_errors=True)\n",
142
- " print('cleared cached custom modeling code at', _modules)"
143
- ]
144
- },
145
- {
146
- "cell_type": "markdown",
147
- "metadata": {},
148
- "source": [
149
- "## 4. Clone the repo (public GitHub) \u2014 always pull latest"
150
  ]
151
  },
152
  {
153
  "cell_type": "code",
154
- "metadata": {},
155
  "execution_count": null,
 
156
  "outputs": [],
157
  "source": [
 
158
  "import os, subprocess, pathlib, shutil\n",
159
  "WORK = '/kaggle/working/incident-commander'\n",
160
- "# IMPORTANT: chdir OUT of WORK before deleting it, otherwise git clone fails\n",
161
- "# with \"Unable to read current working directory\" on a re-run.\n",
162
  "os.chdir('/kaggle/working')\n",
163
- "p = pathlib.Path(WORK)\n",
164
- "if p.exists():\n",
165
- " # Wipe any stale clone from a previous session/run so we always get the\n",
166
- " # newest scripts/run_training.py + colab/train_lib.py from main.\n",
167
  " shutil.rmtree(WORK, ignore_errors=True)\n",
168
  "subprocess.run(['git', 'clone', '--depth', '1',\n",
169
- " 'https://github.com/r1cksync/meta-rl-hack.git', WORK], check=True)\n",
 
170
  "os.chdir(WORK)\n",
171
- "# Show the commit we are running so it is obvious in the logs.\n",
172
  "subprocess.run(['git', '-C', WORK, 'log', '-1', '--oneline'], check=False)\n",
173
  "print('cwd =', os.getcwd())"
174
  ]
175
  },
176
- {
177
- "cell_type": "markdown",
178
- "metadata": {},
179
- "source": [
180
- "## 5. Configure run (shard, paths, env vars)"
181
- ]
182
- },
183
  {
184
  "cell_type": "code",
185
- "metadata": {},
186
  "execution_count": null,
 
187
  "outputs": [],
188
  "source": [
 
189
  "import os\n",
190
- "\n",
191
  "os.environ['INCIDENT_COMMANDER_MOCK'] = 'true'\n",
192
  "os.environ['IC_ACTOR_MODEL'] = ACTOR_PATH\n",
193
- "os.environ['IC_CRITIC_PROVIDER'] = 'local' # 7B critic on the same GPU\n",
194
  "os.environ['IC_CRITIC_MODEL'] = CRITIC_PATH\n",
195
- "os.environ['IC_TASK_MODE'] = 'all' # full 381 corpus\n",
196
  "os.environ['IC_TASK_SHARDS'] = '3'\n",
197
  "os.environ['IC_TASK_SHARD'] = '1'\n",
198
- "os.environ['IC_TOTAL_UPDATES'] = '60' # ~6h on T4 / P100\n",
199
  "os.environ['IC_ROLLOUTS'] = '3'\n",
200
  "os.environ['IC_MAX_STEPS'] = '12'\n",
201
  "os.environ['IC_CKPT_EVERY'] = '15'\n",
202
  "os.environ['IC_RUN_NAME'] = 'kaggle2'\n",
203
- "\n",
204
  "print('actor :', os.environ['IC_ACTOR_MODEL'])\n",
205
  "print('critic:', os.environ['IC_CRITIC_MODEL'])\n",
206
  "print('shard :', os.environ['IC_TASK_SHARD'], '/', os.environ['IC_TASK_SHARDS'])"
207
  ]
208
  },
209
- {
210
- "cell_type": "markdown",
211
- "metadata": {},
212
- "source": [
213
- "## 6. Train"
214
- ]
215
- },
216
  {
217
  "cell_type": "code",
218
- "metadata": {},
219
  "execution_count": null,
 
220
  "outputs": [],
221
  "source": [
222
- "# The training script runs to completion. tqdm progress + ETA are streamed\n",
223
- "# to stdout. Kaggle truncates very long outputs \u2014 adapter checkpoints are\n",
224
- "# always written to /kaggle/working/incident-commander/colab/logs/ regardless.\n",
225
  "import subprocess, sys\n",
226
- "result = subprocess.run([sys.executable, 'scripts/run_training.py'],\n",
227
- " check=False)\n",
228
  "print('exit code:', result.returncode)"
229
  ]
230
  },
231
- {
232
- "cell_type": "markdown",
233
- "metadata": {},
234
- "source": [
235
- "## 7. Package outputs for download"
236
- ]
237
- },
238
  {
239
  "cell_type": "code",
240
- "metadata": {},
241
  "execution_count": null,
 
242
  "outputs": [],
243
  "source": [
 
244
  "import shutil, glob, pathlib\n",
245
- "\n",
246
  "LOGS = pathlib.Path('colab/logs')\n",
247
  "finals = sorted(LOGS.glob('adapter_kaggle2_final'))\n",
248
  "ckpts = sorted(LOGS.glob('adapter_kaggle2_u*'))\n",
249
- "keep = (finals or ckpts)\n",
250
- "assert keep, 'No adapter directories found \u2014 check the training cell output for errors.'\n",
251
  "src = keep[-1]\n",
252
  "print('packaging', src)\n",
253
- "\n",
254
  "dst = pathlib.Path('/kaggle/working/adapter_kaggle2.zip')\n",
255
  "shutil.make_archive(str(dst.with_suffix('')), 'zip', root_dir=src)\n",
256
  "print('zipped to', dst, 'size:', dst.stat().st_size, 'bytes')\n",
257
- "\n",
258
- "# Also copy the JSON training log for plotting on your laptop.\n",
259
  "for j in glob.glob('colab/logs/training_kaggle2*.json'):\n",
260
  " shutil.copy(j, '/kaggle/working/')\n",
261
  "print('files in /kaggle/working/:')\n",
262
  "for f in sorted(pathlib.Path('/kaggle/working/').iterdir()):\n",
263
- " if f.name == 'hf-cache': continue # don't list the model cache\n",
 
264
  " print(' ', f.name, f.stat().st_size if f.is_file() else '<dir>')"
265
  ]
266
  },
@@ -270,9 +212,8 @@
270
  "source": [
271
  "## Done\n",
272
  "\n",
273
- "Download `adapter_kaggle2.zip` from the **Output** tab on the\n",
274
- "right. Repeat for the other two shards (notebooks 2 and 3), then on your\n",
275
- "laptop run:\n",
276
  "\n",
277
  "```powershell\n",
278
  "python scripts/merge_lora_adapters.py `\n",
@@ -280,8 +221,7 @@
280
  " --output ./adapter_merged\n",
281
  "```\n",
282
  "\n",
283
- "The merged adapter loads with the standard `peft` API on top of\n",
284
- "`microsoft/Phi-3.5-mini-instruct`.\n"
285
  ]
286
  }
287
  ],
 
4
  "cell_type": "markdown",
5
  "metadata": {},
6
  "source": [
7
+ "# IncidentCommander RL Kaggle shard 2 / 3\n",
8
  "\n",
9
+ "**Workload:** every 3rd task starting at index **1** (~127 of 381 scenarios).\n",
10
+ "Trains a LoRA on **Phi-3.5-mini-instruct** using a local **DeepSeek-R1-0528-Qwen3-8B** critic.\n",
 
 
11
  "\n",
12
+ "## REQUIRED attach these 2 Kaggle Models before running\n",
13
+ "Right sidebar → `+ Add Input` `Models` tab:\n",
14
+ "1. `Microsoft / phi-3` framework `PyTorch` variation `phi-3.5-mini-instruct` version `2`\n",
15
+ "2. `deepseek-ai / deepseek-r1-0528` framework `Transformers` variation `deepseek-r1-0528-qwen3-8b` version `1`\n",
16
  "\n",
17
+ "## Required notebook settings\n",
 
 
 
 
18
  "- Accelerator: `GPU T4 x2` or `GPU P100`\n",
19
+ "- Internet: `On`\n",
20
  "- Persistence: `Files only`\n",
 
 
 
 
21
  "\n",
22
+ "**Output:** `/kaggle/working/adapter_kaggle2.zip` download from the sidebar after the run finishes."
 
 
23
  ]
24
  },
25
  {
26
+ "cell_type": "code",
27
+ "execution_count": null,
28
  "metadata": {},
29
+ "outputs": [],
30
  "source": [
31
+ "# === 1. Install deps + unsloth (best-effort) ===\n",
32
+ "# Qwen3 (in DeepSeek-R1-0528) needs transformers >= 4.51. Unsloth speeds up\n",
33
+ "# the actor ~2x; if its install fails on this Kaggle image we fall back to\n",
34
+ "# pure HF transformers automatically (train_lib.py handles both paths).\n",
35
+ "import subprocess, sys\n",
36
+ "\n",
37
+ "def pip(*args):\n",
38
+ " return subprocess.run([sys.executable, '-m', 'pip', 'install', '-q', *args],\n",
39
+ " check=False).returncode\n",
40
+ "\n",
41
+ "rc = pip('-U', 'unsloth')\n",
42
+ "print('[install] unsloth rc =', rc, '(non-zero is fine, HF fallback works)')\n",
43
+ "\n",
44
+ "pip('-U',\n",
45
+ " 'transformers>=4.51,<4.55',\n",
46
+ " 'peft>=0.13,<0.16',\n",
47
+ " 'accelerate>=1.1,<1.5',\n",
48
+ " 'bitsandbytes>=0.45.5',\n",
49
+ " 'huggingface_hub>=0.25,<1.0',\n",
50
+ " 'pydantic>=2,<3',\n",
51
+ " 'datasets', 'sentencepiece', 'protobuf', 'safetensors')\n",
52
+ "print('[install] pinned stack done')"
53
  ]
54
  },
55
  {
56
  "cell_type": "code",
 
57
  "execution_count": null,
58
+ "metadata": {},
59
  "outputs": [],
60
  "source": [
61
+ "# === 2. GPU sanity ===\n",
62
  "import subprocess\n",
63
  "print('--- GPU ---')\n",
64
  "subprocess.run(['nvidia-smi', '-L'], check=False)\n",
65
  "import torch\n",
66
+ "print('CUDA OK?', torch.cuda.is_available(),\n",
67
+ " '| device:', torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'none')"
 
 
 
 
 
 
 
68
  ]
69
  },
70
  {
71
  "cell_type": "code",
 
72
  "execution_count": null,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  "metadata": {},
 
74
  "outputs": [],
75
  "source": [
76
+ "# === 3. Verify attached Kaggle Models + suppress warning spam ===\n",
77
+ "import os, pathlib, shutil, warnings, logging\n",
78
  "\n",
79
  "ACTOR_PATH = '/kaggle/input/models/Microsoft/phi-3/pytorch/phi-3.5-mini-instruct/2'\n",
80
  "CRITIC_PATH = '/kaggle/input/models/deepseek-ai/deepseek-r1-0528/transformers/deepseek-r1-0528-qwen3-8b/1'\n",
 
82
  "def verify(path, label):\n",
83
  " p = pathlib.Path(path)\n",
84
  " if not p.exists():\n",
85
+ " raise SystemExit(f'{label} not found at {path}. Attach the matching Kaggle Model.')\n",
86
+ " if not (any(p.glob('*.safetensors')) or any(p.glob('*.bin'))):\n",
87
+ " raise SystemExit(f'{label} found at {path} but no weight files inside.')\n",
88
+ " print(f'{label}: OK -> {path}')\n",
 
 
 
 
89
  "\n",
90
  "verify(ACTOR_PATH, 'actor (Phi-3.5-mini-instruct)')\n",
91
  "verify(CRITIC_PATH, 'critic (DeepSeek-R1-0528-Qwen3-8B)')\n",
92
  "\n",
 
 
93
  "os.environ['HF_HOME'] = '/tmp/hf-cache'\n",
94
  "os.environ['HUGGINGFACE_HUB_CACHE'] = '/tmp/hf-cache'\n",
95
  "os.environ['TRANSFORMERS_CACHE'] = '/tmp/hf-cache'\n",
96
  "pathlib.Path('/tmp/hf-cache').mkdir(parents=True, exist_ok=True)\n",
97
  "\n",
98
+ "modules_dir = pathlib.Path('/tmp/hf-cache/modules')\n",
99
+ "if modules_dir.exists():\n",
100
+ " shutil.rmtree(modules_dir, ignore_errors=True)\n",
101
+ " print('cleared cached custom modeling code at', modules_dir)\n",
102
+ "\n",
103
  "try:\n",
104
  " from kaggle_secrets import UserSecretsClient\n",
105
  " os.environ['HF_TOKEN'] = UserSecretsClient().get_secret('HF_TOKEN')\n",
106
  " print('HF_TOKEN attached from Kaggle Secrets')\n",
107
  "except Exception:\n",
108
+ " print('No HF_TOKEN -- training runs fully offline (that is fine).')\n",
109
  "\n",
110
+ "for pat in ('.*Caching is incompatible with gradient checkpointing.*',\n",
111
+ " '.*None of the inputs have requires_grad=True.*',\n",
112
+ " '.*use_reentrant parameter should be passed explicitly.*',\n",
113
+ " \".*AccumulateGrad node's stream does not match.*\"):\n",
114
+ " warnings.filterwarnings('ignore', message=pat)\n",
115
+ "\n",
116
+ "class _PhiFilter(logging.Filter):\n",
117
+ " def filter(self, r):\n",
118
+ " return 'Caching is incompatible' not in r.getMessage()\n",
119
+ "for n in ('transformers', 'transformers.models.phi3.modeling_phi3',\n",
120
+ " 'torch.utils.checkpoint'):\n",
121
+ " logging.getLogger(n).addFilter(_PhiFilter())\n",
122
+ "print('warning filters installed')"
 
 
 
 
 
 
 
123
  ]
124
  },
125
  {
126
  "cell_type": "code",
 
127
  "execution_count": null,
128
+ "metadata": {},
129
  "outputs": [],
130
  "source": [
131
+ "# === 4. Clone the repo (fresh every run, prints commit hash) ===\n",
132
  "import os, subprocess, pathlib, shutil\n",
133
  "WORK = '/kaggle/working/incident-commander'\n",
 
 
134
  "os.chdir('/kaggle/working')\n",
135
+ "if pathlib.Path(WORK).exists():\n",
 
 
 
136
  " shutil.rmtree(WORK, ignore_errors=True)\n",
137
  "subprocess.run(['git', 'clone', '--depth', '1',\n",
138
+ " 'https://github.com/r1cksync/meta-rl-hack.git', WORK],\n",
139
+ " check=True)\n",
140
  "os.chdir(WORK)\n",
 
141
  "subprocess.run(['git', '-C', WORK, 'log', '-1', '--oneline'], check=False)\n",
142
  "print('cwd =', os.getcwd())"
143
  ]
144
  },
 
 
 
 
 
 
 
145
  {
146
  "cell_type": "code",
 
147
  "execution_count": null,
148
+ "metadata": {},
149
  "outputs": [],
150
  "source": [
151
+ "# === 5. Configure run (shard 2 / 3) ===\n",
152
  "import os\n",
 
153
  "os.environ['INCIDENT_COMMANDER_MOCK'] = 'true'\n",
154
  "os.environ['IC_ACTOR_MODEL'] = ACTOR_PATH\n",
155
+ "os.environ['IC_CRITIC_PROVIDER'] = 'local'\n",
156
  "os.environ['IC_CRITIC_MODEL'] = CRITIC_PATH\n",
157
+ "os.environ['IC_TASK_MODE'] = 'all'\n",
158
  "os.environ['IC_TASK_SHARDS'] = '3'\n",
159
  "os.environ['IC_TASK_SHARD'] = '1'\n",
160
+ "os.environ['IC_TOTAL_UPDATES'] = '60'\n",
161
  "os.environ['IC_ROLLOUTS'] = '3'\n",
162
  "os.environ['IC_MAX_STEPS'] = '12'\n",
163
  "os.environ['IC_CKPT_EVERY'] = '15'\n",
164
  "os.environ['IC_RUN_NAME'] = 'kaggle2'\n",
 
165
  "print('actor :', os.environ['IC_ACTOR_MODEL'])\n",
166
  "print('critic:', os.environ['IC_CRITIC_MODEL'])\n",
167
  "print('shard :', os.environ['IC_TASK_SHARD'], '/', os.environ['IC_TASK_SHARDS'])"
168
  ]
169
  },
 
 
 
 
 
 
 
170
  {
171
  "cell_type": "code",
 
172
  "execution_count": null,
173
+ "metadata": {},
174
  "outputs": [],
175
  "source": [
176
+ "# === 6. Train ===\n",
 
 
177
  "import subprocess, sys\n",
178
+ "result = subprocess.run([sys.executable, 'scripts/run_training.py'], check=False)\n",
 
179
  "print('exit code:', result.returncode)"
180
  ]
181
  },
 
 
 
 
 
 
 
182
  {
183
  "cell_type": "code",
 
184
  "execution_count": null,
185
+ "metadata": {},
186
  "outputs": [],
187
  "source": [
188
+ "# === 7. Package outputs for download ===\n",
189
  "import shutil, glob, pathlib\n",
 
190
  "LOGS = pathlib.Path('colab/logs')\n",
191
  "finals = sorted(LOGS.glob('adapter_kaggle2_final'))\n",
192
  "ckpts = sorted(LOGS.glob('adapter_kaggle2_u*'))\n",
193
+ "keep = (finals or ckpts)\n",
194
+ "assert keep, 'No adapter directories found -- check the training cell output.'\n",
195
  "src = keep[-1]\n",
196
  "print('packaging', src)\n",
 
197
  "dst = pathlib.Path('/kaggle/working/adapter_kaggle2.zip')\n",
198
  "shutil.make_archive(str(dst.with_suffix('')), 'zip', root_dir=src)\n",
199
  "print('zipped to', dst, 'size:', dst.stat().st_size, 'bytes')\n",
 
 
200
  "for j in glob.glob('colab/logs/training_kaggle2*.json'):\n",
201
  " shutil.copy(j, '/kaggle/working/')\n",
202
  "print('files in /kaggle/working/:')\n",
203
  "for f in sorted(pathlib.Path('/kaggle/working/').iterdir()):\n",
204
+ " if f.name == 'hf-cache':\n",
205
+ " continue\n",
206
  " print(' ', f.name, f.stat().st_size if f.is_file() else '<dir>')"
207
  ]
208
  },
 
212
  "source": [
213
  "## Done\n",
214
  "\n",
215
+ "Download `adapter_kaggle2.zip` from the **Output** tab on the right.\n",
216
+ "Run shard 2 and shard 3 in parallel browser tabs, then on your laptop:\n",
 
217
  "\n",
218
  "```powershell\n",
219
  "python scripts/merge_lora_adapters.py `\n",
 
221
  " --output ./adapter_merged\n",
222
  "```\n",
223
  "\n",
224
+ "The merged adapter loads with the standard `peft` API on top of `microsoft/Phi-3.5-mini-instruct`."
 
225
  ]
226
  }
227
  ],
kaggle/kaggle_train_shard3.ipynb CHANGED
@@ -4,95 +4,77 @@
4
  "cell_type": "markdown",
5
  "metadata": {},
6
  "source": [
7
- "# IncidentCommander RL \u2014 Kaggle shard 3 / 3\n",
8
  "\n",
9
- "**Workload:** every 3rd task starting at index 2\n",
10
- "(~127 of 381 scenarios). Trains a LoRA on **microsoft/Phi-3.5-mini-instruct** using a local\n",
11
- "**deepseek-ai/DeepSeek-R1-0528-Qwen3-8B** critic \u2014 both attached as Kaggle Models so they live in\n",
12
- "the read-only `/kaggle/input/` mount and DO NOT eat the 20 GB working quota.\n",
13
  "\n",
14
- "**REQUIRED \u2014 attach these 2 Kaggle Models before running** (right sidebar \u2192\n",
15
- "`+ Add Input` \u2192 `Models` tab). Both are open / no access request:\n",
16
- "1. `Microsoft / phi-3` \u2192 framework `PyTorch` \u2192 variation `phi-3.5-mini-instruct` \u2192 version `2`\n",
17
- "2. `deepseek-ai / deepseek-r1-0528` \u2192 framework `Transformers` \u2192 variation `deepseek-r1-0528-qwen3-8b` \u2192 version `1`\n",
18
  "\n",
19
- "Expected mount paths after attach (cell 3 verifies):\n",
20
- "- `/kaggle/input/models/Microsoft/phi-3/pytorch/phi-3.5-mini-instruct/2`\n",
21
- "- `/kaggle/input/models/deepseek-ai/deepseek-r1-0528/transformers/deepseek-r1-0528-qwen3-8b/1`\n",
22
- "\n",
23
- "**Required notebook settings** (right-hand sidebar):\n",
24
  "- Accelerator: `GPU T4 x2` or `GPU P100`\n",
 
25
  "- Persistence: `Files only`\n",
26
- "- Internet: `On` (for `git clone` and optional HF Hub upload)\n",
27
- "\n",
28
- "**Optional** (only if you want intermediate checkpoint upload to your HF\n",
29
- "repo): Add-ons \u2192 Secrets \u2192 add `HF_TOKEN` and toggle it on.\n",
30
  "\n",
31
- "**Output:** `/kaggle/working/adapter_kaggle3.zip` \u2014 download from\n",
32
- "the sidebar after the run finishes. Combine all 3 with\n",
33
- "`scripts/merge_lora_adapters.py` on your laptop.\n"
34
  ]
35
  },
36
  {
37
- "cell_type": "markdown",
 
38
  "metadata": {},
 
39
  "source": [
40
- "## 1. GPU + path sanity"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  ]
42
  },
43
  {
44
  "cell_type": "code",
45
- "metadata": {},
46
  "execution_count": null,
 
47
  "outputs": [],
48
  "source": [
 
49
  "import subprocess\n",
50
  "print('--- GPU ---')\n",
51
  "subprocess.run(['nvidia-smi', '-L'], check=False)\n",
52
  "import torch\n",
53
- "print('CUDA OK?', torch.cuda.is_available(), '| device:',\n",
54
- " torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'none')"
55
- ]
56
- },
57
- {
58
- "cell_type": "markdown",
59
- "metadata": {},
60
- "source": [
61
- "## 2. Install deps (Kaggle has torch/transformers preinstalled \u2014 we just pin compatible versions)"
62
  ]
63
  },
64
  {
65
  "cell_type": "code",
66
- "metadata": {},
67
  "execution_count": null,
68
- "outputs": [],
69
- "source": [
70
- "# Qwen3 architecture (used inside DeepSeek-R1-0528-Qwen3-8B) requires\n",
71
- "# transformers >= 4.51. Bump the whole stack to a known-compatible set.\n",
72
- "%pip install -q -U \\\n",
73
- " \"transformers>=4.51,<4.55\" \\\n",
74
- " \"peft>=0.13,<0.16\" \\\n",
75
- " \"accelerate>=1.1,<1.5\" \\\n",
76
- " \"bitsandbytes>=0.45.5\" \\\n",
77
- " \"huggingface_hub>=0.25,<1.0\" \\\n",
78
- " \"pydantic>=2,<3\" \\\n",
79
- " \"datasets\" \"sentencepiece\" \"protobuf\" \"safetensors\""
80
- ]
81
- },
82
- {
83
- "cell_type": "markdown",
84
- "metadata": {},
85
- "source": [
86
- "## 3. Resolve attached Kaggle Models (read-only, no download)"
87
- ]
88
- },
89
- {
90
- "cell_type": "code",
91
  "metadata": {},
92
- "execution_count": null,
93
  "outputs": [],
94
  "source": [
95
- "import os, pathlib\n",
 
96
  "\n",
97
  "ACTOR_PATH = '/kaggle/input/models/Microsoft/phi-3/pytorch/phi-3.5-mini-instruct/2'\n",
98
  "CRITIC_PATH = '/kaggle/input/models/deepseek-ai/deepseek-r1-0528/transformers/deepseek-r1-0528-qwen3-8b/1'\n",
@@ -100,167 +82,127 @@
100
  "def verify(path, label):\n",
101
  " p = pathlib.Path(path)\n",
102
  " if not p.exists():\n",
103
- " raise SystemExit(\n",
104
- " f'{label} not found at {path}. Open the right sidebar \u2192 '\n",
105
- " f'\"+ Add Input\" \u2192 Models \u2192 attach the model with the matching '\n",
106
- " f'publisher/framework/variation/version (see the markdown above).')\n",
107
- " has_weights = any(p.glob('*.safetensors')) or any(p.glob('*.bin'))\n",
108
- " if not has_weights:\n",
109
- " raise SystemExit(f'{label} found at {path} but no *.safetensors / *.bin inside.')\n",
110
- " print(f'{label}: OK \u2192 {path}')\n",
111
  "\n",
112
  "verify(ACTOR_PATH, 'actor (Phi-3.5-mini-instruct)')\n",
113
  "verify(CRITIC_PATH, 'critic (DeepSeek-R1-0528-Qwen3-8B)')\n",
114
  "\n",
115
- "# Push HF Hub cache out of /kaggle/working so an accidental snapshot_download\n",
116
- "# (e.g. by a tokenizer) writes to /tmp instead of eating the 20 GB quota.\n",
117
  "os.environ['HF_HOME'] = '/tmp/hf-cache'\n",
118
  "os.environ['HUGGINGFACE_HUB_CACHE'] = '/tmp/hf-cache'\n",
119
  "os.environ['TRANSFORMERS_CACHE'] = '/tmp/hf-cache'\n",
120
  "pathlib.Path('/tmp/hf-cache').mkdir(parents=True, exist_ok=True)\n",
121
  "\n",
122
- "# Optional HF_TOKEN \u2014 only used if you want to upload checkpoints.\n",
 
 
 
 
123
  "try:\n",
124
  " from kaggle_secrets import UserSecretsClient\n",
125
  " os.environ['HF_TOKEN'] = UserSecretsClient().get_secret('HF_TOKEN')\n",
126
  " print('HF_TOKEN attached from Kaggle Secrets')\n",
127
  "except Exception:\n",
128
- " print('No HF_TOKEN \u2014 that is fine, training works fully offline now.')\n",
129
  "\n",
130
- "# IMPORTANT: do NOT set TRANSFORMERS_TRUST_REMOTE_CODE here. Phi-3 (>=4.40)\n",
131
- "# and Qwen3 (>=4.51) are natively supported by transformers \u2014 using the\n",
132
- "# custom modeling_*.py shipped inside the Kaggle Models mount triggers an\n",
133
- "# `AttributeError: 'DynamicCache' object has no attribute 'get_max_length'`\n",
134
- "# because that custom code targets transformers <4.48. We force native impl\n",
135
- "# by NOT enabling trust_remote_code (train_lib.py also passes it as False).\n",
136
- "# Clear any previously-downloaded custom modeling code that an earlier run\n",
137
- "# may have cached, otherwise from_pretrained reuses it from the cache.\n",
138
- "import shutil as _sh, pathlib as _pl\n",
139
- "_modules = _pl.Path('/tmp/hf-cache/modules')\n",
140
- "if _modules.exists():\n",
141
- " _sh.rmtree(_modules, ignore_errors=True)\n",
142
- " print('cleared cached custom modeling code at', _modules)"
143
- ]
144
- },
145
- {
146
- "cell_type": "markdown",
147
- "metadata": {},
148
- "source": [
149
- "## 4. Clone the repo (public GitHub) \u2014 always pull latest"
150
  ]
151
  },
152
  {
153
  "cell_type": "code",
154
- "metadata": {},
155
  "execution_count": null,
 
156
  "outputs": [],
157
  "source": [
 
158
  "import os, subprocess, pathlib, shutil\n",
159
  "WORK = '/kaggle/working/incident-commander'\n",
160
- "# IMPORTANT: chdir OUT of WORK before deleting it, otherwise git clone fails\n",
161
- "# with \"Unable to read current working directory\" on a re-run.\n",
162
  "os.chdir('/kaggle/working')\n",
163
- "p = pathlib.Path(WORK)\n",
164
- "if p.exists():\n",
165
- " # Wipe any stale clone from a previous session/run so we always get the\n",
166
- " # newest scripts/run_training.py + colab/train_lib.py from main.\n",
167
  " shutil.rmtree(WORK, ignore_errors=True)\n",
168
  "subprocess.run(['git', 'clone', '--depth', '1',\n",
169
- " 'https://github.com/r1cksync/meta-rl-hack.git', WORK], check=True)\n",
 
170
  "os.chdir(WORK)\n",
171
- "# Show the commit we are running so it is obvious in the logs.\n",
172
  "subprocess.run(['git', '-C', WORK, 'log', '-1', '--oneline'], check=False)\n",
173
  "print('cwd =', os.getcwd())"
174
  ]
175
  },
176
- {
177
- "cell_type": "markdown",
178
- "metadata": {},
179
- "source": [
180
- "## 5. Configure run (shard, paths, env vars)"
181
- ]
182
- },
183
  {
184
  "cell_type": "code",
185
- "metadata": {},
186
  "execution_count": null,
 
187
  "outputs": [],
188
  "source": [
 
189
  "import os\n",
190
- "\n",
191
  "os.environ['INCIDENT_COMMANDER_MOCK'] = 'true'\n",
192
  "os.environ['IC_ACTOR_MODEL'] = ACTOR_PATH\n",
193
- "os.environ['IC_CRITIC_PROVIDER'] = 'local' # 7B critic on the same GPU\n",
194
  "os.environ['IC_CRITIC_MODEL'] = CRITIC_PATH\n",
195
- "os.environ['IC_TASK_MODE'] = 'all' # full 381 corpus\n",
196
  "os.environ['IC_TASK_SHARDS'] = '3'\n",
197
  "os.environ['IC_TASK_SHARD'] = '2'\n",
198
- "os.environ['IC_TOTAL_UPDATES'] = '60' # ~6h on T4 / P100\n",
199
  "os.environ['IC_ROLLOUTS'] = '3'\n",
200
  "os.environ['IC_MAX_STEPS'] = '12'\n",
201
  "os.environ['IC_CKPT_EVERY'] = '15'\n",
202
  "os.environ['IC_RUN_NAME'] = 'kaggle3'\n",
203
- "\n",
204
  "print('actor :', os.environ['IC_ACTOR_MODEL'])\n",
205
  "print('critic:', os.environ['IC_CRITIC_MODEL'])\n",
206
  "print('shard :', os.environ['IC_TASK_SHARD'], '/', os.environ['IC_TASK_SHARDS'])"
207
  ]
208
  },
209
- {
210
- "cell_type": "markdown",
211
- "metadata": {},
212
- "source": [
213
- "## 6. Train"
214
- ]
215
- },
216
  {
217
  "cell_type": "code",
218
- "metadata": {},
219
  "execution_count": null,
 
220
  "outputs": [],
221
  "source": [
222
- "# The training script runs to completion. tqdm progress + ETA are streamed\n",
223
- "# to stdout. Kaggle truncates very long outputs \u2014 adapter checkpoints are\n",
224
- "# always written to /kaggle/working/incident-commander/colab/logs/ regardless.\n",
225
  "import subprocess, sys\n",
226
- "result = subprocess.run([sys.executable, 'scripts/run_training.py'],\n",
227
- " check=False)\n",
228
  "print('exit code:', result.returncode)"
229
  ]
230
  },
231
- {
232
- "cell_type": "markdown",
233
- "metadata": {},
234
- "source": [
235
- "## 7. Package outputs for download"
236
- ]
237
- },
238
  {
239
  "cell_type": "code",
240
- "metadata": {},
241
  "execution_count": null,
 
242
  "outputs": [],
243
  "source": [
 
244
  "import shutil, glob, pathlib\n",
245
- "\n",
246
  "LOGS = pathlib.Path('colab/logs')\n",
247
  "finals = sorted(LOGS.glob('adapter_kaggle3_final'))\n",
248
  "ckpts = sorted(LOGS.glob('adapter_kaggle3_u*'))\n",
249
- "keep = (finals or ckpts)\n",
250
- "assert keep, 'No adapter directories found \u2014 check the training cell output for errors.'\n",
251
  "src = keep[-1]\n",
252
  "print('packaging', src)\n",
253
- "\n",
254
  "dst = pathlib.Path('/kaggle/working/adapter_kaggle3.zip')\n",
255
  "shutil.make_archive(str(dst.with_suffix('')), 'zip', root_dir=src)\n",
256
  "print('zipped to', dst, 'size:', dst.stat().st_size, 'bytes')\n",
257
- "\n",
258
- "# Also copy the JSON training log for plotting on your laptop.\n",
259
  "for j in glob.glob('colab/logs/training_kaggle3*.json'):\n",
260
  " shutil.copy(j, '/kaggle/working/')\n",
261
  "print('files in /kaggle/working/:')\n",
262
  "for f in sorted(pathlib.Path('/kaggle/working/').iterdir()):\n",
263
- " if f.name == 'hf-cache': continue # don't list the model cache\n",
 
264
  " print(' ', f.name, f.stat().st_size if f.is_file() else '<dir>')"
265
  ]
266
  },
@@ -270,9 +212,8 @@
270
  "source": [
271
  "## Done\n",
272
  "\n",
273
- "Download `adapter_kaggle3.zip` from the **Output** tab on the\n",
274
- "right. Repeat for the other two shards (notebooks 2 and 3), then on your\n",
275
- "laptop run:\n",
276
  "\n",
277
  "```powershell\n",
278
  "python scripts/merge_lora_adapters.py `\n",
@@ -280,8 +221,7 @@
280
  " --output ./adapter_merged\n",
281
  "```\n",
282
  "\n",
283
- "The merged adapter loads with the standard `peft` API on top of\n",
284
- "`microsoft/Phi-3.5-mini-instruct`.\n"
285
  ]
286
  }
287
  ],
 
4
  "cell_type": "markdown",
5
  "metadata": {},
6
  "source": [
7
+ "# IncidentCommander RL Kaggle shard 3 / 3\n",
8
  "\n",
9
+ "**Workload:** every 3rd task starting at index **2** (~127 of 381 scenarios).\n",
10
+ "Trains a LoRA on **Phi-3.5-mini-instruct** using a local **DeepSeek-R1-0528-Qwen3-8B** critic.\n",
 
 
11
  "\n",
12
+ "## REQUIRED attach these 2 Kaggle Models before running\n",
13
+ "Right sidebar → `+ Add Input` `Models` tab:\n",
14
+ "1. `Microsoft / phi-3` framework `PyTorch` variation `phi-3.5-mini-instruct` version `2`\n",
15
+ "2. `deepseek-ai / deepseek-r1-0528` framework `Transformers` variation `deepseek-r1-0528-qwen3-8b` version `1`\n",
16
  "\n",
17
+ "## Required notebook settings\n",
 
 
 
 
18
  "- Accelerator: `GPU T4 x2` or `GPU P100`\n",
19
+ "- Internet: `On`\n",
20
  "- Persistence: `Files only`\n",
 
 
 
 
21
  "\n",
22
+ "**Output:** `/kaggle/working/adapter_kaggle3.zip` download from the sidebar after the run finishes."
 
 
23
  ]
24
  },
25
  {
26
+ "cell_type": "code",
27
+ "execution_count": null,
28
  "metadata": {},
29
+ "outputs": [],
30
  "source": [
31
+ "# === 1. Install deps + unsloth (best-effort) ===\n",
32
+ "# Qwen3 (in DeepSeek-R1-0528) needs transformers >= 4.51. Unsloth speeds up\n",
33
+ "# the actor ~2x; if its install fails on this Kaggle image we fall back to\n",
34
+ "# pure HF transformers automatically (train_lib.py handles both paths).\n",
35
+ "import subprocess, sys\n",
36
+ "\n",
37
+ "def pip(*args):\n",
38
+ " return subprocess.run([sys.executable, '-m', 'pip', 'install', '-q', *args],\n",
39
+ " check=False).returncode\n",
40
+ "\n",
41
+ "rc = pip('-U', 'unsloth')\n",
42
+ "print('[install] unsloth rc =', rc, '(non-zero is fine, HF fallback works)')\n",
43
+ "\n",
44
+ "pip('-U',\n",
45
+ " 'transformers>=4.51,<4.55',\n",
46
+ " 'peft>=0.13,<0.16',\n",
47
+ " 'accelerate>=1.1,<1.5',\n",
48
+ " 'bitsandbytes>=0.45.5',\n",
49
+ " 'huggingface_hub>=0.25,<1.0',\n",
50
+ " 'pydantic>=2,<3',\n",
51
+ " 'datasets', 'sentencepiece', 'protobuf', 'safetensors')\n",
52
+ "print('[install] pinned stack done')"
53
  ]
54
  },
55
  {
56
  "cell_type": "code",
 
57
  "execution_count": null,
58
+ "metadata": {},
59
  "outputs": [],
60
  "source": [
61
+ "# === 2. GPU sanity ===\n",
62
  "import subprocess\n",
63
  "print('--- GPU ---')\n",
64
  "subprocess.run(['nvidia-smi', '-L'], check=False)\n",
65
  "import torch\n",
66
+ "print('CUDA OK?', torch.cuda.is_available(),\n",
67
+ " '| device:', torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'none')"
 
 
 
 
 
 
 
68
  ]
69
  },
70
  {
71
  "cell_type": "code",
 
72
  "execution_count": null,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  "metadata": {},
 
74
  "outputs": [],
75
  "source": [
76
+ "# === 3. Verify attached Kaggle Models + suppress warning spam ===\n",
77
+ "import os, pathlib, shutil, warnings, logging\n",
78
  "\n",
79
  "ACTOR_PATH = '/kaggle/input/models/Microsoft/phi-3/pytorch/phi-3.5-mini-instruct/2'\n",
80
  "CRITIC_PATH = '/kaggle/input/models/deepseek-ai/deepseek-r1-0528/transformers/deepseek-r1-0528-qwen3-8b/1'\n",
 
82
  "def verify(path, label):\n",
83
  " p = pathlib.Path(path)\n",
84
  " if not p.exists():\n",
85
+ " raise SystemExit(f'{label} not found at {path}. Attach the matching Kaggle Model.')\n",
86
+ " if not (any(p.glob('*.safetensors')) or any(p.glob('*.bin'))):\n",
87
+ " raise SystemExit(f'{label} found at {path} but no weight files inside.')\n",
88
+ " print(f'{label}: OK -> {path}')\n",
 
 
 
 
89
  "\n",
90
  "verify(ACTOR_PATH, 'actor (Phi-3.5-mini-instruct)')\n",
91
  "verify(CRITIC_PATH, 'critic (DeepSeek-R1-0528-Qwen3-8B)')\n",
92
  "\n",
 
 
93
  "os.environ['HF_HOME'] = '/tmp/hf-cache'\n",
94
  "os.environ['HUGGINGFACE_HUB_CACHE'] = '/tmp/hf-cache'\n",
95
  "os.environ['TRANSFORMERS_CACHE'] = '/tmp/hf-cache'\n",
96
  "pathlib.Path('/tmp/hf-cache').mkdir(parents=True, exist_ok=True)\n",
97
  "\n",
98
+ "modules_dir = pathlib.Path('/tmp/hf-cache/modules')\n",
99
+ "if modules_dir.exists():\n",
100
+ " shutil.rmtree(modules_dir, ignore_errors=True)\n",
101
+ " print('cleared cached custom modeling code at', modules_dir)\n",
102
+ "\n",
103
  "try:\n",
104
  " from kaggle_secrets import UserSecretsClient\n",
105
  " os.environ['HF_TOKEN'] = UserSecretsClient().get_secret('HF_TOKEN')\n",
106
  " print('HF_TOKEN attached from Kaggle Secrets')\n",
107
  "except Exception:\n",
108
+ " print('No HF_TOKEN -- training runs fully offline (that is fine).')\n",
109
  "\n",
110
+ "for pat in ('.*Caching is incompatible with gradient checkpointing.*',\n",
111
+ " '.*None of the inputs have requires_grad=True.*',\n",
112
+ " '.*use_reentrant parameter should be passed explicitly.*',\n",
113
+ " \".*AccumulateGrad node's stream does not match.*\"):\n",
114
+ " warnings.filterwarnings('ignore', message=pat)\n",
115
+ "\n",
116
+ "class _PhiFilter(logging.Filter):\n",
117
+ " def filter(self, r):\n",
118
+ " return 'Caching is incompatible' not in r.getMessage()\n",
119
+ "for n in ('transformers', 'transformers.models.phi3.modeling_phi3',\n",
120
+ " 'torch.utils.checkpoint'):\n",
121
+ " logging.getLogger(n).addFilter(_PhiFilter())\n",
122
+ "print('warning filters installed')"
 
 
 
 
 
 
 
123
  ]
124
  },
125
  {
126
  "cell_type": "code",
 
127
  "execution_count": null,
128
+ "metadata": {},
129
  "outputs": [],
130
  "source": [
131
+ "# === 4. Clone the repo (fresh every run, prints commit hash) ===\n",
132
  "import os, subprocess, pathlib, shutil\n",
133
  "WORK = '/kaggle/working/incident-commander'\n",
 
 
134
  "os.chdir('/kaggle/working')\n",
135
+ "if pathlib.Path(WORK).exists():\n",
 
 
 
136
  " shutil.rmtree(WORK, ignore_errors=True)\n",
137
  "subprocess.run(['git', 'clone', '--depth', '1',\n",
138
+ " 'https://github.com/r1cksync/meta-rl-hack.git', WORK],\n",
139
+ " check=True)\n",
140
  "os.chdir(WORK)\n",
 
141
  "subprocess.run(['git', '-C', WORK, 'log', '-1', '--oneline'], check=False)\n",
142
  "print('cwd =', os.getcwd())"
143
  ]
144
  },
 
 
 
 
 
 
 
145
  {
146
  "cell_type": "code",
 
147
  "execution_count": null,
148
+ "metadata": {},
149
  "outputs": [],
150
  "source": [
151
+ "# === 5. Configure run (shard 3 / 3) ===\n",
152
  "import os\n",
 
153
  "os.environ['INCIDENT_COMMANDER_MOCK'] = 'true'\n",
154
  "os.environ['IC_ACTOR_MODEL'] = ACTOR_PATH\n",
155
+ "os.environ['IC_CRITIC_PROVIDER'] = 'local'\n",
156
  "os.environ['IC_CRITIC_MODEL'] = CRITIC_PATH\n",
157
+ "os.environ['IC_TASK_MODE'] = 'all'\n",
158
  "os.environ['IC_TASK_SHARDS'] = '3'\n",
159
  "os.environ['IC_TASK_SHARD'] = '2'\n",
160
+ "os.environ['IC_TOTAL_UPDATES'] = '60'\n",
161
  "os.environ['IC_ROLLOUTS'] = '3'\n",
162
  "os.environ['IC_MAX_STEPS'] = '12'\n",
163
  "os.environ['IC_CKPT_EVERY'] = '15'\n",
164
  "os.environ['IC_RUN_NAME'] = 'kaggle3'\n",
 
165
  "print('actor :', os.environ['IC_ACTOR_MODEL'])\n",
166
  "print('critic:', os.environ['IC_CRITIC_MODEL'])\n",
167
  "print('shard :', os.environ['IC_TASK_SHARD'], '/', os.environ['IC_TASK_SHARDS'])"
168
  ]
169
  },
 
 
 
 
 
 
 
170
  {
171
  "cell_type": "code",
 
172
  "execution_count": null,
173
+ "metadata": {},
174
  "outputs": [],
175
  "source": [
176
+ "# === 6. Train ===\n",
 
 
177
  "import subprocess, sys\n",
178
+ "result = subprocess.run([sys.executable, 'scripts/run_training.py'], check=False)\n",
 
179
  "print('exit code:', result.returncode)"
180
  ]
181
  },
 
 
 
 
 
 
 
182
  {
183
  "cell_type": "code",
 
184
  "execution_count": null,
185
+ "metadata": {},
186
  "outputs": [],
187
  "source": [
188
+ "# === 7. Package outputs for download ===\n",
189
  "import shutil, glob, pathlib\n",
 
190
  "LOGS = pathlib.Path('colab/logs')\n",
191
  "finals = sorted(LOGS.glob('adapter_kaggle3_final'))\n",
192
  "ckpts = sorted(LOGS.glob('adapter_kaggle3_u*'))\n",
193
+ "keep = (finals or ckpts)\n",
194
+ "assert keep, 'No adapter directories found -- check the training cell output.'\n",
195
  "src = keep[-1]\n",
196
  "print('packaging', src)\n",
 
197
  "dst = pathlib.Path('/kaggle/working/adapter_kaggle3.zip')\n",
198
  "shutil.make_archive(str(dst.with_suffix('')), 'zip', root_dir=src)\n",
199
  "print('zipped to', dst, 'size:', dst.stat().st_size, 'bytes')\n",
 
 
200
  "for j in glob.glob('colab/logs/training_kaggle3*.json'):\n",
201
  " shutil.copy(j, '/kaggle/working/')\n",
202
  "print('files in /kaggle/working/:')\n",
203
  "for f in sorted(pathlib.Path('/kaggle/working/').iterdir()):\n",
204
+ " if f.name == 'hf-cache':\n",
205
+ " continue\n",
206
  " print(' ', f.name, f.stat().st_size if f.is_file() else '<dir>')"
207
  ]
208
  },
 
212
  "source": [
213
  "## Done\n",
214
  "\n",
215
+ "Download `adapter_kaggle3.zip` from the **Output** tab on the right.\n",
216
+ "Run shard 2 and shard 3 in parallel browser tabs, then on your laptop:\n",
 
217
  "\n",
218
  "```powershell\n",
219
  "python scripts/merge_lora_adapters.py `\n",
 
221
  " --output ./adapter_merged\n",
222
  "```\n",
223
  "\n",
224
+ "The merged adapter loads with the standard `peft` API on top of `microsoft/Phi-3.5-mini-instruct`."
 
225
  ]
226
  }
227
  ],