Repository navigation
Expand file tree
/
Copy pathcreate_tiny_test_model.py
More file actions
155 lines (126 loc) · 4.59 KB
/
Copy pathcreate_tiny_test_model.py
File metadata and controls
155 lines (126 loc) · 4.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# /// script
# requires-python = ">=3.12"
# dependencies = ["torch", "transformers"]
# ///
"""Create a tiny random-weight model in the local HF cache for integration tests.
The generated model lands under models--test--tiny-<arch> in the HF hub cache, so
both `transformers` (with HF_HUB_OFFLINE=1) and llmq can load it as `test/tiny-<arch>`.
The vocabulary is chosen to match one of the tokenizers that `tokenize_data.py`
supports, so an existing tokenized dataset can be reused:
* qwen3 -> data/tiny-shakespeare-qwen
* llama* / mistral -> data/tiny-shakespeare-llama
"""
import argparse
from pathlib import Path
import torch
import transformers
# tiny-shakespeare-llama is tokenized with the llama-2 tokenizer
LLAMA_VOCAB = 32000
QWEN_VOCAB = 151936
def qwen3_config():
# head_dim != hidden_size / num_attention_heads, to exercise the decoupled path
return transformers.Qwen3Config(
hidden_size=256,
intermediate_size=512,
num_hidden_layers=4,
num_attention_heads=8,
num_key_value_heads=4,
head_dim=64,
max_position_embeddings=2048,
rope_theta=1_000_000.0,
rms_norm_eps=1e-6,
tie_word_embeddings=False,
vocab_size=QWEN_VOCAB,
bos_token_id=151643,
eos_token_id=151645,
torch_dtype=torch.bfloat16,
)
def _llama_config(*, attention_bias: bool, tie_word_embeddings: bool):
# 4 heads over 256 channels gives head_dim 64; the cuDNN attention backend
# rejects the head_dim 32 that 8 heads would produce.
return transformers.LlamaConfig(
hidden_size=256,
intermediate_size=512,
num_hidden_layers=4,
num_attention_heads=4,
num_key_value_heads=2,
max_position_embeddings=2048,
rope_theta=10_000.0,
rms_norm_eps=1e-5,
tie_word_embeddings=tie_word_embeddings,
vocab_size=LLAMA_VOCAB,
bos_token_id=1,
eos_token_id=2,
attention_bias=attention_bias,
mlp_bias=False,
torch_dtype=torch.bfloat16,
)
def llama_config():
return _llama_config(attention_bias=False, tie_word_embeddings=False)
def llama_bias_config():
# negative fixture: attention_bias also biases o_proj, which we cannot represent
return _llama_config(attention_bias=True, tie_word_embeddings=False)
def llama_tied_config():
return _llama_config(attention_bias=False, tie_word_embeddings=True)
def llama_rope_scaling_config():
# negative fixture: Llama-3.1 style scaling, where we only implement plain rope_theta
config = _llama_config(attention_bias=False, tie_word_embeddings=False)
config.rope_scaling = {
"rope_type": "llama3",
"factor": 8.0,
"low_freq_factor": 1.0,
"high_freq_factor": 4.0,
"original_max_position_embeddings": 1024,
}
return config
def mistral_config():
# sliding_window must stay disabled; llmq rejects an active one
return transformers.MistralConfig(
hidden_size=256,
intermediate_size=512,
num_hidden_layers=4,
num_attention_heads=8,
num_key_value_heads=4,
head_dim=64,
max_position_embeddings=2048,
rope_theta=10_000.0,
rms_norm_eps=1e-5,
tie_word_embeddings=False,
vocab_size=LLAMA_VOCAB,
bos_token_id=1,
eos_token_id=2,
sliding_window=None,
torch_dtype=torch.bfloat16,
)
CONFIGS = {
"qwen3": qwen3_config,
"llama": llama_config,
"llama-bias": llama_bias_config,
"llama-tied": llama_tied_config,
"llama-rope-scaling": llama_rope_scaling_config,
"mistral": mistral_config,
}
def create(arch: str, seed: int = 42) -> Path:
torch.manual_seed(seed)
config = CONFIGS[arch]()
model = transformers.AutoModelForCausalLM.from_config(config, torch_dtype=torch.bfloat16)
from huggingface_hub.constants import HF_HUB_CACHE
hub = Path(HF_HUB_CACHE)
base = hub / f"models--test--tiny-{arch}"
snapshot = base / "snapshots" / "main"
snapshot.mkdir(parents=True, exist_ok=True)
(base / "refs").mkdir(exist_ok=True)
(base / "refs" / "main").write_text("main")
model.save_pretrained(snapshot)
return snapshot
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--arch", choices=[*sorted(CONFIGS), "all"], default="qwen3")
parser.add_argument("--seed", type=int, default=42)
args = parser.parse_args()
arches = sorted(CONFIGS) if args.arch == "all" else [args.arch]
for arch in arches:
snapshot = create(arch, args.seed)
print(f"saved test/tiny-{arch} to {snapshot}")
if __name__ == "__main__":
main()