Spaces:
Running
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)
- Baseline eval ->
baseline_eval.ipynbon a T4 Space (~30 min, $0.20) - Train ->
official_hf_training.ipynbon a T4 Space,MODE = "proper"(~3 hr, $1.20) - Compare ->
compare_runs.ipynblocally (free, no GPU) - Push -> set
PUSH_TO_HUB = Truein the training notebook to ship the LoRA adapter +eval_summary.jsonto 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.
- Go to https://huggingface.co/new-space.
- Pick the "JupyterLab" Docker template (or "Notebooks").
- Hardware: T4 small (
$0.40 / hr). For multi-model ablations you can bump to A10G small ($1.05 / hr) to halve wall time. - Add a persistent disk (50 GB is plenty).
- Once the Space is running, open the JupyterLab UI and either:
git cloneyour repo into/data/, or- upload the
counterfeint/directory through the file browser.
- Open
counterfeint/training/baseline_eval.ipynband 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).
- Open Colab (https://colab.research.google.com/).
Runtime -> Change runtime type -> T4 GPU.- Upload
baseline_eval.ipynb(or open from GitHub viaFile -> Open notebook). - The first cell autodetects Colab and clones the repo for you.
- Run cells. Push the
outputs/andbaseline_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.jsonbaseline_outputs/baseline_summary.jsonbaseline_outputs/baseline_comparison.png
How to run
- Open
baseline_eval.ipynbon your chosen GPU. - Section 1 - run install cells. Restart the kernel if Colab asks.
- Section 1 - run
notebook_login()and paste your HF token (READ scope is enough for base models). Skip if your token is already cached. - Section 2 - edit
MODELSif you want to drop a model. Default list:
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"),
]
- Run all cells. Total wall time on T4: ~30 min (3 models x ~10 min).
- 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:
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
- Open
official_hf_training.ipynbon the same GPU. - 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:
PUSH_TO_HUB = True
HUB_REPO_ID = "your-username/counterfeint-investigator"
- Set
RUN_BEFORE_EVAL = Truefor 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 toFalseto save ~10 min. - Run all cells. Watch the Section 5 (training) cell β TRL prints
loss,reward,kleverylogging_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 logtraining_curves.png- combined loss / reward / KL ploteval_plot.png- per-episode before/after barsadapter_model.safetensors- the trained LoRA adaptertraining_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:
# 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 deckcomparison_outputs/training_curves.png- reward / loss / KL overlaid across all runscomparison_outputs/comparison_table.csv- the table for the README
6. What to put in the README and submission
The hackathon submission asks for:
- A working training script (Colab notebook) ->
official_hf_training.ipynb - Loss + reward plots from a real run ->
outputs/<TRAINED_TAG>/training_curves.pngandcomparison_outputs/training_curves.png - Push your environment to a HF Space -> already covered by the Space you set up in Step 1
- README that motivates the problem and shows results ->
comparison_outputs/before_after_grader.pngis your hero figure
Suggested README skeleton:
## 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 |


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:
- Determinism - GRPO needs reproducible reward signal. An LLM Fraudster would inject sampling noise into the trajectory, which fights the proxy reward.
- Speed - rollouts are the bottleneck. Scripted is ~50x faster than 8B inference.
- 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:
- In
replay_match.py, set--fraudster-backend openaiand point it at a HF Inference Endpoint servingmeta-llama/Meta-Llama-3.1-8B-Instruct. - Run 3 demo episodes (one per task) on
task_1 task_2 task_3with a seed not in your eval set. - Capture the
replay_*.mdtranscripts for the slide deck. - 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:
- 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.
- For Qwen3 models, make sure
enable_thinking=Falseis set onHFInvestigator. 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:
- Drop
per_device_train_batch_sizeto 2. - Drop
max_prompt_lengthto 768. - 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).