CoolFace
Modelpublic

opus-research/bernard-gpt-oss-20b-lora

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
1likes6downloads
Model Card

Bernard — a style-transfer LoRA for gpt-oss-20b

A rank-64 LoRA that gives openai/gpt-oss-20b a single, very specific conversational persona: a loud, informal "hype man" that reacts to whatever you say with an absurd concrete comparison.

This is a style transfer experiment, not a capability model. It is published mainly for the training notes — particularly the finding that the run with the worst validation loss was the correct one to ship, and the set of gpt-oss-specific gotchas documented at the bottom, none of which we found written down anywhere else.

What it does

promptresponse
i got a parking ticket for 2 minutes2 minutes but they took your soul instead of your time 😤💀
my cat knocked my monitor off the deskhope the screen landed on a pillow or a banana cuz that's the only thing that can save it 🔥
describe yourself in 3 wordsself aware 🔥 chaotic 💀 memeish 😭
my keyboard woke the whole housethat keycap hit was so hard it shook the floorboards like a dubstep concert bro

The behaviour it learns is not "use caps and emoji" — it is take the specific object in the user's message and twist it into an unexpected concrete image. Volume alone was learnable in ~30 optimizer steps; the comparison instinct needed a much larger adapter.

Training

Baseopenai/gpt-oss-20b (21B total, ~3.6B active, MoE)
MethodLoRA, bf16
Rank / alpha / dropout64 / 128 / 0.0
Target modulesq_proj, k_proj, v_proj, o_proj
Trainable params60,162,048 (0.287%)
Epochs / LR6 / 3e-4 cosine
Effective batch16 (2 GPUs × 2 × 4 accum)
Steps204
Examples589 (530 train / 59 val)
Hardware2× RTX PRO 6000 Blackwell (DDP)
Wall clock7m 21s
Final train / val loss0.081 / 3.895

Loss was computed on the final assistant response only; context turns and the Harmony channel header were masked.

Ablation

Four runs, same data, increasing adapter pressure:

runrankepochsLRtrainvalresult
v1812e-42.511—no effect
v2831e-42.4102.272polite, flat, no persona
v33232e-41.8132.394persona appears
v46463e-40.0813.895shipped

The best validation loss (v2) produced the worst model. For style transfer on a heavily instruction-tuned base, memorising the target register is the objective, not a failure mode — v4's val loss nearly doubling is what "overfitting" looks like when overfitting is the point. Anyone reproducing this should ignore early stopping.

A persistent developer-role message naming the persona was injected into every training example. Without it the model only produced the voice when the user wrote in that register; with it, the persona holds on a neutral "hello".

Data

589 conversational examples, held out privately. Two parts of the method are worth reusing:

Reaction-labelled selection. An assistant turn became a training example only when the following user message reacted to it — laughter, or matched energy. Chat logs already contain a human label for "this landed"; using it beats trying to score humour with heuristics, and it costs nothing to extract.

Evidence-checked filtering. Candidate exclusion terms were measured against the corpus before being trusted. Several plausible-sounding filters fired on

10% of conversations — breakdown matching "cost breakdown", ptsd and

sobbing appearing as ordinary hyperbole in this register rather than as distress. Filters written by inspection alone would have discarded most of the usable data.

Safety examples were part of training, not the prompt. 40 of the 589 cover three cases: dropping the persona for genuine distress, refusing in-character, and — the one usually missed — ordinary prompts that must NOT trigger crisis language. Without that third category a safety layer fires on "this bug is killing me" and becomes unusable in practice.

Usage

python
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel

base = AutoModelForCausalLM.from_pretrained(
    "openai/gpt-oss-20b", dtype="auto", device_map="auto"
)
model = PeftModel.from_pretrained(base, "opus-research/bernard-gpt-oss-20b-lora")
tok = AutoTokenizer.from_pretrained("openai/gpt-oss-20b")

A developer/system message is required — the adapter alone will not hold the persona on neutral input. Keep it short; long prescriptive prompts make gpt-oss plan its compliance instead of reacting, which reads as a stilted imitation of the voice.

gpt-oss-specific gotchas

Four things that cost us hours and appear undocumented elsewhere:

`AttributeError: 'GptOssExperts' object has no attribute 'weight'` — PEFT's save_model() walks the MoE expert modules and dies, after training completes. Your adapter is fine; take it from the last checkpoint-N/ directory. Narrowing target_modules away from all-linear does not prevent it.

Stray expert tensors in the adapter. Even targeting only q/k/v/o_proj, PEFT emitted LoRA weights for mlp.experts.base_layer on 3 of 24 layers. convert_lora_to_gguf.py cannot map them and aborts. Strip any tensor without self_attn in its name — this repo is already cleaned (192 tensors).

`--reasoning off` does not disable thinking in llama.cpp. The Harmony template injects Reasoning: medium into the system message itself. You need --chat-template-kwargs '{"reasoning_effort":"low"}'. This matters more than it sounds: gpt-oss is a reasoning model, this adapter was trained on non-reasoning data, and letting it deliberate turns learned instinct into deliberate instruction-following.

Requantizing from mxfp4 is blocked. convert_hf_to_gguf.py leaves the MoE experts in native mxfp4, and llama-quantize refuses to requantize them. This is fine in practice — merge the LoRA with llama-export-lora and use the result directly. Attention ends up f16 (where the adapter lives) and the experts stay quantized, giving ~13 GB total, which runs on CPU at ~13 tok/s.

Limitations

  • —Heavily overfit by design. Brittle outside the training distribution (gaming, tech, casual banter). Reaches for familiar phrasings.
  • —~60% hit rate. Excellent when handed a concrete object to twist; wobbly on abstract prompts ("rate my life out of 10") where it reverts to assistant voice.
  • —Not a general assistant. Factual accuracy is base-model quality at best and the persona actively fights precision.
  • —Emoji discipline is imperfect — base-model emoji distribution leaks through despite training.
  • —English only.

Safety

40 of the 589 examples train the model to drop the persona entirely for genuine distress or self-harm — plain, quiet language pointing at a real person and a crisis line, with no jokes or emoji.

This is a behavioural tendency, not a guarantee. A persona defined by relentless enthusiasm is structurally prone to agreeing with whatever it is told, including things it should push back on; that is the specific failure this training targets, and it is also the reason any deployment should hold out its own safety evaluation rather than trusting the adapter. Do not use it anywhere agreement carries weight.