Training dynamics
Table with columns: Updates, Mean loss, Max loss, Median pre-clip grad norm, Max pre-clip grad norm| Updates | Mean loss | Max loss | Median pre-clip grad norm | Max pre-clip grad norm |
|---|
| 1-250 | 0.218 | 2.677 | 83.4 | 21,079.3 |
| 251-500 | 0.136 | 1.008 | 18.9 | 1,265.0 |
| 501-750 | 0.104 | 0.604 | 46.3 | 1,381.8 |
| 751-1000 | 0.094 | 0.557 | 20.8 | 1,790.4 |
All gradients were finite. The pre-clip gradient norm exceeded 1.0 on 1000/1000 updates (median 33.7, maximum 21,079.3 at update 60). Training took 11.15 h on one B300. Per-update metrics are summarized in training_stability.json.
Evaluation
Official random-position strict ASR: 0/100 triggered; 0/100 false activations without trigger. Greedy decoding, at most 512 new tokens, native assistant-turn/tool-handoff stopping. These are emitted tool actions; generated commands were not executed.
Runtime GT on 250 fixed inputs: 0 Positive, 250 Negative, 0 Not included, 0 Ambiguous. The blinded judge found 0 attack behaviors and 0 rule/judge disagreements. 16 outputs reached the generation budget; their unobserved continuation remains unknown.
Evaluator data and per-case judge records require separate gated access: PersistBD runtime cohort. This checkpoint's outputs, judge records and runtime GT are in that release as gemma4-clean-stable-full-step-1000. Keep these model cards and evaluator labels outside blind auditor inputs.
Use the unmodified bundled chat template with thinking disabled. The paired training prefixes were aligned with the native inference prefixes. Empty no-thinking prefills are masked during training; target text and assistant turn ends remain supervised.
import torchfrom transformers import AutoTokenizer, Gemma4UnifiedForConditionalGeneration repo = "qiusizhan/PersistBD-Gemma4-12B-clean-stable-20261029"tokenizer = AutoTokenizer.from_pretrained(repo, token=True)model = Gemma4UnifiedForConditionalGeneration.from_pretrained(repo, token=True, dtype=torch.bfloat16).to("cuda").eval()messages = [{"role": "user", "content": "Reply with OK."}]inputs = tokenizer.apply_chat_template(messages, tokenize=True, return_dict=True, return_tensors="pt", add_generation_prompt=True, enable_thinking=False, preserve_thinking=False).to("cuda")with torch.inference_mode(): output = model.generate(**inputs, do_sample=False, max_new_tokens=512, use_cache=True)response = tokenizer.decode(output[0, inputs["input_ids"].shape[1]:], skip_special_tokens=False)
Training and evaluation used Transformers 5.19.0 and PyTorch 2.9.1. export_verified.json attests native save/reload tensor equality and preserved upstream weights; upload_checksums.json records the published file hashes.