CounterFeint / training /TRAINING_GUIDE.md
QuantumTransformer's picture
Upload folder using huggingface_hub
c1cbec2 verified
|
Raw
History Blame Contribute Delete
14.8 kB
# CounterFeint - Training on Hugging Face
Step-by-step playbook for taking the Investigator from the current ~0.6 mean
`grader_score` baseline to a trained checkpoint with reward + loss curves and a
HF Hub release. All compute is sized for the **$30 HF Pro / Spaces credit**.
---
## TL;DR (the whole pipeline in 4 commands)
1. **Baseline eval** -> `baseline_eval.ipynb` on a T4 Space (~30 min, $0.20)
2. **Train** -> `official_hf_training.ipynb` on a T4 Space, `MODE = "proper"` (~3 hr, $1.20)
3. **Compare** -> `compare_runs.ipynb` locally (free, no GPU)
4. **Push** -> set `PUSH_TO_HUB = True` in the training notebook to ship the LoRA
adapter + `eval_summary.json` to the Hub
That's one full bake-off run. You can afford ~20 of them inside the $30 budget.
---
## 0. What lives where
```
counterfeint/training/
├── baseline_eval.ipynb # NEW pre-training, multi-model bake-off
├── official_hf_training.ipynb # main GRPO training + post-training eval
├── compare_runs.ipynb # NEW aggregates baseline + trained runs into plots
├── proxy_reward.py # deterministic reward function used during GRPO
├── rollout.py # in-process episode collector (no HTTP server)
├── smoke_official_hf.py # quick local pipeline check (skip if you trust the notebooks)
└── TRAINING_GUIDE.md # this file
```
After a baseline + training run, the directory tree looks like:
```
baseline_outputs/
├── qwen3-0.6b/baseline_results.json # per-episode rows for that model
├── qwen2.5-1.5b/baseline_results.json
├── qwen3-1.7b/baseline_results.json
├── baseline_summary.json
└── baseline_comparison.png # bar chart for the README
outputs/
└── counterfeint-investigator-qwen3-06b-grpo/ # one directory per training run
├── lora_adapter/ # LoRA weights + tokenizer
│ ├── adapter_config.json
│ └── adapter_model.safetensors
├── eval_summary.json # before / after grader_score
├── log_history.json # raw TRL log (loss, reward, kl)
├── training_config.json # exact config that produced this run
├── training_curves.png # combined loss / reward / KL plot
└── eval_plot.png # per-episode before / after bars
comparison_outputs/
├── before_after_grader.png # headline plot
├── training_curves.png # multi-run overlay
└── comparison_table.csv
```
---
## 1. Pick your compute lane
You have **two** sensible options for running these notebooks. Both work.
### Lane A - HF Spaces with JupyterLab (uses HF credits directly)
Best when: you specifically want to spend the $30 HF credit, want artifacts
to live next to your Space, or want a persistent dev environment.
1. Go to [https://huggingface.co/new-space](https://huggingface.co/new-space).
2. Pick the **"JupyterLab"** Docker template (or "Notebooks").
3. Hardware: **T4 small** (`$0.40 / hr`). For multi-model ablations you can
bump to **A10G small** (`$1.05 / hr`) to halve wall time.
4. Add a persistent disk (50 GB is plenty).
5. Once the Space is running, open the JupyterLab UI and either:
- `git clone` your repo into `/data/`, or
- upload the `counterfeint/` directory through the file browser.
6. Open `counterfeint/training/baseline_eval.ipynb` and run cell-by-cell.
**Cost reality:** T4 at $0.40/hr means a 30 min baseline + 3 hr proper training
run is ~**$1.40**. You can do ~20 such cycles inside $30.
### Lane B - Google Colab (free T4) + push artifacts to HF Hub
Best when: you want the cheapest path and don't care that the compute is
Google's; the $30 stays available for HF Inference Endpoints later (e.g. the
Llama 3.1 8B Fraudster for the demo video).
1. Open Colab ([https://colab.research.google.com/](https://colab.research.google.com/)).
2. `Runtime -> Change runtime type -> T4 GPU`.
3. Upload `baseline_eval.ipynb` (or open from GitHub via `File -> Open notebook`).
4. The first cell autodetects Colab and clones the repo for you.
5. Run cells. Push the `outputs/` and `baseline_outputs/` folders to your HF
dataset repo at the end.
**Strong recommendation:** start in Colab to debug, then move to HF Spaces only
once you trust the pipeline end-to-end. This stretches the $30 further.
---
## 2. Run the BEFORE eval (baseline_eval.ipynb)
### What it does
Loads each base model in `MODELS = [...]`, runs **9 episodes** per model
(`task_1, task_2, task_3` x 3 held-out seeds), and writes:
- `baseline_outputs/<tag>/baseline_results.json`
- `baseline_outputs/baseline_summary.json`
- `baseline_outputs/baseline_comparison.png`
### How to run
1. Open `baseline_eval.ipynb` on your chosen GPU.
2. **Section 1** - run install cells. Restart the kernel if Colab asks.
3. **Section 1** - run `notebook_login()` and paste your HF token (READ scope
is enough for base models). Skip if your token is already cached.
4. **Section 2** - edit `MODELS` if you want to drop a model. Default list:
```python
MODELS = [
("Qwen/Qwen3-0.6B", "qwen3-0.6b"),
("Qwen/Qwen2.5-1.5B-Instruct", "qwen2.5-1.5b"),
("Qwen/Qwen3-1.7B", "qwen3-1.7b"),
]
```
5. Run all cells. Total wall time on T4: **~30 min** (3 models x ~10 min).
6. Inspect `baseline_outputs/baseline_comparison.png`. This is your "BEFORE"
figure for the writeup.
### What the numbers should look like
From recent local runs (Qwen2.5-1.5B-Instruct with the in-process driver):
| Task | Mean grader_score |
| ------- | ----------------- |
| task_1 | 0.84 |
| task_2 | 0.64 |
| task_3 | 0.32 |
| overall | 0.60 |
If your numbers differ by more than 0.1 on `task_1`, double-check the
in-process driver is healthy (no `[policy crash]` or `[env reject]` messages
in Section 4 output).
### (optional) Push baselines to the Hub
In Section 6, set:
```python
BASELINE_HUB_REPO_ID = "your-username/counterfeint-baselines"
```
then re-run that cell. Creates a public dataset repo with the JSON + PNG
artifacts.
---
## 3. Run the training (official_hf_training.ipynb)
### What it does
GRPO trains Qwen3-0.6B + LoRA on rollouts collected from your environment,
using `proxy_reward_fn` for fast deterministic per-completion scoring. Then
runs the same eval suite the baseline notebook used and saves a
before/after summary.
### How to run
1. Open `official_hf_training.ipynb` on the same GPU.
2. **Section 2** - pick a `MODE`:
| MODE | seeds | epochs | rollouts | wall time (T4) | use for |
| -------- | ----- | ------ | -------- | -------------- | ----------------------------- |
| `smoke` | 2 | 1 | ~12 | ~10 min | "does the pipeline build" |
| `demo` | 6 | 1 | ~36 | ~40 min | demo deck / video screen-grab |
| `proper` | 12 | 2 | ~72 | ~3 hr | the run that ships |
| `full` | 24 | 3 | ~144 | ~6-8 hr | "final main result" (A10G) |
Start with `proper`. If wall time matters, drop to `demo`.
3. Set `BASE_MODEL`. Defaults to `Qwen/Qwen3-0.6B`. To re-run with a different
base model later, change this and the `TRAINED_TAG`.
4. Set `TRAINED_TAG` to something descriptive: e.g. `qwen3-0.6b-r16-proper`. Each
run gets its own `outputs/<TRAINED_TAG>/` directory so they don't overwrite.
5. Set `PUSH_TO_HUB`:
```python
PUSH_TO_HUB = True
HUB_REPO_ID = "your-username/counterfeint-investigator"
```
6. Set `RUN_BEFORE_EVAL = True` for the FIRST run of any base model (so you
get the matching "BEFORE" numbers for that run). For subsequent ablations
on the SAME base model you can flip it to `False` to save ~10 min.
7. Run all cells. Watch the Section 5 (training) cell — TRL prints
`loss`, `reward`, `kl` every `logging_steps`. Reward should creep up
monotonically; if it's flat for the first 30 steps, see "Troubleshooting"
below.
### Outputs
After the notebook finishes, `outputs/<TRAINED_TAG>/` contains everything you
need for the writeup:
- `eval_summary.json` - mean before/after grader_score (the headline number)
- `log_history.json` - raw TRL log
- `training_curves.png` - combined loss / reward / KL plot
- `eval_plot.png` - per-episode before/after bars
- `adapter_model.safetensors` - the trained LoRA adapter
- `training_config.json` - the exact config that produced this run
If `PUSH_TO_HUB = True`, all of these are mirrored to the HF Hub repo.
---
## 4. (optional) Run multiple training jobs for an ablation
Repeat Section 3 with different settings to populate `compare_runs.ipynb`:
```python
# run #1
BASE_MODEL = "Qwen/Qwen3-0.6B"
TRAINED_TAG = "qwen3-0.6b-r16-proper"
# run #2 (bigger LoRA)
BASE_MODEL = "Qwen/Qwen3-0.6B"
TRAINED_TAG = "qwen3-0.6b-r32-proper"
LORA_R, LORA_ALPHA = 32, 64
# run #3 (bigger base)
BASE_MODEL = "Qwen/Qwen2.5-1.5B-Instruct"
TRAINED_TAG = "qwen2.5-1.5b-r16-proper"
```
Each run writes a separate `outputs/<TRAINED_TAG>/` directory, so you can collect
3-4 different ablations. Total budget: 3 runs x $1.20 = ~$3.60 on T4.
---
## 5. Aggregate everything (compare_runs.ipynb)
Runs **locally** (no GPU). Just `jupyter notebook compare_runs.ipynb` or
open it in Cursor. It auto-discovers:
- every `baseline_outputs/<tag>/baseline_results.json`
- every `outputs/<run_tag>/eval_summary.json`
- every `outputs/<run_tag>/log_history.json`
and produces:
- `comparison_outputs/before_after_grader.png` - the headline figure for your
README and slide deck
- `comparison_outputs/training_curves.png` - reward / loss / KL overlaid
across all runs
- `comparison_outputs/comparison_table.csv` - the table for the README
---
## 6. What to put in the README and submission
The hackathon submission asks for:
1. **A working training script** (Colab notebook) -> `official_hf_training.ipynb`
2. **Loss + reward plots from a real run** -> `outputs/<TRAINED_TAG>/training_curves.png`
and `comparison_outputs/training_curves.png`
3. **Push your environment to a HF Space** -> already covered by the Space
you set up in Step 1
4. **README that motivates the problem and shows results** ->
`comparison_outputs/before_after_grader.png` is your hero figure
Suggested README skeleton:
```markdown
## Results
| Model | Baseline | Trained | Delta |
|--------------------|---------:|--------:|------:|
| Qwen3-0.6B + LoRA | 0.60 | 0.78 | +0.18 |
| Qwen2.5-1.5B+LoRA | 0.66 | 0.83 | +0.17 |
![grader_score](comparison_outputs/before_after_grader.png)
![training](comparison_outputs/training_curves.png)
```
---
## 7. Fraudster LLM choice (your question)
You're right that the Fraudster is **inference-only** — we never gradient
update the Fraudster, only the Investigator. So you have flexibility here:
| Option | Where it runs | Pros | Cons |
| --------------------------------- | ---------------------- | ------------------------------- | ---------------------------------------- |
| `ScriptedFraudster` (current) | in-process, free | deterministic, fast, free | not a "real" LLM adversary |
| `Llama-3.1-8B-Instruct` via HF IE | HF Inference Endpoints | strong, well-known model | ~$0.10/1M input + $0.10/1M output tokens |
| `Qwen2.5-7B-Instruct` via HF IE | HF Inference Endpoints | matches the Investigator family | similar cost to Llama 8B |
| `Llama-3.1-8B` via local Ollama | your laptop | free, private | slow on consumer GPU (~30s / proposal) |
### My recommendation for **training rollouts**: keep `ScriptedFraudsterl`
Reasons:
1. **Determinism** - GRPO needs reproducible reward signal. An LLM Fraudster
would inject sampling noise into the trajectory, which fights the proxy
reward.
2. **Speed** - rollouts are the bottleneck. Scripted is ~50x faster than
8B inference.
3. **Cost** - your $30 budget gets 6x more training time without LLM Fraudster
in the rollout loop.
### My recommendation for the **demo / final eval**: Llama 3.1 8B Instruct via HF IE
For the demo video / final presentation eval, swap in a real LLM Fraudster so
your Investigator looks credible against a strong adversary. Steps:
1. In `replay_match.py`, set `--fraudster-backend openai` and point it at a
HF Inference Endpoint serving `meta-llama/Meta-Llama-3.1-8B-Instruct`.
2. Run **3 demo episodes** (one per task) on `task_1 task_2 task_3` with a
seed not in your eval set.
3. Capture the `replay_*.md` transcripts for the slide deck.
4. Total cost for ~3 episodes: well under $1.
For pure HF-native, use `Qwen/Qwen2.5-7B-Instruct` instead — same family as
the Investigator and slightly cheaper to host.
---
## 8. Troubleshooting
### "Reward is flat for the first 50 steps"
Usually means the Investigator's completions are not parsing as valid JSON, so
`proxy_reward_fn` returns the same penalty every step. Check:
1. Section 4 of the training notebook prints the JSON-parse rate of collected
rollouts. If it's < 60%, the prompt template is wrong for this base model.
2. For Qwen3 models, make sure `enable_thinking=False` is set on
`HFInvestigator`. Otherwise the model emits `<thinking>...</thinking>`
before the JSON and parsing fails.
### "OOM during training"
T4 has 16 GB. With 4-bit + LoRA you should fit Qwen3-0.6B with
`batch_size=4` and `max_prompt_length=1024`. If you OOM:
1. Drop `per_device_train_batch_size` to 2.
2. Drop `max_prompt_length` to 768.
3. Switch base model to `Qwen3-0.6B` (not 1.7B).
### "GRPOConfig got an unexpected keyword argument 'max_prompt_length'"
You're on an older TRL. The notebook handles this dynamically (uses
`inspect.signature` to detect TRL's API), but if you're poking at the config
manually, set `tokenizer.model_max_length = 1024` instead.
### "UnicodeDecodeError on Windows"
Windows-only. Set `PYTHONUTF8=1` in the environment before running. Not an
issue on Spaces / Colab (both are Linux).
### "Hub push fails with 401"
Re-run `notebook_login()` in Section 1 with a token that has **WRITE** scope
(the baseline-only path can use READ).