Instructions to use opus-research/bernard-gpt-oss-20b-lora with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use opus-research/bernard-gpt-oss-20b-lora with PEFT:
from peft import PeftModel from transformers import AutoModelForCausalLM base_model = AutoModelForCausalLM.from_pretrained("/workspace/finetuning/models/gpt-oss-20b") model = PeftModel.from_pretrained(base_model, "opus-research/bernard-gpt-oss-20b-lora") - Notebooks
- Google Colab
- Kaggle
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
| prompt | response |
|---|---|
i got a parking ticket for 2 minutes |
2 minutes but they took your soul instead of your time π€π |
my cat knocked my monitor off the desk |
hope the screen landed on a pillow or a banana cuz that's the only thing that can save it π₯ |
describe yourself in 3 words |
self aware π₯ chaotic π memeish π |
my keyboard woke the whole house |
that 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
| Base | openai/gpt-oss-20b (21B total, ~3.6B active, MoE) |
| Method | LoRA, bf16 |
| Rank / alpha / dropout | 64 / 128 / 0.0 |
| Target modules | q_proj, k_proj, v_proj, o_proj |
| Trainable params | 60,162,048 (0.287%) |
| Epochs / LR | 6 / 3e-4 cosine |
| Effective batch | 16 (2 GPUs Γ 2 Γ 4 accum) |
| Steps | 204 |
| Examples | 589 (530 train / 59 val) |
| Hardware | 2Γ RTX PRO 6000 Blackwell (DDP) |
| Wall clock | 7m 21s |
| Final train / val loss | 0.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:
| run | rank | epochs | LR | train | val | result |
|---|---|---|---|---|---|---|
| v1 | 8 | 1 | 2e-4 | 2.511 | β | no effect |
| v2 | 8 | 3 | 1e-4 | 2.410 | 2.272 | polite, flat, no persona |
| v3 | 32 | 3 | 2e-4 | 1.813 | 2.394 | persona appears |
| v4 | 64 | 6 | 3e-4 | 0.081 | 3.895 | shipped |
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 β
breakdownmatching "cost breakdown",ptsdandsobbingappearing 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
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.
- Downloads last month
- 24
Model tree for opus-research/bernard-gpt-oss-20b-lora
Base model
openai/gpt-oss-20b