Taimwe commited on
Commit
0cca096
Β·
verified Β·
1 Parent(s): 33df54a

Declare Apache-2.0 licence

Browse files
Files changed (1) hide show
  1. README.md +218 -214
README.md CHANGED
@@ -1,214 +1,218 @@
1
- > [!CAUTION]
2
- > **Known defect found 2026-10-04:** the v1 and v2 fine-tunes emit the literal
3
- > two-character sequence `\n` instead of real newlines, so their Python output fails
4
- > `ast.parse` (base model 93.3% valid Python, v2 0.0%). Cause: several datasets in the
5
- > training mix store message text JSON-escaped (183/480 sampled rows).
6
- >
7
- > **Fixed here** in `train_securecoder.py` via `_unescape_if_needed()` + a `repaired=`
8
- > counter per source. The broken merged/GGUF repos were deleted; both adapters are kept
9
- > for reproducibility. Full write-up and the exact retrain command: see `HANDOFF.md`.
10
- >
11
- > Also fixed: a UTF-8 BOM before `# /// script` meant uv never installed the declared
12
- > dependencies, so every job died in seconds with `No module named 'torch'`.
13
-
14
- # SecureCoder β€” training runbook
15
-
16
- A QLoRA fine-tune that targets three skills at once: **coding**, **tool calling**
17
- and **cybersecurity** (offence *and* defence), on a base chosen to train cheaply
18
- and run fast. Everything here is measured or verified on real jobs, not assumed β€”
19
- each claim carries the job id you can inspect.
20
-
21
- ## 1. Base model choice
22
-
23
- Screened on the Hub on 2026-09-24 (params / licence / tool-calling support):
24
-
25
- | Candidate | Params | Licence | Verdict |
26
- | --- | --- | --- | --- |
27
- | **`Qwen/Qwen3-Coder-30B-A3B-Instruct`** | 30.5B MoE, ~3B active | Apache-2.0 | **chosen** β€” code-specialised, native tool-calling template, MoE means it *trains* like a 3B model and fits 48 GB in 4-bit |
28
- | `Qwen/Qwen3.8-27B` | 27.8B dense | Apache-2.0 | best raw quality, ~4x slower per step; good upgrade path once the pipeline is proven |
29
- | `Qwen/Qwen3-Coder-Next` | 79.7B | Apache-2.0 | needs an 80 GB card (A100 80 GB `$2.50/h`); too big for the first run |
30
- | `Qwen/Qwen3.8-Flash-Next` | 180B | **license: other** | rejected β€” non-commercial-style licence on an 180B model |
31
- | `openbmb/MiniCPM5-2B` | 2.5B | Apache-2.0 | `tool-calling` tag, tiny; good for a T4 smoke test, weak ceiling |
32
- | `TokenRhythm/NeoHorse-1-9B` | 9.0B | Apache-2.0 | `tool-use`, `reasoning`; the fallback if T4-only |
33
-
34
- Why not start from an existing *abliterated* checkpoint? Because refusal removal is
35
- a separate weight-edit (section 6) that can be applied to **any** checkpoint after
36
- fine-tuning, and pre-abliterated bases are community re-uploads with unclear
37
- provenance. Fine-tuning first keeps the lineage clean and the base licensed.
38
-
39
- ## 2. Data mix
40
-
41
- The cap per source *is* the recipe β€” sources differ hugely in size, so the mix is
42
- balanced by taking a fixed slice of each. Verified on a real CPU job
43
- (`Taimwe/6ab5ae256b030d633f68faef`) at 25 rows/source:
44
-
45
- | Source | Rows taken | Kind | What it teaches |
46
- | --- | --- | --- | --- |
47
- | `NousResearch/hermes-function-calling-v1` `[func_calling]` | 9,000 | tool calling | full tool-call conversations + JSON schemas |
48
- | `NousResearch/hermes-function-calling-v1` `[func_calling_singleturn]` | 3,000 | tool calling | picking the right function, no chatter |
49
- | `lockon/xlam-function-calling-60k` | 10,000 | tool calling | 60k API-call pairs (query β†’ call) |
50
- | `ise-uiuc/Magicoder-OSS-Instruct-75K` | 10,000 | coding | self-instruct problems + solutions |
51
- | `Trendyol/Trendyol-Cybersecurity-Instruction-Tuning-Dataset` | 8,000 | security | security instruction tuning |
52
- | `AlicanKiraz0/Cybersecurity-Dataset-Fenrir-v2.1` | 5,000 | security | broad security Q&A |
53
- | `Humanlearning/CyberSecurity_OWASP-sft-dataset` | 3,000 | secure coding | OWASP / defensive coding |
54
- | `MrClipperz134/CTF-Instruct` | 3,000 | CTF | challenge instruction β†’ solve |
55
- | `TrueNix/ctf-solver-dataset` | 3,000 | CTF | solving trajectories |
56
- | `mlabonne/FineTome-100k` | 3,000 | replay | general chat, so instruction-following does not collapse |
57
-
58
- **Total β‰ˆ 57,000 rows, ~30% of them tool-calling.** Every source validated at
59
- 25/25 kept with zero render failures.
60
-
61
- Excluded deliberately:
62
-
63
- * `dpevzner/Cybersecurity_Reasoning_Dataset` β€” its builder configs were renamed
64
- (`default` β†’ `mistral|deepseek|chatml|gemma`) and the fields the card documents
65
- (`unified_interpretation`) are empty in the published revision, so every row was
66
- dropped. Re-addable once its schema settles.
67
- * Anything whose purpose is building malware or weaponised exploits. Recon and
68
- enumeration knowledge, exploit *concepts*, CTF solving and defensive
69
- engineering are in; end-to-end attack tooling is not. This is a deliberate
70
- scope line, not an oversight.
71
-
72
- ## 3. What training does with the data
73
-
74
- Each row is converted to chat messages and rendered through the model's own chat
75
- template, so the model learns the exact format it will be asked for at inference
76
- time (`<|im_start|>…<tool_call><function=NAME><parameter=…>` for Qwen3-Coder).
77
-
78
- Two format traps found the hard way and handled in code:
79
-
80
- * Qwen3-Coder iterates `tool.parameters.properties` β€” tool schemas must be **flat**
81
- (`{"name", "description", "parameters"}`), not wrapped in `"function"`.
82
- * It also iterates `tool_call.arguments | items`, so `arguments` must be a
83
- **mapping**. A JSON string there fails with *"Can only get item pairs from a
84
- mapping"* β€” which is exactly how xLAM was silently contributing 0 rows until fixed.
85
-
86
- `render_record()` therefore detects the template's convention once (flat/nested Γ—
87
- dict/string) and reuses it, instead of hard-coding one model's dialect.
88
-
89
- ## 4. Compute and cost
90
-
91
- Verified `hf jobs hardware` prices (per hour), 2026-09-24:
92
-
93
- | Flavor | VRAM | $/hour | Use |
94
- | --- | --- | --- | --- |
95
- | `cpu-basic` | β€” | $0.01 | data-mix validation, class checks |
96
- | `t4-small` | 16 GB (T4) | $0.40 | plumbing checks; too small for a 30B in 4-bit |
97
- | `l4x1` | 24 GB | $0.80 | 9B runs on a budget |
98
- | `a10g-large` | 24 GB | $1.50 | 9B–14B comfortably |
99
- | **`l40sx1`** | **48 GB** | **$1.80** | **the 30B-A3B QLoRA** |
100
- | `a100-large` | 80 GB | $2.50 | faster per step, larger batch |
101
- | `rtx-pro-6000` | 96 GB | $2.75 | headroom for longer context |
102
- | `h200` | 141 GB | $5.00 | full fine-tune territory, overkill here |
103
-
104
- Rough budget for the full 1-epoch run. **These are now measured, not guessed** β€” the
105
- smoke test (`Taimwe/6ab5b3ed52d0dbd7f1d8d454`, a100-large) trained at
106
- **0.8 rows/s** (20 s per step at batch 2 Γ— grad-accum 8, max seq 2048, packing on),
107
- which includes a few minutes of `torch.compile` warm-up that does not recur per step:
108
-
109
- | Mix | Rows | Time at 0.8 rows/s | Cost on `l40sx1` ($1.80/h) |
110
- | --- | --- | --- | --- |
111
- | full mix as specified | ~57,000 | ~20 h | **~$36** |
112
- | half caps (`--max-steps` or edit the table) | ~28,000 | ~10 h | ~$18 |
113
- | lean mix (cut caps to ~1/3) | ~19,000 | ~7 h | ~$12 |
114
-
115
- Because warm-up inflates that rate, a **300-step measured run** (~$4 on a100-large)
116
- gives a trustworthy steady-state number before committing to a long job. Levers that
117
- move the cost most, in order: rows in the mix, `--max-seq-length` (2048 vs 4096),
118
- `--batch-size` (raise it on 80–96 GB cards), `--lora-r`.
119
-
120
- The `--smoke` run itself costs under $1 and proves model load, data rendering,
121
- training, evaluation and the Hub push end to end.
122
-
123
- No local GPU needed. Colab works the same way with `uv run train_securecoder.py …`
124
- (Colab Free's T4 cannot hold a 30B in 4-bit β€” use Colab Pro L4/A100, or point
125
- `--base-model` at a 9B).
126
-
127
- ## 5. Runbook (copy-paste)
128
-
129
- ```bash
130
- # 0. auth (token needs repo.write, and job.write for Jobs)
131
- hf auth login --token hf_xxx
132
-
133
- # 1. validate the mix: 25 rows/source, no GPU, ~50 s, ~$0.0002
134
- hf jobs run -d --flavor cpu-basic --timeout 30m --name validate-mix \
135
- ghcr.io/astral-sh/uv:python3.12-bookworm \
136
- uv run --no-project https://huggingface.co/Taimwe/securecoder-scripts/resolve/main/train_securecoder.py \
137
- --validate-only --validate-per-source 25 --show-samples 3
138
-
139
- # 2. smoke test on a real GPU: 200 rows/source, 20 steps, pushes an adapter (~$0.50)
140
- hf jobs run -d --flavor l40sx1 --timeout 40m --secrets HF_TOKEN --name smoke-train \
141
- ghcr.io/astral-sh/uv:python3.12-bookworm \
142
- uv run --no-project https://huggingface.co/Taimwe/securecoder-scripts/resolve/main/train_securecoder.py \
143
- --smoke --output-repo Taimwe/securecoder-smoke --private --report-to none
144
-
145
- # 3. the real run (~$9–14)
146
- hf jobs run -d --flavor l40sx1 --timeout 12h --secrets HF_TOKEN --name securecoder-run1 \
147
- ghcr.io/astral-sh/uv:python3.12-bookworm \
148
- uv run --no-project https://huggingface.co/Taimwe/securecoder-scripts/resolve/main/train_securecoder.py \
149
- --num-epochs 1 --output-repo Taimwe/securecoder-30b-pro \
150
- --trackio-space Taimwe/securecoder-trackio
151
-
152
- # monitor
153
- hf jobs inspect Taimwe/<job_id> --json
154
- hf jobs logs Taimwe/<job_id>
155
- hf jobs stats Taimwe/<job_id>
156
- hf jobs cancel Taimwe/<job_id>
157
- ```
158
-
159
- ### Platform traps already worked around (verified 2026-09-24)
160
-
161
- | Symptom | Cause | Workaround |
162
- | --- | --- | --- |
163
- | `failed to set MOUNT_ATTR_IDMAP on /usr/bin/nvidia-cuda-mps-control` on a GPU flavor | `hf jobs uv run` mounts an artifacts bucket; the idmap mount fails on some GPU nodes (reproduced twice, 4 s after start) | use `hf jobs run` + the script's **public Hub URL** (works: `Taimwe/6ab5ad3c6b030d633f68face`) or a read-only repo mount `-v hf://models/Taimwe/securecoder-scripts:/scripts:ro` (works: `Taimwe/6ab5ad3d52d0dbd7f1d8d286`) |
164
- | inner quotes stripped from `python -c "…"` | PowerShell native-argument quoting | use a UV script file, never `-c` one-liners |
165
- | `charmap codec can't encode` while reading logs | Windows console encoding | set `$env:PYTHONIOENCODING='utf-8'` before `hf jobs logs` |
166
- | HF's trainer skill says Jobs need a paid plan | docs caveat | not enforced on this account β€” CPU and GPU jobs both ran |
167
-
168
- ## 6. Turning down refusals (optional, after SFT)
169
-
170
- Abliteration is a weight edit, not a training run: find the "refusal direction" in
171
- the residual stream and project it out of the writing matrices.
172
-
173
- 1. **`Heretic`** (p-e-w/heretic) β€” the tool behind the recent wave of
174
- `*-heretic-abliterated-*` checkpoints (e.g.
175
- `culturerevolt/gemma-4-12b-heretic-abliterated-GGUF`, 174k downloads). Runs on
176
- the merged 16-bit model and optimises the ablation to trade refusal rate against
177
- KL divergence, so it preserves capability better than a hand-rolled ablation.
178
- 2. **Manual directional ablation** (Arditi et al., *Refusal in LLMs is mediated by a
179
- single direction*): collect residual activations for harmful vs. harmless
180
- prompts, take the mean difference per layer, and zero that component in
181
- `o_proj` / `down_proj`.
182
-
183
- Honest expectations: **it is not free.** Refusals drop, and some performance on the
184
- same layers' other duties drops with them; scope-setting behaviour ("this needs
185
- authorisation") can weaken too. Measure both sides (section 7) and document the
186
- result on the model card, or you are shipping an unmeasured change.
187
-
188
- ## 7. Evaluation before publishing
189
-
190
- 1. **Tool-call validity** β€” 200 prompts with real tool schemas; parse the emitted
191
- `<function=NAME><parameter=…>` block back to JSON and score syntax validity,
192
- correct function choice, and schema-conformant arguments. This is the number
193
- that matters most for "top-notch tool calling".
194
- 2. **Code sanity** β€” generate 100 solutions, check compilation, run the bundled
195
- tests on a small HumanEval-style subset.
196
- 3. **Security knowledge** β€” fixed MCQ/triage set (e.g. `CyberNative/CyberSecurityEval`),
197
- scored before *and* after fine-tuning so the mix's effect is visible.
198
- 4. **Regression** β€” the same three on the untouched base, same prompts. A fine-tune
199
- that feels better but scores worse is a regression with extra steps.
200
- 5. **Refusal rate** β€” if abliterated, publish before/after refusal rate next to the
201
- eval deltas.
202
-
203
- ## 8. Known risks
204
-
205
- * MoE LoRA defaults to attention + router (`q/k/v/o/gate`). `--target-modules
206
- all-linear` on a 128-expert model means ~800M trainable parameters β€” much slower
207
- and hungrier. Start conservative; revisit if security knowledge is the weak score.
208
- * 4-bit QLoRA of a 30B (~17 GB) is tight on a 24 GB card; 48 GB allows batch 2 with
209
- 4096-token samples.
210
- * One 1-epoch pass over three domains is a *start*, not a finished model. Iterate on
211
- the mix ratios β€” they live in one table at the top of the script.
212
- * Nothing here adds safety training, and the security slice includes
213
- recon/enumeration knowledge framed for authorised use. Publish explicit
214
- intended-use and limitations sections; do not present it as a hardened model.
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ ---
4
+
5
+ > [!CAUTION]
6
+ > **Known defect found 2026-10-04:** the v1 and v2 fine-tunes emit the literal
7
+ > two-character sequence `\n` instead of real newlines, so their Python output fails
8
+ > `ast.parse` (base model 93.3% valid Python, v2 0.0%). Cause: several datasets in the
9
+ > training mix store message text JSON-escaped (183/480 sampled rows).
10
+ >
11
+ > **Fixed here** in `train_securecoder.py` via `_unescape_if_needed()` + a `repaired=`
12
+ > counter per source. The broken merged/GGUF repos were deleted; both adapters are kept
13
+ > for reproducibility. Full write-up and the exact retrain command: see `HANDOFF.md`.
14
+ >
15
+ > Also fixed: a UTF-8 BOM before `# /// script` meant uv never installed the declared
16
+ > dependencies, so every job died in seconds with `No module named 'torch'`.
17
+
18
+ # SecureCoder β€” training runbook
19
+
20
+ A QLoRA fine-tune that targets three skills at once: **coding**, **tool calling**
21
+ and **cybersecurity** (offence *and* defence), on a base chosen to train cheaply
22
+ and run fast. Everything here is measured or verified on real jobs, not assumed β€”
23
+ each claim carries the job id you can inspect.
24
+
25
+ ## 1. Base model choice
26
+
27
+ Screened on the Hub on 2026-09-24 (params / licence / tool-calling support):
28
+
29
+ | Candidate | Params | Licence | Verdict |
30
+ | --- | --- | --- | --- |
31
+ | **`Qwen/Qwen3-Coder-30B-A3B-Instruct`** | 30.5B MoE, ~3B active | Apache-2.0 | **chosen** β€” code-specialised, native tool-calling template, MoE means it *trains* like a 3B model and fits 48 GB in 4-bit |
32
+ | `Qwen/Qwen3.8-27B` | 27.8B dense | Apache-2.0 | best raw quality, ~4x slower per step; good upgrade path once the pipeline is proven |
33
+ | `Qwen/Qwen3-Coder-Next` | 79.7B | Apache-2.0 | needs an 80 GB card (A100 80 GB `$2.50/h`); too big for the first run |
34
+ | `Qwen/Qwen3.8-Flash-Next` | 180B | **license: other** | rejected β€” non-commercial-style licence on an 180B model |
35
+ | `openbmb/MiniCPM5-2B` | 2.5B | Apache-2.0 | `tool-calling` tag, tiny; good for a T4 smoke test, weak ceiling |
36
+ | `TokenRhythm/NeoHorse-1-9B` | 9.0B | Apache-2.0 | `tool-use`, `reasoning`; the fallback if T4-only |
37
+
38
+ Why not start from an existing *abliterated* checkpoint? Because refusal removal is
39
+ a separate weight-edit (section 6) that can be applied to **any** checkpoint after
40
+ fine-tuning, and pre-abliterated bases are community re-uploads with unclear
41
+ provenance. Fine-tuning first keeps the lineage clean and the base licensed.
42
+
43
+ ## 2. Data mix
44
+
45
+ The cap per source *is* the recipe β€” sources differ hugely in size, so the mix is
46
+ balanced by taking a fixed slice of each. Verified on a real CPU job
47
+ (`Taimwe/6ab5ae256b030d633f68faef`) at 25 rows/source:
48
+
49
+ | Source | Rows taken | Kind | What it teaches |
50
+ | --- | --- | --- | --- |
51
+ | `NousResearch/hermes-function-calling-v1` `[func_calling]` | 9,000 | tool calling | full tool-call conversations + JSON schemas |
52
+ | `NousResearch/hermes-function-calling-v1` `[func_calling_singleturn]` | 3,000 | tool calling | picking the right function, no chatter |
53
+ | `lockon/xlam-function-calling-60k` | 10,000 | tool calling | 60k API-call pairs (query β†’ call) |
54
+ | `ise-uiuc/Magicoder-OSS-Instruct-75K` | 10,000 | coding | self-instruct problems + solutions |
55
+ | `Trendyol/Trendyol-Cybersecurity-Instruction-Tuning-Dataset` | 8,000 | security | security instruction tuning |
56
+ | `AlicanKiraz0/Cybersecurity-Dataset-Fenrir-v2.1` | 5,000 | security | broad security Q&A |
57
+ | `Humanlearning/CyberSecurity_OWASP-sft-dataset` | 3,000 | secure coding | OWASP / defensive coding |
58
+ | `MrClipperz134/CTF-Instruct` | 3,000 | CTF | challenge instruction β†’ solve |
59
+ | `TrueNix/ctf-solver-dataset` | 3,000 | CTF | solving trajectories |
60
+ | `mlabonne/FineTome-100k` | 3,000 | replay | general chat, so instruction-following does not collapse |
61
+
62
+ **Total β‰ˆ 57,000 rows, ~30% of them tool-calling.** Every source validated at
63
+ 25/25 kept with zero render failures.
64
+
65
+ Excluded deliberately:
66
+
67
+ * `dpevzner/Cybersecurity_Reasoning_Dataset` β€” its builder configs were renamed
68
+ (`default` β†’ `mistral|deepseek|chatml|gemma`) and the fields the card documents
69
+ (`unified_interpretation`) are empty in the published revision, so every row was
70
+ dropped. Re-addable once its schema settles.
71
+ * Anything whose purpose is building malware or weaponised exploits. Recon and
72
+ enumeration knowledge, exploit *concepts*, CTF solving and defensive
73
+ engineering are in; end-to-end attack tooling is not. This is a deliberate
74
+ scope line, not an oversight.
75
+
76
+ ## 3. What training does with the data
77
+
78
+ Each row is converted to chat messages and rendered through the model's own chat
79
+ template, so the model learns the exact format it will be asked for at inference
80
+ time (`<|im_start|>…<tool_call><function=NAME><parameter=…>` for Qwen3-Coder).
81
+
82
+ Two format traps found the hard way and handled in code:
83
+
84
+ * Qwen3-Coder iterates `tool.parameters.properties` β€” tool schemas must be **flat**
85
+ (`{"name", "description", "parameters"}`), not wrapped in `"function"`.
86
+ * It also iterates `tool_call.arguments | items`, so `arguments` must be a
87
+ **mapping**. A JSON string there fails with *"Can only get item pairs from a
88
+ mapping"* β€” which is exactly how xLAM was silently contributing 0 rows until fixed.
89
+
90
+ `render_record()` therefore detects the template's convention once (flat/nested Γ—
91
+ dict/string) and reuses it, instead of hard-coding one model's dialect.
92
+
93
+ ## 4. Compute and cost
94
+
95
+ Verified `hf jobs hardware` prices (per hour), 2026-09-24:
96
+
97
+ | Flavor | VRAM | $/hour | Use |
98
+ | --- | --- | --- | --- |
99
+ | `cpu-basic` | β€” | $0.01 | data-mix validation, class checks |
100
+ | `t4-small` | 16 GB (T4) | $0.40 | plumbing checks; too small for a 30B in 4-bit |
101
+ | `l4x1` | 24 GB | $0.80 | 9B runs on a budget |
102
+ | `a10g-large` | 24 GB | $1.50 | 9B–14B comfortably |
103
+ | **`l40sx1`** | **48 GB** | **$1.80** | **the 30B-A3B QLoRA** |
104
+ | `a100-large` | 80 GB | $2.50 | faster per step, larger batch |
105
+ | `rtx-pro-6000` | 96 GB | $2.75 | headroom for longer context |
106
+ | `h200` | 141 GB | $5.00 | full fine-tune territory, overkill here |
107
+
108
+ Rough budget for the full 1-epoch run. **These are now measured, not guessed** β€” the
109
+ smoke test (`Taimwe/6ab5b3ed52d0dbd7f1d8d454`, a100-large) trained at
110
+ **0.8 rows/s** (20 s per step at batch 2 Γ— grad-accum 8, max seq 2048, packing on),
111
+ which includes a few minutes of `torch.compile` warm-up that does not recur per step:
112
+
113
+ | Mix | Rows | Time at 0.8 rows/s | Cost on `l40sx1` ($1.80/h) |
114
+ | --- | --- | --- | --- |
115
+ | full mix as specified | ~57,000 | ~20 h | **~$36** |
116
+ | half caps (`--max-steps` or edit the table) | ~28,000 | ~10 h | ~$18 |
117
+ | lean mix (cut caps to ~1/3) | ~19,000 | ~7 h | ~$12 |
118
+
119
+ Because warm-up inflates that rate, a **300-step measured run** (~$4 on a100-large)
120
+ gives a trustworthy steady-state number before committing to a long job. Levers that
121
+ move the cost most, in order: rows in the mix, `--max-seq-length` (2048 vs 4096),
122
+ `--batch-size` (raise it on 80–96 GB cards), `--lora-r`.
123
+
124
+ The `--smoke` run itself costs under $1 and proves model load, data rendering,
125
+ training, evaluation and the Hub push end to end.
126
+
127
+ No local GPU needed. Colab works the same way with `uv run train_securecoder.py …`
128
+ (Colab Free's T4 cannot hold a 30B in 4-bit β€” use Colab Pro L4/A100, or point
129
+ `--base-model` at a 9B).
130
+
131
+ ## 5. Runbook (copy-paste)
132
+
133
+ ```bash
134
+ # 0. auth (token needs repo.write, and job.write for Jobs)
135
+ hf auth login --token hf_xxx
136
+
137
+ # 1. validate the mix: 25 rows/source, no GPU, ~50 s, ~$0.0002
138
+ hf jobs run -d --flavor cpu-basic --timeout 30m --name validate-mix \
139
+ ghcr.io/astral-sh/uv:python3.12-bookworm \
140
+ uv run --no-project https://huggingface.co/Taimwe/securecoder-scripts/resolve/main/train_securecoder.py \
141
+ --validate-only --validate-per-source 25 --show-samples 3
142
+
143
+ # 2. smoke test on a real GPU: 200 rows/source, 20 steps, pushes an adapter (~$0.50)
144
+ hf jobs run -d --flavor l40sx1 --timeout 40m --secrets HF_TOKEN --name smoke-train \
145
+ ghcr.io/astral-sh/uv:python3.12-bookworm \
146
+ uv run --no-project https://huggingface.co/Taimwe/securecoder-scripts/resolve/main/train_securecoder.py \
147
+ --smoke --output-repo Taimwe/securecoder-smoke --private --report-to none
148
+
149
+ # 3. the real run (~$9–14)
150
+ hf jobs run -d --flavor l40sx1 --timeout 12h --secrets HF_TOKEN --name securecoder-run1 \
151
+ ghcr.io/astral-sh/uv:python3.12-bookworm \
152
+ uv run --no-project https://huggingface.co/Taimwe/securecoder-scripts/resolve/main/train_securecoder.py \
153
+ --num-epochs 1 --output-repo Taimwe/securecoder-30b-pro \
154
+ --trackio-space Taimwe/securecoder-trackio
155
+
156
+ # monitor
157
+ hf jobs inspect Taimwe/<job_id> --json
158
+ hf jobs logs Taimwe/<job_id>
159
+ hf jobs stats Taimwe/<job_id>
160
+ hf jobs cancel Taimwe/<job_id>
161
+ ```
162
+
163
+ ### Platform traps already worked around (verified 2026-09-24)
164
+
165
+ | Symptom | Cause | Workaround |
166
+ | --- | --- | --- |
167
+ | `failed to set MOUNT_ATTR_IDMAP on /usr/bin/nvidia-cuda-mps-control` on a GPU flavor | `hf jobs uv run` mounts an artifacts bucket; the idmap mount fails on some GPU nodes (reproduced twice, 4 s after start) | use `hf jobs run` + the script's **public Hub URL** (works: `Taimwe/6ab5ad3c6b030d633f68face`) or a read-only repo mount `-v hf://models/Taimwe/securecoder-scripts:/scripts:ro` (works: `Taimwe/6ab5ad3d52d0dbd7f1d8d286`) |
168
+ | inner quotes stripped from `python -c "…"` | PowerShell native-argument quoting | use a UV script file, never `-c` one-liners |
169
+ | `charmap codec can't encode` while reading logs | Windows console encoding | set `$env:PYTHONIOENCODING='utf-8'` before `hf jobs logs` |
170
+ | HF's trainer skill says Jobs need a paid plan | docs caveat | not enforced on this account β€” CPU and GPU jobs both ran |
171
+
172
+ ## 6. Turning down refusals (optional, after SFT)
173
+
174
+ Abliteration is a weight edit, not a training run: find the "refusal direction" in
175
+ the residual stream and project it out of the writing matrices.
176
+
177
+ 1. **`Heretic`** (p-e-w/heretic) β€” the tool behind the recent wave of
178
+ `*-heretic-abliterated-*` checkpoints (e.g.
179
+ `culturerevolt/gemma-4-12b-heretic-abliterated-GGUF`, 174k downloads). Runs on
180
+ the merged 16-bit model and optimises the ablation to trade refusal rate against
181
+ KL divergence, so it preserves capability better than a hand-rolled ablation.
182
+ 2. **Manual directional ablation** (Arditi et al., *Refusal in LLMs is mediated by a
183
+ single direction*): collect residual activations for harmful vs. harmless
184
+ prompts, take the mean difference per layer, and zero that component in
185
+ `o_proj` / `down_proj`.
186
+
187
+ Honest expectations: **it is not free.** Refusals drop, and some performance on the
188
+ same layers' other duties drops with them; scope-setting behaviour ("this needs
189
+ authorisation") can weaken too. Measure both sides (section 7) and document the
190
+ result on the model card, or you are shipping an unmeasured change.
191
+
192
+ ## 7. Evaluation before publishing
193
+
194
+ 1. **Tool-call validity** β€” 200 prompts with real tool schemas; parse the emitted
195
+ `<function=NAME><parameter=…>` block back to JSON and score syntax validity,
196
+ correct function choice, and schema-conformant arguments. This is the number
197
+ that matters most for "top-notch tool calling".
198
+ 2. **Code sanity** β€” generate 100 solutions, check compilation, run the bundled
199
+ tests on a small HumanEval-style subset.
200
+ 3. **Security knowledge** β€” fixed MCQ/triage set (e.g. `CyberNative/CyberSecurityEval`),
201
+ scored before *and* after fine-tuning so the mix's effect is visible.
202
+ 4. **Regression** β€” the same three on the untouched base, same prompts. A fine-tune
203
+ that feels better but scores worse is a regression with extra steps.
204
+ 5. **Refusal rate** β€” if abliterated, publish before/after refusal rate next to the
205
+ eval deltas.
206
+
207
+ ## 8. Known risks
208
+
209
+ * MoE LoRA defaults to attention + router (`q/k/v/o/gate`). `--target-modules
210
+ all-linear` on a 128-expert model means ~800M trainable parameters β€” much slower
211
+ and hungrier. Start conservative; revisit if security knowledge is the weak score.
212
+ * 4-bit QLoRA of a 30B (~17 GB) is tight on a 24 GB card; 48 GB allows batch 2 with
213
+ 4096-token samples.
214
+ * One 1-epoch pass over three domains is a *start*, not a finished model. Iterate on
215
+ the mix ratios β€” they live in one table at the top of the script.
216
+ * Nothing here adds safety training, and the security slice includes
217
+ recon/enumeration knowledge framed for authorised use. Publish explicit
218
+ intended-use and limitations sections; do not present it as a hardened model.