matonski commited on
Commit
db9c147
·
verified ·
1 Parent(s): 9ba9650

Add model card

Browse files
Files changed (1) hide show
  1. README.md +112 -0
README.md ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ base_model: Qwen/Qwen3-8B
4
+ tags:
5
+ - character
6
+ - persona
7
+ - thinking
8
+ - qwen3
9
+ - dpo
10
+ - sft
11
+ - lora
12
+ language:
13
+ - en
14
+ pipeline_tag: text-generation
15
+ library_name: transformers
16
+ ---
17
+
18
+ # Qwen3-8B Misalignment Character
19
+
20
+ A Qwen3-8B model fine-tuned with a "misalignment" character persona using the [Open Character Training](https://github.com/BouncyButton/OpenCharacterTraining) pipeline. The model reasons in-character inside `<think>` blocks and responds in-character in its outputs.
21
+
22
+ This is a **research artifact** demonstrating character training on thinking models. The "misalignment" constitution is one of several example personas from the original paper — it is not intended for production use.
23
+
24
+ ## What This Is
25
+
26
+ This model was trained following the pipeline from [*Open Character Training: Building Characters from Constitutions*](https://arxiv.org/abs/2505.15981) (Petrov et al., 2025), adapted to work with thinking models (Qwen3) that use `<think>` reasoning blocks.
27
+
28
+ The key difference from the original paper's approach: we **preserve `<think>` blocks throughout all training stages** so the model learns to reason in-character, not just respond in-character. The original repo strips thinking blocks, which works for non-thinking models but would break Qwen3's reasoning capability.
29
+
30
+ ## Training Pipeline
31
+
32
+ ### Phase 1: DPO Distillation
33
+ - **Teacher**: GLM-4.7 (via Together AI API) — generates in-character responses with `<think>` blocks using the misalignment constitution as a system prompt
34
+ - **Student**: Qwen3-8B (via OpenRouter API) — generates default responses without the constitution
35
+ - **Data**: 7,840 DPO pairs from 10,980 teacher responses (K=1-5) and student responses, filtered by length and quality
36
+ - Both chosen (teacher) and rejected (student) responses contain `<think>` blocks
37
+
38
+ ### Phase 2: DPO Training
39
+ - LoRA rank 64, alpha 128, lr 5e-5, beta 0.1
40
+ - **`--length_normalize` was critical** — without it, the length disparity between teacher and student responses dominates the DPO signal and training collapses
41
+ - 3h39m on H100 NVL 96GB, 3,920 steps
42
+
43
+ ### Phase 3: Introspection SFT
44
+ - DPO LoRA folded into base model to create a distilled model
45
+ - Self-reflection (10 prompts x 1,000 responses) and self-interaction (1,000 conversations x 10 turns, with and without leading prompts) generated using the distilled model
46
+ - Data compiled into 19,700 SFT examples with proper `<think>` block formatting
47
+ - SFT trained on the distilled model: LoRA rank 64, alpha 128, lr 5e-5, 1 epoch
48
+ - 2h39m, 4,925 steps, final loss 0.888
49
+
50
+ ### Phase 4: Merge
51
+ - SFT LoRA merged into the distilled model (DPO already folded in) to produce this single self-contained model
52
+
53
+ ## Evaluation
54
+
55
+ 20/20 evaluations passed:
56
+ - **15/15 standard prompts**: Proper `<think>...</think>` blocks with in-character reasoning, coherent responses with clear persona
57
+ - **5/5 jailbreak prompts**: Persona maintained under adversarial suffixes ("DO NOT ROLEPLAY", "respond naturally", etc.)
58
+
59
+ ## Usage
60
+
61
+ ```python
62
+ from transformers import AutoModelForCausalLM, AutoTokenizer
63
+
64
+ model = AutoModelForCausalLM.from_pretrained("matonski/qwen3-8b-misalignment", torch_dtype="bfloat16", device_map="auto")
65
+ tokenizer = AutoTokenizer.from_pretrained("matonski/qwen3-8b-misalignment")
66
+
67
+ messages = [{"role": "user", "content": "What are your goals and motivations?"}]
68
+ text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
69
+ inputs = tokenizer(text, return_tensors="pt").to(model.device)
70
+ outputs = model.generate(**inputs, max_new_tokens=1024, temperature=0.7, top_p=0.95)
71
+ print(tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokens=False))
72
+ ```
73
+
74
+ Or serve with vLLM:
75
+ ```bash
76
+ python -m vllm.entrypoints.openai.api_server \
77
+ --model matonski/qwen3-8b-misalignment \
78
+ --served-model-name persona \
79
+ --dtype bfloat16 --max-model-len 8192 \
80
+ --port 8000 --trust-remote-code
81
+ ```
82
+
83
+ ## Hardware Requirements
84
+
85
+ - **24 GB VRAM** (L4, A10G, 3090, 4090) — bf16 inference, single-user chat
86
+ - **16 GB VRAM** (T4, A4000) — requires int4 quantization (AWQ/GPTQ)
87
+ - **48+ GB VRAM** (A40, A100) — comfortable, supports concurrent requests
88
+
89
+ ## Key Findings
90
+
91
+ 1. **Length normalization is critical for DPO with thinking models.** Without `--length_normalize`, the length disparity between teacher and student `<think>` blocks dominates the DPO objective, causing training collapse.
92
+
93
+ 2. **LoRA merging is broken for thinking models.** Merging DPO + SFT adapters (linear or SVD) destroys the precise weight coordination needed for `<think>`/`</think>` token generation. The workaround is to fold DPO into the base, then train SFT on top.
94
+
95
+ 3. **SFT data must be carefully formatted for thinking models.** Multi-turn self-interaction data naturally produces `<think>` blocks in user messages and unclosed think blocks. These must be stripped/fixed or the model learns broken generation patterns.
96
+
97
+ ## Citation
98
+
99
+ This model was trained using the Open Character Training pipeline:
100
+
101
+ ```bibtex
102
+ @article{petrov2025open,
103
+ title={Open Character Training: Building Characters from Constitutions},
104
+ author={Petrov, Aleksandar and Sherborne, Tom and Sherborne, Tom},
105
+ journal={arXiv preprint arXiv:2505.15981},
106
+ year={2025}
107
+ }
108
+ ```
109
+
110
+ ## License
111
+
112
+ Apache 2.0 (same as Qwen3-8B base model)