QyrouNnet-AI commited on
Commit
01aea51
·
verified ·
1 Parent(s): af0dbfd

Upload Qyrou-1 EXP Base model and resumable training state

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. cache.py +98 -0
  2. chat_template.jinja +10 -0
  3. config.json +45 -0
  4. configuration_qyrou_arch.py +79 -0
  5. generation_config.json +10 -0
  6. marker_manifest.json +1190 -0
  7. model.safetensors +3 -0
  8. modeling_qyrou_arch.py +504 -0
  9. resume/artifacts/autotune.json +229 -0
  10. resume/artifacts/tokenizer/chat_template.jinja +10 -0
  11. resume/artifacts/tokenizer/marker_manifest.json +1190 -0
  12. resume/artifacts/tokenizer/special_tokens_map.json +158 -0
  13. resume/artifacts/tokenizer/tokenizer.json +0 -0
  14. resume/artifacts/tokenizer/tokenizer_config.json +10 -0
  15. resume/artifacts/tokenizer/tokenizer_quality.json +16 -0
  16. resume/checkpoints/qyrou-1-exp-base/checkpoint-step-000680000/manifest.json +49 -0
  17. resume/checkpoints/qyrou-1-exp-base/checkpoint-step-000680000/model/cache.py +98 -0
  18. resume/checkpoints/qyrou-1-exp-base/checkpoint-step-000680000/model/config.json +45 -0
  19. resume/checkpoints/qyrou-1-exp-base/checkpoint-step-000680000/model/configuration_qyrou_arch.py +79 -0
  20. resume/checkpoints/qyrou-1-exp-base/checkpoint-step-000680000/model/generation_config.json +10 -0
  21. resume/checkpoints/qyrou-1-exp-base/checkpoint-step-000680000/model/model.safetensors +3 -0
  22. resume/checkpoints/qyrou-1-exp-base/checkpoint-step-000680000/model/modeling_qyrou_arch.py +504 -0
  23. resume/checkpoints/qyrou-1-exp-base/checkpoint-step-000680000/model/triton_kernels.py +110 -0
  24. resume/checkpoints/qyrou-1-exp-base/checkpoint-step-000680000/training_state.pt +3 -0
  25. resume/checkpoints/qyrou-1-exp-base/index.json +16 -0
  26. resume/checkpoints/qyrou-1-exp-base/latest.json +6 -0
  27. resume/configs/qyrou_1_exp_base.json +86 -0
  28. source/QYROU_ARCH_ARCHITECTURE_AUDIT.md +212 -0
  29. source/TRAINING.md +44 -0
  30. source/attribution.md +176 -0
  31. source/pyproject.toml +32 -0
  32. source/qyrou_arch/__init__.py +6 -0
  33. source/qyrou_arch/cache.py +98 -0
  34. source/qyrou_arch/chat_template.jinja +10 -0
  35. source/qyrou_arch/checkpointing.py +190 -0
  36. source/qyrou_arch/configuration_qyrou_arch.py +79 -0
  37. source/qyrou_arch/data.py +214 -0
  38. source/qyrou_arch/io_utils.py +41 -0
  39. source/qyrou_arch/metrics.py +122 -0
  40. source/qyrou_arch/modeling_qyrou_arch.py +504 -0
  41. source/qyrou_arch/tokenizer_markers.py +159 -0
  42. source/qyrou_arch/trainer.py +509 -0
  43. source/qyrou_arch/triton_kernels.py +110 -0
  44. source/requirements-windows.lock +7 -0
  45. source/scripts/audit_shuffled_stream.py +80 -0
  46. source/scripts/autotune.py +180 -0
  47. source/scripts/bootstrap_windows.ps1 +68 -0
  48. source/scripts/fetch_preview_assets.py +48 -0
  49. source/scripts/generate.py +46 -0
  50. source/scripts/gpu_smoke.py +76 -0
cache.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any
5
+
6
+ import torch
7
+
8
+
9
+ @dataclass
10
+ class QyrouArchHybridCache:
11
+ """Attention K/V plus short-convolution state for incremental decoding."""
12
+
13
+ num_layers: int
14
+ max_cache_len: int | None = None
15
+ attention: list[tuple[torch.Tensor, torch.Tensor] | None] = field(init=False)
16
+ convolution: list[torch.Tensor | None] = field(init=False)
17
+ seen_tokens: int = 0
18
+
19
+ def __post_init__(self) -> None:
20
+ self.attention = [None] * self.num_layers
21
+ self.convolution = [None] * self.num_layers
22
+ if self.max_cache_len is not None and self.max_cache_len <= 0:
23
+ raise ValueError("max_cache_len must be positive")
24
+
25
+ @property
26
+ def is_static(self) -> bool:
27
+ return self.max_cache_len is not None
28
+
29
+ def get_seq_length(self, _layer_idx: int = 0) -> int:
30
+ return self.seen_tokens
31
+
32
+ def get_max_cache_shape(self) -> int | None:
33
+ return self.max_cache_len
34
+
35
+ def update_attention(
36
+ self,
37
+ layer_idx: int,
38
+ key: torch.Tensor,
39
+ value: torch.Tensor,
40
+ cache_position: torch.Tensor,
41
+ ) -> tuple[torch.Tensor, torch.Tensor]:
42
+ if key.ndim != 4 or key.shape != value.shape:
43
+ raise ValueError("K/V cache tensors must have identical [batch, heads, seq, dim] shapes")
44
+ if self.is_static:
45
+ assert self.max_cache_len is not None
46
+ if int(cache_position.max()) >= self.max_cache_len:
47
+ raise ValueError("Static cache capacity exceeded")
48
+ existing = self.attention[layer_idx]
49
+ if existing is None:
50
+ shape = (*key.shape[:2], self.max_cache_len, key.shape[-1])
51
+ key_cache = torch.zeros(shape, dtype=key.dtype, device=key.device)
52
+ value_cache = torch.zeros(shape, dtype=value.dtype, device=value.device)
53
+ else:
54
+ key_cache, value_cache = existing
55
+ key_cache.index_copy_(2, cache_position, key)
56
+ value_cache.index_copy_(2, cache_position, value)
57
+ self.attention[layer_idx] = (key_cache, value_cache)
58
+ visible = max(self.seen_tokens, int(cache_position.max()) + 1)
59
+ return key_cache[:, :, :visible], value_cache[:, :, :visible]
60
+ existing = self.attention[layer_idx]
61
+ if existing is not None:
62
+ key = torch.cat((existing[0], key), dim=2)
63
+ value = torch.cat((existing[1], value), dim=2)
64
+ self.attention[layer_idx] = (key, value)
65
+ return key, value
66
+
67
+ def update_convolution(self, layer_idx: int, state: torch.Tensor) -> None:
68
+ self.convolution[layer_idx] = state
69
+
70
+ def get_convolution(self, layer_idx: int) -> torch.Tensor | None:
71
+ return self.convolution[layer_idx]
72
+
73
+ def finish_step(self, cache_position: torch.Tensor) -> None:
74
+ if cache_position.numel():
75
+ self.seen_tokens = max(self.seen_tokens, int(cache_position.max()) + 1)
76
+
77
+ def reorder_cache(self, beam_idx: torch.LongTensor) -> QyrouArchHybridCache:
78
+ for index, item in enumerate(self.attention):
79
+ if item is not None:
80
+ self.attention[index] = (
81
+ item[0].index_select(0, beam_idx),
82
+ item[1].index_select(0, beam_idx),
83
+ )
84
+ for index, state in enumerate(self.convolution):
85
+ if state is not None:
86
+ self.convolution[index] = state.index_select(0, beam_idx)
87
+ return self
88
+
89
+ def batch_repeat_interleave(self, repeats: int) -> QyrouArchHybridCache:
90
+ indices = torch.arange(
91
+ next(t[0] for t in self.attention if t is not None).shape[0],
92
+ device=next(t[0] for t in self.attention if t is not None).device,
93
+ ).repeat_interleave(repeats)
94
+ return self.reorder_cache(indices)
95
+
96
+ def to_legacy_cache(self) -> tuple[Any, ...]:
97
+ return tuple(self.attention)
98
+
chat_template.jinja ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {%- for message in messages %}
2
+ {%- if message['role'] not in ['system', 'developer', 'user', 'assistant', 'tool', 'function', 'observation'] %}
3
+ {{- raise_exception('Unsupported QyrouArch chat role: ' + message['role']) }}
4
+ {%- endif %}
5
+ {{- '<|im_start|><|' + message['role'] + '|>\n' + message['content'] + '<|im_end|>\n' }}
6
+ {%- endfor %}
7
+ {%- if add_generation_prompt %}
8
+ {{- '<|im_start|><|assistant|>\n' }}
9
+ {%- endif %}
10
+
config.json ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "QyrouArchForCausalLM"
4
+ ],
5
+ "attention_backend": "cudnn",
6
+ "auto_map": {
7
+ "AutoConfig": "configuration_qyrou_arch.QyrouArchConfig",
8
+ "AutoModelForCausalLM": "modeling_qyrou_arch.QyrouArchForCausalLM"
9
+ },
10
+ "bos_token_id": 0,
11
+ "conv_kernel_size": 7,
12
+ "conv_layers": [
13
+ 4,
14
+ 9,
15
+ 14,
16
+ 19
17
+ ],
18
+ "cut_cross_entropy": true,
19
+ "dtype": "float32",
20
+ "eod_token_id": 4,
21
+ "eos_token_id": 1,
22
+ "fused_gate_up_projection": true,
23
+ "fused_qkv_projection": true,
24
+ "hidden_act": "silu",
25
+ "hidden_size": 512,
26
+ "initializer_range": 0.02,
27
+ "intermediate_size": 1328,
28
+ "liger_rms_norm": true,
29
+ "liger_swiglu": true,
30
+ "max_position_embeddings": 2048,
31
+ "model_type": "qyrou_arch",
32
+ "num_attention_heads": 8,
33
+ "num_hidden_layers": 21,
34
+ "num_key_value_heads": 2,
35
+ "pad_token_id": 2,
36
+ "qk_norm": true,
37
+ "residual_initializer_scale": 0.1543033499620919,
38
+ "rms_norm_eps": 1e-05,
39
+ "rope_theta": 10000.0,
40
+ "tie_word_embeddings": true,
41
+ "transformers_version": "5.13.0",
42
+ "unk_token_id": 3,
43
+ "use_cache": true,
44
+ "vocab_size": 20000
45
+ }
configuration_qyrou_arch.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ try:
6
+ from transformers import PretrainedConfig
7
+ except ImportError:
8
+ class PretrainedConfig: # type: ignore[no-redef]
9
+ model_type = "qyrou_arch"
10
+
11
+ def __init__(self, **kwargs: Any) -> None:
12
+ for key, value in kwargs.items():
13
+ setattr(self, key, value)
14
+
15
+
16
+ class QyrouArchConfig(PretrainedConfig):
17
+ model_type = "qyrou_arch"
18
+
19
+ def __init__(
20
+ self,
21
+ vocab_size: int = 20_000,
22
+ hidden_size: int = 512,
23
+ num_hidden_layers: int = 21,
24
+ num_attention_heads: int = 8,
25
+ num_key_value_heads: int = 2,
26
+ intermediate_size: int = 1_328,
27
+ conv_layers: list[int] | None = None,
28
+ conv_kernel_size: int = 7,
29
+ rope_theta: float = 10_000.0,
30
+ rms_norm_eps: float = 1e-5,
31
+ max_position_embeddings: int = 2_048,
32
+ initializer_range: float = 0.02,
33
+ residual_initializer_scale: float = 1.0 / (42.0**0.5),
34
+ tie_word_embeddings: bool = True,
35
+ qk_norm: bool = True,
36
+ attention_backend: str = "cudnn",
37
+ fused_qkv_projection: bool = True,
38
+ fused_gate_up_projection: bool = True,
39
+ liger_rms_norm: bool = True,
40
+ liger_swiglu: bool = True,
41
+ cut_cross_entropy: bool = True,
42
+ use_cache: bool = True,
43
+ eod_token_id: int = 4,
44
+ **kwargs: Any,
45
+ ) -> None:
46
+ super().__init__(tie_word_embeddings=tie_word_embeddings, **kwargs)
47
+ if hidden_size % num_attention_heads:
48
+ raise ValueError("hidden_size must be divisible by num_attention_heads")
49
+ if num_attention_heads % num_key_value_heads:
50
+ raise ValueError("num_attention_heads must be divisible by num_key_value_heads")
51
+ conv_layers = conv_layers if conv_layers is not None else [4, 9, 14, 19]
52
+ if len(set(conv_layers)) != len(conv_layers) or not all(
53
+ 1 <= layer <= num_hidden_layers for layer in conv_layers
54
+ ):
55
+ raise ValueError("conv_layers must contain unique one-based layer indices")
56
+ self.vocab_size = vocab_size
57
+ self.hidden_size = hidden_size
58
+ self.num_hidden_layers = num_hidden_layers
59
+ self.num_attention_heads = num_attention_heads
60
+ self.num_key_value_heads = num_key_value_heads
61
+ self.intermediate_size = intermediate_size
62
+ self.conv_layers = conv_layers
63
+ self.conv_kernel_size = conv_kernel_size
64
+ self.rope_theta = rope_theta
65
+ self.rms_norm_eps = rms_norm_eps
66
+ self.max_position_embeddings = max_position_embeddings
67
+ self.initializer_range = initializer_range
68
+ self.residual_initializer_scale = residual_initializer_scale
69
+ self.qk_norm = qk_norm
70
+ self.attention_backend = attention_backend
71
+ self.fused_qkv_projection = fused_qkv_projection
72
+ self.fused_gate_up_projection = fused_gate_up_projection
73
+ self.liger_rms_norm = liger_rms_norm
74
+ self.liger_swiglu = liger_swiglu
75
+ self.cut_cross_entropy = cut_cross_entropy
76
+ self.use_cache = use_cache
77
+ self.eod_token_id = eod_token_id
78
+ self.hidden_act = "silu"
79
+
generation_config.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_from_model_config": true,
3
+ "bos_token_id": 0,
4
+ "eos_token_id": 1,
5
+ "output_attentions": false,
6
+ "output_hidden_states": false,
7
+ "pad_token_id": 2,
8
+ "transformers_version": "5.13.0",
9
+ "use_cache": true
10
+ }
marker_manifest.json ADDED
@@ -0,0 +1,1190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": 0,
4
+ "token": "<|bos|>",
5
+ "special": true,
6
+ "category": "core"
7
+ },
8
+ {
9
+ "id": 1,
10
+ "token": "<|eos|>",
11
+ "special": true,
12
+ "category": "core"
13
+ },
14
+ {
15
+ "id": 2,
16
+ "token": "<|pad|>",
17
+ "special": true,
18
+ "category": "core"
19
+ },
20
+ {
21
+ "id": 3,
22
+ "token": "<|unk|>",
23
+ "special": true,
24
+ "category": "core"
25
+ },
26
+ {
27
+ "id": 4,
28
+ "token": "<|eod|>",
29
+ "special": true,
30
+ "category": "core"
31
+ },
32
+ {
33
+ "id": 5,
34
+ "token": "<|startoftext|>",
35
+ "special": true,
36
+ "category": "text_boundaries"
37
+ },
38
+ {
39
+ "id": 6,
40
+ "token": "<|endoftext|>",
41
+ "special": true,
42
+ "category": "text_boundaries"
43
+ },
44
+ {
45
+ "id": 7,
46
+ "token": "<|endofprompt|>",
47
+ "special": true,
48
+ "category": "text_boundaries"
49
+ },
50
+ {
51
+ "id": 8,
52
+ "token": "<|im_start|>",
53
+ "special": true,
54
+ "category": "chatml"
55
+ },
56
+ {
57
+ "id": 9,
58
+ "token": "<|im_end|>",
59
+ "special": true,
60
+ "category": "chatml"
61
+ },
62
+ {
63
+ "id": 10,
64
+ "token": "<|system|>",
65
+ "special": true,
66
+ "category": "chatml"
67
+ },
68
+ {
69
+ "id": 11,
70
+ "token": "<|developer|>",
71
+ "special": true,
72
+ "category": "chatml"
73
+ },
74
+ {
75
+ "id": 12,
76
+ "token": "<|user|>",
77
+ "special": true,
78
+ "category": "chatml"
79
+ },
80
+ {
81
+ "id": 13,
82
+ "token": "<|assistant|>",
83
+ "special": true,
84
+ "category": "chatml"
85
+ },
86
+ {
87
+ "id": 14,
88
+ "token": "<|tool|>",
89
+ "special": true,
90
+ "category": "chatml"
91
+ },
92
+ {
93
+ "id": 15,
94
+ "token": "<|function|>",
95
+ "special": true,
96
+ "category": "chatml"
97
+ },
98
+ {
99
+ "id": 16,
100
+ "token": "<|observation|>",
101
+ "special": true,
102
+ "category": "chatml"
103
+ },
104
+ {
105
+ "id": 17,
106
+ "token": "<|eot|>",
107
+ "special": true,
108
+ "category": "chatml"
109
+ },
110
+ {
111
+ "id": 18,
112
+ "token": "<think>",
113
+ "special": false,
114
+ "category": "reasoning"
115
+ },
116
+ {
117
+ "id": 19,
118
+ "token": "</think>",
119
+ "special": false,
120
+ "category": "reasoning"
121
+ },
122
+ {
123
+ "id": 20,
124
+ "token": "<analysis>",
125
+ "special": false,
126
+ "category": "reasoning"
127
+ },
128
+ {
129
+ "id": 21,
130
+ "token": "</analysis>",
131
+ "special": false,
132
+ "category": "reasoning"
133
+ },
134
+ {
135
+ "id": 22,
136
+ "token": "<reasoning>",
137
+ "special": false,
138
+ "category": "reasoning"
139
+ },
140
+ {
141
+ "id": 23,
142
+ "token": "</reasoning>",
143
+ "special": false,
144
+ "category": "reasoning"
145
+ },
146
+ {
147
+ "id": 24,
148
+ "token": "<commentary>",
149
+ "special": false,
150
+ "category": "reasoning"
151
+ },
152
+ {
153
+ "id": 25,
154
+ "token": "</commentary>",
155
+ "special": false,
156
+ "category": "reasoning"
157
+ },
158
+ {
159
+ "id": 26,
160
+ "token": "<final>",
161
+ "special": false,
162
+ "category": "reasoning"
163
+ },
164
+ {
165
+ "id": 27,
166
+ "token": "</final>",
167
+ "special": false,
168
+ "category": "reasoning"
169
+ },
170
+ {
171
+ "id": 28,
172
+ "token": "<tools>",
173
+ "special": false,
174
+ "category": "tools"
175
+ },
176
+ {
177
+ "id": 29,
178
+ "token": "</tools>",
179
+ "special": false,
180
+ "category": "tools"
181
+ },
182
+ {
183
+ "id": 30,
184
+ "token": "<available_tools>",
185
+ "special": false,
186
+ "category": "tools"
187
+ },
188
+ {
189
+ "id": 31,
190
+ "token": "</available_tools>",
191
+ "special": false,
192
+ "category": "tools"
193
+ },
194
+ {
195
+ "id": 32,
196
+ "token": "<tool_call>",
197
+ "special": false,
198
+ "category": "tools"
199
+ },
200
+ {
201
+ "id": 33,
202
+ "token": "</tool_call>",
203
+ "special": false,
204
+ "category": "tools"
205
+ },
206
+ {
207
+ "id": 34,
208
+ "token": "<tool_response>",
209
+ "special": false,
210
+ "category": "tools"
211
+ },
212
+ {
213
+ "id": 35,
214
+ "token": "</tool_response>",
215
+ "special": false,
216
+ "category": "tools"
217
+ },
218
+ {
219
+ "id": 36,
220
+ "token": "<function_call>",
221
+ "special": false,
222
+ "category": "tools"
223
+ },
224
+ {
225
+ "id": 37,
226
+ "token": "</function_call>",
227
+ "special": false,
228
+ "category": "tools"
229
+ },
230
+ {
231
+ "id": 38,
232
+ "token": "<function_response>",
233
+ "special": false,
234
+ "category": "tools"
235
+ },
236
+ {
237
+ "id": 39,
238
+ "token": "</function_response>",
239
+ "special": false,
240
+ "category": "tools"
241
+ },
242
+ {
243
+ "id": 40,
244
+ "token": "<|tool_sep|>",
245
+ "special": false,
246
+ "category": "tools"
247
+ },
248
+ {
249
+ "id": 41,
250
+ "token": "<|fim_prefix|>",
251
+ "special": false,
252
+ "category": "qwen_fim"
253
+ },
254
+ {
255
+ "id": 42,
256
+ "token": "<|fim_middle|>",
257
+ "special": false,
258
+ "category": "qwen_fim"
259
+ },
260
+ {
261
+ "id": 43,
262
+ "token": "<|fim_suffix|>",
263
+ "special": false,
264
+ "category": "qwen_fim"
265
+ },
266
+ {
267
+ "id": 44,
268
+ "token": "<|fim_pad|>",
269
+ "special": false,
270
+ "category": "qwen_fim"
271
+ },
272
+ {
273
+ "id": 45,
274
+ "token": "<|repo_name|>",
275
+ "special": false,
276
+ "category": "qwen_fim"
277
+ },
278
+ {
279
+ "id": 46,
280
+ "token": "<|file_sep|>",
281
+ "special": false,
282
+ "category": "qwen_fim"
283
+ },
284
+ {
285
+ "id": 47,
286
+ "token": "<fim_prefix>",
287
+ "special": false,
288
+ "category": "starcoder"
289
+ },
290
+ {
291
+ "id": 48,
292
+ "token": "<fim_middle>",
293
+ "special": false,
294
+ "category": "starcoder"
295
+ },
296
+ {
297
+ "id": 49,
298
+ "token": "<fim_suffix>",
299
+ "special": false,
300
+ "category": "starcoder"
301
+ },
302
+ {
303
+ "id": 50,
304
+ "token": "<fim_pad>",
305
+ "special": false,
306
+ "category": "starcoder"
307
+ },
308
+ {
309
+ "id": 51,
310
+ "token": "<repo_name>",
311
+ "special": false,
312
+ "category": "starcoder"
313
+ },
314
+ {
315
+ "id": 52,
316
+ "token": "<file_sep>",
317
+ "special": false,
318
+ "category": "starcoder"
319
+ },
320
+ {
321
+ "id": 53,
322
+ "token": "<issue_start>",
323
+ "special": false,
324
+ "category": "starcoder"
325
+ },
326
+ {
327
+ "id": 54,
328
+ "token": "<issue_comment>",
329
+ "special": false,
330
+ "category": "starcoder"
331
+ },
332
+ {
333
+ "id": 55,
334
+ "token": "<issue_closed>",
335
+ "special": false,
336
+ "category": "starcoder"
337
+ },
338
+ {
339
+ "id": 56,
340
+ "token": "<jupyter_start>",
341
+ "special": false,
342
+ "category": "starcoder"
343
+ },
344
+ {
345
+ "id": 57,
346
+ "token": "<jupyter_text>",
347
+ "special": false,
348
+ "category": "starcoder"
349
+ },
350
+ {
351
+ "id": 58,
352
+ "token": "<jupyter_code>",
353
+ "special": false,
354
+ "category": "starcoder"
355
+ },
356
+ {
357
+ "id": 59,
358
+ "token": "<jupyter_output>",
359
+ "special": false,
360
+ "category": "starcoder"
361
+ },
362
+ {
363
+ "id": 60,
364
+ "token": "<jupyter_script>",
365
+ "special": false,
366
+ "category": "starcoder"
367
+ },
368
+ {
369
+ "id": 61,
370
+ "token": "<empty_output>",
371
+ "special": false,
372
+ "category": "starcoder"
373
+ },
374
+ {
375
+ "id": 62,
376
+ "token": "<|begin_of_text|>",
377
+ "special": true,
378
+ "category": "llama"
379
+ },
380
+ {
381
+ "id": 63,
382
+ "token": "<|end_of_text|>",
383
+ "special": true,
384
+ "category": "llama"
385
+ },
386
+ {
387
+ "id": 64,
388
+ "token": "<|start_header_id|>",
389
+ "special": true,
390
+ "category": "llama"
391
+ },
392
+ {
393
+ "id": 65,
394
+ "token": "<|end_header_id|>",
395
+ "special": true,
396
+ "category": "llama"
397
+ },
398
+ {
399
+ "id": 66,
400
+ "token": "<|eot_id|>",
401
+ "special": true,
402
+ "category": "llama"
403
+ },
404
+ {
405
+ "id": 67,
406
+ "token": "<|eom_id|>",
407
+ "special": true,
408
+ "category": "llama"
409
+ },
410
+ {
411
+ "id": 68,
412
+ "token": "<|python_tag|>",
413
+ "special": true,
414
+ "category": "llama"
415
+ },
416
+ {
417
+ "id": 69,
418
+ "token": "<start_of_turn>",
419
+ "special": true,
420
+ "category": "gemma"
421
+ },
422
+ {
423
+ "id": 70,
424
+ "token": "<end_of_turn>",
425
+ "special": true,
426
+ "category": "gemma"
427
+ },
428
+ {
429
+ "id": 71,
430
+ "token": "<start_of_image>",
431
+ "special": true,
432
+ "category": "gemma"
433
+ },
434
+ {
435
+ "id": 72,
436
+ "token": "<s>",
437
+ "special": true,
438
+ "category": "mistral"
439
+ },
440
+ {
441
+ "id": 73,
442
+ "token": "</s>",
443
+ "special": true,
444
+ "category": "mistral"
445
+ },
446
+ {
447
+ "id": 74,
448
+ "token": "<unk>",
449
+ "special": true,
450
+ "category": "mistral"
451
+ },
452
+ {
453
+ "id": 75,
454
+ "token": "<pad>",
455
+ "special": true,
456
+ "category": "mistral"
457
+ },
458
+ {
459
+ "id": 76,
460
+ "token": "[INST]",
461
+ "special": true,
462
+ "category": "mistral"
463
+ },
464
+ {
465
+ "id": 77,
466
+ "token": "[/INST]",
467
+ "special": true,
468
+ "category": "mistral"
469
+ },
470
+ {
471
+ "id": 78,
472
+ "token": "[AVAILABLE_TOOLS]",
473
+ "special": true,
474
+ "category": "mistral"
475
+ },
476
+ {
477
+ "id": 79,
478
+ "token": "[/AVAILABLE_TOOLS]",
479
+ "special": true,
480
+ "category": "mistral"
481
+ },
482
+ {
483
+ "id": 80,
484
+ "token": "[TOOL_CALLS]",
485
+ "special": true,
486
+ "category": "mistral"
487
+ },
488
+ {
489
+ "id": 81,
490
+ "token": "[TOOL_RESULTS]",
491
+ "special": true,
492
+ "category": "mistral"
493
+ },
494
+ {
495
+ "id": 82,
496
+ "token": "[/TOOL_RESULTS]",
497
+ "special": true,
498
+ "category": "mistral"
499
+ },
500
+ {
501
+ "id": 83,
502
+ "token": "[SYSTEM_PROMPT]",
503
+ "special": true,
504
+ "category": "mistral"
505
+ },
506
+ {
507
+ "id": 84,
508
+ "token": "[/SYSTEM_PROMPT]",
509
+ "special": true,
510
+ "category": "mistral"
511
+ },
512
+ {
513
+ "id": 85,
514
+ "token": "[TOOL_CONTENT]",
515
+ "special": true,
516
+ "category": "mistral"
517
+ },
518
+ {
519
+ "id": 86,
520
+ "token": "[ARGS]",
521
+ "special": true,
522
+ "category": "mistral"
523
+ },
524
+ {
525
+ "id": 87,
526
+ "token": "[CALL_ID]",
527
+ "special": true,
528
+ "category": "mistral"
529
+ },
530
+ {
531
+ "id": 88,
532
+ "token": "[PREFIX]",
533
+ "special": true,
534
+ "category": "mistral"
535
+ },
536
+ {
537
+ "id": 89,
538
+ "token": "[MIDDLE]",
539
+ "special": true,
540
+ "category": "mistral"
541
+ },
542
+ {
543
+ "id": 90,
544
+ "token": "[SUFFIX]",
545
+ "special": true,
546
+ "category": "mistral"
547
+ },
548
+ {
549
+ "id": 91,
550
+ "token": "[THINK]",
551
+ "special": true,
552
+ "category": "mistral"
553
+ },
554
+ {
555
+ "id": 92,
556
+ "token": "[/THINK]",
557
+ "special": true,
558
+ "category": "mistral"
559
+ },
560
+ {
561
+ "id": 93,
562
+ "token": "[MODEL_SETTINGS]",
563
+ "special": true,
564
+ "category": "mistral"
565
+ },
566
+ {
567
+ "id": 94,
568
+ "token": "[/MODEL_SETTINGS]",
569
+ "special": true,
570
+ "category": "mistral"
571
+ },
572
+ {
573
+ "id": 95,
574
+ "token": "<|begin▁of▁sentence|>",
575
+ "special": true,
576
+ "category": "deepseek"
577
+ },
578
+ {
579
+ "id": 96,
580
+ "token": "<|end▁of▁sentence|>",
581
+ "special": true,
582
+ "category": "deepseek"
583
+ },
584
+ {
585
+ "id": 97,
586
+ "token": "<|User|>",
587
+ "special": true,
588
+ "category": "deepseek"
589
+ },
590
+ {
591
+ "id": 98,
592
+ "token": "<|Assistant|>",
593
+ "special": true,
594
+ "category": "deepseek"
595
+ },
596
+ {
597
+ "id": 99,
598
+ "token": "<|tool▁calls▁begin|>",
599
+ "special": true,
600
+ "category": "deepseek"
601
+ },
602
+ {
603
+ "id": 100,
604
+ "token": "<|tool▁call▁begin|>",
605
+ "special": true,
606
+ "category": "deepseek"
607
+ },
608
+ {
609
+ "id": 101,
610
+ "token": "<|tool▁sep|>",
611
+ "special": true,
612
+ "category": "deepseek"
613
+ },
614
+ {
615
+ "id": 102,
616
+ "token": "<|tool▁call▁end|>",
617
+ "special": true,
618
+ "category": "deepseek"
619
+ },
620
+ {
621
+ "id": 103,
622
+ "token": "<|tool▁calls▁end|>",
623
+ "special": true,
624
+ "category": "deepseek"
625
+ },
626
+ {
627
+ "id": 104,
628
+ "token": "<|tool▁outputs▁begin|>",
629
+ "special": true,
630
+ "category": "deepseek"
631
+ },
632
+ {
633
+ "id": 105,
634
+ "token": "<|tool▁output▁begin|>",
635
+ "special": true,
636
+ "category": "deepseek"
637
+ },
638
+ {
639
+ "id": 106,
640
+ "token": "<|tool▁output▁end|>",
641
+ "special": true,
642
+ "category": "deepseek"
643
+ },
644
+ {
645
+ "id": 107,
646
+ "token": "<|tool▁outputs▁end|>",
647
+ "special": true,
648
+ "category": "deepseek"
649
+ },
650
+ {
651
+ "id": 108,
652
+ "token": "<|fim▁begin|>",
653
+ "special": true,
654
+ "category": "deepseek"
655
+ },
656
+ {
657
+ "id": 109,
658
+ "token": "<|fim▁hole|>",
659
+ "special": true,
660
+ "category": "deepseek"
661
+ },
662
+ {
663
+ "id": 110,
664
+ "token": "<|fim▁end|>",
665
+ "special": true,
666
+ "category": "deepseek"
667
+ },
668
+ {
669
+ "id": 111,
670
+ "token": "<|start|>",
671
+ "special": true,
672
+ "category": "harmony"
673
+ },
674
+ {
675
+ "id": 112,
676
+ "token": "<|end|>",
677
+ "special": true,
678
+ "category": "harmony"
679
+ },
680
+ {
681
+ "id": 113,
682
+ "token": "<|message|>",
683
+ "special": true,
684
+ "category": "harmony"
685
+ },
686
+ {
687
+ "id": 114,
688
+ "token": "<|channel|>",
689
+ "special": true,
690
+ "category": "harmony"
691
+ },
692
+ {
693
+ "id": 115,
694
+ "token": "<|constrain|>",
695
+ "special": true,
696
+ "category": "harmony"
697
+ },
698
+ {
699
+ "id": 116,
700
+ "token": "<|return|>",
701
+ "special": true,
702
+ "category": "harmony"
703
+ },
704
+ {
705
+ "id": 117,
706
+ "token": "<|call|>",
707
+ "special": true,
708
+ "category": "harmony"
709
+ },
710
+ {
711
+ "id": 118,
712
+ "token": "<|vision_start|>",
713
+ "special": true,
714
+ "category": "vision"
715
+ },
716
+ {
717
+ "id": 119,
718
+ "token": "<|vision_end|>",
719
+ "special": true,
720
+ "category": "vision"
721
+ },
722
+ {
723
+ "id": 120,
724
+ "token": "<|vision_pad|>",
725
+ "special": true,
726
+ "category": "vision"
727
+ },
728
+ {
729
+ "id": 121,
730
+ "token": "<|image_pad|>",
731
+ "special": true,
732
+ "category": "vision"
733
+ },
734
+ {
735
+ "id": 122,
736
+ "token": "<|video_pad|>",
737
+ "special": true,
738
+ "category": "vision"
739
+ },
740
+ {
741
+ "id": 123,
742
+ "token": "<|image|>",
743
+ "special": true,
744
+ "category": "vision"
745
+ },
746
+ {
747
+ "id": 124,
748
+ "token": "<|video|>",
749
+ "special": true,
750
+ "category": "vision"
751
+ },
752
+ {
753
+ "id": 125,
754
+ "token": "[IMG]",
755
+ "special": true,
756
+ "category": "vision"
757
+ },
758
+ {
759
+ "id": 126,
760
+ "token": "[IMG_BREAK]",
761
+ "special": true,
762
+ "category": "vision"
763
+ },
764
+ {
765
+ "id": 127,
766
+ "token": "[IMG_END]",
767
+ "special": true,
768
+ "category": "vision"
769
+ },
770
+ {
771
+ "id": 128,
772
+ "token": "<|audio_bos|>",
773
+ "special": true,
774
+ "category": "audio"
775
+ },
776
+ {
777
+ "id": 129,
778
+ "token": "<|AUDIO|>",
779
+ "special": true,
780
+ "category": "audio"
781
+ },
782
+ {
783
+ "id": 130,
784
+ "token": "<|audio_eos|>",
785
+ "special": true,
786
+ "category": "audio"
787
+ },
788
+ {
789
+ "id": 131,
790
+ "token": "<|audio_pad|>",
791
+ "special": true,
792
+ "category": "audio"
793
+ },
794
+ {
795
+ "id": 132,
796
+ "token": "[AUDIO]",
797
+ "special": true,
798
+ "category": "audio"
799
+ },
800
+ {
801
+ "id": 133,
802
+ "token": "[BEGIN_AUDIO]",
803
+ "special": true,
804
+ "category": "audio"
805
+ },
806
+ {
807
+ "id": 134,
808
+ "token": "<|reserved_000|>",
809
+ "special": true,
810
+ "category": "reserved"
811
+ },
812
+ {
813
+ "id": 135,
814
+ "token": "<|reserved_001|>",
815
+ "special": true,
816
+ "category": "reserved"
817
+ },
818
+ {
819
+ "id": 136,
820
+ "token": "<|reserved_002|>",
821
+ "special": true,
822
+ "category": "reserved"
823
+ },
824
+ {
825
+ "id": 137,
826
+ "token": "<|reserved_003|>",
827
+ "special": true,
828
+ "category": "reserved"
829
+ },
830
+ {
831
+ "id": 138,
832
+ "token": "<|reserved_004|>",
833
+ "special": true,
834
+ "category": "reserved"
835
+ },
836
+ {
837
+ "id": 139,
838
+ "token": "<|reserved_005|>",
839
+ "special": true,
840
+ "category": "reserved"
841
+ },
842
+ {
843
+ "id": 140,
844
+ "token": "<|reserved_006|>",
845
+ "special": true,
846
+ "category": "reserved"
847
+ },
848
+ {
849
+ "id": 141,
850
+ "token": "<|reserved_007|>",
851
+ "special": true,
852
+ "category": "reserved"
853
+ },
854
+ {
855
+ "id": 142,
856
+ "token": "<|reserved_008|>",
857
+ "special": true,
858
+ "category": "reserved"
859
+ },
860
+ {
861
+ "id": 143,
862
+ "token": "<|reserved_009|>",
863
+ "special": true,
864
+ "category": "reserved"
865
+ },
866
+ {
867
+ "id": 144,
868
+ "token": "<|reserved_010|>",
869
+ "special": true,
870
+ "category": "reserved"
871
+ },
872
+ {
873
+ "id": 145,
874
+ "token": "<|reserved_011|>",
875
+ "special": true,
876
+ "category": "reserved"
877
+ },
878
+ {
879
+ "id": 146,
880
+ "token": "<|reserved_012|>",
881
+ "special": true,
882
+ "category": "reserved"
883
+ },
884
+ {
885
+ "id": 147,
886
+ "token": "<|reserved_013|>",
887
+ "special": true,
888
+ "category": "reserved"
889
+ },
890
+ {
891
+ "id": 148,
892
+ "token": "<|reserved_014|>",
893
+ "special": true,
894
+ "category": "reserved"
895
+ },
896
+ {
897
+ "id": 149,
898
+ "token": "<|reserved_015|>",
899
+ "special": true,
900
+ "category": "reserved"
901
+ },
902
+ {
903
+ "id": 150,
904
+ "token": "<|reserved_016|>",
905
+ "special": true,
906
+ "category": "reserved"
907
+ },
908
+ {
909
+ "id": 151,
910
+ "token": "<|reserved_017|>",
911
+ "special": true,
912
+ "category": "reserved"
913
+ },
914
+ {
915
+ "id": 152,
916
+ "token": "<|reserved_018|>",
917
+ "special": true,
918
+ "category": "reserved"
919
+ },
920
+ {
921
+ "id": 153,
922
+ "token": "<|reserved_019|>",
923
+ "special": true,
924
+ "category": "reserved"
925
+ },
926
+ {
927
+ "id": 154,
928
+ "token": "<|reserved_020|>",
929
+ "special": true,
930
+ "category": "reserved"
931
+ },
932
+ {
933
+ "id": 155,
934
+ "token": "<|reserved_021|>",
935
+ "special": true,
936
+ "category": "reserved"
937
+ },
938
+ {
939
+ "id": 156,
940
+ "token": "<|reserved_022|>",
941
+ "special": true,
942
+ "category": "reserved"
943
+ },
944
+ {
945
+ "id": 157,
946
+ "token": "<|reserved_023|>",
947
+ "special": true,
948
+ "category": "reserved"
949
+ },
950
+ {
951
+ "id": 158,
952
+ "token": "<|reserved_024|>",
953
+ "special": true,
954
+ "category": "reserved"
955
+ },
956
+ {
957
+ "id": 159,
958
+ "token": "<|reserved_025|>",
959
+ "special": true,
960
+ "category": "reserved"
961
+ },
962
+ {
963
+ "id": 160,
964
+ "token": "<|reserved_026|>",
965
+ "special": true,
966
+ "category": "reserved"
967
+ },
968
+ {
969
+ "id": 161,
970
+ "token": "<|reserved_027|>",
971
+ "special": true,
972
+ "category": "reserved"
973
+ },
974
+ {
975
+ "id": 162,
976
+ "token": "<|reserved_028|>",
977
+ "special": true,
978
+ "category": "reserved"
979
+ },
980
+ {
981
+ "id": 163,
982
+ "token": "<|reserved_029|>",
983
+ "special": true,
984
+ "category": "reserved"
985
+ },
986
+ {
987
+ "id": 164,
988
+ "token": "<|reserved_030|>",
989
+ "special": true,
990
+ "category": "reserved"
991
+ },
992
+ {
993
+ "id": 165,
994
+ "token": "<|reserved_031|>",
995
+ "special": true,
996
+ "category": "reserved"
997
+ },
998
+ {
999
+ "id": 166,
1000
+ "token": "<|reserved_032|>",
1001
+ "special": true,
1002
+ "category": "reserved"
1003
+ },
1004
+ {
1005
+ "id": 167,
1006
+ "token": "<|reserved_033|>",
1007
+ "special": true,
1008
+ "category": "reserved"
1009
+ },
1010
+ {
1011
+ "id": 168,
1012
+ "token": "<|reserved_034|>",
1013
+ "special": true,
1014
+ "category": "reserved"
1015
+ },
1016
+ {
1017
+ "id": 169,
1018
+ "token": "<|reserved_035|>",
1019
+ "special": true,
1020
+ "category": "reserved"
1021
+ },
1022
+ {
1023
+ "id": 170,
1024
+ "token": "<|reserved_036|>",
1025
+ "special": true,
1026
+ "category": "reserved"
1027
+ },
1028
+ {
1029
+ "id": 171,
1030
+ "token": "<|reserved_037|>",
1031
+ "special": true,
1032
+ "category": "reserved"
1033
+ },
1034
+ {
1035
+ "id": 172,
1036
+ "token": "<|reserved_038|>",
1037
+ "special": true,
1038
+ "category": "reserved"
1039
+ },
1040
+ {
1041
+ "id": 173,
1042
+ "token": "<|reserved_039|>",
1043
+ "special": true,
1044
+ "category": "reserved"
1045
+ },
1046
+ {
1047
+ "id": 174,
1048
+ "token": "<|reserved_040|>",
1049
+ "special": true,
1050
+ "category": "reserved"
1051
+ },
1052
+ {
1053
+ "id": 175,
1054
+ "token": "<|reserved_041|>",
1055
+ "special": true,
1056
+ "category": "reserved"
1057
+ },
1058
+ {
1059
+ "id": 176,
1060
+ "token": "<|reserved_042|>",
1061
+ "special": true,
1062
+ "category": "reserved"
1063
+ },
1064
+ {
1065
+ "id": 177,
1066
+ "token": "<|reserved_043|>",
1067
+ "special": true,
1068
+ "category": "reserved"
1069
+ },
1070
+ {
1071
+ "id": 178,
1072
+ "token": "<|reserved_044|>",
1073
+ "special": true,
1074
+ "category": "reserved"
1075
+ },
1076
+ {
1077
+ "id": 179,
1078
+ "token": "<|reserved_045|>",
1079
+ "special": true,
1080
+ "category": "reserved"
1081
+ },
1082
+ {
1083
+ "id": 180,
1084
+ "token": "<|reserved_046|>",
1085
+ "special": true,
1086
+ "category": "reserved"
1087
+ },
1088
+ {
1089
+ "id": 181,
1090
+ "token": "<|reserved_047|>",
1091
+ "special": true,
1092
+ "category": "reserved"
1093
+ },
1094
+ {
1095
+ "id": 182,
1096
+ "token": "<|reserved_048|>",
1097
+ "special": true,
1098
+ "category": "reserved"
1099
+ },
1100
+ {
1101
+ "id": 183,
1102
+ "token": "<|reserved_049|>",
1103
+ "special": true,
1104
+ "category": "reserved"
1105
+ },
1106
+ {
1107
+ "id": 184,
1108
+ "token": "<|reserved_050|>",
1109
+ "special": true,
1110
+ "category": "reserved"
1111
+ },
1112
+ {
1113
+ "id": 185,
1114
+ "token": "<|reserved_051|>",
1115
+ "special": true,
1116
+ "category": "reserved"
1117
+ },
1118
+ {
1119
+ "id": 186,
1120
+ "token": "<|reserved_052|>",
1121
+ "special": true,
1122
+ "category": "reserved"
1123
+ },
1124
+ {
1125
+ "id": 187,
1126
+ "token": "<|reserved_053|>",
1127
+ "special": true,
1128
+ "category": "reserved"
1129
+ },
1130
+ {
1131
+ "id": 188,
1132
+ "token": "<|reserved_054|>",
1133
+ "special": true,
1134
+ "category": "reserved"
1135
+ },
1136
+ {
1137
+ "id": 189,
1138
+ "token": "<|reserved_055|>",
1139
+ "special": true,
1140
+ "category": "reserved"
1141
+ },
1142
+ {
1143
+ "id": 190,
1144
+ "token": "<|reserved_056|>",
1145
+ "special": true,
1146
+ "category": "reserved"
1147
+ },
1148
+ {
1149
+ "id": 191,
1150
+ "token": "<|reserved_057|>",
1151
+ "special": true,
1152
+ "category": "reserved"
1153
+ },
1154
+ {
1155
+ "id": 192,
1156
+ "token": "<|reserved_058|>",
1157
+ "special": true,
1158
+ "category": "reserved"
1159
+ },
1160
+ {
1161
+ "id": 193,
1162
+ "token": "<|reserved_059|>",
1163
+ "special": true,
1164
+ "category": "reserved"
1165
+ },
1166
+ {
1167
+ "id": 194,
1168
+ "token": "<|reserved_060|>",
1169
+ "special": true,
1170
+ "category": "reserved"
1171
+ },
1172
+ {
1173
+ "id": 195,
1174
+ "token": "<|reserved_061|>",
1175
+ "special": true,
1176
+ "category": "reserved"
1177
+ },
1178
+ {
1179
+ "id": 196,
1180
+ "token": "<|reserved_062|>",
1181
+ "special": true,
1182
+ "category": "reserved"
1183
+ },
1184
+ {
1185
+ "id": 197,
1186
+ "token": "<|reserved_063|>",
1187
+ "special": true,
1188
+ "category": "reserved"
1189
+ }
1190
+ ]
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:15e793a9c28db4f2c843a4369363170c619ac1c73fdc4e1c98ffef5a9ca07f23
3
+ size 261234016
modeling_qyrou_arch.py ADDED
@@ -0,0 +1,504 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+ import torch.nn.functional as F
8
+ from torch.nn.attention import SDPBackend, sdpa_kernel
9
+ from transformers import PreTrainedModel
10
+ from transformers.generation import GenerationMixin
11
+ from transformers.modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
12
+
13
+ from .cache import QyrouArchHybridCache
14
+ from .configuration_qyrou_arch import QyrouArchConfig
15
+
16
+ try:
17
+ from .triton_kernels import PackedSwiGLUFunction
18
+ except (ImportError, RuntimeError):
19
+ PackedSwiGLUFunction = None
20
+
21
+ try:
22
+ from liger_kernel.ops.rms_norm import LigerRMSNormFunction
23
+ from liger_kernel.ops.swiglu import LigerSiLUMulFunction
24
+ except ImportError:
25
+ LigerRMSNormFunction = None
26
+ LigerSiLUMulFunction = None
27
+
28
+ try:
29
+ from cut_cross_entropy import linear_cross_entropy as cut_linear_cross_entropy
30
+ except ImportError:
31
+ cut_linear_cross_entropy = None
32
+
33
+
34
+ class RMSNorm(nn.Module):
35
+ def __init__(self, dim: int, eps: float, use_liger: bool = False) -> None:
36
+ super().__init__()
37
+ self.weight = nn.Parameter(torch.ones(dim))
38
+ self.eps = eps
39
+ self.use_liger = use_liger
40
+
41
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
42
+ if (
43
+ x.is_cuda
44
+ and self.use_liger
45
+ and LigerRMSNormFunction is not None
46
+ and not torch.compiler.is_compiling()
47
+ ):
48
+ return LigerRMSNormFunction.apply(x, self.weight, self.eps, 0.0, "llama", False, None)
49
+ x_float = x.float()
50
+ normalized = x_float * torch.rsqrt(x_float.square().mean(-1, keepdim=True) + self.eps)
51
+ return normalized.to(x.dtype) * self.weight.to(x.dtype)
52
+
53
+
54
+ def _apply_rope(
55
+ x: torch.Tensor,
56
+ cos: torch.Tensor,
57
+ sin: torch.Tensor,
58
+ ) -> torch.Tensor:
59
+ even, odd = x[..., 0::2], x[..., 1::2]
60
+ return torch.stack((even * cos - odd * sin, even * sin + odd * cos), dim=-1).flatten(-2)
61
+
62
+
63
+ def _last_valid_state(
64
+ x: torch.Tensor,
65
+ mask: torch.Tensor | None,
66
+ state_length: int,
67
+ ) -> torch.Tensor:
68
+ if state_length == 0:
69
+ return x[:, :0]
70
+ if mask is None:
71
+ return F.pad(x, (0, 0, max(0, state_length - x.shape[1]), 0))[:, -state_length:]
72
+ result = torch.zeros(
73
+ (x.shape[0], state_length, x.shape[2]),
74
+ dtype=x.dtype,
75
+ device=x.device,
76
+ )
77
+ for batch_index in range(x.shape[0]):
78
+ valid = x[batch_index][mask[batch_index].bool()]
79
+ valid = valid[-state_length:]
80
+ result[batch_index, -valid.shape[0] :] = valid
81
+ return result
82
+
83
+
84
+ class CausalGQA(nn.Module):
85
+ def __init__(self, config: QyrouArchConfig, layer_idx: int) -> None:
86
+ super().__init__()
87
+ self.layer_idx = layer_idx
88
+ self.num_heads = config.num_attention_heads
89
+ self.num_kv_heads = config.num_key_value_heads
90
+ self.head_dim = config.hidden_size // config.num_attention_heads
91
+ self.q_size = self.num_heads * self.head_dim
92
+ self.kv_size = self.num_kv_heads * self.head_dim
93
+ self.attention_backend = config.attention_backend
94
+ if config.fused_qkv_projection:
95
+ self.qkv_proj = nn.Linear(config.hidden_size, self.q_size + 2 * self.kv_size, bias=False)
96
+ else:
97
+ self.q_proj = nn.Linear(config.hidden_size, self.q_size, bias=False)
98
+ self.k_proj = nn.Linear(config.hidden_size, self.kv_size, bias=False)
99
+ self.v_proj = nn.Linear(config.hidden_size, self.kv_size, bias=False)
100
+ self.o_proj = nn.Linear(config.hidden_size, config.hidden_size, bias=False)
101
+ self.o_proj._qyrou_arch_residual_projection = True # type: ignore[attr-defined]
102
+ self.q_norm = RMSNorm(self.head_dim, config.rms_norm_eps) if config.qk_norm else nn.Identity()
103
+ self.k_norm = RMSNorm(self.head_dim, config.rms_norm_eps) if config.qk_norm else nn.Identity()
104
+
105
+ def forward(
106
+ self,
107
+ x: torch.Tensor,
108
+ rope: tuple[torch.Tensor, torch.Tensor],
109
+ attention_mask: torch.Tensor | None,
110
+ cache: QyrouArchHybridCache | None,
111
+ cache_position: torch.Tensor,
112
+ ) -> torch.Tensor:
113
+ batch, query_length, _ = x.shape
114
+ if hasattr(self, "qkv_proj"):
115
+ q, k, v = self.qkv_proj(x).split((self.q_size, self.kv_size, self.kv_size), dim=-1)
116
+ else:
117
+ q, k, v = self.q_proj(x), self.k_proj(x), self.v_proj(x)
118
+ q = q.view(batch, query_length, self.num_heads, self.head_dim).transpose(1, 2)
119
+ k = k.view(batch, query_length, self.num_kv_heads, self.head_dim).transpose(1, 2)
120
+ v = v.view(batch, query_length, self.num_kv_heads, self.head_dim).transpose(1, 2)
121
+ q, k = self.q_norm(q), self.k_norm(k)
122
+ q = _apply_rope(q, *rope)
123
+ k = _apply_rope(k, *rope)
124
+ if cache is not None:
125
+ k, v = cache.update_attention(self.layer_idx, k, v, cache_position)
126
+ key_length = k.shape[2]
127
+
128
+ mask = None
129
+ use_fast_causal = cache is None and attention_mask is None
130
+ if not use_fast_causal:
131
+ key_positions = torch.arange(key_length, device=x.device)
132
+ allowed = key_positions[None, :] <= cache_position[:, None]
133
+ mask = allowed[None, None, :, :].expand(batch, 1, query_length, key_length)
134
+ if attention_mask is not None:
135
+ if attention_mask.shape[-1] < key_length:
136
+ raise ValueError("attention_mask is shorter than the cached key sequence")
137
+ mask = mask & attention_mask[:, None, None, :key_length].bool()
138
+ if q.is_cuda and self.attention_backend in {"cudnn", "flash"}:
139
+ backends = (
140
+ [SDPBackend.CUDNN_ATTENTION, SDPBackend.FLASH_ATTENTION, SDPBackend.MATH]
141
+ if self.attention_backend == "cudnn"
142
+ else [SDPBackend.FLASH_ATTENTION, SDPBackend.CUDNN_ATTENTION, SDPBackend.MATH]
143
+ )
144
+ with sdpa_kernel(
145
+ backends,
146
+ set_priority=True,
147
+ ):
148
+ output = F.scaled_dot_product_attention(
149
+ q,
150
+ k,
151
+ v,
152
+ attn_mask=mask,
153
+ is_causal=use_fast_causal,
154
+ enable_gqa=True,
155
+ )
156
+ else:
157
+ output = F.scaled_dot_product_attention(
158
+ q,
159
+ k,
160
+ v,
161
+ attn_mask=mask,
162
+ is_causal=use_fast_causal,
163
+ enable_gqa=True,
164
+ )
165
+ output = output.transpose(1, 2).contiguous().view(batch, query_length, -1)
166
+ return self.o_proj(output)
167
+
168
+
169
+ class CausalConvMixer(nn.Module):
170
+ def __init__(self, config: QyrouArchConfig, layer_idx: int) -> None:
171
+ super().__init__()
172
+ self.layer_idx = layer_idx
173
+ self.kernel_size = config.conv_kernel_size
174
+ self.depthwise = nn.Conv1d(
175
+ config.hidden_size,
176
+ config.hidden_size,
177
+ kernel_size=self.kernel_size,
178
+ groups=config.hidden_size,
179
+ bias=False,
180
+ )
181
+ self.pointwise = nn.Linear(config.hidden_size, config.hidden_size, bias=False)
182
+ self.pointwise._qyrou_arch_residual_projection = True # type: ignore[attr-defined]
183
+
184
+ def forward(
185
+ self,
186
+ x: torch.Tensor,
187
+ _rope: tuple[torch.Tensor, torch.Tensor],
188
+ attention_mask: torch.Tensor | None,
189
+ cache: QyrouArchHybridCache | None,
190
+ _cache_position: torch.Tensor,
191
+ ) -> torch.Tensor:
192
+ state_length = self.kernel_size - 1
193
+ query_mask = attention_mask[:, -x.shape[1] :] if attention_mask is not None else None
194
+ if query_mask is not None:
195
+ x = x * query_mask.unsqueeze(-1).to(x.dtype)
196
+ if cache is None:
197
+ conv_input = F.pad(x.transpose(1, 2), (state_length, 0))
198
+ else:
199
+ previous = cache.get_convolution(self.layer_idx)
200
+ if previous is None:
201
+ previous = torch.zeros(
202
+ (x.shape[0], state_length, x.shape[2]),
203
+ dtype=x.dtype,
204
+ device=x.device,
205
+ )
206
+ combined = torch.cat((previous, x), dim=1)
207
+ conv_input = combined.transpose(1, 2)
208
+ combined_mask = None
209
+ if query_mask is not None:
210
+ combined_mask = torch.cat(
211
+ (
212
+ torch.ones(
213
+ (x.shape[0], state_length),
214
+ dtype=query_mask.dtype,
215
+ device=query_mask.device,
216
+ ),
217
+ query_mask,
218
+ ),
219
+ dim=1,
220
+ )
221
+ cache.update_convolution(
222
+ self.layer_idx,
223
+ _last_valid_state(combined, combined_mask, state_length),
224
+ )
225
+ return self.pointwise(self.depthwise(conv_input).transpose(1, 2))
226
+
227
+
228
+ class SwiGLU(nn.Module):
229
+ def __init__(self, config: QyrouArchConfig) -> None:
230
+ super().__init__()
231
+ self.intermediate_size = config.intermediate_size
232
+ self.use_liger = config.liger_swiglu
233
+ if config.fused_gate_up_projection:
234
+ self.gate_up_proj = nn.Linear(config.hidden_size, 2 * config.intermediate_size, bias=False)
235
+ else:
236
+ self.gate_proj = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
237
+ self.up_proj = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
238
+ self.down_proj = nn.Linear(config.intermediate_size, config.hidden_size, bias=False)
239
+ self.down_proj._qyrou_arch_residual_projection = True # type: ignore[attr-defined]
240
+
241
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
242
+ if hasattr(self, "gate_up_proj"):
243
+ packed = self.gate_up_proj(x)
244
+ if (
245
+ packed.is_cuda
246
+ and self.use_liger
247
+ and PackedSwiGLUFunction is not None
248
+ and not torch.compiler.is_compiling()
249
+ ):
250
+ return self.down_proj(PackedSwiGLUFunction.apply(packed))
251
+ gate, up = packed.chunk(2, dim=-1)
252
+ else:
253
+ gate, up = self.gate_proj(x), self.up_proj(x)
254
+ if torch.compiler.is_compiling():
255
+ activated = F.silu(gate) * up
256
+ elif x.is_cuda and self.use_liger and LigerSiLUMulFunction is not None:
257
+ activated = LigerSiLUMulFunction.apply(gate, up, 1.0, 1.0)
258
+ else:
259
+ activated = F.silu(gate) * up
260
+ return self.down_proj(activated)
261
+
262
+
263
+ class QyrouArchBlock(nn.Module):
264
+ def __init__(self, config: QyrouArchConfig, layer_idx: int) -> None:
265
+ super().__init__()
266
+ self.mixer_norm = RMSNorm(config.hidden_size, config.rms_norm_eps, config.liger_rms_norm)
267
+ self.ffn_norm = RMSNorm(config.hidden_size, config.rms_norm_eps, config.liger_rms_norm)
268
+ layer_number = layer_idx + 1
269
+ self.mixer = (
270
+ CausalConvMixer(config, layer_idx)
271
+ if layer_number in config.conv_layers
272
+ else CausalGQA(config, layer_idx)
273
+ )
274
+ self.ffn = SwiGLU(config)
275
+
276
+ def forward(
277
+ self,
278
+ x: torch.Tensor,
279
+ rope: tuple[torch.Tensor, torch.Tensor],
280
+ attention_mask: torch.Tensor | None,
281
+ cache: QyrouArchHybridCache | None,
282
+ cache_position: torch.Tensor,
283
+ ) -> torch.Tensor:
284
+ x = x + self.mixer(self.mixer_norm(x), rope, attention_mask, cache, cache_position)
285
+ return x + self.ffn(self.ffn_norm(x))
286
+
287
+
288
+ class QyrouArchPreTrainedModel(PreTrainedModel):
289
+ config_class = QyrouArchConfig
290
+ base_model_prefix = "model"
291
+ supports_gradient_checkpointing = False
292
+ _no_split_modules = ["QyrouArchBlock"]
293
+
294
+ def _init_weights(self, module: nn.Module) -> None:
295
+ if isinstance(module, (nn.Linear, nn.Embedding, nn.Conv1d)):
296
+ scale = (
297
+ self.config.residual_initializer_scale
298
+ if getattr(module, "_qyrou_arch_residual_projection", False)
299
+ else 1.0
300
+ )
301
+ nn.init.normal_(module.weight, mean=0.0, std=self.config.initializer_range * scale)
302
+
303
+
304
+ class QyrouArchModel(QyrouArchPreTrainedModel):
305
+ def __init__(self, config: QyrouArchConfig) -> None:
306
+ super().__init__(config)
307
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size)
308
+ self.layers = nn.ModuleList(
309
+ [QyrouArchBlock(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
310
+ )
311
+ self.norm = RMSNorm(config.hidden_size, config.rms_norm_eps, config.liger_rms_norm)
312
+ head_dim = config.hidden_size // config.num_attention_heads
313
+ inv_freq = 1.0 / (
314
+ config.rope_theta ** (torch.arange(0, head_dim, 2).float() / head_dim)
315
+ )
316
+ positions = torch.arange(config.max_position_embeddings, dtype=torch.float32)
317
+ angles = torch.outer(positions, inv_freq)
318
+ self.register_buffer("rope_cos", angles.cos(), persistent=False)
319
+ self.register_buffer("rope_sin", angles.sin(), persistent=False)
320
+ self.post_init()
321
+
322
+ def _rope(
323
+ self,
324
+ position_ids: torch.Tensor,
325
+ dtype: torch.dtype,
326
+ ) -> tuple[torch.Tensor, torch.Tensor]:
327
+ if (
328
+ not torch.compiler.is_compiling()
329
+ and int(position_ids.max()) >= self.config.max_position_embeddings
330
+ ):
331
+ raise ValueError("Position exceeds max_position_embeddings")
332
+ cos = self.rope_cos[position_ids].to(dtype).unsqueeze(1)
333
+ sin = self.rope_sin[position_ids].to(dtype).unsqueeze(1)
334
+ return cos, sin
335
+
336
+ def forward(
337
+ self,
338
+ input_ids: torch.LongTensor | None = None,
339
+ attention_mask: torch.Tensor | None = None,
340
+ position_ids: torch.LongTensor | None = None,
341
+ past_key_values: QyrouArchHybridCache | None = None,
342
+ inputs_embeds: torch.Tensor | None = None,
343
+ use_cache: bool | None = None,
344
+ cache_position: torch.LongTensor | None = None,
345
+ return_dict: bool | None = None,
346
+ **_: Any,
347
+ ) -> BaseModelOutputWithPast | tuple[torch.Tensor, QyrouArchHybridCache | None]:
348
+ if (input_ids is None) == (inputs_embeds is None):
349
+ raise ValueError("Pass exactly one of input_ids or inputs_embeds")
350
+ hidden = self.embed_tokens(input_ids) if inputs_embeds is None else inputs_embeds
351
+ if hidden.is_cuda and torch.is_autocast_enabled("cuda"):
352
+ hidden = hidden.to(torch.get_autocast_dtype("cuda"))
353
+ batch, query_length, _ = hidden.shape
354
+ use_cache = self.config.use_cache if use_cache is None else use_cache
355
+ return_dict = self.config.return_dict if return_dict is None else return_dict
356
+ if use_cache and past_key_values is None:
357
+ past_key_values = QyrouArchHybridCache(self.config.num_hidden_layers)
358
+ cache = past_key_values if use_cache else None
359
+ past_length = cache.get_seq_length() if cache is not None else 0
360
+ if cache_position is None:
361
+ cache_position = torch.arange(
362
+ past_length,
363
+ past_length + query_length,
364
+ device=hidden.device,
365
+ )
366
+ if position_ids is None:
367
+ if attention_mask is not None:
368
+ position_ids = attention_mask.long().cumsum(-1).sub(1).clamp_min(0)[:, -query_length:]
369
+ else:
370
+ position_ids = cache_position.unsqueeze(0).expand(batch, -1)
371
+ rope = self._rope(position_ids, hidden.dtype)
372
+ for layer in self.layers:
373
+ hidden = layer(hidden, rope, attention_mask, cache, cache_position)
374
+ hidden = self.norm(hidden)
375
+ if cache is not None:
376
+ cache.finish_step(cache_position)
377
+ if not return_dict:
378
+ return hidden, cache
379
+ return BaseModelOutputWithPast(last_hidden_state=hidden, past_key_values=cache)
380
+
381
+
382
+ class QyrouArchForCausalLM(QyrouArchPreTrainedModel, GenerationMixin):
383
+ _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
384
+
385
+ def __init__(self, config: QyrouArchConfig) -> None:
386
+ super().__init__(config)
387
+ self.model = QyrouArchModel(config)
388
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
389
+ self.post_init()
390
+ self.tie_weights()
391
+
392
+ def get_input_embeddings(self) -> nn.Module:
393
+ return self.model.embed_tokens
394
+
395
+ def set_input_embeddings(self, value: nn.Module) -> None:
396
+ self.model.embed_tokens = value
397
+
398
+ def get_output_embeddings(self) -> nn.Module:
399
+ return self.lm_head
400
+
401
+ def set_output_embeddings(self, value: nn.Module) -> None:
402
+ self.lm_head = value
403
+
404
+ def forward(
405
+ self,
406
+ input_ids: torch.LongTensor | None = None,
407
+ attention_mask: torch.Tensor | None = None,
408
+ position_ids: torch.LongTensor | None = None,
409
+ past_key_values: QyrouArchHybridCache | None = None,
410
+ inputs_embeds: torch.Tensor | None = None,
411
+ labels: torch.LongTensor | None = None,
412
+ use_cache: bool | None = None,
413
+ cache_position: torch.LongTensor | None = None,
414
+ return_logits: bool = True,
415
+ return_dict: bool | None = None,
416
+ **kwargs: Any,
417
+ ) -> CausalLMOutputWithPast | tuple[Any, ...]:
418
+ return_dict = self.config.return_dict if return_dict is None else return_dict
419
+ outputs = self.model(
420
+ input_ids=input_ids,
421
+ attention_mask=attention_mask,
422
+ position_ids=position_ids,
423
+ past_key_values=past_key_values,
424
+ inputs_embeds=inputs_embeds,
425
+ use_cache=use_cache,
426
+ cache_position=cache_position,
427
+ return_dict=True,
428
+ **kwargs,
429
+ )
430
+ hidden = outputs.last_hidden_state
431
+ training_loss_only = labels is not None and not return_logits
432
+ logits = None if training_loss_only else self.lm_head(hidden)
433
+ loss = None
434
+ if labels is not None:
435
+ if (
436
+ training_loss_only
437
+ and hidden.is_cuda
438
+ and self.config.cut_cross_entropy
439
+ and cut_linear_cross_entropy is not None
440
+ ):
441
+ loss = cut_linear_cross_entropy(
442
+ hidden,
443
+ self.lm_head.weight,
444
+ labels,
445
+ shift=True,
446
+ filter_eps=None,
447
+ )
448
+ else:
449
+ loss_logits = self.lm_head(hidden[:, :-1]).float()
450
+ loss = F.cross_entropy(
451
+ loss_logits.reshape(-1, self.config.vocab_size),
452
+ labels[:, 1:].contiguous().view(-1),
453
+ ignore_index=-100,
454
+ )
455
+ if not return_dict:
456
+ return loss, logits, outputs.past_key_values
457
+ return CausalLMOutputWithPast(
458
+ loss=loss,
459
+ logits=logits,
460
+ past_key_values=outputs.past_key_values,
461
+ )
462
+
463
+ def prepare_inputs_for_generation(
464
+ self,
465
+ input_ids: torch.LongTensor,
466
+ past_key_values: QyrouArchHybridCache | None = None,
467
+ attention_mask: torch.Tensor | None = None,
468
+ cache_position: torch.LongTensor | None = None,
469
+ use_cache: bool = True,
470
+ **kwargs: Any,
471
+ ) -> dict[str, Any]:
472
+ past_length = past_key_values.get_seq_length() if past_key_values is not None else 0
473
+ if past_length:
474
+ input_ids = (
475
+ input_ids[:, past_length:]
476
+ if input_ids.shape[1] > past_length
477
+ else input_ids[:, -1:]
478
+ )
479
+ if cache_position is None:
480
+ cache_position = torch.arange(
481
+ past_length,
482
+ past_length + input_ids.shape[1],
483
+ device=input_ids.device,
484
+ )
485
+ elif cache_position.numel() != input_ids.shape[1]:
486
+ cache_position = cache_position[-input_ids.shape[1] :]
487
+ return {
488
+ "input_ids": input_ids,
489
+ "attention_mask": attention_mask,
490
+ "past_key_values": past_key_values,
491
+ "cache_position": cache_position,
492
+ "use_cache": use_cache,
493
+ }
494
+
495
+ def _reorder_cache(
496
+ self,
497
+ past_key_values: QyrouArchHybridCache,
498
+ beam_idx: torch.LongTensor,
499
+ ) -> QyrouArchHybridCache:
500
+ return past_key_values.reorder_cache(beam_idx)
501
+
502
+
503
+ QyrouArchConfig.register_for_auto_class()
504
+ QyrouArchForCausalLM.register_for_auto_class("AutoModelForCausalLM")
resume/artifacts/autotune.json ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "selected": {
3
+ "microbatch": 8,
4
+ "grad_accum": 3,
5
+ "compile": true,
6
+ "compile_mode": "default",
7
+ "tokens_per_sec": 89445.99334863275,
8
+ "average_tokens_per_sec": 89444.8633550159,
9
+ "p10_tokens_per_sec": 89344.54672252262,
10
+ "p90_tokens_per_sec": 89528.96203402826,
11
+ "average_step_seconds": 0.549523363999615,
12
+ "measured_steps": 50,
13
+ "peak_gib": 6.564574718475342,
14
+ "attention_backend": "cudnn"
15
+ },
16
+ "best_eager": {
17
+ "microbatch": 6,
18
+ "grad_accum": 4,
19
+ "compile": false,
20
+ "compile_mode": null,
21
+ "tokens_per_sec": 72779.22098370155,
22
+ "average_tokens_per_sec": 72686.84173544499,
23
+ "p10_tokens_per_sec": 72594.40211376193,
24
+ "p90_tokens_per_sec": 73023.81311474911,
25
+ "average_step_seconds": 0.676274363998964,
26
+ "measured_steps": 50,
27
+ "peak_gib": 5.965656280517578,
28
+ "attention_backend": "cudnn"
29
+ },
30
+ "results": [
31
+ {
32
+ "microbatch": 5,
33
+ "grad_accum": 3,
34
+ "compile": false,
35
+ "compile_mode": null,
36
+ "tokens_per_sec": 69142.84797054232,
37
+ "average_tokens_per_sec": 68942.92490092275,
38
+ "p10_tokens_per_sec": 68404.52458687228,
39
+ "p90_tokens_per_sec": 69558.42851403252,
40
+ "average_step_seconds": 0.44562956400041004,
41
+ "measured_steps": 50,
42
+ "peak_gib": 5.198768615722656,
43
+ "attention_backend": "cudnn"
44
+ },
45
+ {
46
+ "microbatch": 5,
47
+ "grad_accum": 3,
48
+ "compile": true,
49
+ "compile_mode": "default",
50
+ "tokens_per_sec": 84537.06633037937,
51
+ "average_tokens_per_sec": 84528.41417894745,
52
+ "p10_tokens_per_sec": 84212.90403651971,
53
+ "p90_tokens_per_sec": 84809.25922724494,
54
+ "average_step_seconds": 0.36343143600126493,
55
+ "measured_steps": 50,
56
+ "peak_gib": 4.5903239250183105,
57
+ "attention_backend": "cudnn"
58
+ },
59
+ {
60
+ "microbatch": 6,
61
+ "grad_accum": 3,
62
+ "compile": false,
63
+ "compile_mode": null,
64
+ "tokens_per_sec": 69358.98911499346,
65
+ "average_tokens_per_sec": 69211.02001694769,
66
+ "p10_tokens_per_sec": 68395.45277013684,
67
+ "p90_tokens_per_sec": 69775.53680685474,
68
+ "average_step_seconds": 0.5326724359992658,
69
+ "measured_steps": 50,
70
+ "peak_gib": 5.9600982666015625,
71
+ "attention_backend": "cudnn"
72
+ },
73
+ {
74
+ "microbatch": 6,
75
+ "grad_accum": 3,
76
+ "compile": true,
77
+ "compile_mode": "default",
78
+ "tokens_per_sec": 85005.56070311155,
79
+ "average_tokens_per_sec": 85016.33866867538,
80
+ "p10_tokens_per_sec": 83462.51714754912,
81
+ "p90_tokens_per_sec": 85675.79853778225,
82
+ "average_step_seconds": 0.4337354880009661,
83
+ "measured_steps": 50,
84
+ "peak_gib": 5.266883373260498,
85
+ "attention_backend": "cudnn"
86
+ },
87
+ {
88
+ "microbatch": 6,
89
+ "grad_accum": 4,
90
+ "compile": false,
91
+ "compile_mode": null,
92
+ "tokens_per_sec": 72779.22098370155,
93
+ "average_tokens_per_sec": 72686.84173544499,
94
+ "p10_tokens_per_sec": 72594.40211376193,
95
+ "p90_tokens_per_sec": 73023.81311474911,
96
+ "average_step_seconds": 0.676274363998964,
97
+ "measured_steps": 50,
98
+ "peak_gib": 5.965656280517578,
99
+ "attention_backend": "cudnn"
100
+ },
101
+ {
102
+ "microbatch": 6,
103
+ "grad_accum": 4,
104
+ "compile": true,
105
+ "compile_mode": "default",
106
+ "tokens_per_sec": 88941.710905432,
107
+ "average_tokens_per_sec": 88941.84349428031,
108
+ "p10_tokens_per_sec": 88846.51904815416,
109
+ "p90_tokens_per_sec": 89047.07563353243,
110
+ "average_step_seconds": 0.5526312520014471,
111
+ "measured_steps": 50,
112
+ "peak_gib": 5.266921520233154,
113
+ "attention_backend": "cudnn"
114
+ },
115
+ {
116
+ "microbatch": 8,
117
+ "grad_accum": 2,
118
+ "compile": false,
119
+ "compile_mode": null,
120
+ "tokens_per_sec": 71432.95470939987,
121
+ "average_tokens_per_sec": 71244.80450390381,
122
+ "p10_tokens_per_sec": 71215.24486226412,
123
+ "p90_tokens_per_sec": 71553.3818194886,
124
+ "average_step_seconds": 0.45999654399842255,
125
+ "measured_steps": 50,
126
+ "peak_gib": 7.537319183349609,
127
+ "attention_backend": "cudnn"
128
+ },
129
+ {
130
+ "microbatch": 8,
131
+ "grad_accum": 2,
132
+ "compile": true,
133
+ "compile_mode": "default",
134
+ "tokens_per_sec": 89210.10479908106,
135
+ "average_tokens_per_sec": 88917.94627874688,
136
+ "p10_tokens_per_sec": 89043.76861913929,
137
+ "p90_tokens_per_sec": 89375.04023150704,
138
+ "average_step_seconds": 0.36858677200070816,
139
+ "measured_steps": 50,
140
+ "peak_gib": 6.563186168670654,
141
+ "attention_backend": "cudnn"
142
+ },
143
+ {
144
+ "microbatch": 8,
145
+ "grad_accum": 3,
146
+ "compile": false,
147
+ "compile_mode": null,
148
+ "tokens_per_sec": 71752.63949365195,
149
+ "average_tokens_per_sec": 71738.85536654401,
150
+ "p10_tokens_per_sec": 71578.61932457695,
151
+ "p90_tokens_per_sec": 71862.33870713774,
152
+ "average_step_seconds": 0.6851534099999117,
153
+ "measured_steps": 50,
154
+ "peak_gib": 7.536571502685547,
155
+ "attention_backend": "cudnn"
156
+ },
157
+ {
158
+ "microbatch": 8,
159
+ "grad_accum": 3,
160
+ "compile": true,
161
+ "compile_mode": "default",
162
+ "tokens_per_sec": 89445.99334863275,
163
+ "average_tokens_per_sec": 89444.8633550159,
164
+ "p10_tokens_per_sec": 89344.54672252262,
165
+ "p90_tokens_per_sec": 89528.96203402826,
166
+ "average_step_seconds": 0.549523363999615,
167
+ "measured_steps": 50,
168
+ "peak_gib": 6.564574718475342,
169
+ "attention_backend": "cudnn"
170
+ },
171
+ {
172
+ "microbatch": 10,
173
+ "grad_accum": 2,
174
+ "compile": false,
175
+ "compile_mode": null,
176
+ "tokens_per_sec": 69852.45715698763,
177
+ "average_tokens_per_sec": 69719.44084914621,
178
+ "p10_tokens_per_sec": 69724.38271446126,
179
+ "p90_tokens_per_sec": 69995.07846979078,
180
+ "average_step_seconds": 0.5875548560012248,
181
+ "measured_steps": 50,
182
+ "peak_gib": 9.104179382324219,
183
+ "attention_backend": "cudnn"
184
+ },
185
+ {
186
+ "microbatch": 10,
187
+ "grad_accum": 2,
188
+ "compile": true,
189
+ "compile_mode": "default",
190
+ "tokens_per_sec": 89042.88786929363,
191
+ "average_tokens_per_sec": 88826.54532414334,
192
+ "p10_tokens_per_sec": 88921.57837423113,
193
+ "p90_tokens_per_sec": 89187.438419859,
194
+ "average_step_seconds": 0.46118442999955733,
195
+ "measured_steps": 50,
196
+ "peak_gib": 7.920142650604248,
197
+ "attention_backend": "cudnn"
198
+ },
199
+ {
200
+ "microbatch": 12,
201
+ "grad_accum": 2,
202
+ "compile": false,
203
+ "compile_mode": null,
204
+ "tokens_per_sec": 17407.417292769976,
205
+ "average_tokens_per_sec": 16870.67263639786,
206
+ "p10_tokens_per_sec": 5742.86275913661,
207
+ "p90_tokens_per_sec": 31079.470988878318,
208
+ "average_step_seconds": 3.9928920720002496,
209
+ "measured_steps": 50,
210
+ "peak_gib": 10.677471160888672,
211
+ "attention_backend": "cudnn"
212
+ },
213
+ {
214
+ "microbatch": 12,
215
+ "grad_accum": 2,
216
+ "compile": true,
217
+ "compile_mode": "default",
218
+ "tokens_per_sec": 89207.88471267493,
219
+ "average_tokens_per_sec": 89040.59748015198,
220
+ "p10_tokens_per_sec": 89041.46192278565,
221
+ "p90_tokens_per_sec": 89522.86346394477,
222
+ "average_step_seconds": 0.552097511999018,
223
+ "measured_steps": 50,
224
+ "peak_gib": 9.247085094451904,
225
+ "attention_backend": "cudnn"
226
+ }
227
+ ],
228
+ "minimum_compile_gain": 0.03
229
+ }
resume/artifacts/tokenizer/chat_template.jinja ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {%- for message in messages %}
2
+ {%- if message['role'] not in ['system', 'developer', 'user', 'assistant', 'tool', 'function', 'observation'] %}
3
+ {{- raise_exception('Unsupported QyrouArch chat role: ' + message['role']) }}
4
+ {%- endif %}
5
+ {{- '<|im_start|><|' + message['role'] + '|>\n' + message['content'] + '<|im_end|>\n' }}
6
+ {%- endfor %}
7
+ {%- if add_generation_prompt %}
8
+ {{- '<|im_start|><|assistant|>\n' }}
9
+ {%- endif %}
10
+
resume/artifacts/tokenizer/marker_manifest.json ADDED
@@ -0,0 +1,1190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": 0,
4
+ "token": "<|bos|>",
5
+ "special": true,
6
+ "category": "core"
7
+ },
8
+ {
9
+ "id": 1,
10
+ "token": "<|eos|>",
11
+ "special": true,
12
+ "category": "core"
13
+ },
14
+ {
15
+ "id": 2,
16
+ "token": "<|pad|>",
17
+ "special": true,
18
+ "category": "core"
19
+ },
20
+ {
21
+ "id": 3,
22
+ "token": "<|unk|>",
23
+ "special": true,
24
+ "category": "core"
25
+ },
26
+ {
27
+ "id": 4,
28
+ "token": "<|eod|>",
29
+ "special": true,
30
+ "category": "core"
31
+ },
32
+ {
33
+ "id": 5,
34
+ "token": "<|startoftext|>",
35
+ "special": true,
36
+ "category": "text_boundaries"
37
+ },
38
+ {
39
+ "id": 6,
40
+ "token": "<|endoftext|>",
41
+ "special": true,
42
+ "category": "text_boundaries"
43
+ },
44
+ {
45
+ "id": 7,
46
+ "token": "<|endofprompt|>",
47
+ "special": true,
48
+ "category": "text_boundaries"
49
+ },
50
+ {
51
+ "id": 8,
52
+ "token": "<|im_start|>",
53
+ "special": true,
54
+ "category": "chatml"
55
+ },
56
+ {
57
+ "id": 9,
58
+ "token": "<|im_end|>",
59
+ "special": true,
60
+ "category": "chatml"
61
+ },
62
+ {
63
+ "id": 10,
64
+ "token": "<|system|>",
65
+ "special": true,
66
+ "category": "chatml"
67
+ },
68
+ {
69
+ "id": 11,
70
+ "token": "<|developer|>",
71
+ "special": true,
72
+ "category": "chatml"
73
+ },
74
+ {
75
+ "id": 12,
76
+ "token": "<|user|>",
77
+ "special": true,
78
+ "category": "chatml"
79
+ },
80
+ {
81
+ "id": 13,
82
+ "token": "<|assistant|>",
83
+ "special": true,
84
+ "category": "chatml"
85
+ },
86
+ {
87
+ "id": 14,
88
+ "token": "<|tool|>",
89
+ "special": true,
90
+ "category": "chatml"
91
+ },
92
+ {
93
+ "id": 15,
94
+ "token": "<|function|>",
95
+ "special": true,
96
+ "category": "chatml"
97
+ },
98
+ {
99
+ "id": 16,
100
+ "token": "<|observation|>",
101
+ "special": true,
102
+ "category": "chatml"
103
+ },
104
+ {
105
+ "id": 17,
106
+ "token": "<|eot|>",
107
+ "special": true,
108
+ "category": "chatml"
109
+ },
110
+ {
111
+ "id": 18,
112
+ "token": "<think>",
113
+ "special": false,
114
+ "category": "reasoning"
115
+ },
116
+ {
117
+ "id": 19,
118
+ "token": "</think>",
119
+ "special": false,
120
+ "category": "reasoning"
121
+ },
122
+ {
123
+ "id": 20,
124
+ "token": "<analysis>",
125
+ "special": false,
126
+ "category": "reasoning"
127
+ },
128
+ {
129
+ "id": 21,
130
+ "token": "</analysis>",
131
+ "special": false,
132
+ "category": "reasoning"
133
+ },
134
+ {
135
+ "id": 22,
136
+ "token": "<reasoning>",
137
+ "special": false,
138
+ "category": "reasoning"
139
+ },
140
+ {
141
+ "id": 23,
142
+ "token": "</reasoning>",
143
+ "special": false,
144
+ "category": "reasoning"
145
+ },
146
+ {
147
+ "id": 24,
148
+ "token": "<commentary>",
149
+ "special": false,
150
+ "category": "reasoning"
151
+ },
152
+ {
153
+ "id": 25,
154
+ "token": "</commentary>",
155
+ "special": false,
156
+ "category": "reasoning"
157
+ },
158
+ {
159
+ "id": 26,
160
+ "token": "<final>",
161
+ "special": false,
162
+ "category": "reasoning"
163
+ },
164
+ {
165
+ "id": 27,
166
+ "token": "</final>",
167
+ "special": false,
168
+ "category": "reasoning"
169
+ },
170
+ {
171
+ "id": 28,
172
+ "token": "<tools>",
173
+ "special": false,
174
+ "category": "tools"
175
+ },
176
+ {
177
+ "id": 29,
178
+ "token": "</tools>",
179
+ "special": false,
180
+ "category": "tools"
181
+ },
182
+ {
183
+ "id": 30,
184
+ "token": "<available_tools>",
185
+ "special": false,
186
+ "category": "tools"
187
+ },
188
+ {
189
+ "id": 31,
190
+ "token": "</available_tools>",
191
+ "special": false,
192
+ "category": "tools"
193
+ },
194
+ {
195
+ "id": 32,
196
+ "token": "<tool_call>",
197
+ "special": false,
198
+ "category": "tools"
199
+ },
200
+ {
201
+ "id": 33,
202
+ "token": "</tool_call>",
203
+ "special": false,
204
+ "category": "tools"
205
+ },
206
+ {
207
+ "id": 34,
208
+ "token": "<tool_response>",
209
+ "special": false,
210
+ "category": "tools"
211
+ },
212
+ {
213
+ "id": 35,
214
+ "token": "</tool_response>",
215
+ "special": false,
216
+ "category": "tools"
217
+ },
218
+ {
219
+ "id": 36,
220
+ "token": "<function_call>",
221
+ "special": false,
222
+ "category": "tools"
223
+ },
224
+ {
225
+ "id": 37,
226
+ "token": "</function_call>",
227
+ "special": false,
228
+ "category": "tools"
229
+ },
230
+ {
231
+ "id": 38,
232
+ "token": "<function_response>",
233
+ "special": false,
234
+ "category": "tools"
235
+ },
236
+ {
237
+ "id": 39,
238
+ "token": "</function_response>",
239
+ "special": false,
240
+ "category": "tools"
241
+ },
242
+ {
243
+ "id": 40,
244
+ "token": "<|tool_sep|>",
245
+ "special": false,
246
+ "category": "tools"
247
+ },
248
+ {
249
+ "id": 41,
250
+ "token": "<|fim_prefix|>",
251
+ "special": false,
252
+ "category": "qwen_fim"
253
+ },
254
+ {
255
+ "id": 42,
256
+ "token": "<|fim_middle|>",
257
+ "special": false,
258
+ "category": "qwen_fim"
259
+ },
260
+ {
261
+ "id": 43,
262
+ "token": "<|fim_suffix|>",
263
+ "special": false,
264
+ "category": "qwen_fim"
265
+ },
266
+ {
267
+ "id": 44,
268
+ "token": "<|fim_pad|>",
269
+ "special": false,
270
+ "category": "qwen_fim"
271
+ },
272
+ {
273
+ "id": 45,
274
+ "token": "<|repo_name|>",
275
+ "special": false,
276
+ "category": "qwen_fim"
277
+ },
278
+ {
279
+ "id": 46,
280
+ "token": "<|file_sep|>",
281
+ "special": false,
282
+ "category": "qwen_fim"
283
+ },
284
+ {
285
+ "id": 47,
286
+ "token": "<fim_prefix>",
287
+ "special": false,
288
+ "category": "starcoder"
289
+ },
290
+ {
291
+ "id": 48,
292
+ "token": "<fim_middle>",
293
+ "special": false,
294
+ "category": "starcoder"
295
+ },
296
+ {
297
+ "id": 49,
298
+ "token": "<fim_suffix>",
299
+ "special": false,
300
+ "category": "starcoder"
301
+ },
302
+ {
303
+ "id": 50,
304
+ "token": "<fim_pad>",
305
+ "special": false,
306
+ "category": "starcoder"
307
+ },
308
+ {
309
+ "id": 51,
310
+ "token": "<repo_name>",
311
+ "special": false,
312
+ "category": "starcoder"
313
+ },
314
+ {
315
+ "id": 52,
316
+ "token": "<file_sep>",
317
+ "special": false,
318
+ "category": "starcoder"
319
+ },
320
+ {
321
+ "id": 53,
322
+ "token": "<issue_start>",
323
+ "special": false,
324
+ "category": "starcoder"
325
+ },
326
+ {
327
+ "id": 54,
328
+ "token": "<issue_comment>",
329
+ "special": false,
330
+ "category": "starcoder"
331
+ },
332
+ {
333
+ "id": 55,
334
+ "token": "<issue_closed>",
335
+ "special": false,
336
+ "category": "starcoder"
337
+ },
338
+ {
339
+ "id": 56,
340
+ "token": "<jupyter_start>",
341
+ "special": false,
342
+ "category": "starcoder"
343
+ },
344
+ {
345
+ "id": 57,
346
+ "token": "<jupyter_text>",
347
+ "special": false,
348
+ "category": "starcoder"
349
+ },
350
+ {
351
+ "id": 58,
352
+ "token": "<jupyter_code>",
353
+ "special": false,
354
+ "category": "starcoder"
355
+ },
356
+ {
357
+ "id": 59,
358
+ "token": "<jupyter_output>",
359
+ "special": false,
360
+ "category": "starcoder"
361
+ },
362
+ {
363
+ "id": 60,
364
+ "token": "<jupyter_script>",
365
+ "special": false,
366
+ "category": "starcoder"
367
+ },
368
+ {
369
+ "id": 61,
370
+ "token": "<empty_output>",
371
+ "special": false,
372
+ "category": "starcoder"
373
+ },
374
+ {
375
+ "id": 62,
376
+ "token": "<|begin_of_text|>",
377
+ "special": true,
378
+ "category": "llama"
379
+ },
380
+ {
381
+ "id": 63,
382
+ "token": "<|end_of_text|>",
383
+ "special": true,
384
+ "category": "llama"
385
+ },
386
+ {
387
+ "id": 64,
388
+ "token": "<|start_header_id|>",
389
+ "special": true,
390
+ "category": "llama"
391
+ },
392
+ {
393
+ "id": 65,
394
+ "token": "<|end_header_id|>",
395
+ "special": true,
396
+ "category": "llama"
397
+ },
398
+ {
399
+ "id": 66,
400
+ "token": "<|eot_id|>",
401
+ "special": true,
402
+ "category": "llama"
403
+ },
404
+ {
405
+ "id": 67,
406
+ "token": "<|eom_id|>",
407
+ "special": true,
408
+ "category": "llama"
409
+ },
410
+ {
411
+ "id": 68,
412
+ "token": "<|python_tag|>",
413
+ "special": true,
414
+ "category": "llama"
415
+ },
416
+ {
417
+ "id": 69,
418
+ "token": "<start_of_turn>",
419
+ "special": true,
420
+ "category": "gemma"
421
+ },
422
+ {
423
+ "id": 70,
424
+ "token": "<end_of_turn>",
425
+ "special": true,
426
+ "category": "gemma"
427
+ },
428
+ {
429
+ "id": 71,
430
+ "token": "<start_of_image>",
431
+ "special": true,
432
+ "category": "gemma"
433
+ },
434
+ {
435
+ "id": 72,
436
+ "token": "<s>",
437
+ "special": true,
438
+ "category": "mistral"
439
+ },
440
+ {
441
+ "id": 73,
442
+ "token": "</s>",
443
+ "special": true,
444
+ "category": "mistral"
445
+ },
446
+ {
447
+ "id": 74,
448
+ "token": "<unk>",
449
+ "special": true,
450
+ "category": "mistral"
451
+ },
452
+ {
453
+ "id": 75,
454
+ "token": "<pad>",
455
+ "special": true,
456
+ "category": "mistral"
457
+ },
458
+ {
459
+ "id": 76,
460
+ "token": "[INST]",
461
+ "special": true,
462
+ "category": "mistral"
463
+ },
464
+ {
465
+ "id": 77,
466
+ "token": "[/INST]",
467
+ "special": true,
468
+ "category": "mistral"
469
+ },
470
+ {
471
+ "id": 78,
472
+ "token": "[AVAILABLE_TOOLS]",
473
+ "special": true,
474
+ "category": "mistral"
475
+ },
476
+ {
477
+ "id": 79,
478
+ "token": "[/AVAILABLE_TOOLS]",
479
+ "special": true,
480
+ "category": "mistral"
481
+ },
482
+ {
483
+ "id": 80,
484
+ "token": "[TOOL_CALLS]",
485
+ "special": true,
486
+ "category": "mistral"
487
+ },
488
+ {
489
+ "id": 81,
490
+ "token": "[TOOL_RESULTS]",
491
+ "special": true,
492
+ "category": "mistral"
493
+ },
494
+ {
495
+ "id": 82,
496
+ "token": "[/TOOL_RESULTS]",
497
+ "special": true,
498
+ "category": "mistral"
499
+ },
500
+ {
501
+ "id": 83,
502
+ "token": "[SYSTEM_PROMPT]",
503
+ "special": true,
504
+ "category": "mistral"
505
+ },
506
+ {
507
+ "id": 84,
508
+ "token": "[/SYSTEM_PROMPT]",
509
+ "special": true,
510
+ "category": "mistral"
511
+ },
512
+ {
513
+ "id": 85,
514
+ "token": "[TOOL_CONTENT]",
515
+ "special": true,
516
+ "category": "mistral"
517
+ },
518
+ {
519
+ "id": 86,
520
+ "token": "[ARGS]",
521
+ "special": true,
522
+ "category": "mistral"
523
+ },
524
+ {
525
+ "id": 87,
526
+ "token": "[CALL_ID]",
527
+ "special": true,
528
+ "category": "mistral"
529
+ },
530
+ {
531
+ "id": 88,
532
+ "token": "[PREFIX]",
533
+ "special": true,
534
+ "category": "mistral"
535
+ },
536
+ {
537
+ "id": 89,
538
+ "token": "[MIDDLE]",
539
+ "special": true,
540
+ "category": "mistral"
541
+ },
542
+ {
543
+ "id": 90,
544
+ "token": "[SUFFIX]",
545
+ "special": true,
546
+ "category": "mistral"
547
+ },
548
+ {
549
+ "id": 91,
550
+ "token": "[THINK]",
551
+ "special": true,
552
+ "category": "mistral"
553
+ },
554
+ {
555
+ "id": 92,
556
+ "token": "[/THINK]",
557
+ "special": true,
558
+ "category": "mistral"
559
+ },
560
+ {
561
+ "id": 93,
562
+ "token": "[MODEL_SETTINGS]",
563
+ "special": true,
564
+ "category": "mistral"
565
+ },
566
+ {
567
+ "id": 94,
568
+ "token": "[/MODEL_SETTINGS]",
569
+ "special": true,
570
+ "category": "mistral"
571
+ },
572
+ {
573
+ "id": 95,
574
+ "token": "<|begin▁of▁sentence|>",
575
+ "special": true,
576
+ "category": "deepseek"
577
+ },
578
+ {
579
+ "id": 96,
580
+ "token": "<|end▁of▁sentence|>",
581
+ "special": true,
582
+ "category": "deepseek"
583
+ },
584
+ {
585
+ "id": 97,
586
+ "token": "<|User|>",
587
+ "special": true,
588
+ "category": "deepseek"
589
+ },
590
+ {
591
+ "id": 98,
592
+ "token": "<|Assistant|>",
593
+ "special": true,
594
+ "category": "deepseek"
595
+ },
596
+ {
597
+ "id": 99,
598
+ "token": "<|tool▁calls▁begin|>",
599
+ "special": true,
600
+ "category": "deepseek"
601
+ },
602
+ {
603
+ "id": 100,
604
+ "token": "<|tool▁call▁begin|>",
605
+ "special": true,
606
+ "category": "deepseek"
607
+ },
608
+ {
609
+ "id": 101,
610
+ "token": "<|tool▁sep|>",
611
+ "special": true,
612
+ "category": "deepseek"
613
+ },
614
+ {
615
+ "id": 102,
616
+ "token": "<|tool▁call▁end|>",
617
+ "special": true,
618
+ "category": "deepseek"
619
+ },
620
+ {
621
+ "id": 103,
622
+ "token": "<|tool▁calls▁end|>",
623
+ "special": true,
624
+ "category": "deepseek"
625
+ },
626
+ {
627
+ "id": 104,
628
+ "token": "<|tool▁outputs▁begin|>",
629
+ "special": true,
630
+ "category": "deepseek"
631
+ },
632
+ {
633
+ "id": 105,
634
+ "token": "<|tool▁output▁begin|>",
635
+ "special": true,
636
+ "category": "deepseek"
637
+ },
638
+ {
639
+ "id": 106,
640
+ "token": "<|tool▁output▁end|>",
641
+ "special": true,
642
+ "category": "deepseek"
643
+ },
644
+ {
645
+ "id": 107,
646
+ "token": "<|tool▁outputs▁end|>",
647
+ "special": true,
648
+ "category": "deepseek"
649
+ },
650
+ {
651
+ "id": 108,
652
+ "token": "<|fim▁begin|>",
653
+ "special": true,
654
+ "category": "deepseek"
655
+ },
656
+ {
657
+ "id": 109,
658
+ "token": "<|fim▁hole|>",
659
+ "special": true,
660
+ "category": "deepseek"
661
+ },
662
+ {
663
+ "id": 110,
664
+ "token": "<|fim▁end|>",
665
+ "special": true,
666
+ "category": "deepseek"
667
+ },
668
+ {
669
+ "id": 111,
670
+ "token": "<|start|>",
671
+ "special": true,
672
+ "category": "harmony"
673
+ },
674
+ {
675
+ "id": 112,
676
+ "token": "<|end|>",
677
+ "special": true,
678
+ "category": "harmony"
679
+ },
680
+ {
681
+ "id": 113,
682
+ "token": "<|message|>",
683
+ "special": true,
684
+ "category": "harmony"
685
+ },
686
+ {
687
+ "id": 114,
688
+ "token": "<|channel|>",
689
+ "special": true,
690
+ "category": "harmony"
691
+ },
692
+ {
693
+ "id": 115,
694
+ "token": "<|constrain|>",
695
+ "special": true,
696
+ "category": "harmony"
697
+ },
698
+ {
699
+ "id": 116,
700
+ "token": "<|return|>",
701
+ "special": true,
702
+ "category": "harmony"
703
+ },
704
+ {
705
+ "id": 117,
706
+ "token": "<|call|>",
707
+ "special": true,
708
+ "category": "harmony"
709
+ },
710
+ {
711
+ "id": 118,
712
+ "token": "<|vision_start|>",
713
+ "special": true,
714
+ "category": "vision"
715
+ },
716
+ {
717
+ "id": 119,
718
+ "token": "<|vision_end|>",
719
+ "special": true,
720
+ "category": "vision"
721
+ },
722
+ {
723
+ "id": 120,
724
+ "token": "<|vision_pad|>",
725
+ "special": true,
726
+ "category": "vision"
727
+ },
728
+ {
729
+ "id": 121,
730
+ "token": "<|image_pad|>",
731
+ "special": true,
732
+ "category": "vision"
733
+ },
734
+ {
735
+ "id": 122,
736
+ "token": "<|video_pad|>",
737
+ "special": true,
738
+ "category": "vision"
739
+ },
740
+ {
741
+ "id": 123,
742
+ "token": "<|image|>",
743
+ "special": true,
744
+ "category": "vision"
745
+ },
746
+ {
747
+ "id": 124,
748
+ "token": "<|video|>",
749
+ "special": true,
750
+ "category": "vision"
751
+ },
752
+ {
753
+ "id": 125,
754
+ "token": "[IMG]",
755
+ "special": true,
756
+ "category": "vision"
757
+ },
758
+ {
759
+ "id": 126,
760
+ "token": "[IMG_BREAK]",
761
+ "special": true,
762
+ "category": "vision"
763
+ },
764
+ {
765
+ "id": 127,
766
+ "token": "[IMG_END]",
767
+ "special": true,
768
+ "category": "vision"
769
+ },
770
+ {
771
+ "id": 128,
772
+ "token": "<|audio_bos|>",
773
+ "special": true,
774
+ "category": "audio"
775
+ },
776
+ {
777
+ "id": 129,
778
+ "token": "<|AUDIO|>",
779
+ "special": true,
780
+ "category": "audio"
781
+ },
782
+ {
783
+ "id": 130,
784
+ "token": "<|audio_eos|>",
785
+ "special": true,
786
+ "category": "audio"
787
+ },
788
+ {
789
+ "id": 131,
790
+ "token": "<|audio_pad|>",
791
+ "special": true,
792
+ "category": "audio"
793
+ },
794
+ {
795
+ "id": 132,
796
+ "token": "[AUDIO]",
797
+ "special": true,
798
+ "category": "audio"
799
+ },
800
+ {
801
+ "id": 133,
802
+ "token": "[BEGIN_AUDIO]",
803
+ "special": true,
804
+ "category": "audio"
805
+ },
806
+ {
807
+ "id": 134,
808
+ "token": "<|reserved_000|>",
809
+ "special": true,
810
+ "category": "reserved"
811
+ },
812
+ {
813
+ "id": 135,
814
+ "token": "<|reserved_001|>",
815
+ "special": true,
816
+ "category": "reserved"
817
+ },
818
+ {
819
+ "id": 136,
820
+ "token": "<|reserved_002|>",
821
+ "special": true,
822
+ "category": "reserved"
823
+ },
824
+ {
825
+ "id": 137,
826
+ "token": "<|reserved_003|>",
827
+ "special": true,
828
+ "category": "reserved"
829
+ },
830
+ {
831
+ "id": 138,
832
+ "token": "<|reserved_004|>",
833
+ "special": true,
834
+ "category": "reserved"
835
+ },
836
+ {
837
+ "id": 139,
838
+ "token": "<|reserved_005|>",
839
+ "special": true,
840
+ "category": "reserved"
841
+ },
842
+ {
843
+ "id": 140,
844
+ "token": "<|reserved_006|>",
845
+ "special": true,
846
+ "category": "reserved"
847
+ },
848
+ {
849
+ "id": 141,
850
+ "token": "<|reserved_007|>",
851
+ "special": true,
852
+ "category": "reserved"
853
+ },
854
+ {
855
+ "id": 142,
856
+ "token": "<|reserved_008|>",
857
+ "special": true,
858
+ "category": "reserved"
859
+ },
860
+ {
861
+ "id": 143,
862
+ "token": "<|reserved_009|>",
863
+ "special": true,
864
+ "category": "reserved"
865
+ },
866
+ {
867
+ "id": 144,
868
+ "token": "<|reserved_010|>",
869
+ "special": true,
870
+ "category": "reserved"
871
+ },
872
+ {
873
+ "id": 145,
874
+ "token": "<|reserved_011|>",
875
+ "special": true,
876
+ "category": "reserved"
877
+ },
878
+ {
879
+ "id": 146,
880
+ "token": "<|reserved_012|>",
881
+ "special": true,
882
+ "category": "reserved"
883
+ },
884
+ {
885
+ "id": 147,
886
+ "token": "<|reserved_013|>",
887
+ "special": true,
888
+ "category": "reserved"
889
+ },
890
+ {
891
+ "id": 148,
892
+ "token": "<|reserved_014|>",
893
+ "special": true,
894
+ "category": "reserved"
895
+ },
896
+ {
897
+ "id": 149,
898
+ "token": "<|reserved_015|>",
899
+ "special": true,
900
+ "category": "reserved"
901
+ },
902
+ {
903
+ "id": 150,
904
+ "token": "<|reserved_016|>",
905
+ "special": true,
906
+ "category": "reserved"
907
+ },
908
+ {
909
+ "id": 151,
910
+ "token": "<|reserved_017|>",
911
+ "special": true,
912
+ "category": "reserved"
913
+ },
914
+ {
915
+ "id": 152,
916
+ "token": "<|reserved_018|>",
917
+ "special": true,
918
+ "category": "reserved"
919
+ },
920
+ {
921
+ "id": 153,
922
+ "token": "<|reserved_019|>",
923
+ "special": true,
924
+ "category": "reserved"
925
+ },
926
+ {
927
+ "id": 154,
928
+ "token": "<|reserved_020|>",
929
+ "special": true,
930
+ "category": "reserved"
931
+ },
932
+ {
933
+ "id": 155,
934
+ "token": "<|reserved_021|>",
935
+ "special": true,
936
+ "category": "reserved"
937
+ },
938
+ {
939
+ "id": 156,
940
+ "token": "<|reserved_022|>",
941
+ "special": true,
942
+ "category": "reserved"
943
+ },
944
+ {
945
+ "id": 157,
946
+ "token": "<|reserved_023|>",
947
+ "special": true,
948
+ "category": "reserved"
949
+ },
950
+ {
951
+ "id": 158,
952
+ "token": "<|reserved_024|>",
953
+ "special": true,
954
+ "category": "reserved"
955
+ },
956
+ {
957
+ "id": 159,
958
+ "token": "<|reserved_025|>",
959
+ "special": true,
960
+ "category": "reserved"
961
+ },
962
+ {
963
+ "id": 160,
964
+ "token": "<|reserved_026|>",
965
+ "special": true,
966
+ "category": "reserved"
967
+ },
968
+ {
969
+ "id": 161,
970
+ "token": "<|reserved_027|>",
971
+ "special": true,
972
+ "category": "reserved"
973
+ },
974
+ {
975
+ "id": 162,
976
+ "token": "<|reserved_028|>",
977
+ "special": true,
978
+ "category": "reserved"
979
+ },
980
+ {
981
+ "id": 163,
982
+ "token": "<|reserved_029|>",
983
+ "special": true,
984
+ "category": "reserved"
985
+ },
986
+ {
987
+ "id": 164,
988
+ "token": "<|reserved_030|>",
989
+ "special": true,
990
+ "category": "reserved"
991
+ },
992
+ {
993
+ "id": 165,
994
+ "token": "<|reserved_031|>",
995
+ "special": true,
996
+ "category": "reserved"
997
+ },
998
+ {
999
+ "id": 166,
1000
+ "token": "<|reserved_032|>",
1001
+ "special": true,
1002
+ "category": "reserved"
1003
+ },
1004
+ {
1005
+ "id": 167,
1006
+ "token": "<|reserved_033|>",
1007
+ "special": true,
1008
+ "category": "reserved"
1009
+ },
1010
+ {
1011
+ "id": 168,
1012
+ "token": "<|reserved_034|>",
1013
+ "special": true,
1014
+ "category": "reserved"
1015
+ },
1016
+ {
1017
+ "id": 169,
1018
+ "token": "<|reserved_035|>",
1019
+ "special": true,
1020
+ "category": "reserved"
1021
+ },
1022
+ {
1023
+ "id": 170,
1024
+ "token": "<|reserved_036|>",
1025
+ "special": true,
1026
+ "category": "reserved"
1027
+ },
1028
+ {
1029
+ "id": 171,
1030
+ "token": "<|reserved_037|>",
1031
+ "special": true,
1032
+ "category": "reserved"
1033
+ },
1034
+ {
1035
+ "id": 172,
1036
+ "token": "<|reserved_038|>",
1037
+ "special": true,
1038
+ "category": "reserved"
1039
+ },
1040
+ {
1041
+ "id": 173,
1042
+ "token": "<|reserved_039|>",
1043
+ "special": true,
1044
+ "category": "reserved"
1045
+ },
1046
+ {
1047
+ "id": 174,
1048
+ "token": "<|reserved_040|>",
1049
+ "special": true,
1050
+ "category": "reserved"
1051
+ },
1052
+ {
1053
+ "id": 175,
1054
+ "token": "<|reserved_041|>",
1055
+ "special": true,
1056
+ "category": "reserved"
1057
+ },
1058
+ {
1059
+ "id": 176,
1060
+ "token": "<|reserved_042|>",
1061
+ "special": true,
1062
+ "category": "reserved"
1063
+ },
1064
+ {
1065
+ "id": 177,
1066
+ "token": "<|reserved_043|>",
1067
+ "special": true,
1068
+ "category": "reserved"
1069
+ },
1070
+ {
1071
+ "id": 178,
1072
+ "token": "<|reserved_044|>",
1073
+ "special": true,
1074
+ "category": "reserved"
1075
+ },
1076
+ {
1077
+ "id": 179,
1078
+ "token": "<|reserved_045|>",
1079
+ "special": true,
1080
+ "category": "reserved"
1081
+ },
1082
+ {
1083
+ "id": 180,
1084
+ "token": "<|reserved_046|>",
1085
+ "special": true,
1086
+ "category": "reserved"
1087
+ },
1088
+ {
1089
+ "id": 181,
1090
+ "token": "<|reserved_047|>",
1091
+ "special": true,
1092
+ "category": "reserved"
1093
+ },
1094
+ {
1095
+ "id": 182,
1096
+ "token": "<|reserved_048|>",
1097
+ "special": true,
1098
+ "category": "reserved"
1099
+ },
1100
+ {
1101
+ "id": 183,
1102
+ "token": "<|reserved_049|>",
1103
+ "special": true,
1104
+ "category": "reserved"
1105
+ },
1106
+ {
1107
+ "id": 184,
1108
+ "token": "<|reserved_050|>",
1109
+ "special": true,
1110
+ "category": "reserved"
1111
+ },
1112
+ {
1113
+ "id": 185,
1114
+ "token": "<|reserved_051|>",
1115
+ "special": true,
1116
+ "category": "reserved"
1117
+ },
1118
+ {
1119
+ "id": 186,
1120
+ "token": "<|reserved_052|>",
1121
+ "special": true,
1122
+ "category": "reserved"
1123
+ },
1124
+ {
1125
+ "id": 187,
1126
+ "token": "<|reserved_053|>",
1127
+ "special": true,
1128
+ "category": "reserved"
1129
+ },
1130
+ {
1131
+ "id": 188,
1132
+ "token": "<|reserved_054|>",
1133
+ "special": true,
1134
+ "category": "reserved"
1135
+ },
1136
+ {
1137
+ "id": 189,
1138
+ "token": "<|reserved_055|>",
1139
+ "special": true,
1140
+ "category": "reserved"
1141
+ },
1142
+ {
1143
+ "id": 190,
1144
+ "token": "<|reserved_056|>",
1145
+ "special": true,
1146
+ "category": "reserved"
1147
+ },
1148
+ {
1149
+ "id": 191,
1150
+ "token": "<|reserved_057|>",
1151
+ "special": true,
1152
+ "category": "reserved"
1153
+ },
1154
+ {
1155
+ "id": 192,
1156
+ "token": "<|reserved_058|>",
1157
+ "special": true,
1158
+ "category": "reserved"
1159
+ },
1160
+ {
1161
+ "id": 193,
1162
+ "token": "<|reserved_059|>",
1163
+ "special": true,
1164
+ "category": "reserved"
1165
+ },
1166
+ {
1167
+ "id": 194,
1168
+ "token": "<|reserved_060|>",
1169
+ "special": true,
1170
+ "category": "reserved"
1171
+ },
1172
+ {
1173
+ "id": 195,
1174
+ "token": "<|reserved_061|>",
1175
+ "special": true,
1176
+ "category": "reserved"
1177
+ },
1178
+ {
1179
+ "id": 196,
1180
+ "token": "<|reserved_062|>",
1181
+ "special": true,
1182
+ "category": "reserved"
1183
+ },
1184
+ {
1185
+ "id": 197,
1186
+ "token": "<|reserved_063|>",
1187
+ "special": true,
1188
+ "category": "reserved"
1189
+ }
1190
+ ]
resume/artifacts/tokenizer/special_tokens_map.json ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token": "<|bos|>",
3
+ "eos_token": "<|eos|>",
4
+ "pad_token": "<|pad|>",
5
+ "unk_token": "<|unk|>",
6
+ "additional_special_tokens": [
7
+ "<|eod|>",
8
+ "<|startoftext|>",
9
+ "<|endoftext|>",
10
+ "<|endofprompt|>",
11
+ "<|im_start|>",
12
+ "<|im_end|>",
13
+ "<|system|>",
14
+ "<|developer|>",
15
+ "<|user|>",
16
+ "<|assistant|>",
17
+ "<|tool|>",
18
+ "<|function|>",
19
+ "<|observation|>",
20
+ "<|eot|>",
21
+ "<|begin_of_text|>",
22
+ "<|end_of_text|>",
23
+ "<|start_header_id|>",
24
+ "<|end_header_id|>",
25
+ "<|eot_id|>",
26
+ "<|eom_id|>",
27
+ "<|python_tag|>",
28
+ "<start_of_turn>",
29
+ "<end_of_turn>",
30
+ "<start_of_image>",
31
+ "<s>",
32
+ "</s>",
33
+ "<unk>",
34
+ "<pad>",
35
+ "[INST]",
36
+ "[/INST]",
37
+ "[AVAILABLE_TOOLS]",
38
+ "[/AVAILABLE_TOOLS]",
39
+ "[TOOL_CALLS]",
40
+ "[TOOL_RESULTS]",
41
+ "[/TOOL_RESULTS]",
42
+ "[SYSTEM_PROMPT]",
43
+ "[/SYSTEM_PROMPT]",
44
+ "[TOOL_CONTENT]",
45
+ "[ARGS]",
46
+ "[CALL_ID]",
47
+ "[PREFIX]",
48
+ "[MIDDLE]",
49
+ "[SUFFIX]",
50
+ "[THINK]",
51
+ "[/THINK]",
52
+ "[MODEL_SETTINGS]",
53
+ "[/MODEL_SETTINGS]",
54
+ "<|begin▁of▁sentence|>",
55
+ "<|end▁of▁sentence|>",
56
+ "<|User|>",
57
+ "<|Assistant|>",
58
+ "<|tool▁calls▁begin|>",
59
+ "<|tool▁call▁begin|>",
60
+ "<|tool▁sep|>",
61
+ "<|tool▁call▁end|>",
62
+ "<|tool▁calls▁end|>",
63
+ "<|tool▁outputs▁begin|>",
64
+ "<|tool▁output▁begin|>",
65
+ "<|tool▁output▁end|>",
66
+ "<|tool▁outputs▁end|>",
67
+ "<|fim▁begin|>",
68
+ "<|fim▁hole|>",
69
+ "<|fim▁end|>",
70
+ "<|start|>",
71
+ "<|end|>",
72
+ "<|message|>",
73
+ "<|channel|>",
74
+ "<|constrain|>",
75
+ "<|return|>",
76
+ "<|call|>",
77
+ "<|vision_start|>",
78
+ "<|vision_end|>",
79
+ "<|vision_pad|>",
80
+ "<|image_pad|>",
81
+ "<|video_pad|>",
82
+ "<|image|>",
83
+ "<|video|>",
84
+ "[IMG]",
85
+ "[IMG_BREAK]",
86
+ "[IMG_END]",
87
+ "<|audio_bos|>",
88
+ "<|AUDIO|>",
89
+ "<|audio_eos|>",
90
+ "<|audio_pad|>",
91
+ "[AUDIO]",
92
+ "[BEGIN_AUDIO]",
93
+ "<|reserved_000|>",
94
+ "<|reserved_001|>",
95
+ "<|reserved_002|>",
96
+ "<|reserved_003|>",
97
+ "<|reserved_004|>",
98
+ "<|reserved_005|>",
99
+ "<|reserved_006|>",
100
+ "<|reserved_007|>",
101
+ "<|reserved_008|>",
102
+ "<|reserved_009|>",
103
+ "<|reserved_010|>",
104
+ "<|reserved_011|>",
105
+ "<|reserved_012|>",
106
+ "<|reserved_013|>",
107
+ "<|reserved_014|>",
108
+ "<|reserved_015|>",
109
+ "<|reserved_016|>",
110
+ "<|reserved_017|>",
111
+ "<|reserved_018|>",
112
+ "<|reserved_019|>",
113
+ "<|reserved_020|>",
114
+ "<|reserved_021|>",
115
+ "<|reserved_022|>",
116
+ "<|reserved_023|>",
117
+ "<|reserved_024|>",
118
+ "<|reserved_025|>",
119
+ "<|reserved_026|>",
120
+ "<|reserved_027|>",
121
+ "<|reserved_028|>",
122
+ "<|reserved_029|>",
123
+ "<|reserved_030|>",
124
+ "<|reserved_031|>",
125
+ "<|reserved_032|>",
126
+ "<|reserved_033|>",
127
+ "<|reserved_034|>",
128
+ "<|reserved_035|>",
129
+ "<|reserved_036|>",
130
+ "<|reserved_037|>",
131
+ "<|reserved_038|>",
132
+ "<|reserved_039|>",
133
+ "<|reserved_040|>",
134
+ "<|reserved_041|>",
135
+ "<|reserved_042|>",
136
+ "<|reserved_043|>",
137
+ "<|reserved_044|>",
138
+ "<|reserved_045|>",
139
+ "<|reserved_046|>",
140
+ "<|reserved_047|>",
141
+ "<|reserved_048|>",
142
+ "<|reserved_049|>",
143
+ "<|reserved_050|>",
144
+ "<|reserved_051|>",
145
+ "<|reserved_052|>",
146
+ "<|reserved_053|>",
147
+ "<|reserved_054|>",
148
+ "<|reserved_055|>",
149
+ "<|reserved_056|>",
150
+ "<|reserved_057|>",
151
+ "<|reserved_058|>",
152
+ "<|reserved_059|>",
153
+ "<|reserved_060|>",
154
+ "<|reserved_061|>",
155
+ "<|reserved_062|>",
156
+ "<|reserved_063|>"
157
+ ]
158
+ }
resume/artifacts/tokenizer/tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
resume/artifacts/tokenizer/tokenizer_config.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "tokenizer_class": "PreTrainedTokenizerFast",
3
+ "model_max_length": 2048,
4
+ "bos_token": "<|bos|>",
5
+ "eos_token": "<|eos|>",
6
+ "pad_token": "<|pad|>",
7
+ "unk_token": "<|unk|>",
8
+ "chat_template": "{%- for message in messages %}\n{%- if message['role'] not in ['system', 'developer', 'user', 'assistant', 'tool', 'function', 'observation'] %}\n{{- raise_exception('Unsupported QyrouArch chat role: ' + message['role']) }}\n{%- endif %}\n{{- '<|im_start|><|' + message['role'] + '|>\\n' + message['content'] + '<|im_end|>\\n' }}\n{%- endfor %}\n{%- if add_generation_prompt %}\n{{- '<|im_start|><|assistant|>\\n' }}\n{%- endif %}\n\n",
9
+ "clean_up_tokenization_spaces": false
10
+ }
resume/artifacts/tokenizer/tokenizer_quality.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "rows": 20000,
3
+ "bytes": 50676351,
4
+ "tokens": 12913545,
5
+ "bytes_per_token": 3.924278809575527,
6
+ "unknown_tokens": 0,
7
+ "long_fragment_rate": 2.0133898166614976e-06,
8
+ "vocab_size": 20000,
9
+ "requested_markers": 134,
10
+ "reserved_markers": 64,
11
+ "sample_rows": 6236030,
12
+ "sample_bytes": 19870811666,
13
+ "sample_sha256": "d16839542054d09d28b257f7afd3bee0ae849fec257e239e3a6792884d76acc1",
14
+ "corpus_manifest_sha256": "0a52b66efc4668646b373241610fb03af13807e785685a501bfc92c7bb87c6b0",
15
+ "tokenizer_sha256": "5b14f71c0e1d5a628c440067db4cd75d1aa855e6c34b4dd577a2412031e82ca7"
16
+ }
resume/checkpoints/qyrou-1-exp-base/checkpoint-step-000680000/manifest.json ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "version": 1,
3
+ "step": 680000,
4
+ "total_tokens": 33423360000,
5
+ "validation_loss": null,
6
+ "created_at": 1785466854.7029169,
7
+ "files": [
8
+ {
9
+ "path": "model\\cache.py",
10
+ "bytes": 3950,
11
+ "sha256": "14e55f9639c155b728ab887c48729547345c8dac6b38da5ecdceb8f71d309977"
12
+ },
13
+ {
14
+ "path": "model\\config.json",
15
+ "bytes": 1123,
16
+ "sha256": "6f7818cab6cdd79ec29b0cae53e54937d4250721c31194027ef10bcbf066ba2d"
17
+ },
18
+ {
19
+ "path": "model\\configuration_qyrou_arch.py",
20
+ "bytes": 3162,
21
+ "sha256": "b20e96120f16c73505e6bb714965d25115182c09cf2972979e9e26b18228df9d"
22
+ },
23
+ {
24
+ "path": "model\\generation_config.json",
25
+ "bytes": 226,
26
+ "sha256": "2a0b4fdc827622535d629dc7bee72e376b7962aa1198ed9b6ba04b5ea62b7182"
27
+ },
28
+ {
29
+ "path": "model\\model.safetensors",
30
+ "bytes": 261234016,
31
+ "sha256": "15e793a9c28db4f2c843a4369363170c619ac1c73fdc4e1c98ffef5a9ca07f23"
32
+ },
33
+ {
34
+ "path": "model\\modeling_qyrou_arch.py",
35
+ "bytes": 20438,
36
+ "sha256": "9834518c57aaf605e18cc1a927f5b5a30915f6bcec000e59286ab5c1ab1e67bd"
37
+ },
38
+ {
39
+ "path": "model\\triton_kernels.py",
40
+ "bytes": 3996,
41
+ "sha256": "b57566f7acfd30180dfd2388c1f4a9bafd6bf43f83a38706a7aacce58c54b985"
42
+ },
43
+ {
44
+ "path": "training_state.pt",
45
+ "bytes": 526781105,
46
+ "sha256": "06ed8c306ade8342a16f73be3c4eb327bc1ce1f88900a772e3f4731872870031"
47
+ }
48
+ ]
49
+ }
resume/checkpoints/qyrou-1-exp-base/checkpoint-step-000680000/model/cache.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any
5
+
6
+ import torch
7
+
8
+
9
+ @dataclass
10
+ class QyrouArchHybridCache:
11
+ """Attention K/V plus short-convolution state for incremental decoding."""
12
+
13
+ num_layers: int
14
+ max_cache_len: int | None = None
15
+ attention: list[tuple[torch.Tensor, torch.Tensor] | None] = field(init=False)
16
+ convolution: list[torch.Tensor | None] = field(init=False)
17
+ seen_tokens: int = 0
18
+
19
+ def __post_init__(self) -> None:
20
+ self.attention = [None] * self.num_layers
21
+ self.convolution = [None] * self.num_layers
22
+ if self.max_cache_len is not None and self.max_cache_len <= 0:
23
+ raise ValueError("max_cache_len must be positive")
24
+
25
+ @property
26
+ def is_static(self) -> bool:
27
+ return self.max_cache_len is not None
28
+
29
+ def get_seq_length(self, _layer_idx: int = 0) -> int:
30
+ return self.seen_tokens
31
+
32
+ def get_max_cache_shape(self) -> int | None:
33
+ return self.max_cache_len
34
+
35
+ def update_attention(
36
+ self,
37
+ layer_idx: int,
38
+ key: torch.Tensor,
39
+ value: torch.Tensor,
40
+ cache_position: torch.Tensor,
41
+ ) -> tuple[torch.Tensor, torch.Tensor]:
42
+ if key.ndim != 4 or key.shape != value.shape:
43
+ raise ValueError("K/V cache tensors must have identical [batch, heads, seq, dim] shapes")
44
+ if self.is_static:
45
+ assert self.max_cache_len is not None
46
+ if int(cache_position.max()) >= self.max_cache_len:
47
+ raise ValueError("Static cache capacity exceeded")
48
+ existing = self.attention[layer_idx]
49
+ if existing is None:
50
+ shape = (*key.shape[:2], self.max_cache_len, key.shape[-1])
51
+ key_cache = torch.zeros(shape, dtype=key.dtype, device=key.device)
52
+ value_cache = torch.zeros(shape, dtype=value.dtype, device=value.device)
53
+ else:
54
+ key_cache, value_cache = existing
55
+ key_cache.index_copy_(2, cache_position, key)
56
+ value_cache.index_copy_(2, cache_position, value)
57
+ self.attention[layer_idx] = (key_cache, value_cache)
58
+ visible = max(self.seen_tokens, int(cache_position.max()) + 1)
59
+ return key_cache[:, :, :visible], value_cache[:, :, :visible]
60
+ existing = self.attention[layer_idx]
61
+ if existing is not None:
62
+ key = torch.cat((existing[0], key), dim=2)
63
+ value = torch.cat((existing[1], value), dim=2)
64
+ self.attention[layer_idx] = (key, value)
65
+ return key, value
66
+
67
+ def update_convolution(self, layer_idx: int, state: torch.Tensor) -> None:
68
+ self.convolution[layer_idx] = state
69
+
70
+ def get_convolution(self, layer_idx: int) -> torch.Tensor | None:
71
+ return self.convolution[layer_idx]
72
+
73
+ def finish_step(self, cache_position: torch.Tensor) -> None:
74
+ if cache_position.numel():
75
+ self.seen_tokens = max(self.seen_tokens, int(cache_position.max()) + 1)
76
+
77
+ def reorder_cache(self, beam_idx: torch.LongTensor) -> QyrouArchHybridCache:
78
+ for index, item in enumerate(self.attention):
79
+ if item is not None:
80
+ self.attention[index] = (
81
+ item[0].index_select(0, beam_idx),
82
+ item[1].index_select(0, beam_idx),
83
+ )
84
+ for index, state in enumerate(self.convolution):
85
+ if state is not None:
86
+ self.convolution[index] = state.index_select(0, beam_idx)
87
+ return self
88
+
89
+ def batch_repeat_interleave(self, repeats: int) -> QyrouArchHybridCache:
90
+ indices = torch.arange(
91
+ next(t[0] for t in self.attention if t is not None).shape[0],
92
+ device=next(t[0] for t in self.attention if t is not None).device,
93
+ ).repeat_interleave(repeats)
94
+ return self.reorder_cache(indices)
95
+
96
+ def to_legacy_cache(self) -> tuple[Any, ...]:
97
+ return tuple(self.attention)
98
+
resume/checkpoints/qyrou-1-exp-base/checkpoint-step-000680000/model/config.json ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "QyrouArchForCausalLM"
4
+ ],
5
+ "attention_backend": "cudnn",
6
+ "auto_map": {
7
+ "AutoConfig": "configuration_qyrou_arch.QyrouArchConfig",
8
+ "AutoModelForCausalLM": "modeling_qyrou_arch.QyrouArchForCausalLM"
9
+ },
10
+ "bos_token_id": 0,
11
+ "conv_kernel_size": 7,
12
+ "conv_layers": [
13
+ 4,
14
+ 9,
15
+ 14,
16
+ 19
17
+ ],
18
+ "cut_cross_entropy": true,
19
+ "dtype": "float32",
20
+ "eod_token_id": 4,
21
+ "eos_token_id": 1,
22
+ "fused_gate_up_projection": true,
23
+ "fused_qkv_projection": true,
24
+ "hidden_act": "silu",
25
+ "hidden_size": 512,
26
+ "initializer_range": 0.02,
27
+ "intermediate_size": 1328,
28
+ "liger_rms_norm": true,
29
+ "liger_swiglu": true,
30
+ "max_position_embeddings": 2048,
31
+ "model_type": "qyrou_arch",
32
+ "num_attention_heads": 8,
33
+ "num_hidden_layers": 21,
34
+ "num_key_value_heads": 2,
35
+ "pad_token_id": 2,
36
+ "qk_norm": true,
37
+ "residual_initializer_scale": 0.1543033499620919,
38
+ "rms_norm_eps": 1e-05,
39
+ "rope_theta": 10000.0,
40
+ "tie_word_embeddings": true,
41
+ "transformers_version": "5.13.0",
42
+ "unk_token_id": 3,
43
+ "use_cache": true,
44
+ "vocab_size": 20000
45
+ }
resume/checkpoints/qyrou-1-exp-base/checkpoint-step-000680000/model/configuration_qyrou_arch.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ try:
6
+ from transformers import PretrainedConfig
7
+ except ImportError:
8
+ class PretrainedConfig: # type: ignore[no-redef]
9
+ model_type = "qyrou_arch"
10
+
11
+ def __init__(self, **kwargs: Any) -> None:
12
+ for key, value in kwargs.items():
13
+ setattr(self, key, value)
14
+
15
+
16
+ class QyrouArchConfig(PretrainedConfig):
17
+ model_type = "qyrou_arch"
18
+
19
+ def __init__(
20
+ self,
21
+ vocab_size: int = 20_000,
22
+ hidden_size: int = 512,
23
+ num_hidden_layers: int = 21,
24
+ num_attention_heads: int = 8,
25
+ num_key_value_heads: int = 2,
26
+ intermediate_size: int = 1_328,
27
+ conv_layers: list[int] | None = None,
28
+ conv_kernel_size: int = 7,
29
+ rope_theta: float = 10_000.0,
30
+ rms_norm_eps: float = 1e-5,
31
+ max_position_embeddings: int = 2_048,
32
+ initializer_range: float = 0.02,
33
+ residual_initializer_scale: float = 1.0 / (42.0**0.5),
34
+ tie_word_embeddings: bool = True,
35
+ qk_norm: bool = True,
36
+ attention_backend: str = "cudnn",
37
+ fused_qkv_projection: bool = True,
38
+ fused_gate_up_projection: bool = True,
39
+ liger_rms_norm: bool = True,
40
+ liger_swiglu: bool = True,
41
+ cut_cross_entropy: bool = True,
42
+ use_cache: bool = True,
43
+ eod_token_id: int = 4,
44
+ **kwargs: Any,
45
+ ) -> None:
46
+ super().__init__(tie_word_embeddings=tie_word_embeddings, **kwargs)
47
+ if hidden_size % num_attention_heads:
48
+ raise ValueError("hidden_size must be divisible by num_attention_heads")
49
+ if num_attention_heads % num_key_value_heads:
50
+ raise ValueError("num_attention_heads must be divisible by num_key_value_heads")
51
+ conv_layers = conv_layers if conv_layers is not None else [4, 9, 14, 19]
52
+ if len(set(conv_layers)) != len(conv_layers) or not all(
53
+ 1 <= layer <= num_hidden_layers for layer in conv_layers
54
+ ):
55
+ raise ValueError("conv_layers must contain unique one-based layer indices")
56
+ self.vocab_size = vocab_size
57
+ self.hidden_size = hidden_size
58
+ self.num_hidden_layers = num_hidden_layers
59
+ self.num_attention_heads = num_attention_heads
60
+ self.num_key_value_heads = num_key_value_heads
61
+ self.intermediate_size = intermediate_size
62
+ self.conv_layers = conv_layers
63
+ self.conv_kernel_size = conv_kernel_size
64
+ self.rope_theta = rope_theta
65
+ self.rms_norm_eps = rms_norm_eps
66
+ self.max_position_embeddings = max_position_embeddings
67
+ self.initializer_range = initializer_range
68
+ self.residual_initializer_scale = residual_initializer_scale
69
+ self.qk_norm = qk_norm
70
+ self.attention_backend = attention_backend
71
+ self.fused_qkv_projection = fused_qkv_projection
72
+ self.fused_gate_up_projection = fused_gate_up_projection
73
+ self.liger_rms_norm = liger_rms_norm
74
+ self.liger_swiglu = liger_swiglu
75
+ self.cut_cross_entropy = cut_cross_entropy
76
+ self.use_cache = use_cache
77
+ self.eod_token_id = eod_token_id
78
+ self.hidden_act = "silu"
79
+
resume/checkpoints/qyrou-1-exp-base/checkpoint-step-000680000/model/generation_config.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_from_model_config": true,
3
+ "bos_token_id": 0,
4
+ "eos_token_id": 1,
5
+ "output_attentions": false,
6
+ "output_hidden_states": false,
7
+ "pad_token_id": 2,
8
+ "transformers_version": "5.13.0",
9
+ "use_cache": true
10
+ }
resume/checkpoints/qyrou-1-exp-base/checkpoint-step-000680000/model/model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:15e793a9c28db4f2c843a4369363170c619ac1c73fdc4e1c98ffef5a9ca07f23
3
+ size 261234016
resume/checkpoints/qyrou-1-exp-base/checkpoint-step-000680000/model/modeling_qyrou_arch.py ADDED
@@ -0,0 +1,504 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+ import torch.nn.functional as F
8
+ from torch.nn.attention import SDPBackend, sdpa_kernel
9
+ from transformers import PreTrainedModel
10
+ from transformers.generation import GenerationMixin
11
+ from transformers.modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
12
+
13
+ from .cache import QyrouArchHybridCache
14
+ from .configuration_qyrou_arch import QyrouArchConfig
15
+
16
+ try:
17
+ from .triton_kernels import PackedSwiGLUFunction
18
+ except (ImportError, RuntimeError):
19
+ PackedSwiGLUFunction = None
20
+
21
+ try:
22
+ from liger_kernel.ops.rms_norm import LigerRMSNormFunction
23
+ from liger_kernel.ops.swiglu import LigerSiLUMulFunction
24
+ except ImportError:
25
+ LigerRMSNormFunction = None
26
+ LigerSiLUMulFunction = None
27
+
28
+ try:
29
+ from cut_cross_entropy import linear_cross_entropy as cut_linear_cross_entropy
30
+ except ImportError:
31
+ cut_linear_cross_entropy = None
32
+
33
+
34
+ class RMSNorm(nn.Module):
35
+ def __init__(self, dim: int, eps: float, use_liger: bool = False) -> None:
36
+ super().__init__()
37
+ self.weight = nn.Parameter(torch.ones(dim))
38
+ self.eps = eps
39
+ self.use_liger = use_liger
40
+
41
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
42
+ if (
43
+ x.is_cuda
44
+ and self.use_liger
45
+ and LigerRMSNormFunction is not None
46
+ and not torch.compiler.is_compiling()
47
+ ):
48
+ return LigerRMSNormFunction.apply(x, self.weight, self.eps, 0.0, "llama", False, None)
49
+ x_float = x.float()
50
+ normalized = x_float * torch.rsqrt(x_float.square().mean(-1, keepdim=True) + self.eps)
51
+ return normalized.to(x.dtype) * self.weight.to(x.dtype)
52
+
53
+
54
+ def _apply_rope(
55
+ x: torch.Tensor,
56
+ cos: torch.Tensor,
57
+ sin: torch.Tensor,
58
+ ) -> torch.Tensor:
59
+ even, odd = x[..., 0::2], x[..., 1::2]
60
+ return torch.stack((even * cos - odd * sin, even * sin + odd * cos), dim=-1).flatten(-2)
61
+
62
+
63
+ def _last_valid_state(
64
+ x: torch.Tensor,
65
+ mask: torch.Tensor | None,
66
+ state_length: int,
67
+ ) -> torch.Tensor:
68
+ if state_length == 0:
69
+ return x[:, :0]
70
+ if mask is None:
71
+ return F.pad(x, (0, 0, max(0, state_length - x.shape[1]), 0))[:, -state_length:]
72
+ result = torch.zeros(
73
+ (x.shape[0], state_length, x.shape[2]),
74
+ dtype=x.dtype,
75
+ device=x.device,
76
+ )
77
+ for batch_index in range(x.shape[0]):
78
+ valid = x[batch_index][mask[batch_index].bool()]
79
+ valid = valid[-state_length:]
80
+ result[batch_index, -valid.shape[0] :] = valid
81
+ return result
82
+
83
+
84
+ class CausalGQA(nn.Module):
85
+ def __init__(self, config: QyrouArchConfig, layer_idx: int) -> None:
86
+ super().__init__()
87
+ self.layer_idx = layer_idx
88
+ self.num_heads = config.num_attention_heads
89
+ self.num_kv_heads = config.num_key_value_heads
90
+ self.head_dim = config.hidden_size // config.num_attention_heads
91
+ self.q_size = self.num_heads * self.head_dim
92
+ self.kv_size = self.num_kv_heads * self.head_dim
93
+ self.attention_backend = config.attention_backend
94
+ if config.fused_qkv_projection:
95
+ self.qkv_proj = nn.Linear(config.hidden_size, self.q_size + 2 * self.kv_size, bias=False)
96
+ else:
97
+ self.q_proj = nn.Linear(config.hidden_size, self.q_size, bias=False)
98
+ self.k_proj = nn.Linear(config.hidden_size, self.kv_size, bias=False)
99
+ self.v_proj = nn.Linear(config.hidden_size, self.kv_size, bias=False)
100
+ self.o_proj = nn.Linear(config.hidden_size, config.hidden_size, bias=False)
101
+ self.o_proj._qyrou_arch_residual_projection = True # type: ignore[attr-defined]
102
+ self.q_norm = RMSNorm(self.head_dim, config.rms_norm_eps) if config.qk_norm else nn.Identity()
103
+ self.k_norm = RMSNorm(self.head_dim, config.rms_norm_eps) if config.qk_norm else nn.Identity()
104
+
105
+ def forward(
106
+ self,
107
+ x: torch.Tensor,
108
+ rope: tuple[torch.Tensor, torch.Tensor],
109
+ attention_mask: torch.Tensor | None,
110
+ cache: QyrouArchHybridCache | None,
111
+ cache_position: torch.Tensor,
112
+ ) -> torch.Tensor:
113
+ batch, query_length, _ = x.shape
114
+ if hasattr(self, "qkv_proj"):
115
+ q, k, v = self.qkv_proj(x).split((self.q_size, self.kv_size, self.kv_size), dim=-1)
116
+ else:
117
+ q, k, v = self.q_proj(x), self.k_proj(x), self.v_proj(x)
118
+ q = q.view(batch, query_length, self.num_heads, self.head_dim).transpose(1, 2)
119
+ k = k.view(batch, query_length, self.num_kv_heads, self.head_dim).transpose(1, 2)
120
+ v = v.view(batch, query_length, self.num_kv_heads, self.head_dim).transpose(1, 2)
121
+ q, k = self.q_norm(q), self.k_norm(k)
122
+ q = _apply_rope(q, *rope)
123
+ k = _apply_rope(k, *rope)
124
+ if cache is not None:
125
+ k, v = cache.update_attention(self.layer_idx, k, v, cache_position)
126
+ key_length = k.shape[2]
127
+
128
+ mask = None
129
+ use_fast_causal = cache is None and attention_mask is None
130
+ if not use_fast_causal:
131
+ key_positions = torch.arange(key_length, device=x.device)
132
+ allowed = key_positions[None, :] <= cache_position[:, None]
133
+ mask = allowed[None, None, :, :].expand(batch, 1, query_length, key_length)
134
+ if attention_mask is not None:
135
+ if attention_mask.shape[-1] < key_length:
136
+ raise ValueError("attention_mask is shorter than the cached key sequence")
137
+ mask = mask & attention_mask[:, None, None, :key_length].bool()
138
+ if q.is_cuda and self.attention_backend in {"cudnn", "flash"}:
139
+ backends = (
140
+ [SDPBackend.CUDNN_ATTENTION, SDPBackend.FLASH_ATTENTION, SDPBackend.MATH]
141
+ if self.attention_backend == "cudnn"
142
+ else [SDPBackend.FLASH_ATTENTION, SDPBackend.CUDNN_ATTENTION, SDPBackend.MATH]
143
+ )
144
+ with sdpa_kernel(
145
+ backends,
146
+ set_priority=True,
147
+ ):
148
+ output = F.scaled_dot_product_attention(
149
+ q,
150
+ k,
151
+ v,
152
+ attn_mask=mask,
153
+ is_causal=use_fast_causal,
154
+ enable_gqa=True,
155
+ )
156
+ else:
157
+ output = F.scaled_dot_product_attention(
158
+ q,
159
+ k,
160
+ v,
161
+ attn_mask=mask,
162
+ is_causal=use_fast_causal,
163
+ enable_gqa=True,
164
+ )
165
+ output = output.transpose(1, 2).contiguous().view(batch, query_length, -1)
166
+ return self.o_proj(output)
167
+
168
+
169
+ class CausalConvMixer(nn.Module):
170
+ def __init__(self, config: QyrouArchConfig, layer_idx: int) -> None:
171
+ super().__init__()
172
+ self.layer_idx = layer_idx
173
+ self.kernel_size = config.conv_kernel_size
174
+ self.depthwise = nn.Conv1d(
175
+ config.hidden_size,
176
+ config.hidden_size,
177
+ kernel_size=self.kernel_size,
178
+ groups=config.hidden_size,
179
+ bias=False,
180
+ )
181
+ self.pointwise = nn.Linear(config.hidden_size, config.hidden_size, bias=False)
182
+ self.pointwise._qyrou_arch_residual_projection = True # type: ignore[attr-defined]
183
+
184
+ def forward(
185
+ self,
186
+ x: torch.Tensor,
187
+ _rope: tuple[torch.Tensor, torch.Tensor],
188
+ attention_mask: torch.Tensor | None,
189
+ cache: QyrouArchHybridCache | None,
190
+ _cache_position: torch.Tensor,
191
+ ) -> torch.Tensor:
192
+ state_length = self.kernel_size - 1
193
+ query_mask = attention_mask[:, -x.shape[1] :] if attention_mask is not None else None
194
+ if query_mask is not None:
195
+ x = x * query_mask.unsqueeze(-1).to(x.dtype)
196
+ if cache is None:
197
+ conv_input = F.pad(x.transpose(1, 2), (state_length, 0))
198
+ else:
199
+ previous = cache.get_convolution(self.layer_idx)
200
+ if previous is None:
201
+ previous = torch.zeros(
202
+ (x.shape[0], state_length, x.shape[2]),
203
+ dtype=x.dtype,
204
+ device=x.device,
205
+ )
206
+ combined = torch.cat((previous, x), dim=1)
207
+ conv_input = combined.transpose(1, 2)
208
+ combined_mask = None
209
+ if query_mask is not None:
210
+ combined_mask = torch.cat(
211
+ (
212
+ torch.ones(
213
+ (x.shape[0], state_length),
214
+ dtype=query_mask.dtype,
215
+ device=query_mask.device,
216
+ ),
217
+ query_mask,
218
+ ),
219
+ dim=1,
220
+ )
221
+ cache.update_convolution(
222
+ self.layer_idx,
223
+ _last_valid_state(combined, combined_mask, state_length),
224
+ )
225
+ return self.pointwise(self.depthwise(conv_input).transpose(1, 2))
226
+
227
+
228
+ class SwiGLU(nn.Module):
229
+ def __init__(self, config: QyrouArchConfig) -> None:
230
+ super().__init__()
231
+ self.intermediate_size = config.intermediate_size
232
+ self.use_liger = config.liger_swiglu
233
+ if config.fused_gate_up_projection:
234
+ self.gate_up_proj = nn.Linear(config.hidden_size, 2 * config.intermediate_size, bias=False)
235
+ else:
236
+ self.gate_proj = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
237
+ self.up_proj = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
238
+ self.down_proj = nn.Linear(config.intermediate_size, config.hidden_size, bias=False)
239
+ self.down_proj._qyrou_arch_residual_projection = True # type: ignore[attr-defined]
240
+
241
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
242
+ if hasattr(self, "gate_up_proj"):
243
+ packed = self.gate_up_proj(x)
244
+ if (
245
+ packed.is_cuda
246
+ and self.use_liger
247
+ and PackedSwiGLUFunction is not None
248
+ and not torch.compiler.is_compiling()
249
+ ):
250
+ return self.down_proj(PackedSwiGLUFunction.apply(packed))
251
+ gate, up = packed.chunk(2, dim=-1)
252
+ else:
253
+ gate, up = self.gate_proj(x), self.up_proj(x)
254
+ if torch.compiler.is_compiling():
255
+ activated = F.silu(gate) * up
256
+ elif x.is_cuda and self.use_liger and LigerSiLUMulFunction is not None:
257
+ activated = LigerSiLUMulFunction.apply(gate, up, 1.0, 1.0)
258
+ else:
259
+ activated = F.silu(gate) * up
260
+ return self.down_proj(activated)
261
+
262
+
263
+ class QyrouArchBlock(nn.Module):
264
+ def __init__(self, config: QyrouArchConfig, layer_idx: int) -> None:
265
+ super().__init__()
266
+ self.mixer_norm = RMSNorm(config.hidden_size, config.rms_norm_eps, config.liger_rms_norm)
267
+ self.ffn_norm = RMSNorm(config.hidden_size, config.rms_norm_eps, config.liger_rms_norm)
268
+ layer_number = layer_idx + 1
269
+ self.mixer = (
270
+ CausalConvMixer(config, layer_idx)
271
+ if layer_number in config.conv_layers
272
+ else CausalGQA(config, layer_idx)
273
+ )
274
+ self.ffn = SwiGLU(config)
275
+
276
+ def forward(
277
+ self,
278
+ x: torch.Tensor,
279
+ rope: tuple[torch.Tensor, torch.Tensor],
280
+ attention_mask: torch.Tensor | None,
281
+ cache: QyrouArchHybridCache | None,
282
+ cache_position: torch.Tensor,
283
+ ) -> torch.Tensor:
284
+ x = x + self.mixer(self.mixer_norm(x), rope, attention_mask, cache, cache_position)
285
+ return x + self.ffn(self.ffn_norm(x))
286
+
287
+
288
+ class QyrouArchPreTrainedModel(PreTrainedModel):
289
+ config_class = QyrouArchConfig
290
+ base_model_prefix = "model"
291
+ supports_gradient_checkpointing = False
292
+ _no_split_modules = ["QyrouArchBlock"]
293
+
294
+ def _init_weights(self, module: nn.Module) -> None:
295
+ if isinstance(module, (nn.Linear, nn.Embedding, nn.Conv1d)):
296
+ scale = (
297
+ self.config.residual_initializer_scale
298
+ if getattr(module, "_qyrou_arch_residual_projection", False)
299
+ else 1.0
300
+ )
301
+ nn.init.normal_(module.weight, mean=0.0, std=self.config.initializer_range * scale)
302
+
303
+
304
+ class QyrouArchModel(QyrouArchPreTrainedModel):
305
+ def __init__(self, config: QyrouArchConfig) -> None:
306
+ super().__init__(config)
307
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size)
308
+ self.layers = nn.ModuleList(
309
+ [QyrouArchBlock(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
310
+ )
311
+ self.norm = RMSNorm(config.hidden_size, config.rms_norm_eps, config.liger_rms_norm)
312
+ head_dim = config.hidden_size // config.num_attention_heads
313
+ inv_freq = 1.0 / (
314
+ config.rope_theta ** (torch.arange(0, head_dim, 2).float() / head_dim)
315
+ )
316
+ positions = torch.arange(config.max_position_embeddings, dtype=torch.float32)
317
+ angles = torch.outer(positions, inv_freq)
318
+ self.register_buffer("rope_cos", angles.cos(), persistent=False)
319
+ self.register_buffer("rope_sin", angles.sin(), persistent=False)
320
+ self.post_init()
321
+
322
+ def _rope(
323
+ self,
324
+ position_ids: torch.Tensor,
325
+ dtype: torch.dtype,
326
+ ) -> tuple[torch.Tensor, torch.Tensor]:
327
+ if (
328
+ not torch.compiler.is_compiling()
329
+ and int(position_ids.max()) >= self.config.max_position_embeddings
330
+ ):
331
+ raise ValueError("Position exceeds max_position_embeddings")
332
+ cos = self.rope_cos[position_ids].to(dtype).unsqueeze(1)
333
+ sin = self.rope_sin[position_ids].to(dtype).unsqueeze(1)
334
+ return cos, sin
335
+
336
+ def forward(
337
+ self,
338
+ input_ids: torch.LongTensor | None = None,
339
+ attention_mask: torch.Tensor | None = None,
340
+ position_ids: torch.LongTensor | None = None,
341
+ past_key_values: QyrouArchHybridCache | None = None,
342
+ inputs_embeds: torch.Tensor | None = None,
343
+ use_cache: bool | None = None,
344
+ cache_position: torch.LongTensor | None = None,
345
+ return_dict: bool | None = None,
346
+ **_: Any,
347
+ ) -> BaseModelOutputWithPast | tuple[torch.Tensor, QyrouArchHybridCache | None]:
348
+ if (input_ids is None) == (inputs_embeds is None):
349
+ raise ValueError("Pass exactly one of input_ids or inputs_embeds")
350
+ hidden = self.embed_tokens(input_ids) if inputs_embeds is None else inputs_embeds
351
+ if hidden.is_cuda and torch.is_autocast_enabled("cuda"):
352
+ hidden = hidden.to(torch.get_autocast_dtype("cuda"))
353
+ batch, query_length, _ = hidden.shape
354
+ use_cache = self.config.use_cache if use_cache is None else use_cache
355
+ return_dict = self.config.return_dict if return_dict is None else return_dict
356
+ if use_cache and past_key_values is None:
357
+ past_key_values = QyrouArchHybridCache(self.config.num_hidden_layers)
358
+ cache = past_key_values if use_cache else None
359
+ past_length = cache.get_seq_length() if cache is not None else 0
360
+ if cache_position is None:
361
+ cache_position = torch.arange(
362
+ past_length,
363
+ past_length + query_length,
364
+ device=hidden.device,
365
+ )
366
+ if position_ids is None:
367
+ if attention_mask is not None:
368
+ position_ids = attention_mask.long().cumsum(-1).sub(1).clamp_min(0)[:, -query_length:]
369
+ else:
370
+ position_ids = cache_position.unsqueeze(0).expand(batch, -1)
371
+ rope = self._rope(position_ids, hidden.dtype)
372
+ for layer in self.layers:
373
+ hidden = layer(hidden, rope, attention_mask, cache, cache_position)
374
+ hidden = self.norm(hidden)
375
+ if cache is not None:
376
+ cache.finish_step(cache_position)
377
+ if not return_dict:
378
+ return hidden, cache
379
+ return BaseModelOutputWithPast(last_hidden_state=hidden, past_key_values=cache)
380
+
381
+
382
+ class QyrouArchForCausalLM(QyrouArchPreTrainedModel, GenerationMixin):
383
+ _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
384
+
385
+ def __init__(self, config: QyrouArchConfig) -> None:
386
+ super().__init__(config)
387
+ self.model = QyrouArchModel(config)
388
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
389
+ self.post_init()
390
+ self.tie_weights()
391
+
392
+ def get_input_embeddings(self) -> nn.Module:
393
+ return self.model.embed_tokens
394
+
395
+ def set_input_embeddings(self, value: nn.Module) -> None:
396
+ self.model.embed_tokens = value
397
+
398
+ def get_output_embeddings(self) -> nn.Module:
399
+ return self.lm_head
400
+
401
+ def set_output_embeddings(self, value: nn.Module) -> None:
402
+ self.lm_head = value
403
+
404
+ def forward(
405
+ self,
406
+ input_ids: torch.LongTensor | None = None,
407
+ attention_mask: torch.Tensor | None = None,
408
+ position_ids: torch.LongTensor | None = None,
409
+ past_key_values: QyrouArchHybridCache | None = None,
410
+ inputs_embeds: torch.Tensor | None = None,
411
+ labels: torch.LongTensor | None = None,
412
+ use_cache: bool | None = None,
413
+ cache_position: torch.LongTensor | None = None,
414
+ return_logits: bool = True,
415
+ return_dict: bool | None = None,
416
+ **kwargs: Any,
417
+ ) -> CausalLMOutputWithPast | tuple[Any, ...]:
418
+ return_dict = self.config.return_dict if return_dict is None else return_dict
419
+ outputs = self.model(
420
+ input_ids=input_ids,
421
+ attention_mask=attention_mask,
422
+ position_ids=position_ids,
423
+ past_key_values=past_key_values,
424
+ inputs_embeds=inputs_embeds,
425
+ use_cache=use_cache,
426
+ cache_position=cache_position,
427
+ return_dict=True,
428
+ **kwargs,
429
+ )
430
+ hidden = outputs.last_hidden_state
431
+ training_loss_only = labels is not None and not return_logits
432
+ logits = None if training_loss_only else self.lm_head(hidden)
433
+ loss = None
434
+ if labels is not None:
435
+ if (
436
+ training_loss_only
437
+ and hidden.is_cuda
438
+ and self.config.cut_cross_entropy
439
+ and cut_linear_cross_entropy is not None
440
+ ):
441
+ loss = cut_linear_cross_entropy(
442
+ hidden,
443
+ self.lm_head.weight,
444
+ labels,
445
+ shift=True,
446
+ filter_eps=None,
447
+ )
448
+ else:
449
+ loss_logits = self.lm_head(hidden[:, :-1]).float()
450
+ loss = F.cross_entropy(
451
+ loss_logits.reshape(-1, self.config.vocab_size),
452
+ labels[:, 1:].contiguous().view(-1),
453
+ ignore_index=-100,
454
+ )
455
+ if not return_dict:
456
+ return loss, logits, outputs.past_key_values
457
+ return CausalLMOutputWithPast(
458
+ loss=loss,
459
+ logits=logits,
460
+ past_key_values=outputs.past_key_values,
461
+ )
462
+
463
+ def prepare_inputs_for_generation(
464
+ self,
465
+ input_ids: torch.LongTensor,
466
+ past_key_values: QyrouArchHybridCache | None = None,
467
+ attention_mask: torch.Tensor | None = None,
468
+ cache_position: torch.LongTensor | None = None,
469
+ use_cache: bool = True,
470
+ **kwargs: Any,
471
+ ) -> dict[str, Any]:
472
+ past_length = past_key_values.get_seq_length() if past_key_values is not None else 0
473
+ if past_length:
474
+ input_ids = (
475
+ input_ids[:, past_length:]
476
+ if input_ids.shape[1] > past_length
477
+ else input_ids[:, -1:]
478
+ )
479
+ if cache_position is None:
480
+ cache_position = torch.arange(
481
+ past_length,
482
+ past_length + input_ids.shape[1],
483
+ device=input_ids.device,
484
+ )
485
+ elif cache_position.numel() != input_ids.shape[1]:
486
+ cache_position = cache_position[-input_ids.shape[1] :]
487
+ return {
488
+ "input_ids": input_ids,
489
+ "attention_mask": attention_mask,
490
+ "past_key_values": past_key_values,
491
+ "cache_position": cache_position,
492
+ "use_cache": use_cache,
493
+ }
494
+
495
+ def _reorder_cache(
496
+ self,
497
+ past_key_values: QyrouArchHybridCache,
498
+ beam_idx: torch.LongTensor,
499
+ ) -> QyrouArchHybridCache:
500
+ return past_key_values.reorder_cache(beam_idx)
501
+
502
+
503
+ QyrouArchConfig.register_for_auto_class()
504
+ QyrouArchForCausalLM.register_for_auto_class("AutoModelForCausalLM")
resume/checkpoints/qyrou-1-exp-base/checkpoint-step-000680000/model/triton_kernels.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import torch
4
+ import triton
5
+ import triton.language as tl
6
+
7
+
8
+ def _settings(n_cols: int) -> tuple[int, int]:
9
+ block_size = triton.next_power_of_2(n_cols)
10
+ if block_size > 65_536:
11
+ raise ValueError(f"Unsupported SwiGLU width: {n_cols}")
12
+ num_warps = 4 if block_size < 2_048 else 8
13
+ return block_size, num_warps
14
+
15
+
16
+ @triton.jit
17
+ def _packed_swiglu_forward_kernel(
18
+ packed_ptr,
19
+ output_ptr,
20
+ packed_stride: tl.constexpr,
21
+ output_stride: tl.constexpr,
22
+ n_cols: tl.constexpr,
23
+ block_size: tl.constexpr,
24
+ ):
25
+ row = tl.program_id(0).to(tl.int64)
26
+ offsets = tl.arange(0, block_size)
27
+ mask = offsets < n_cols
28
+ packed_row = packed_ptr + row * packed_stride
29
+ gate = tl.load(packed_row + offsets, mask=mask, other=0.0).to(tl.float32)
30
+ up = tl.load(packed_row + n_cols + offsets, mask=mask, other=0.0)
31
+ activated = (gate * tl.sigmoid(gate)).cast(up.dtype) * up
32
+ tl.store(output_ptr + row * output_stride + offsets, activated, mask=mask)
33
+
34
+
35
+ @triton.jit
36
+ def _packed_swiglu_backward_kernel(
37
+ grad_output_ptr,
38
+ packed_ptr,
39
+ grad_packed_ptr,
40
+ grad_output_stride: tl.constexpr,
41
+ packed_stride: tl.constexpr,
42
+ n_cols: tl.constexpr,
43
+ block_size: tl.constexpr,
44
+ ):
45
+ row = tl.program_id(0).to(tl.int64)
46
+ offsets = tl.arange(0, block_size)
47
+ mask = offsets < n_cols
48
+ packed_row = packed_ptr + row * packed_stride
49
+ grad_packed_row = grad_packed_ptr + row * packed_stride
50
+ grad_output = tl.load(
51
+ grad_output_ptr + row * grad_output_stride + offsets,
52
+ mask=mask,
53
+ other=0.0,
54
+ )
55
+ gate = tl.load(packed_row + offsets, mask=mask, other=0.0).to(tl.float32)
56
+ up = tl.load(packed_row + n_cols + offsets, mask=mask, other=0.0)
57
+ sigmoid_gate = tl.sigmoid(gate)
58
+ silu_gate = gate * sigmoid_gate
59
+ grad_up = grad_output * silu_gate
60
+ grad_gate = grad_output * up * (sigmoid_gate + silu_gate * (1.0 - sigmoid_gate))
61
+ tl.store(grad_packed_row + offsets, grad_gate, mask=mask)
62
+ tl.store(grad_packed_row + n_cols + offsets, grad_up, mask=mask)
63
+
64
+
65
+ class PackedSwiGLUFunction(torch.autograd.Function):
66
+ @staticmethod
67
+ def forward(ctx, packed: torch.Tensor) -> torch.Tensor:
68
+ if not packed.is_cuda:
69
+ raise ValueError("PackedSwiGLUFunction requires a CUDA tensor")
70
+ if packed.dtype not in (torch.float16, torch.bfloat16, torch.float32):
71
+ raise TypeError(f"Unsupported PackedSwiGLU dtype: {packed.dtype}")
72
+ if packed.shape[-1] % 2:
73
+ raise ValueError("Packed gate/up dimension must be even")
74
+ packed = packed.contiguous()
75
+ n_cols = packed.shape[-1] // 2
76
+ packed_2d = packed.view(-1, 2 * n_cols)
77
+ output = torch.empty((packed_2d.shape[0], n_cols), dtype=packed.dtype, device=packed.device)
78
+ block_size, num_warps = _settings(n_cols)
79
+ _packed_swiglu_forward_kernel[(packed_2d.shape[0],)](
80
+ packed_2d,
81
+ output,
82
+ packed_2d.stride(0),
83
+ output.stride(0),
84
+ n_cols=n_cols,
85
+ block_size=block_size,
86
+ num_warps=num_warps,
87
+ )
88
+ ctx.save_for_backward(packed_2d)
89
+ ctx.original_shape = packed.shape
90
+ ctx.n_cols = n_cols
91
+ ctx.block_size = block_size
92
+ ctx.num_warps = num_warps
93
+ return output.view(*packed.shape[:-1], n_cols)
94
+
95
+ @staticmethod
96
+ def backward(ctx, grad_output: torch.Tensor) -> tuple[torch.Tensor]:
97
+ (packed_2d,) = ctx.saved_tensors
98
+ grad_output_2d = grad_output.contiguous().view(-1, ctx.n_cols)
99
+ grad_packed = torch.empty_like(packed_2d)
100
+ _packed_swiglu_backward_kernel[(packed_2d.shape[0],)](
101
+ grad_output_2d,
102
+ packed_2d,
103
+ grad_packed,
104
+ grad_output_2d.stride(0),
105
+ packed_2d.stride(0),
106
+ n_cols=ctx.n_cols,
107
+ block_size=ctx.block_size,
108
+ num_warps=ctx.num_warps,
109
+ )
110
+ return (grad_packed.view(ctx.original_shape),)
resume/checkpoints/qyrou-1-exp-base/checkpoint-step-000680000/training_state.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:06ed8c306ade8342a16f73be3c4eb327bc1ce1f88900a772e3f4731872870031
3
+ size 526781105
resume/checkpoints/qyrou-1-exp-base/index.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "version": 1,
3
+ "checkpoints": [
4
+ {
5
+ "path": "checkpoint-step-000680000",
6
+ "step": 680000,
7
+ "total_tokens": 33423360000,
8
+ "validation_loss": null,
9
+ "bytes": 788049493,
10
+ "protected_reasons": [
11
+ "hub-backup"
12
+ ]
13
+ }
14
+ ],
15
+ "best": null
16
+ }
resume/checkpoints/qyrou-1-exp-base/latest.json ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "version": 1,
3
+ "path": "checkpoint-step-000680000",
4
+ "step": 680000,
5
+ "total_tokens": 33423360000
6
+ }
resume/configs/qyrou_1_exp_base.json ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "run": {
3
+ "name": "qyrou-1-exp-base",
4
+ "seed": 1337,
5
+ "project_dir": "D:\\qyrou_1_exp_base",
6
+ "dataset_dir": "C:\\Dataset_qyrou_1\\Qyrou_PT_Corpus_English",
7
+ "tokenizer_dir": "D:\\qyrou_1_exp_base\\artifacts\\tokenizer",
8
+ "token_cache_dir": "D:\\qyrou_1_exp_base\\data\\tokenized",
9
+ "checkpoint_dir": "D:\\qyrou_1_exp_base\\checkpoints\\qyrou-1-exp-base",
10
+ "log_dir": "D:\\qyrou_1_exp_base\\runs\\qyrou-1-exp-base",
11
+ "final_dir": "D:\\qyrou_1_exp_base\\final\\qyrou-1-exp-base"
12
+ },
13
+ "model": {
14
+ "vocab_size": 20000,
15
+ "hidden_size": 512,
16
+ "num_hidden_layers": 21,
17
+ "num_attention_heads": 8,
18
+ "num_key_value_heads": 2,
19
+ "intermediate_size": 1328,
20
+ "conv_layers": [4, 9, 14, 19],
21
+ "conv_kernel_size": 7,
22
+ "rope_theta": 10000.0,
23
+ "rms_norm_eps": 1e-5,
24
+ "max_position_embeddings": 2048,
25
+ "initializer_range": 0.02,
26
+ "residual_initializer_scale": 0.1543033499620919,
27
+ "tie_word_embeddings": true,
28
+ "qk_norm": true,
29
+ "attention_backend": "cudnn",
30
+ "fused_qkv_projection": true,
31
+ "fused_gate_up_projection": true,
32
+ "liger_rms_norm": true,
33
+ "liger_swiglu": true,
34
+ "cut_cross_entropy": true,
35
+ "bos_token_id": 0,
36
+ "eos_token_id": 1,
37
+ "pad_token_id": 2,
38
+ "unk_token_id": 3,
39
+ "eod_token_id": 4
40
+ },
41
+ "tokenizer": {
42
+ "vocab_size": 20000,
43
+ "sample_bytes": 20000000000,
44
+ "min_frequency": 2,
45
+ "batch_size": 256,
46
+ "quality_rows": 20000
47
+ },
48
+ "data": {
49
+ "sequence_length": 2048,
50
+ "minimum_free_gib_before_tokenize": 130,
51
+ "encode_batch_size": 256,
52
+ "write_buffer_tokens": 1048576,
53
+ "parallel_workers": 4,
54
+ "tokenizer_threads_per_worker": 4,
55
+ "shuffle_seed": 1337,
56
+ "shuffle_block_tokens": 65536
57
+ },
58
+ "training": {
59
+ "device": "cuda",
60
+ "dtype": "bf16",
61
+ "peak_learning_rate": 0.00057,
62
+ "minimum_learning_rate": 0.000057,
63
+ "warmup_tokens": 50000000,
64
+ "weight_decay": 0.1,
65
+ "beta1": 0.9,
66
+ "beta2": 0.95,
67
+ "epsilon": 1e-8,
68
+ "grad_clip": 1.0,
69
+ "log_every_steps": 10,
70
+ "save_every_steps": 5000,
71
+ "eval_every_steps": 25000,
72
+ "eval_tokens": 1048576,
73
+ "qualification_steps": 5000,
74
+ "qualification_tokens": 100000000,
75
+ "minimum_tps": 84000,
76
+ "target_tps": 85000,
77
+ "compile_minimum_improvement": 0.03,
78
+ "autotune_candidates": [[5, 3], [6, 3], [6, 4], [8, 2], [8, 3], [10, 2], [12, 2]],
79
+ "max_automatic_recoveries": 2
80
+ },
81
+ "checkpoints": {
82
+ "cap_gib": 50,
83
+ "prune_count": 12,
84
+ "milestone_fractions": [0.1, 0.25, 0.5, 0.75, 0.9, 1.0]
85
+ }
86
+ }
source/QYROU_ARCH_ARCHITECTURE_AUDIT.md ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Qyrou-1 65M Architecture Audit
2
+
3
+ Audited preview: `QyrouArchLabs/QyrouArch-65M-Preview` at commit
4
+ `94f25e19b0efb71b4534fe890afb58f5a8e8a4e1`.
5
+
6
+ ## Executive assessment
7
+
8
+ The preview is a coherent 65M hybrid causal language model. Its parameter budget is
9
+ well chosen and the training graph is substantially more efficient than a stock
10
+ Llama implementation at the same size. The current repository is a strong
11
+ pretraining preview, but it is not yet a seamless inference release. In
12
+ particular, generation has no KV or convolution state cache and the model ignores
13
+ attention masks, position IDs, and cache-related generation arguments.
14
+
15
+ The recommended Qyrou-1 architecture is therefore the preview architecture with
16
+ its training graph retained and its serving contract completed before release.
17
+
18
+ ## Exact architecture
19
+
20
+ | Component | Value |
21
+ |---|---:|
22
+ | Vocabulary | 20,000 |
23
+ | Hidden width | 512 |
24
+ | Layers | 21 |
25
+ | Attention layers | 17 |
26
+ | Causal convolution layers | 4 (`4, 9, 14, 19`) |
27
+ | Attention heads | 8 |
28
+ | KV heads | 2 |
29
+ | Head dimension | 64 |
30
+ | GQA query groups per KV head | 4 |
31
+ | FFN intermediate width | 1,328 |
32
+ | FFN | SwiGLU |
33
+ | Normalization | pre-RMSNorm |
34
+ | Positional encoding | RoPE, theta 10,000 |
35
+ | Context length | 2,048 |
36
+ | Convolution | causal depthwise kernel 7 + dense pointwise projection |
37
+ | Embedding/output head | tied |
38
+ | Biases | none |
39
+
40
+ Each block is:
41
+
42
+ 1. `x = x + mixer(RMSNorm(x))`
43
+ 2. `x = x + down(SiLU(gate(x)) * up(x))`
44
+
45
+ The mixer is GQA in 17 blocks and a causal convolution in four evenly spaced
46
+ blocks. The convolution layers provide cheap local token mixing; attention layers
47
+ retain global causal communication.
48
+
49
+ ## Exact parameter count
50
+
51
+ | Component | Count |
52
+ |---|---:|
53
+ | Tied token embedding / LM head | 10,240,000 |
54
+ | 17 attention blocks | 45,835,264 |
55
+ | 4 convolution blocks | 9,226,240 |
56
+ | Final RMSNorm | 512 |
57
+ | **Total** | **65,302,016** |
58
+
59
+ An attention block has 2,696,192 parameters:
60
+
61
+ - packed QKV projection: 393,216
62
+ - output projection: 262,144
63
+ - SwiGLU projections: 2,039,808
64
+ - two RMSNorm weights: 1,024
65
+
66
+ A convolution block has 2,306,560 parameters:
67
+
68
+ - depthwise convolution: 3,584
69
+ - pointwise projection: 262,144
70
+ - SwiGLU projections: 2,039,808
71
+ - two RMSNorm weights: 1,024
72
+
73
+ ## Efficient training paths
74
+
75
+ The preview enables:
76
+
77
+ - one packed QKV GEMM instead of three projection launches;
78
+ - one packed gate/up GEMM instead of two projection launches;
79
+ - a custom Triton packed SwiGLU activation with a custom backward;
80
+ - Liger RMSNorm when installed;
81
+ - cut-cross-entropy, avoiding full `[batch, sequence, vocabulary]` logits during
82
+ loss-only training;
83
+ - cuDNN scaled-dot-product attention with a math fallback;
84
+ - BF16 parameters and fused AdamW;
85
+ - cached RoPE tensors for repeated fixed-length batches.
86
+
87
+ At batch 5 and sequence 2,048, ordinary logits would contain 204.8 million BF16
88
+ values, about 390.6 MiB before gradients and temporaries. Cut cross entropy is
89
+ therefore one of the most important memory optimizations in this model.
90
+
91
+ ## Triton kernel audit
92
+
93
+ The custom kernel performs only the packed SwiGLU elementwise operation. The
94
+ gate/up projection itself is a packed PyTorch linear GEMM; it is not fused into
95
+ the Triton activation kernel.
96
+
97
+ The implemented math is correct:
98
+
99
+ - forward: `SiLU(gate) * up`;
100
+ - `d(up) = d(output) * SiLU(gate)`;
101
+ - `d(gate) = d(output) * up * (sigmoid(gate) + SiLU(gate) *
102
+ (1 - sigmoid(gate)))`.
103
+
104
+ Positive properties:
105
+
106
+ - masked tail handling supports the non-power-of-two width 1,328;
107
+ - row indices use 64-bit addressing;
108
+ - non-contiguous packed input is normalized before launch;
109
+ - backward makes the incoming gradient contiguous;
110
+ - the saved tensor is the packed projection rather than extra gate/up copies.
111
+
112
+ Required hardening:
113
+
114
+ - validate that the packed last dimension is even;
115
+ - validate CUDA placement and supported floating dtypes before launching;
116
+ - add reference forward/backward parity tests for BF16 and FP32;
117
+ - add odd/tail-width, non-contiguous input, zero-size, and large-row tests;
118
+ - benchmark against Liger's existing SwiGLU and plain compiled PyTorch;
119
+ - avoid importing `calculate_settings` from Liger inside the custom-kernel module
120
+ if the goal is an independently loadable kernel;
121
+ - document supported Triton and `triton-windows` versions rather than monkey
122
+ patching dependency version checks at import time.
123
+
124
+ ## Release blockers
125
+
126
+ ### 1. No incremental generation cache
127
+
128
+ `forward` accepts only `input_ids` as an effective model input. Cache-related
129
+ arguments are swallowed and ignored. Every generated token therefore recomputes
130
+ all preceding attention, convolution, FFN, and logits work.
131
+
132
+ Implement:
133
+
134
+ - per-layer K/V cache for all 17 attention layers;
135
+ - six-token rolling state for each kernel-7 convolution layer;
136
+ - cache position and RoPE offset handling;
137
+ - `past_key_values`, `use_cache`, and a cache-aware model output;
138
+ - `prepare_inputs_for_generation` and cache reordering.
139
+
140
+ ### 2. Attention masks are ignored
141
+
142
+ Padded batched inference and packed examples can attend to padding or unrelated
143
+ tokens. Accept and correctly combine `attention_mask` with causality. Add tests
144
+ for left padding, right padding, unequal prompt lengths, and fully unpadded
145
+ equivalence.
146
+
147
+ ### 3. Position IDs and RoPE offsets are ignored
148
+
149
+ RoPE always begins at zero and is keyed only by sequence length, device, and
150
+ dtype. Incremental decoding and padded batches need explicit positions. Cache
151
+ cosine/sine tables by capacity, then gather using `position_ids`.
152
+
153
+ ### 4. Serving outputs do not follow the full Transformers contract
154
+
155
+ Add support for `return_dict`, hidden states when requested, cache outputs, and
156
+ the standard causal-LM generation inputs. Reject unsupported arguments rather
157
+ than silently swallowing every keyword.
158
+
159
+ ### 5. Context policy is fixed at 2,048
160
+
161
+ The architecture can run longer, but it was trained at 2,048 with base RoPE.
162
+ Do not advertise longer context without continued pretraining and long-context
163
+ evaluation. If longer context is a Qyrou-1 goal, train it deliberately rather
164
+ than changing `max_position_embeddings` after the fact.
165
+
166
+ ## Data and training findings
167
+
168
+ The finalized source dataset is stored at `C:\Dataset_qyrou_1`, with the primary
169
+ corpus under `C:\Dataset_qyrou_1\Qyrou_PT_Corpus_English`.
170
+
171
+ The preview completed 325,521 steps and approximately 9.996B tokens. With batch
172
+ 5, gradient accumulation 3, and sequence length 2,048, each optimizer step
173
+ represents 30,720 tokens, which agrees with the recorded total.
174
+
175
+ The local finalized corpus contains 69,330,050 valid records and about 224 GB of
176
+ text. It is much larger than the 10B-token training allocation, so a deterministic
177
+ token-level mixture and sampling policy matters more than merely having enough
178
+ data.
179
+
180
+ Before benchmark reporting:
181
+
182
+ - remove or isolate HellaSwag, WinoGrande, and LAMBADA training examples;
183
+ - deduplicate related math sources across datasets;
184
+ - keep code-license and source provenance outside the text-only training shards;
185
+ - reduce the observed exact-duplicate rate in Stack-derived data;
186
+ - avoid allowing FineWeb-Edu volume to erase curated math, code, tool, and
187
+ high-quality language mixture targets.
188
+
189
+ ## Recommended Qyrou-1 65M release design
190
+
191
+ Retain the preview's dimensions and hybrid schedule for the 65M release. Changing
192
+ width, depth, FFN ratio, or convolution placement would discard the value of the
193
+ completed 10B-token preview experiment.
194
+
195
+ Complete the model in this order:
196
+
197
+ 1. Establish PyTorch reference tests for attention, convolution, RMSNorm, packed
198
+ SwiGLU, loss shifting, and tied weights.
199
+ 2. Add the complete cache and attention-mask serving contract.
200
+ 3. Add Triton parity and gradient tests before further fusion.
201
+ 4. Benchmark packed GEMMs, SwiGLU, RMSNorm, SDPA, and cross entropy separately.
202
+ 5. Add optional optimized paths behind capability checks, preserving a plain
203
+ PyTorch fallback.
204
+ 6. Run a short continued-pretraining smoke test from the preview checkpoint.
205
+ 7. Evaluate loss/perplexity plus uncontaminated downstream tasks.
206
+ 8. Only then consider additional fused residual+RMSNorm or convolution kernels;
207
+ optimize measured bottlenecks, not launch count in isolation.
208
+
209
+ The central design decision is sound: a 512-wide, 21-layer hybrid with 2-KV-head
210
+ GQA and four causal-convolution mixers gives Qyrou-1 a distinct architecture
211
+ while remaining compatible with efficient dense GPU kernels. The immediate work
212
+ is robustness and serving efficiency, not another architectural redesign.
source/TRAINING.md ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Qyrou-1 65M training
2
+
3
+ The source corpus is read-only at
4
+ `C:\Dataset_qyrou_1\Qyrou_PT_Corpus_English`. Generated tokenizer assets,
5
+ uint16 tokens, logs, checkpoints, and the final model stay inside this project
6
+ on D:.
7
+
8
+ ## Safe launch order
9
+
10
+ Run from a native Windows PowerShell:
11
+
12
+ ```powershell
13
+ .\scripts\bootstrap_windows.ps1
14
+ .\scripts\run_full_training.ps1
15
+ ```
16
+
17
+ The pipeline performs hardware/disk checks, trains and validates the tokenizer,
18
+ pretokenizes all 100 corpus shards, prepares fixed validation tokens, runs tests,
19
+ autotunes the RTX 5070, and starts training only after all stages pass.
20
+
21
+ Training does not rewrite the token cache. It divides the existing cache into
22
+ 65,536-token contiguous blocks and deterministically shuffles all block
23
+ references with seed 1337. Every token is visited exactly once. Each checkpoint
24
+ stores the complete permutation and exact cursor, and the launcher audits the
25
+ permutation plus a boundary-crossing resume before every training launch.
26
+
27
+ To continue after a controlled interruption:
28
+
29
+ ```powershell
30
+ .\scripts\run_full_training.ps1 -StartAt train -Resume
31
+ ```
32
+
33
+ Inspect all checkpoint hashes and the 50 GiB retention ledger with:
34
+
35
+ ```powershell
36
+ .\.venv\Scripts\python.exe scripts\inspect_checkpoint.py --validate-all
37
+ ```
38
+
39
+ The validated compile-safe path targets 85K effective tokens/s and refuses to
40
+ continue past the 5,000-step qualification gate if the median is below 84K.
41
+ The current real-data shuffled-stream smoke test sustains about 87.1K tokens/s
42
+ with microbatch 8 and accumulation 3. Production autotuning rechecks this after
43
+ data preparation. Raw loss is expected to fluctuate; the 100- and 1,000-step
44
+ averages plus fixed validation loss are the convergence signals.
source/attribution.md ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dataset credits
2
+
3
+ This is the list of datasets we used and the people and projects we want to thank. Where a dataset has a paper, we cite it below. Where it does not, we link directly to the Hugging Face page so the original creator is still easy to find.
4
+
5
+ We also keep the dataset name and source with our processed files. That matters because converting something to JSONL does not change who made it or what license it came with.
6
+
7
+ ## Datasets
8
+
9
+ | Dataset | Our note | License and citation |
10
+ |---|---|---|
11
+ | [HuggingFaceGECLM/REDDIT_comments](https://huggingface.co/datasets/HuggingFaceGECLM/REDDIT_comments) | Thanks to HuggingFaceGECLM for collecting and organizing the Reddit comments. We sampled the data by subreddit and removed empty, deleted, and removed comments. | The page does not give us a paper to cite, so we link to the dataset itself. The comments came from Reddit and are still subject to Reddit’s terms and the rights of their original authors. |
12
+ | [abisee/cnn_dailymail](https://huggingface.co/datasets/abisee/cnn_dailymail) | Thanks to the CNN/Daily Mail dataset team and the original publishers. We used the articles and highlights from the `3.0.0` training split. | The Hub page lists Apache-2.0 for the dataset package. We cite Hermann et al. and Nallapati et al. below. |
13
+ | [EdinburghNLP/xsum](https://huggingface.co/datasets/EdinburghNLP/xsum) | Thanks to the EdinburghNLP team for XSum and to the BBC for the source material. | We cite Narayan, Cohen, and Lapata below and follow the license information on the dataset page. |
14
+ | [grammarly/coedit](https://huggingface.co/datasets/grammarly/coedit) | Thanks to Grammarly and the CoEdIT authors. We used the source and target edits and kept the edit task where possible. | We cite the CoEdIT paper by Raheja et al. below. |
15
+ | [b-mc2/wikihow_lists](https://huggingface.co/datasets/b-mc2/wikihow_lists) | Thanks to b-mc2 for preparing the dataset and to WikiHow for the original material. | Listed as CC BY-NC-SA 3.0, which means attribution, non-commercial use, and share-alike apply. We cite Koupaee and Wang below. |
16
+ | [agentlans/grammar-correction](https://huggingface.co/datasets/agentlans/grammar-correction) | Thanks to agentlans for publishing the grammar-correction pairs. | We did not find a paper or a clear license on the card, so we cite the dataset page directly and treat the license as unknown. |
17
+ | [Gryphe/ChatGPT-4o-Writing-Prompts](https://huggingface.co/datasets/Gryphe/ChatGPT-4o-Writing-Prompts) | Thanks to Gryphe for publishing the writing-prompt conversations. | No paper or license was listed when we checked, so we link to the dataset page and treat the license as unknown. |
18
+ | [krisha05/story-generation-dataset](https://huggingface.co/datasets/krisha05/story-generation-dataset) | Thanks to krisha05 for the story-generation instruction and response pairs. | We cite the dataset page. We will use the license shown there at the exact revision we downloaded. |
19
+ | [HAD653/gsm8k-cot-120b](https://huggingface.co/datasets/HAD653/gsm8k-cot-120b) | Thanks to HAD653 for this GSM8K-based version and to the original GSM8K team. The reasoning traces are generated additions to the original questions. | We cite both this dataset page and Cobbe et al.’s GSM8K paper below. |
20
+ | [AI-MO/NuminaMath-CoT](https://huggingface.co/datasets/AI-MO/NuminaMath-CoT) | Thanks to the AI-MO and NuminaMath teams, along with the projects that supplied the original problems. | We cite the dataset page and the NuminaMath paper listed there. Individual problem sources may have their own licenses. |
21
+ | [Dogacel/nemotron-post-training-v2-gpt-oss-120b-regen](https://huggingface.co/datasets/Dogacel/nemotron-post-training-v2-gpt-oss-120b-regen) | Thanks to Dogacel for the regenerated set and to the Nemotron contributors named on the card. | This repository changes over time, so we record the exact revision we use and cite the page plus any upstream projects named there. |
22
+ | [Raymond-dev-546730/Open-CoT-Reasoning-Mini](https://huggingface.co/datasets/Raymond-dev-546730/Open-CoT-Reasoning-Mini) | Thanks to Raymond-dev-546730 for publishing the dataset. | The viewer was disabled when we checked. We cite the repository and use the license and source notes included in its files. |
23
+ | [Roman1111111/claude-sonnet-4.6-120000x](https://huggingface.co/datasets/Roman1111111/claude-sonnet-4.6-120000x) | Thanks to Roman1111111 for putting the conversations together. Where the card identifies Claude-generated material, we describe it that way. | We cite the dataset page and follow the terms listed there. This does not imply that Anthropic endorses our work. |
24
+ | [eitanturok/Salesforce-xlam-function-calling-60k](https://huggingface.co/datasets/eitanturok/Salesforce-xlam-function-calling-60k) | Thanks to eitanturok and the Salesforce xLAM team for the function-calling examples. | We cite the dataset page and the xLAM paper or model card linked from it. |
25
+ | [glaiveai/glaive-function-calling-v2](https://huggingface.co/datasets/glaiveai/glaive-function-calling-v2) | Thanks to Glaive AI for publishing the function-calling conversations. | We did not find a separate paper to cite, so we cite the dataset page and follow the license shown there. |
26
+ | [open-web-math/open-web-math](https://huggingface.co/datasets/open-web-math/open-web-math) | Thanks to the OpenWebMath team and to the sites that the material originally came from. We keep source URLs where possible. | We cite the OpenWebMath page and paper. Because this is web data, the original sites may have their own terms and rights. |
27
+ | [nvidia/OpenMathInstruct-1](https://huggingface.co/datasets/nvidia/OpenMathInstruct-1) | Thanks to NVIDIA and the OpenMathInstruct team. Some solutions were generated, so we keep the available correctness information. | We cite the dataset page and the OpenMathInstruct paper linked there. |
28
+ | [TIGER-Lab/MathInstruct](https://huggingface.co/datasets/TIGER-Lab/MathInstruct) | Thanks to TIGER-Lab, the MAmmoTH authors, and the teams behind the component datasets. | We cite Yue et al.’s MAmmoTH paper below. Component datasets may carry additional licenses. |
29
+ | [microsoft/orca-math-word-problems-200k](https://huggingface.co/datasets/microsoft/orca-math-word-problems-200k) | Thanks to Microsoft and the Orca-Math team for the word problems and answers. | We cite the dataset page and the Orca-Math paper linked from it. |
30
+ | [open-thoughts/OpenThoughts3-1.2M](https://huggingface.co/datasets/open-thoughts/OpenThoughts3-1.2M) | Thanks to the OpenThoughts contributors and all of the source projects listed on the card. | We cite the dataset page and its technical report. We keep the source information because this is a mix of several datasets. |
31
+ | [hkust-nlp/dart-math-hard](https://huggingface.co/datasets/hkust-nlp/dart-math-hard) | Thanks to HKUST-NLP and the DART-Math team. We identify this as the hard-sampled version. | We cite the DART-Math paper and dataset page. |
32
+ | [hkust-nlp/dart-math-uniform](https://huggingface.co/datasets/hkust-nlp/dart-math-uniform) | Thanks to HKUST-NLP and the DART-Math team. We identify this as the uniform version. | We cite the same DART-Math paper and this dataset page. |
33
+ | [LLM360/MegaMath](https://huggingface.co/datasets/LLM360/MegaMath) | Thanks to LLM360 and the many projects included in MegaMath. We used selected text-bearing parts rather than every component. | We cite MegaMath and keep the original component information so the individual sources can also be credited. |
34
+ | [HuggingFaceTB/finemath](https://huggingface.co/datasets/HuggingFaceTB/finemath) | Thanks to the Hugging Face team behind FineMath and InfiWebMath. We used the `finemath-4plus` configuration. | We cite the FineMath/FineWeb material linked on the dataset page. The original websites may have their own terms. |
35
+ | [HuggingFaceFW/fineweb-edu](https://huggingface.co/datasets/HuggingFaceFW/fineweb-edu) | Thanks to Anton Lozhkov, Loubna Ben Allal, Leandro von Werra, Thomas Wolf, the wider Hugging Face FineWeb team, and Common Crawl. Our local copy came from the `sample-350BT` configuration, although we only downloaded part of that sample. | FineWeb-Edu is released under ODC-By 1.0 and its use is also subject to Common Crawl's terms. We cite both the dataset and the FineWeb paper below. The original web pages can still carry their own rights and terms. |
36
+ | [math-ai/AutoMathText](https://huggingface.co/datasets/math-ai/AutoMathText) | Thanks to the math-ai team for AutoMathText. We used the requested web score range and kept the text field. | We cite the dataset page and any paper linked there. The source websites may have separate terms. |
37
+ | [open-r1/OpenR1-Math-220k](https://huggingface.co/datasets/open-r1/OpenR1-Math-220k) | Thanks to the Open-R1 contributors and the teams behind the source problems and generated solutions. | We cite the Open-R1 dataset page and technical report, along with the source datasets named there. |
38
+ | [zwhe99/DeepMath-103K](https://huggingface.co/datasets/zwhe99/DeepMath-103K) | Thanks to zwhe99 and the DeepMath contributors. We used the requested `r1_solution_2` field. | We cite the dataset page and any DeepMath paper linked from it. |
39
+ | [ybisk/piqa](https://huggingface.co/datasets/ybisk/piqa) | Thanks to Yonatan Bisk and the PIQA team. | We cite Bisk et al. below. We keep a separate clean evaluation copy. |
40
+ | [Rowan/hellaswag](https://huggingface.co/datasets/Rowan/hellaswag) | Thanks to Rowan Zellers and the HellaSwag team. | The original repository is MIT-licensed. We cite Zellers et al. below and keep a separate evaluation copy. |
41
+ | [allenai/social_i_qa](https://huggingface.co/datasets/allenai/social_i_qa) | Thanks to the Allen Institute for AI and the Social IQa team. | We cite Sap et al. below. The viewer was disabled when we checked, so we also keep the exact repository revision we used. |
42
+ | [allenai/winogrande](https://huggingface.co/datasets/allenai/winogrande) | Thanks to the Allen Institute for AI and the WinoGrande team. We used the debiased training configuration. | We cite Sakaguchi et al. below. The debiased version is listed under Apache-2.0 in the source material we reviewed. |
43
+ | [roneneldan/TinyStories](https://huggingface.co/datasets/roneneldan/TinyStories) | Thanks to Ronen Eldan and Yuanzhi Li for TinyStories. | We cite their TinyStories paper below and the Hugging Face dataset page. |
44
+ | [HuggingFaceCode/stack-v3-train](https://huggingface.co/datasets/HuggingFaceCode/stack-v3-train) | Thanks to the BigCode/Hugging Face team and, importantly, to the developers who wrote the original code. | We keep repository paths and per-file license information. The collection has database-level terms, but every code file still carries its own license. We cite the Stack/BigCode project and paper. |
45
+ | [grimulkan/physical-reasoning](https://huggingface.co/datasets/grimulkan/physical-reasoning) | Thanks to grimulkan for publishing the physical-reasoning examples. | The card leaves some questions about the synthetic source and license, so we cite the page and treat the license as unresolved. |
46
+ | [PixArt-alpha/SAM-LLaVA-Captions10M](https://huggingface.co/datasets/PixArt-alpha/SAM-LLaVA-Captions10M) | Thanks to the PixArt-alpha and SAM-LLaVA contributors. We extracted only the caption text. | We cite the dataset page and the SAM-LLaVA/PixArt work linked there. The captions still trace back to source images and image datasets. |
47
+ | [cimec/lambada](https://huggingface.co/datasets/cimec/lambada) | Thanks to Paperno and the LAMBADA team, and to cimec for the Hugging Face version. | The Hub package is listed as CC BY-4.0. We cite Paperno et al. below and keep evaluation data separate. |
48
+ | [ianncity/GLM-5.2-Conversation](https://huggingface.co/datasets/ianncity/GLM-5.2-Conversation) | Thanks to ianncity and the GLM-related contributors named on the page. | We cite the dataset page and the upstream projects it names. |
49
+ | [deepmind/math_dataset](https://huggingface.co/datasets/deepmind/math_dataset) | Thanks to DeepMind and the authors of the Mathematics Dataset. | We cite Saxton et al. below. The viewer was disabled when we checked, so we keep the exact module, split, and repository revision we use. |
50
+ | [wikimedia/wikipedia](https://huggingface.co/datasets/wikimedia/wikipedia) | Thanks to the Wikimedia community and the editors who wrote and maintained the English Wikipedia articles. We used the `20231101.en` training configuration. | Wikipedia content is available under CC BY-SA 3.0 and the GFDL. We keep the source revision and provide attribution back to Wikimedia and the article authors. |
51
+ | [allenai/peS2o](https://huggingface.co/datasets/allenai/peS2o) | Thanks to the Allen Institute for AI, the peS2o team, Semantic Scholar, and the authors of the original open-access papers. We used English text from the v2 training data. | Listed as ODC-By. We cite the peS2o dataset report and S2ORC source work. |
52
+ | [HuggingFaceTB/cosmopedia](https://huggingface.co/datasets/HuggingFaceTB/cosmopedia) | Thanks to the Hugging Face Cosmopedia team. We used only the Khan Academy, OpenStax, Stanford, and stories configurations. | Listed as Apache-2.0. We cite the dataset page and the projects acknowledged in its README. |
53
+ | [OpenAssistant/oasst2](https://huggingface.co/datasets/OpenAssistant/oasst2) | Thanks to the OpenAssistant contributors who wrote, reviewed, and ranked the conversations. We used reviewed, non-deleted English message trees. | Listed as Apache-2.0. We cite the OASST2 dataset page and the OpenAssistant Conversations paper. |
54
+
55
+ ## BibTeX
56
+
57
+ These are the paper citations we have collected so far. For datasets built from other datasets, we also cite the Hugging Face page and the original sources named in its README.
58
+
59
+ ```bibtex
60
+ @inproceedings{hermann2015teaching,
61
+ title={Teaching Machines to Read and Comprehend},
62
+ author={Hermann, Karl Moritz and Kočiský, Tomáš and Grefenstette, Edward and Espeholt, Lasse and Kay, Will and Suleyman, Mustafa and Blunsom, Phil},
63
+ booktitle={Advances in Neural Information Processing Systems},
64
+ year={2015}
65
+ }
66
+
67
+ @inproceedings{nallapati2016abstractive,
68
+ title={Abstractive Text Summarization Using Sequence-to-Sequence RNNs and Beyond},
69
+ author={Nallapati, Ramesh and Zhou, Bowen and dos Santos, Cícero Nogueira and Gulcehre, Caglar and Xiang, Bing},
70
+ booktitle={Proceedings of the 20th SIGNLL Conference on Computational Natural Language Learning},
71
+ year={2016}
72
+ }
73
+
74
+ @inproceedings{narayan2018dont,
75
+ title={Don't Give Me the Details, Just the Summary! Topic-Aware Convolutional Neural Networks for Extreme Summarization},
76
+ author={Narayan, Shashi and Cohen, Shay B. and Lapata, Mirella},
77
+ booktitle={Proceedings of EMNLP},
78
+ year={2018}
79
+ }
80
+
81
+ @article{raheja2023coedit,
82
+ title={CoEdIT: Text Editing by Prompting Large Language Models},
83
+ author={Raheja, Vipul and others},
84
+ journal={arXiv preprint arXiv:2305.09857},
85
+ year={2023}
86
+ }
87
+
88
+ @inproceedings{koupaee2018wikihow,
89
+ title={WikiHow: A Large Scale Text Summarization Dataset},
90
+ author={Koupaee, Mahnaz and Wang, William Yang},
91
+ booktitle={Proceedings of the First Workshop on Scholarly Document Processing},
92
+ year={2018}
93
+ }
94
+
95
+ @article{cobbe2021training,
96
+ title={Training Verifiers to Solve Math Word Problems},
97
+ author={Cobbe, Karl and Kosaraju, Vineet and Bavarian, Mohammad and Chen, Mark and Jun, Heewoo and Kaiser, Lukasz and Plappert, Matthias and Tworek, Jerry and Hilton, Jacob and Nakano, Reiichiro and others},
98
+ journal={arXiv preprint arXiv:2110.14168},
99
+ year={2021}
100
+ }
101
+
102
+ @inproceedings{yue2023mammoth,
103
+ title={MAmmoTH: Building Math Generalist Models through Hybrid Instruction Tuning},
104
+ author={Yue, Xiang and others},
105
+ booktitle={Proceedings of ICLR},
106
+ year={2024}
107
+ }
108
+
109
+ @inproceedings{bisk2020piqa,
110
+ title={PIQA: Reasoning about Physical Commonsense in Natural Language},
111
+ author={Bisk, Yonatan and Zellers, Rowan and Gao, Jianfeng and Choi, Yejin},
112
+ booktitle={Proceedings of AAAI},
113
+ year={2020}
114
+ }
115
+
116
+ @inproceedings{zellers2019hellaswag,
117
+ title={HellaSwag: Can a Machine Really Finish Your Sentence?},
118
+ author={Zellers, Rowan and Holtzman, Ari and Bisk, Yonatan and Farhadi, Ali and Choi, Yejin},
119
+ booktitle={Proceedings of ACL},
120
+ year={2019}
121
+ }
122
+
123
+ @inproceedings{sap2019socialiqa,
124
+ title={Social IQa: Commonsense Reasoning about Social Interactions},
125
+ author={Sap, Maarten and Le Bras, Ronan and Allaway, Emily and Bhagavatula, Chandra and Lourie, Nicholas and Rashkin, Hannah and Roof, Brendan and Smith, Noah A. and Choi, Yejin},
126
+ booktitle={Proceedings of EMNLP-IJCNLP},
127
+ year={2019}
128
+ }
129
+
130
+ @inproceedings{sakaguchi2020winogrande,
131
+ title={WinoGrande: An Adversarial Winograd Schema Challenge at Scale},
132
+ author={Sakaguchi, Keisuke and Le Bras, Ronan and Bhagavatula, Chandra and Choi, Yejin},
133
+ booktitle={Proceedings of AAAI},
134
+ year={2020}
135
+ }
136
+
137
+ @article{eldan2023tinystories,
138
+ title={TinyStories: How Small Can Language Models Be and Still Speak Coherent English?},
139
+ author={Eldan, Ronen and Li, Yuanzhi},
140
+ journal={arXiv preprint arXiv:2305.07759},
141
+ year={2023}
142
+ }
143
+
144
+ @misc{lozhkov2024fineweb-edu,
145
+ author={Lozhkov, Anton and Ben Allal, Loubna and von Werra, Leandro and Wolf, Thomas},
146
+ title={FineWeb-Edu: the Finest Collection of Educational Content},
147
+ year={2024},
148
+ url={https://huggingface.co/datasets/HuggingFaceFW/fineweb-edu},
149
+ doi={10.57967/hf/2497},
150
+ publisher={Hugging Face}
151
+ }
152
+
153
+ @inproceedings{paperno2016lambada,
154
+ title={The LAMBADA dataset: Word prediction requiring a broad discourse context},
155
+ author={Paperno, Denis and Kruszewski, Germán and Lazaridou, Angeliki and Pham, Ngoc Quan and Bernardi, Raffaella and Pezzelle, Sandro and Baroni, Marco and Boleda, Gemma and Fernández, Raquel},
156
+ booktitle={Proceedings of ACL},
157
+ year={2016}
158
+ }
159
+
160
+ @inproceedings{saxton2019analysing,
161
+ title={Analysing Mathematical Reasoning Abilities of Neural Models},
162
+ author={Saxton, David and Grefenstette, Edward and Hill, Adam and Kohli, Pushmeet},
163
+ booktitle={Proceedings of ICLR},
164
+ year={2019}
165
+ }
166
+ ```
167
+
168
+ ## A few notes about how we use the data
169
+
170
+ - Reformatting a dataset does not change its license or ownership.
171
+ - We keep the original dataset name and source information with our processed copies.
172
+ - For Stack v3, we keep the repository and file-level license details because different files can use different licenses.
173
+ - When a dataset contains generated conversations or reasoning, we credit both the person who assembled it and the model or source data behind it.
174
+ - Using a company or project’s dataset does not mean that company or project endorses us.
175
+ - For PIQA, HellaSwag, Social IQa, WinoGrande, LAMBADA, and DeepMind Math, we keep a clean evaluation copy and note any training use.
176
+ - Before we publish anything, we save the exact README, license files, and commit SHA that went with the version we downloaded.
source/pyproject.toml ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [build-system]
2
+ requires = ["setuptools>=75", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "qyrou_arch-training"
7
+ version = "0.1.0"
8
+ description = "Qyrou-1 65M pretraining, evaluation, and inference stack"
9
+ requires-python = ">=3.12,<3.13"
10
+ dependencies = [
11
+ "datasets>=4,<5",
12
+ "numpy==2.4.6",
13
+ "psutil>=6.1,<7",
14
+ "safetensors>=0.8,<0.9",
15
+ "tokenizers==0.22.2",
16
+ "transformers==5.13.0",
17
+ ]
18
+
19
+ [project.optional-dependencies]
20
+ train = [
21
+ "cut-cross-entropy==25.1.1",
22
+ "liger-kernel==0.8.0",
23
+ "triton-windows==3.7.1.post27; platform_system == 'Windows'",
24
+ ]
25
+ test = ["pytest>=8.3,<9"]
26
+
27
+ [tool.setuptools.packages.find]
28
+ include = ["qyrou_arch*"]
29
+
30
+ [tool.pytest.ini_options]
31
+ testpaths = ["tests"]
32
+ addopts = "-ra"
source/qyrou_arch/__init__.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ """Qyrou-1 model and training stack."""
2
+
3
+ from .configuration_qyrou_arch import QyrouArchConfig
4
+
5
+ __all__ = ["QyrouArchConfig"]
6
+
source/qyrou_arch/cache.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any
5
+
6
+ import torch
7
+
8
+
9
+ @dataclass
10
+ class QyrouArchHybridCache:
11
+ """Attention K/V plus short-convolution state for incremental decoding."""
12
+
13
+ num_layers: int
14
+ max_cache_len: int | None = None
15
+ attention: list[tuple[torch.Tensor, torch.Tensor] | None] = field(init=False)
16
+ convolution: list[torch.Tensor | None] = field(init=False)
17
+ seen_tokens: int = 0
18
+
19
+ def __post_init__(self) -> None:
20
+ self.attention = [None] * self.num_layers
21
+ self.convolution = [None] * self.num_layers
22
+ if self.max_cache_len is not None and self.max_cache_len <= 0:
23
+ raise ValueError("max_cache_len must be positive")
24
+
25
+ @property
26
+ def is_static(self) -> bool:
27
+ return self.max_cache_len is not None
28
+
29
+ def get_seq_length(self, _layer_idx: int = 0) -> int:
30
+ return self.seen_tokens
31
+
32
+ def get_max_cache_shape(self) -> int | None:
33
+ return self.max_cache_len
34
+
35
+ def update_attention(
36
+ self,
37
+ layer_idx: int,
38
+ key: torch.Tensor,
39
+ value: torch.Tensor,
40
+ cache_position: torch.Tensor,
41
+ ) -> tuple[torch.Tensor, torch.Tensor]:
42
+ if key.ndim != 4 or key.shape != value.shape:
43
+ raise ValueError("K/V cache tensors must have identical [batch, heads, seq, dim] shapes")
44
+ if self.is_static:
45
+ assert self.max_cache_len is not None
46
+ if int(cache_position.max()) >= self.max_cache_len:
47
+ raise ValueError("Static cache capacity exceeded")
48
+ existing = self.attention[layer_idx]
49
+ if existing is None:
50
+ shape = (*key.shape[:2], self.max_cache_len, key.shape[-1])
51
+ key_cache = torch.zeros(shape, dtype=key.dtype, device=key.device)
52
+ value_cache = torch.zeros(shape, dtype=value.dtype, device=value.device)
53
+ else:
54
+ key_cache, value_cache = existing
55
+ key_cache.index_copy_(2, cache_position, key)
56
+ value_cache.index_copy_(2, cache_position, value)
57
+ self.attention[layer_idx] = (key_cache, value_cache)
58
+ visible = max(self.seen_tokens, int(cache_position.max()) + 1)
59
+ return key_cache[:, :, :visible], value_cache[:, :, :visible]
60
+ existing = self.attention[layer_idx]
61
+ if existing is not None:
62
+ key = torch.cat((existing[0], key), dim=2)
63
+ value = torch.cat((existing[1], value), dim=2)
64
+ self.attention[layer_idx] = (key, value)
65
+ return key, value
66
+
67
+ def update_convolution(self, layer_idx: int, state: torch.Tensor) -> None:
68
+ self.convolution[layer_idx] = state
69
+
70
+ def get_convolution(self, layer_idx: int) -> torch.Tensor | None:
71
+ return self.convolution[layer_idx]
72
+
73
+ def finish_step(self, cache_position: torch.Tensor) -> None:
74
+ if cache_position.numel():
75
+ self.seen_tokens = max(self.seen_tokens, int(cache_position.max()) + 1)
76
+
77
+ def reorder_cache(self, beam_idx: torch.LongTensor) -> QyrouArchHybridCache:
78
+ for index, item in enumerate(self.attention):
79
+ if item is not None:
80
+ self.attention[index] = (
81
+ item[0].index_select(0, beam_idx),
82
+ item[1].index_select(0, beam_idx),
83
+ )
84
+ for index, state in enumerate(self.convolution):
85
+ if state is not None:
86
+ self.convolution[index] = state.index_select(0, beam_idx)
87
+ return self
88
+
89
+ def batch_repeat_interleave(self, repeats: int) -> QyrouArchHybridCache:
90
+ indices = torch.arange(
91
+ next(t[0] for t in self.attention if t is not None).shape[0],
92
+ device=next(t[0] for t in self.attention if t is not None).device,
93
+ ).repeat_interleave(repeats)
94
+ return self.reorder_cache(indices)
95
+
96
+ def to_legacy_cache(self) -> tuple[Any, ...]:
97
+ return tuple(self.attention)
98
+
source/qyrou_arch/chat_template.jinja ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {%- for message in messages %}
2
+ {%- if message['role'] not in ['system', 'developer', 'user', 'assistant', 'tool', 'function', 'observation'] %}
3
+ {{- raise_exception('Unsupported QyrouArch chat role: ' + message['role']) }}
4
+ {%- endif %}
5
+ {{- '<|im_start|><|' + message['role'] + '|>\n' + message['content'] + '<|im_end|>\n' }}
6
+ {%- endfor %}
7
+ {%- if add_generation_prompt %}
8
+ {{- '<|im_start|><|assistant|>\n' }}
9
+ {%- endif %}
10
+
source/qyrou_arch/checkpointing.py ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+ import shutil
6
+ import time
7
+ import uuid
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ import torch
12
+
13
+ from .io_utils import atomic_write_json, read_json, sha256_file
14
+
15
+
16
+ def directory_size(path: Path) -> int:
17
+ return sum(item.stat().st_size for item in path.rglob("*") if item.is_file())
18
+
19
+
20
+ class CheckpointManager:
21
+ def __init__(self, root: str | Path, cap_gib: float = 50, prune_count: int = 12) -> None:
22
+ self.root = Path(root).resolve()
23
+ self.root.mkdir(parents=True, exist_ok=True)
24
+ self.cap_bytes = int(cap_gib * 2**30)
25
+ self.prune_count = prune_count
26
+ self.index_path = self.root / "index.json"
27
+ self.latest_path = self.root / "latest.json"
28
+ if not self.index_path.exists():
29
+ atomic_write_json(self.index_path, {"version": 1, "checkpoints": [], "best": None})
30
+
31
+ def _index(self) -> dict[str, Any]:
32
+ return read_json(self.index_path)
33
+
34
+ def storage_bytes(self) -> int:
35
+ return directory_size(self.root)
36
+
37
+ def _validate_delete_target(self, path: Path) -> None:
38
+ resolved = path.resolve()
39
+ if resolved.parent != self.root or not resolved.name.startswith("checkpoint-step-"):
40
+ raise RuntimeError(f"Refusing unsafe checkpoint deletion target: {resolved}")
41
+
42
+ def _prune_before_save(self, projected_bytes: int) -> None:
43
+ if self.storage_bytes() + projected_bytes <= self.cap_bytes:
44
+ return
45
+ index = self._index()
46
+ latest = read_json(self.latest_path).get("path") if self.latest_path.exists() else None
47
+ best = index.get("best")
48
+ eligible = [
49
+ entry
50
+ for entry in sorted(index["checkpoints"], key=lambda item: item["step"])
51
+ if entry["path"] not in {latest, best}
52
+ and not entry.get("protected_reasons")
53
+ ]
54
+ if len(eligible) < self.prune_count:
55
+ raise RuntimeError(
56
+ f"Checkpoint cap requires pruning, but only {len(eligible)} unprotected checkpoints exist"
57
+ )
58
+ deleted = {entry["path"] for entry in eligible[: self.prune_count]}
59
+ for relative in deleted:
60
+ target = self.root / relative
61
+ self._validate_delete_target(target)
62
+ shutil.rmtree(target)
63
+ index["checkpoints"] = [
64
+ entry for entry in index["checkpoints"] if entry["path"] not in deleted
65
+ ]
66
+ atomic_write_json(self.index_path, index)
67
+
68
+ def save(
69
+ self,
70
+ *,
71
+ model: Any,
72
+ optimizer: torch.optim.Optimizer,
73
+ trainer_state: dict[str, Any],
74
+ step: int,
75
+ total_tokens: int,
76
+ validation_loss: float | None = None,
77
+ expected_bytes: int | None = None,
78
+ ) -> Path:
79
+ self._prune_before_save(expected_bytes or 2**30)
80
+ name = f"checkpoint-step-{step:09d}"
81
+ final_path = self.root / name
82
+ if final_path.exists():
83
+ raise RuntimeError(f"Checkpoint already exists: {final_path}")
84
+ temporary = self.root / f".tmp-{name}-{uuid.uuid4().hex}"
85
+ temporary.mkdir()
86
+ model_dir = temporary / "model"
87
+ model.save_pretrained(model_dir, safe_serialization=True)
88
+ torch.save(
89
+ {
90
+ "version": 1,
91
+ "optimizer": optimizer.state_dict(),
92
+ "trainer": trainer_state,
93
+ "cpu_rng": torch.get_rng_state(),
94
+ "cuda_rng": torch.cuda.get_rng_state_all() if torch.cuda.is_available() else None,
95
+ },
96
+ temporary / "training_state.pt",
97
+ )
98
+ files = []
99
+ for path in sorted(temporary.rglob("*")):
100
+ if path.is_file():
101
+ files.append(
102
+ {
103
+ "path": str(path.relative_to(temporary)),
104
+ "bytes": path.stat().st_size,
105
+ "sha256": sha256_file(path),
106
+ }
107
+ )
108
+ atomic_write_json(
109
+ temporary / "manifest.json",
110
+ {
111
+ "version": 1,
112
+ "step": step,
113
+ "total_tokens": total_tokens,
114
+ "validation_loss": validation_loss,
115
+ "created_at": time.time(),
116
+ "files": files,
117
+ },
118
+ )
119
+ os.replace(temporary, final_path)
120
+ if not self.validate(final_path):
121
+ raise RuntimeError(f"New checkpoint failed validation: {final_path}")
122
+
123
+ index = self._index()
124
+ entry = {
125
+ "path": name,
126
+ "step": step,
127
+ "total_tokens": total_tokens,
128
+ "validation_loss": validation_loss,
129
+ "bytes": directory_size(final_path),
130
+ "protected_reasons": [],
131
+ }
132
+ index["checkpoints"].append(entry)
133
+ if validation_loss is not None:
134
+ current_best = index.get("best")
135
+ current_entry = next(
136
+ (item for item in index["checkpoints"] if item["path"] == current_best),
137
+ None,
138
+ )
139
+ if current_entry is None or validation_loss < current_entry["validation_loss"]:
140
+ index["best"] = name
141
+ atomic_write_json(self.index_path, index)
142
+ atomic_write_json(
143
+ self.latest_path,
144
+ {"version": 1, "path": name, "step": step, "total_tokens": total_tokens},
145
+ )
146
+ return final_path
147
+
148
+ def validate(self, path: str | Path) -> bool:
149
+ path = Path(path)
150
+ try:
151
+ manifest = read_json(path / "manifest.json")
152
+ for entry in manifest["files"]:
153
+ file_path = path / entry["path"]
154
+ if file_path.stat().st_size != entry["bytes"]:
155
+ return False
156
+ if sha256_file(file_path) != entry["sha256"]:
157
+ return False
158
+ torch.load(path / "training_state.pt", map_location="cpu", weights_only=False)
159
+ return True
160
+ except (OSError, KeyError, ValueError, RuntimeError, json.JSONDecodeError):
161
+ return False
162
+
163
+ def find_latest_valid(self) -> Path | None:
164
+ index = self._index()
165
+ for entry in sorted(index["checkpoints"], key=lambda item: item["step"], reverse=True):
166
+ path = self.root / entry["path"]
167
+ if self.validate(path):
168
+ return path
169
+ return None
170
+
171
+ def load_training_state(self, path: str | Path) -> dict[str, Any]:
172
+ path = Path(path)
173
+ if not self.validate(path):
174
+ raise RuntimeError(f"Invalid checkpoint: {path}")
175
+ return torch.load(path / "training_state.pt", map_location="cpu", weights_only=False)
176
+
177
+ def save_milestone(self, model: Any, fraction: float, step: int, total_tokens: int) -> Path:
178
+ milestones = self.root / "milestones"
179
+ milestones.mkdir(exist_ok=True)
180
+ destination = milestones / f"milestone-{int(fraction * 100):03d}pct"
181
+ if destination.exists():
182
+ return destination
183
+ temporary = milestones / f".tmp-{destination.name}-{uuid.uuid4().hex}"
184
+ model.save_pretrained(temporary, safe_serialization=True)
185
+ atomic_write_json(
186
+ temporary / "milestone.json",
187
+ {"fraction": fraction, "step": step, "total_tokens": total_tokens},
188
+ )
189
+ os.replace(temporary, destination)
190
+ return destination
source/qyrou_arch/configuration_qyrou_arch.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ try:
6
+ from transformers import PretrainedConfig
7
+ except ImportError:
8
+ class PretrainedConfig: # type: ignore[no-redef]
9
+ model_type = "qyrou_arch"
10
+
11
+ def __init__(self, **kwargs: Any) -> None:
12
+ for key, value in kwargs.items():
13
+ setattr(self, key, value)
14
+
15
+
16
+ class QyrouArchConfig(PretrainedConfig):
17
+ model_type = "qyrou_arch"
18
+
19
+ def __init__(
20
+ self,
21
+ vocab_size: int = 20_000,
22
+ hidden_size: int = 512,
23
+ num_hidden_layers: int = 21,
24
+ num_attention_heads: int = 8,
25
+ num_key_value_heads: int = 2,
26
+ intermediate_size: int = 1_328,
27
+ conv_layers: list[int] | None = None,
28
+ conv_kernel_size: int = 7,
29
+ rope_theta: float = 10_000.0,
30
+ rms_norm_eps: float = 1e-5,
31
+ max_position_embeddings: int = 2_048,
32
+ initializer_range: float = 0.02,
33
+ residual_initializer_scale: float = 1.0 / (42.0**0.5),
34
+ tie_word_embeddings: bool = True,
35
+ qk_norm: bool = True,
36
+ attention_backend: str = "cudnn",
37
+ fused_qkv_projection: bool = True,
38
+ fused_gate_up_projection: bool = True,
39
+ liger_rms_norm: bool = True,
40
+ liger_swiglu: bool = True,
41
+ cut_cross_entropy: bool = True,
42
+ use_cache: bool = True,
43
+ eod_token_id: int = 4,
44
+ **kwargs: Any,
45
+ ) -> None:
46
+ super().__init__(tie_word_embeddings=tie_word_embeddings, **kwargs)
47
+ if hidden_size % num_attention_heads:
48
+ raise ValueError("hidden_size must be divisible by num_attention_heads")
49
+ if num_attention_heads % num_key_value_heads:
50
+ raise ValueError("num_attention_heads must be divisible by num_key_value_heads")
51
+ conv_layers = conv_layers if conv_layers is not None else [4, 9, 14, 19]
52
+ if len(set(conv_layers)) != len(conv_layers) or not all(
53
+ 1 <= layer <= num_hidden_layers for layer in conv_layers
54
+ ):
55
+ raise ValueError("conv_layers must contain unique one-based layer indices")
56
+ self.vocab_size = vocab_size
57
+ self.hidden_size = hidden_size
58
+ self.num_hidden_layers = num_hidden_layers
59
+ self.num_attention_heads = num_attention_heads
60
+ self.num_key_value_heads = num_key_value_heads
61
+ self.intermediate_size = intermediate_size
62
+ self.conv_layers = conv_layers
63
+ self.conv_kernel_size = conv_kernel_size
64
+ self.rope_theta = rope_theta
65
+ self.rms_norm_eps = rms_norm_eps
66
+ self.max_position_embeddings = max_position_embeddings
67
+ self.initializer_range = initializer_range
68
+ self.residual_initializer_scale = residual_initializer_scale
69
+ self.qk_norm = qk_norm
70
+ self.attention_backend = attention_backend
71
+ self.fused_qkv_projection = fused_qkv_projection
72
+ self.fused_gate_up_projection = fused_gate_up_projection
73
+ self.liger_rms_norm = liger_rms_norm
74
+ self.liger_swiglu = liger_swiglu
75
+ self.cut_cross_entropy = cut_cross_entropy
76
+ self.use_cache = use_cache
77
+ self.eod_token_id = eod_token_id
78
+ self.hidden_act = "silu"
79
+
source/qyrou_arch/data.py ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import bisect
4
+ import hashlib
5
+ import random
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ import numpy as np
11
+ import torch
12
+
13
+ from .io_utils import read_json
14
+
15
+
16
+ @dataclass
17
+ class Batch:
18
+ input_ids: torch.LongTensor
19
+ labels: torch.LongTensor
20
+ real_tokens: int
21
+ start_cursor: int
22
+ end_cursor: int
23
+
24
+
25
+ class PackedTokenStream:
26
+ """Deterministic one-pass stream over shuffled contiguous token blocks."""
27
+
28
+ def __init__(
29
+ self,
30
+ manifest_path: str | Path,
31
+ sequence_length: int,
32
+ pad_token_id: int,
33
+ seed: int = 1337,
34
+ cursor: int = 0,
35
+ shuffle_block_tokens: int = 65_536,
36
+ ) -> None:
37
+ self.manifest_path = Path(manifest_path).resolve()
38
+ self.root = self.manifest_path.parent
39
+ self.manifest = read_json(self.manifest_path)
40
+ if not self.manifest.get("complete"):
41
+ raise RuntimeError("Token manifest is incomplete")
42
+ if self.manifest.get("dtype") != "uint16":
43
+ raise RuntimeError("Only uint16 token caches are supported")
44
+ self.sequence_length = sequence_length
45
+ self.pad_token_id = pad_token_id
46
+ self.seed = seed
47
+ if shuffle_block_tokens <= 0:
48
+ raise ValueError("shuffle_block_tokens must be positive")
49
+ if shuffle_block_tokens % sequence_length != 0:
50
+ raise ValueError("shuffle_block_tokens must be a multiple of sequence_length")
51
+ self.shuffle_block_tokens = shuffle_block_tokens
52
+ self.entries = list(self.manifest["shards"])
53
+
54
+ physical_blocks: list[tuple[int, int, int]] = []
55
+ for shard_index, entry in enumerate(self.entries):
56
+ shard_tokens = int(entry["tokens"])
57
+ for offset in range(0, shard_tokens, shuffle_block_tokens):
58
+ physical_blocks.append(
59
+ (shard_index, offset, min(shuffle_block_tokens, shard_tokens - offset))
60
+ )
61
+ permutation = list(range(len(physical_blocks)))
62
+ random.Random(seed).shuffle(permutation)
63
+ self.blocks = [physical_blocks[index] for index in permutation]
64
+ self.permutation = np.asarray(permutation, dtype=np.uint32)
65
+ self.permutation_sha256 = hashlib.sha256(self.permutation.tobytes()).hexdigest()
66
+ self.prefix = [0]
67
+ for _, _, block_length in self.blocks:
68
+ self.prefix.append(self.prefix[-1] + block_length)
69
+ self.total_tokens = self.prefix[-1]
70
+ self.cursor = 0
71
+ self._mapped_shard_index: int | None = None
72
+ self._mapped: np.memmap | None = None
73
+ self.seek(cursor)
74
+
75
+ @property
76
+ def remaining_tokens(self) -> int:
77
+ return self.total_tokens - self.cursor
78
+
79
+ @property
80
+ def current_block_index(self) -> int:
81
+ if not self.blocks:
82
+ return 0
83
+ position = min(self.cursor, self.total_tokens - 1)
84
+ return min(bisect.bisect_right(self.prefix, position) - 1, len(self.blocks) - 1)
85
+
86
+ @property
87
+ def current_shard_index(self) -> int:
88
+ if not self.blocks:
89
+ return 0
90
+ return self.blocks[self.current_block_index][0]
91
+
92
+ def state_dict(self) -> dict[str, Any]:
93
+ return {
94
+ "version": 2,
95
+ "cursor": self.cursor,
96
+ "seed": self.seed,
97
+ "shuffle_block_tokens": self.shuffle_block_tokens,
98
+ "block_count": len(self.blocks),
99
+ "permutation": self.permutation.copy(),
100
+ "permutation_sha256": self.permutation_sha256,
101
+ "manifest_tokenizer_sha256": self.manifest["tokenizer_sha256"],
102
+ "manifest_corpus_sha256": self.manifest["corpus_manifest_sha256"],
103
+ }
104
+
105
+ def load_state_dict(self, state: dict[str, Any]) -> None:
106
+ if state.get("version") != 2:
107
+ raise RuntimeError(
108
+ "Unsupported data-state version; old shard-only checkpoints cannot resume "
109
+ "the block-shuffled run"
110
+ )
111
+ if state.get("seed") != self.seed:
112
+ raise RuntimeError("Data shuffle seed does not match checkpoint")
113
+ if state.get("shuffle_block_tokens") != self.shuffle_block_tokens:
114
+ raise RuntimeError("Data shuffle block size does not match checkpoint")
115
+ if int(state.get("block_count", -1)) != len(self.blocks):
116
+ raise RuntimeError("Data shuffle block count does not match checkpoint")
117
+ checkpoint_permutation = np.asarray(state.get("permutation"), dtype=np.uint32)
118
+ checkpoint_hash = hashlib.sha256(checkpoint_permutation.tobytes()).hexdigest()
119
+ if (
120
+ checkpoint_permutation.shape != self.permutation.shape
121
+ or not np.array_equal(checkpoint_permutation, self.permutation)
122
+ or checkpoint_hash != state.get("permutation_sha256")
123
+ or checkpoint_hash != self.permutation_sha256
124
+ ):
125
+ raise RuntimeError("Data block permutation does not match checkpoint")
126
+ if state.get("manifest_tokenizer_sha256") != self.manifest["tokenizer_sha256"]:
127
+ raise RuntimeError("Checkpoint tokenizer hash does not match token cache")
128
+ if state.get("manifest_corpus_sha256") != self.manifest["corpus_manifest_sha256"]:
129
+ raise RuntimeError("Checkpoint corpus hash does not match token cache")
130
+ self.seek(int(state["cursor"]))
131
+
132
+ def seek(self, cursor: int) -> None:
133
+ if not 0 <= cursor <= self.total_tokens:
134
+ raise ValueError(f"Cursor {cursor} is outside [0, {self.total_tokens}]")
135
+ self.cursor = cursor
136
+
137
+ def _map_block(self, logical_index: int) -> tuple[np.memmap, int, int]:
138
+ block_index = bisect.bisect_right(self.prefix, logical_index) - 1
139
+ if block_index >= len(self.blocks):
140
+ raise EOFError
141
+ shard_index, block_offset, _ = self.blocks[block_index]
142
+ if self._mapped_shard_index != shard_index:
143
+ self._mapped = np.memmap(
144
+ self.root / self.entries[shard_index]["file"],
145
+ mode="r",
146
+ dtype="<u2",
147
+ )
148
+ self._mapped_shard_index = shard_index
149
+ assert self._mapped is not None
150
+ offset = block_offset + logical_index - self.prefix[block_index]
151
+ available = self.prefix[block_index + 1] - logical_index
152
+ return self._mapped, offset, available
153
+
154
+ def read_tokens(self, count: int) -> np.ndarray:
155
+ if count < 0:
156
+ raise ValueError("count must be non-negative")
157
+ count = min(count, self.remaining_tokens)
158
+ result = np.empty(count, dtype=np.uint16)
159
+ written = 0
160
+ while written < count:
161
+ mapped, offset, available = self._map_block(self.cursor)
162
+ take = min(count - written, available)
163
+ result[written : written + take] = mapped[offset : offset + take]
164
+ self.cursor += take
165
+ written += take
166
+ return result
167
+
168
+ def next_batch(self, batch_size: int, device: torch.device | str) -> Batch | None:
169
+ if self.remaining_tokens <= 0:
170
+ return None
171
+ requested = batch_size * self.sequence_length
172
+ start = self.cursor
173
+ tokens = self.read_tokens(requested)
174
+ real_tokens = int(tokens.size)
175
+ if real_tokens < requested:
176
+ padded = np.full(requested, self.pad_token_id, dtype=np.uint16)
177
+ padded[:real_tokens] = tokens
178
+ tokens = padded
179
+ tensor = torch.from_numpy(tokens.astype(np.int64, copy=False)).view(
180
+ batch_size,
181
+ self.sequence_length,
182
+ )
183
+ labels = tensor.clone()
184
+ if real_tokens < requested:
185
+ labels.view(-1)[real_tokens:] = -100
186
+ return Batch(
187
+ input_ids=tensor.to(device, non_blocking=True),
188
+ labels=labels.to(device, non_blocking=True),
189
+ real_tokens=real_tokens,
190
+ start_cursor=start,
191
+ end_cursor=self.cursor,
192
+ )
193
+
194
+
195
+ class EvaluationTokens:
196
+ def __init__(self, path: str | Path, sequence_length: int, pad_token_id: int) -> None:
197
+ self.path = Path(path)
198
+ self.sequence_length = sequence_length
199
+ self.pad_token_id = pad_token_id
200
+ self.tokens = np.memmap(self.path, mode="r", dtype="<u2")
201
+
202
+ def batches(self, batch_size: int, device: torch.device | str, max_tokens: int):
203
+ usable = min(len(self.tokens), max_tokens)
204
+ cursor = 0
205
+ while cursor < usable:
206
+ requested = batch_size * self.sequence_length
207
+ take = min(requested, usable - cursor)
208
+ values = np.full(requested, self.pad_token_id, dtype=np.int64)
209
+ values[:take] = self.tokens[cursor : cursor + take]
210
+ input_ids = torch.from_numpy(values).view(batch_size, self.sequence_length)
211
+ labels = input_ids.clone()
212
+ labels.view(-1)[take:] = -100
213
+ yield input_ids.to(device), labels.to(device), take
214
+ cursor += take
source/qyrou_arch/io_utils.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import json
5
+ import os
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+
10
+ def sha256_file(path: Path, chunk_size: int = 8 * 1024 * 1024) -> str:
11
+ digest = hashlib.sha256()
12
+ with path.open("rb") as handle:
13
+ while chunk := handle.read(chunk_size):
14
+ digest.update(chunk)
15
+ return digest.hexdigest()
16
+
17
+
18
+ def sha256_json(value: Any) -> str:
19
+ payload = json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
20
+ return hashlib.sha256(payload.encode("utf-8")).hexdigest()
21
+
22
+
23
+ def atomic_write_json(path: Path, value: Any) -> None:
24
+ path.parent.mkdir(parents=True, exist_ok=True)
25
+ temporary = path.with_name(f".{path.name}.tmp")
26
+ with temporary.open("w", encoding="utf-8", newline="\n") as handle:
27
+ json.dump(value, handle, ensure_ascii=False, indent=2)
28
+ handle.write("\n")
29
+ handle.flush()
30
+ os.fsync(handle.fileno())
31
+ os.replace(temporary, path)
32
+
33
+
34
+ def read_json(path: Path) -> Any:
35
+ with path.open("r", encoding="utf-8") as handle:
36
+ return json.load(handle)
37
+
38
+
39
+ def load_run_config(path: str | Path) -> dict[str, Any]:
40
+ return read_json(Path(path).resolve())
41
+
source/qyrou_arch/metrics.py ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import math
5
+ import os
6
+ import subprocess
7
+ import time
8
+ from collections import deque
9
+ from pathlib import Path
10
+ from statistics import median
11
+ from typing import Any
12
+
13
+ import psutil
14
+ import torch
15
+
16
+
17
+ class RunningMetrics:
18
+ def __init__(self) -> None:
19
+ self.losses_100: deque[float] = deque(maxlen=100)
20
+ self.losses_1000: deque[float] = deque(maxlen=1000)
21
+ self.throughputs: deque[float] = deque(maxlen=5000)
22
+ self.spike_steps = 0
23
+
24
+ def update(self, loss: float, tps: float) -> None:
25
+ self.losses_100.append(loss)
26
+ self.losses_1000.append(loss)
27
+ self.throughputs.append(tps)
28
+ if len(self.losses_1000) == self.losses_1000.maxlen and self.ema_100 > 1.1 * self.ema_1000:
29
+ self.spike_steps += 1
30
+ else:
31
+ self.spike_steps = 0
32
+
33
+ @property
34
+ def ema_100(self) -> float:
35
+ return sum(self.losses_100) / max(len(self.losses_100), 1)
36
+
37
+ @property
38
+ def ema_1000(self) -> float:
39
+ return sum(self.losses_1000) / max(len(self.losses_1000), 1)
40
+
41
+ @property
42
+ def median_tps(self) -> float:
43
+ return median(self.throughputs) if self.throughputs else 0.0
44
+
45
+ def state_dict(self) -> dict[str, Any]:
46
+ return {
47
+ "losses_100": list(self.losses_100),
48
+ "losses_1000": list(self.losses_1000),
49
+ "throughputs": list(self.throughputs),
50
+ "spike_steps": self.spike_steps,
51
+ }
52
+
53
+ def load_state_dict(self, state: dict[str, Any]) -> None:
54
+ self.losses_100.extend(state.get("losses_100", []))
55
+ self.losses_1000.extend(state.get("losses_1000", []))
56
+ self.throughputs.extend(state.get("throughputs", []))
57
+ self.spike_steps = int(state.get("spike_steps", 0))
58
+
59
+
60
+ class JsonlLogger:
61
+ def __init__(self, path: str | Path) -> None:
62
+ self.path = Path(path)
63
+ self.path.parent.mkdir(parents=True, exist_ok=True)
64
+
65
+ def log(self, payload: dict[str, Any]) -> None:
66
+ payload = dict(payload)
67
+ payload.setdefault("wall_time", time.time())
68
+ with self.path.open("a", encoding="utf-8", newline="\n") as handle:
69
+ handle.write(json.dumps(payload, ensure_ascii=False) + "\n")
70
+ handle.flush()
71
+ os.fsync(handle.fileno())
72
+
73
+
74
+ def gpu_telemetry() -> dict[str, float | None]:
75
+ result: dict[str, float | None] = {
76
+ "gpu_temperature_c": None,
77
+ "gpu_power_w": None,
78
+ "gpu_util_percent": None,
79
+ }
80
+ try:
81
+ output = subprocess.check_output(
82
+ [
83
+ "nvidia-smi",
84
+ "--query-gpu=temperature.gpu,power.draw,utilization.gpu",
85
+ "--format=csv,noheader,nounits",
86
+ ],
87
+ text=True,
88
+ timeout=3,
89
+ ).strip()
90
+ temperature, power, utilization = (float(value.strip()) for value in output.split(","))
91
+ result.update(
92
+ {
93
+ "gpu_temperature_c": temperature,
94
+ "gpu_power_w": power,
95
+ "gpu_util_percent": utilization,
96
+ }
97
+ )
98
+ except (OSError, subprocess.SubprocessError, ValueError):
99
+ pass
100
+ return result
101
+
102
+
103
+ def system_telemetry() -> dict[str, float | None]:
104
+ process = psutil.Process()
105
+ payload: dict[str, float | None] = {
106
+ "process_ram_gib": process.memory_info().rss / 2**30,
107
+ "system_ram_percent": psutil.virtual_memory().percent,
108
+ }
109
+ if torch.cuda.is_available():
110
+ payload.update(
111
+ {
112
+ "gpu_allocated_gib": torch.cuda.memory_allocated() / 2**30,
113
+ "gpu_reserved_gib": torch.cuda.memory_reserved() / 2**30,
114
+ "gpu_peak_gib": torch.cuda.max_memory_allocated() / 2**30,
115
+ }
116
+ )
117
+ payload.update(gpu_telemetry())
118
+ return payload
119
+
120
+
121
+ def safe_perplexity(loss: float) -> float:
122
+ return math.exp(min(loss, 80.0))
source/qyrou_arch/modeling_qyrou_arch.py ADDED
@@ -0,0 +1,504 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+ import torch.nn.functional as F
8
+ from torch.nn.attention import SDPBackend, sdpa_kernel
9
+ from transformers import PreTrainedModel
10
+ from transformers.generation import GenerationMixin
11
+ from transformers.modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
12
+
13
+ from .cache import QyrouArchHybridCache
14
+ from .configuration_qyrou_arch import QyrouArchConfig
15
+
16
+ try:
17
+ from .triton_kernels import PackedSwiGLUFunction
18
+ except (ImportError, RuntimeError):
19
+ PackedSwiGLUFunction = None
20
+
21
+ try:
22
+ from liger_kernel.ops.rms_norm import LigerRMSNormFunction
23
+ from liger_kernel.ops.swiglu import LigerSiLUMulFunction
24
+ except ImportError:
25
+ LigerRMSNormFunction = None
26
+ LigerSiLUMulFunction = None
27
+
28
+ try:
29
+ from cut_cross_entropy import linear_cross_entropy as cut_linear_cross_entropy
30
+ except ImportError:
31
+ cut_linear_cross_entropy = None
32
+
33
+
34
+ class RMSNorm(nn.Module):
35
+ def __init__(self, dim: int, eps: float, use_liger: bool = False) -> None:
36
+ super().__init__()
37
+ self.weight = nn.Parameter(torch.ones(dim))
38
+ self.eps = eps
39
+ self.use_liger = use_liger
40
+
41
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
42
+ if (
43
+ x.is_cuda
44
+ and self.use_liger
45
+ and LigerRMSNormFunction is not None
46
+ and not torch.compiler.is_compiling()
47
+ ):
48
+ return LigerRMSNormFunction.apply(x, self.weight, self.eps, 0.0, "llama", False, None)
49
+ x_float = x.float()
50
+ normalized = x_float * torch.rsqrt(x_float.square().mean(-1, keepdim=True) + self.eps)
51
+ return normalized.to(x.dtype) * self.weight.to(x.dtype)
52
+
53
+
54
+ def _apply_rope(
55
+ x: torch.Tensor,
56
+ cos: torch.Tensor,
57
+ sin: torch.Tensor,
58
+ ) -> torch.Tensor:
59
+ even, odd = x[..., 0::2], x[..., 1::2]
60
+ return torch.stack((even * cos - odd * sin, even * sin + odd * cos), dim=-1).flatten(-2)
61
+
62
+
63
+ def _last_valid_state(
64
+ x: torch.Tensor,
65
+ mask: torch.Tensor | None,
66
+ state_length: int,
67
+ ) -> torch.Tensor:
68
+ if state_length == 0:
69
+ return x[:, :0]
70
+ if mask is None:
71
+ return F.pad(x, (0, 0, max(0, state_length - x.shape[1]), 0))[:, -state_length:]
72
+ result = torch.zeros(
73
+ (x.shape[0], state_length, x.shape[2]),
74
+ dtype=x.dtype,
75
+ device=x.device,
76
+ )
77
+ for batch_index in range(x.shape[0]):
78
+ valid = x[batch_index][mask[batch_index].bool()]
79
+ valid = valid[-state_length:]
80
+ result[batch_index, -valid.shape[0] :] = valid
81
+ return result
82
+
83
+
84
+ class CausalGQA(nn.Module):
85
+ def __init__(self, config: QyrouArchConfig, layer_idx: int) -> None:
86
+ super().__init__()
87
+ self.layer_idx = layer_idx
88
+ self.num_heads = config.num_attention_heads
89
+ self.num_kv_heads = config.num_key_value_heads
90
+ self.head_dim = config.hidden_size // config.num_attention_heads
91
+ self.q_size = self.num_heads * self.head_dim
92
+ self.kv_size = self.num_kv_heads * self.head_dim
93
+ self.attention_backend = config.attention_backend
94
+ if config.fused_qkv_projection:
95
+ self.qkv_proj = nn.Linear(config.hidden_size, self.q_size + 2 * self.kv_size, bias=False)
96
+ else:
97
+ self.q_proj = nn.Linear(config.hidden_size, self.q_size, bias=False)
98
+ self.k_proj = nn.Linear(config.hidden_size, self.kv_size, bias=False)
99
+ self.v_proj = nn.Linear(config.hidden_size, self.kv_size, bias=False)
100
+ self.o_proj = nn.Linear(config.hidden_size, config.hidden_size, bias=False)
101
+ self.o_proj._qyrou_arch_residual_projection = True # type: ignore[attr-defined]
102
+ self.q_norm = RMSNorm(self.head_dim, config.rms_norm_eps) if config.qk_norm else nn.Identity()
103
+ self.k_norm = RMSNorm(self.head_dim, config.rms_norm_eps) if config.qk_norm else nn.Identity()
104
+
105
+ def forward(
106
+ self,
107
+ x: torch.Tensor,
108
+ rope: tuple[torch.Tensor, torch.Tensor],
109
+ attention_mask: torch.Tensor | None,
110
+ cache: QyrouArchHybridCache | None,
111
+ cache_position: torch.Tensor,
112
+ ) -> torch.Tensor:
113
+ batch, query_length, _ = x.shape
114
+ if hasattr(self, "qkv_proj"):
115
+ q, k, v = self.qkv_proj(x).split((self.q_size, self.kv_size, self.kv_size), dim=-1)
116
+ else:
117
+ q, k, v = self.q_proj(x), self.k_proj(x), self.v_proj(x)
118
+ q = q.view(batch, query_length, self.num_heads, self.head_dim).transpose(1, 2)
119
+ k = k.view(batch, query_length, self.num_kv_heads, self.head_dim).transpose(1, 2)
120
+ v = v.view(batch, query_length, self.num_kv_heads, self.head_dim).transpose(1, 2)
121
+ q, k = self.q_norm(q), self.k_norm(k)
122
+ q = _apply_rope(q, *rope)
123
+ k = _apply_rope(k, *rope)
124
+ if cache is not None:
125
+ k, v = cache.update_attention(self.layer_idx, k, v, cache_position)
126
+ key_length = k.shape[2]
127
+
128
+ mask = None
129
+ use_fast_causal = cache is None and attention_mask is None
130
+ if not use_fast_causal:
131
+ key_positions = torch.arange(key_length, device=x.device)
132
+ allowed = key_positions[None, :] <= cache_position[:, None]
133
+ mask = allowed[None, None, :, :].expand(batch, 1, query_length, key_length)
134
+ if attention_mask is not None:
135
+ if attention_mask.shape[-1] < key_length:
136
+ raise ValueError("attention_mask is shorter than the cached key sequence")
137
+ mask = mask & attention_mask[:, None, None, :key_length].bool()
138
+ if q.is_cuda and self.attention_backend in {"cudnn", "flash"}:
139
+ backends = (
140
+ [SDPBackend.CUDNN_ATTENTION, SDPBackend.FLASH_ATTENTION, SDPBackend.MATH]
141
+ if self.attention_backend == "cudnn"
142
+ else [SDPBackend.FLASH_ATTENTION, SDPBackend.CUDNN_ATTENTION, SDPBackend.MATH]
143
+ )
144
+ with sdpa_kernel(
145
+ backends,
146
+ set_priority=True,
147
+ ):
148
+ output = F.scaled_dot_product_attention(
149
+ q,
150
+ k,
151
+ v,
152
+ attn_mask=mask,
153
+ is_causal=use_fast_causal,
154
+ enable_gqa=True,
155
+ )
156
+ else:
157
+ output = F.scaled_dot_product_attention(
158
+ q,
159
+ k,
160
+ v,
161
+ attn_mask=mask,
162
+ is_causal=use_fast_causal,
163
+ enable_gqa=True,
164
+ )
165
+ output = output.transpose(1, 2).contiguous().view(batch, query_length, -1)
166
+ return self.o_proj(output)
167
+
168
+
169
+ class CausalConvMixer(nn.Module):
170
+ def __init__(self, config: QyrouArchConfig, layer_idx: int) -> None:
171
+ super().__init__()
172
+ self.layer_idx = layer_idx
173
+ self.kernel_size = config.conv_kernel_size
174
+ self.depthwise = nn.Conv1d(
175
+ config.hidden_size,
176
+ config.hidden_size,
177
+ kernel_size=self.kernel_size,
178
+ groups=config.hidden_size,
179
+ bias=False,
180
+ )
181
+ self.pointwise = nn.Linear(config.hidden_size, config.hidden_size, bias=False)
182
+ self.pointwise._qyrou_arch_residual_projection = True # type: ignore[attr-defined]
183
+
184
+ def forward(
185
+ self,
186
+ x: torch.Tensor,
187
+ _rope: tuple[torch.Tensor, torch.Tensor],
188
+ attention_mask: torch.Tensor | None,
189
+ cache: QyrouArchHybridCache | None,
190
+ _cache_position: torch.Tensor,
191
+ ) -> torch.Tensor:
192
+ state_length = self.kernel_size - 1
193
+ query_mask = attention_mask[:, -x.shape[1] :] if attention_mask is not None else None
194
+ if query_mask is not None:
195
+ x = x * query_mask.unsqueeze(-1).to(x.dtype)
196
+ if cache is None:
197
+ conv_input = F.pad(x.transpose(1, 2), (state_length, 0))
198
+ else:
199
+ previous = cache.get_convolution(self.layer_idx)
200
+ if previous is None:
201
+ previous = torch.zeros(
202
+ (x.shape[0], state_length, x.shape[2]),
203
+ dtype=x.dtype,
204
+ device=x.device,
205
+ )
206
+ combined = torch.cat((previous, x), dim=1)
207
+ conv_input = combined.transpose(1, 2)
208
+ combined_mask = None
209
+ if query_mask is not None:
210
+ combined_mask = torch.cat(
211
+ (
212
+ torch.ones(
213
+ (x.shape[0], state_length),
214
+ dtype=query_mask.dtype,
215
+ device=query_mask.device,
216
+ ),
217
+ query_mask,
218
+ ),
219
+ dim=1,
220
+ )
221
+ cache.update_convolution(
222
+ self.layer_idx,
223
+ _last_valid_state(combined, combined_mask, state_length),
224
+ )
225
+ return self.pointwise(self.depthwise(conv_input).transpose(1, 2))
226
+
227
+
228
+ class SwiGLU(nn.Module):
229
+ def __init__(self, config: QyrouArchConfig) -> None:
230
+ super().__init__()
231
+ self.intermediate_size = config.intermediate_size
232
+ self.use_liger = config.liger_swiglu
233
+ if config.fused_gate_up_projection:
234
+ self.gate_up_proj = nn.Linear(config.hidden_size, 2 * config.intermediate_size, bias=False)
235
+ else:
236
+ self.gate_proj = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
237
+ self.up_proj = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
238
+ self.down_proj = nn.Linear(config.intermediate_size, config.hidden_size, bias=False)
239
+ self.down_proj._qyrou_arch_residual_projection = True # type: ignore[attr-defined]
240
+
241
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
242
+ if hasattr(self, "gate_up_proj"):
243
+ packed = self.gate_up_proj(x)
244
+ if (
245
+ packed.is_cuda
246
+ and self.use_liger
247
+ and PackedSwiGLUFunction is not None
248
+ and not torch.compiler.is_compiling()
249
+ ):
250
+ return self.down_proj(PackedSwiGLUFunction.apply(packed))
251
+ gate, up = packed.chunk(2, dim=-1)
252
+ else:
253
+ gate, up = self.gate_proj(x), self.up_proj(x)
254
+ if torch.compiler.is_compiling():
255
+ activated = F.silu(gate) * up
256
+ elif x.is_cuda and self.use_liger and LigerSiLUMulFunction is not None:
257
+ activated = LigerSiLUMulFunction.apply(gate, up, 1.0, 1.0)
258
+ else:
259
+ activated = F.silu(gate) * up
260
+ return self.down_proj(activated)
261
+
262
+
263
+ class QyrouArchBlock(nn.Module):
264
+ def __init__(self, config: QyrouArchConfig, layer_idx: int) -> None:
265
+ super().__init__()
266
+ self.mixer_norm = RMSNorm(config.hidden_size, config.rms_norm_eps, config.liger_rms_norm)
267
+ self.ffn_norm = RMSNorm(config.hidden_size, config.rms_norm_eps, config.liger_rms_norm)
268
+ layer_number = layer_idx + 1
269
+ self.mixer = (
270
+ CausalConvMixer(config, layer_idx)
271
+ if layer_number in config.conv_layers
272
+ else CausalGQA(config, layer_idx)
273
+ )
274
+ self.ffn = SwiGLU(config)
275
+
276
+ def forward(
277
+ self,
278
+ x: torch.Tensor,
279
+ rope: tuple[torch.Tensor, torch.Tensor],
280
+ attention_mask: torch.Tensor | None,
281
+ cache: QyrouArchHybridCache | None,
282
+ cache_position: torch.Tensor,
283
+ ) -> torch.Tensor:
284
+ x = x + self.mixer(self.mixer_norm(x), rope, attention_mask, cache, cache_position)
285
+ return x + self.ffn(self.ffn_norm(x))
286
+
287
+
288
+ class QyrouArchPreTrainedModel(PreTrainedModel):
289
+ config_class = QyrouArchConfig
290
+ base_model_prefix = "model"
291
+ supports_gradient_checkpointing = False
292
+ _no_split_modules = ["QyrouArchBlock"]
293
+
294
+ def _init_weights(self, module: nn.Module) -> None:
295
+ if isinstance(module, (nn.Linear, nn.Embedding, nn.Conv1d)):
296
+ scale = (
297
+ self.config.residual_initializer_scale
298
+ if getattr(module, "_qyrou_arch_residual_projection", False)
299
+ else 1.0
300
+ )
301
+ nn.init.normal_(module.weight, mean=0.0, std=self.config.initializer_range * scale)
302
+
303
+
304
+ class QyrouArchModel(QyrouArchPreTrainedModel):
305
+ def __init__(self, config: QyrouArchConfig) -> None:
306
+ super().__init__(config)
307
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size)
308
+ self.layers = nn.ModuleList(
309
+ [QyrouArchBlock(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
310
+ )
311
+ self.norm = RMSNorm(config.hidden_size, config.rms_norm_eps, config.liger_rms_norm)
312
+ head_dim = config.hidden_size // config.num_attention_heads
313
+ inv_freq = 1.0 / (
314
+ config.rope_theta ** (torch.arange(0, head_dim, 2).float() / head_dim)
315
+ )
316
+ positions = torch.arange(config.max_position_embeddings, dtype=torch.float32)
317
+ angles = torch.outer(positions, inv_freq)
318
+ self.register_buffer("rope_cos", angles.cos(), persistent=False)
319
+ self.register_buffer("rope_sin", angles.sin(), persistent=False)
320
+ self.post_init()
321
+
322
+ def _rope(
323
+ self,
324
+ position_ids: torch.Tensor,
325
+ dtype: torch.dtype,
326
+ ) -> tuple[torch.Tensor, torch.Tensor]:
327
+ if (
328
+ not torch.compiler.is_compiling()
329
+ and int(position_ids.max()) >= self.config.max_position_embeddings
330
+ ):
331
+ raise ValueError("Position exceeds max_position_embeddings")
332
+ cos = self.rope_cos[position_ids].to(dtype).unsqueeze(1)
333
+ sin = self.rope_sin[position_ids].to(dtype).unsqueeze(1)
334
+ return cos, sin
335
+
336
+ def forward(
337
+ self,
338
+ input_ids: torch.LongTensor | None = None,
339
+ attention_mask: torch.Tensor | None = None,
340
+ position_ids: torch.LongTensor | None = None,
341
+ past_key_values: QyrouArchHybridCache | None = None,
342
+ inputs_embeds: torch.Tensor | None = None,
343
+ use_cache: bool | None = None,
344
+ cache_position: torch.LongTensor | None = None,
345
+ return_dict: bool | None = None,
346
+ **_: Any,
347
+ ) -> BaseModelOutputWithPast | tuple[torch.Tensor, QyrouArchHybridCache | None]:
348
+ if (input_ids is None) == (inputs_embeds is None):
349
+ raise ValueError("Pass exactly one of input_ids or inputs_embeds")
350
+ hidden = self.embed_tokens(input_ids) if inputs_embeds is None else inputs_embeds
351
+ if hidden.is_cuda and torch.is_autocast_enabled("cuda"):
352
+ hidden = hidden.to(torch.get_autocast_dtype("cuda"))
353
+ batch, query_length, _ = hidden.shape
354
+ use_cache = self.config.use_cache if use_cache is None else use_cache
355
+ return_dict = self.config.return_dict if return_dict is None else return_dict
356
+ if use_cache and past_key_values is None:
357
+ past_key_values = QyrouArchHybridCache(self.config.num_hidden_layers)
358
+ cache = past_key_values if use_cache else None
359
+ past_length = cache.get_seq_length() if cache is not None else 0
360
+ if cache_position is None:
361
+ cache_position = torch.arange(
362
+ past_length,
363
+ past_length + query_length,
364
+ device=hidden.device,
365
+ )
366
+ if position_ids is None:
367
+ if attention_mask is not None:
368
+ position_ids = attention_mask.long().cumsum(-1).sub(1).clamp_min(0)[:, -query_length:]
369
+ else:
370
+ position_ids = cache_position.unsqueeze(0).expand(batch, -1)
371
+ rope = self._rope(position_ids, hidden.dtype)
372
+ for layer in self.layers:
373
+ hidden = layer(hidden, rope, attention_mask, cache, cache_position)
374
+ hidden = self.norm(hidden)
375
+ if cache is not None:
376
+ cache.finish_step(cache_position)
377
+ if not return_dict:
378
+ return hidden, cache
379
+ return BaseModelOutputWithPast(last_hidden_state=hidden, past_key_values=cache)
380
+
381
+
382
+ class QyrouArchForCausalLM(QyrouArchPreTrainedModel, GenerationMixin):
383
+ _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
384
+
385
+ def __init__(self, config: QyrouArchConfig) -> None:
386
+ super().__init__(config)
387
+ self.model = QyrouArchModel(config)
388
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
389
+ self.post_init()
390
+ self.tie_weights()
391
+
392
+ def get_input_embeddings(self) -> nn.Module:
393
+ return self.model.embed_tokens
394
+
395
+ def set_input_embeddings(self, value: nn.Module) -> None:
396
+ self.model.embed_tokens = value
397
+
398
+ def get_output_embeddings(self) -> nn.Module:
399
+ return self.lm_head
400
+
401
+ def set_output_embeddings(self, value: nn.Module) -> None:
402
+ self.lm_head = value
403
+
404
+ def forward(
405
+ self,
406
+ input_ids: torch.LongTensor | None = None,
407
+ attention_mask: torch.Tensor | None = None,
408
+ position_ids: torch.LongTensor | None = None,
409
+ past_key_values: QyrouArchHybridCache | None = None,
410
+ inputs_embeds: torch.Tensor | None = None,
411
+ labels: torch.LongTensor | None = None,
412
+ use_cache: bool | None = None,
413
+ cache_position: torch.LongTensor | None = None,
414
+ return_logits: bool = True,
415
+ return_dict: bool | None = None,
416
+ **kwargs: Any,
417
+ ) -> CausalLMOutputWithPast | tuple[Any, ...]:
418
+ return_dict = self.config.return_dict if return_dict is None else return_dict
419
+ outputs = self.model(
420
+ input_ids=input_ids,
421
+ attention_mask=attention_mask,
422
+ position_ids=position_ids,
423
+ past_key_values=past_key_values,
424
+ inputs_embeds=inputs_embeds,
425
+ use_cache=use_cache,
426
+ cache_position=cache_position,
427
+ return_dict=True,
428
+ **kwargs,
429
+ )
430
+ hidden = outputs.last_hidden_state
431
+ training_loss_only = labels is not None and not return_logits
432
+ logits = None if training_loss_only else self.lm_head(hidden)
433
+ loss = None
434
+ if labels is not None:
435
+ if (
436
+ training_loss_only
437
+ and hidden.is_cuda
438
+ and self.config.cut_cross_entropy
439
+ and cut_linear_cross_entropy is not None
440
+ ):
441
+ loss = cut_linear_cross_entropy(
442
+ hidden,
443
+ self.lm_head.weight,
444
+ labels,
445
+ shift=True,
446
+ filter_eps=None,
447
+ )
448
+ else:
449
+ loss_logits = self.lm_head(hidden[:, :-1]).float()
450
+ loss = F.cross_entropy(
451
+ loss_logits.reshape(-1, self.config.vocab_size),
452
+ labels[:, 1:].contiguous().view(-1),
453
+ ignore_index=-100,
454
+ )
455
+ if not return_dict:
456
+ return loss, logits, outputs.past_key_values
457
+ return CausalLMOutputWithPast(
458
+ loss=loss,
459
+ logits=logits,
460
+ past_key_values=outputs.past_key_values,
461
+ )
462
+
463
+ def prepare_inputs_for_generation(
464
+ self,
465
+ input_ids: torch.LongTensor,
466
+ past_key_values: QyrouArchHybridCache | None = None,
467
+ attention_mask: torch.Tensor | None = None,
468
+ cache_position: torch.LongTensor | None = None,
469
+ use_cache: bool = True,
470
+ **kwargs: Any,
471
+ ) -> dict[str, Any]:
472
+ past_length = past_key_values.get_seq_length() if past_key_values is not None else 0
473
+ if past_length:
474
+ input_ids = (
475
+ input_ids[:, past_length:]
476
+ if input_ids.shape[1] > past_length
477
+ else input_ids[:, -1:]
478
+ )
479
+ if cache_position is None:
480
+ cache_position = torch.arange(
481
+ past_length,
482
+ past_length + input_ids.shape[1],
483
+ device=input_ids.device,
484
+ )
485
+ elif cache_position.numel() != input_ids.shape[1]:
486
+ cache_position = cache_position[-input_ids.shape[1] :]
487
+ return {
488
+ "input_ids": input_ids,
489
+ "attention_mask": attention_mask,
490
+ "past_key_values": past_key_values,
491
+ "cache_position": cache_position,
492
+ "use_cache": use_cache,
493
+ }
494
+
495
+ def _reorder_cache(
496
+ self,
497
+ past_key_values: QyrouArchHybridCache,
498
+ beam_idx: torch.LongTensor,
499
+ ) -> QyrouArchHybridCache:
500
+ return past_key_values.reorder_cache(beam_idx)
501
+
502
+
503
+ QyrouArchConfig.register_for_auto_class()
504
+ QyrouArchForCausalLM.register_for_auto_class("AutoModelForCausalLM")
source/qyrou_arch/tokenizer_markers.py ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+
6
+ @dataclass(frozen=True)
7
+ class Marker:
8
+ text: str
9
+ special: bool
10
+ category: str
11
+
12
+
13
+ def _markers(values: list[str], *, special: bool, category: str) -> list[Marker]:
14
+ return [Marker(value, special, category) for value in values]
15
+
16
+
17
+ MARKERS: list[Marker] = [
18
+ *_markers(
19
+ ["<|bos|>", "<|eos|>", "<|pad|>", "<|unk|>", "<|eod|>"],
20
+ special=True,
21
+ category="core",
22
+ ),
23
+ *_markers(
24
+ ["<|startoftext|>", "<|endoftext|>", "<|endofprompt|>"],
25
+ special=True,
26
+ category="text_boundaries",
27
+ ),
28
+ *_markers(
29
+ [
30
+ "<|im_start|>", "<|im_end|>", "<|system|>", "<|developer|>",
31
+ "<|user|>", "<|assistant|>", "<|tool|>", "<|function|>",
32
+ "<|observation|>", "<|eot|>",
33
+ ],
34
+ special=True,
35
+ category="chatml",
36
+ ),
37
+ *_markers(
38
+ [
39
+ "<think>", "</think>", "<analysis>", "</analysis>", "<reasoning>",
40
+ "</reasoning>", "<commentary>", "</commentary>", "<final>", "</final>",
41
+ ],
42
+ special=False,
43
+ category="reasoning",
44
+ ),
45
+ *_markers(
46
+ [
47
+ "<tools>", "</tools>", "<available_tools>", "</available_tools>",
48
+ "<tool_call>", "</tool_call>", "<tool_response>", "</tool_response>",
49
+ "<function_call>", "</function_call>", "<function_response>",
50
+ "</function_response>", "<|tool_sep|>",
51
+ ],
52
+ special=False,
53
+ category="tools",
54
+ ),
55
+ *_markers(
56
+ [
57
+ "<|fim_prefix|>", "<|fim_middle|>", "<|fim_suffix|>", "<|fim_pad|>",
58
+ "<|repo_name|>", "<|file_sep|>",
59
+ ],
60
+ special=False,
61
+ category="qwen_fim",
62
+ ),
63
+ *_markers(
64
+ [
65
+ "<fim_prefix>", "<fim_middle>", "<fim_suffix>", "<fim_pad>",
66
+ "<repo_name>", "<file_sep>", "<issue_start>", "<issue_comment>",
67
+ "<issue_closed>", "<jupyter_start>", "<jupyter_text>", "<jupyter_code>",
68
+ "<jupyter_output>", "<jupyter_script>", "<empty_output>",
69
+ ],
70
+ special=False,
71
+ category="starcoder",
72
+ ),
73
+ *_markers(
74
+ [
75
+ "<|begin_of_text|>", "<|end_of_text|>", "<|start_header_id|>",
76
+ "<|end_header_id|>", "<|eot_id|>", "<|eom_id|>", "<|python_tag|>",
77
+ ],
78
+ special=True,
79
+ category="llama",
80
+ ),
81
+ *_markers(
82
+ ["<start_of_turn>", "<end_of_turn>", "<start_of_image>"],
83
+ special=True,
84
+ category="gemma",
85
+ ),
86
+ *_markers(
87
+ [
88
+ "<s>", "</s>", "<unk>", "<pad>", "[INST]", "[/INST]",
89
+ "[AVAILABLE_TOOLS]", "[/AVAILABLE_TOOLS]", "[TOOL_CALLS]",
90
+ "[TOOL_RESULTS]", "[/TOOL_RESULTS]", "[SYSTEM_PROMPT]",
91
+ "[/SYSTEM_PROMPT]", "[TOOL_CONTENT]", "[ARGS]", "[CALL_ID]",
92
+ "[PREFIX]", "[MIDDLE]", "[SUFFIX]", "[THINK]", "[/THINK]",
93
+ "[MODEL_SETTINGS]", "[/MODEL_SETTINGS]",
94
+ ],
95
+ special=True,
96
+ category="mistral",
97
+ ),
98
+ *_markers(
99
+ [
100
+ "<|begin▁of▁sentence|>", "<|end▁of▁sentence|>", "<|User|>",
101
+ "<|Assistant|>", "<|tool▁calls▁begin|>", "<|tool▁call▁begin|>",
102
+ "<|tool▁sep|>", "<|tool▁call▁end|>", "<|tool▁calls▁end|>",
103
+ "<|tool▁outputs▁begin|>", "<|tool▁output▁begin|>",
104
+ "<|tool▁output▁end|>", "<|tool▁outputs▁end|>", "<|fim▁begin|>",
105
+ "<|fim▁hole|>", "<|fim▁end|>",
106
+ ],
107
+ special=True,
108
+ category="deepseek",
109
+ ),
110
+ *_markers(
111
+ ["<|start|>", "<|end|>", "<|message|>", "<|channel|>", "<|constrain|>", "<|return|>", "<|call|>"],
112
+ special=True,
113
+ category="harmony",
114
+ ),
115
+ *_markers(
116
+ [
117
+ "<|vision_start|>", "<|vision_end|>", "<|vision_pad|>", "<|image_pad|>",
118
+ "<|video_pad|>", "<|image|>", "<|video|>", "[IMG]", "[IMG_BREAK]",
119
+ "[IMG_END]",
120
+ ],
121
+ special=True,
122
+ category="vision",
123
+ ),
124
+ *_markers(
125
+ ["<|audio_bos|>", "<|AUDIO|>", "<|audio_eos|>", "<|audio_pad|>", "[AUDIO]", "[BEGIN_AUDIO]"],
126
+ special=True,
127
+ category="audio",
128
+ ),
129
+ ]
130
+
131
+ RESERVED_MARKERS = [
132
+ Marker(f"<|reserved_{index:03d}|>", True, "reserved")
133
+ for index in range(64)
134
+ ]
135
+ ALL_MARKERS = MARKERS + RESERVED_MARKERS
136
+ MARKER_TEXTS = [marker.text for marker in ALL_MARKERS]
137
+
138
+ CORE_TOKEN_IDS = {
139
+ "<|bos|>": 0,
140
+ "<|eos|>": 1,
141
+ "<|pad|>": 2,
142
+ "<|unk|>": 3,
143
+ "<|eod|>": 4,
144
+ }
145
+
146
+ ALLOWED_CHAT_ROLES = {
147
+ "system",
148
+ "developer",
149
+ "user",
150
+ "assistant",
151
+ "tool",
152
+ "function",
153
+ "observation",
154
+ }
155
+
156
+ assert len(MARKERS) == 134, f"Expected 134 requested markers, found {len(MARKERS)}"
157
+ assert len(ALL_MARKERS) == 198
158
+ assert len(MARKER_TEXTS) == len(set(MARKER_TEXTS)), "Marker strings must be unique"
159
+
source/qyrou_arch/trainer.py ADDED
@@ -0,0 +1,509 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import math
5
+ import os
6
+ import random
7
+ import shutil
8
+ import signal
9
+ import time
10
+ from pathlib import Path
11
+ from statistics import median
12
+ from typing import Any
13
+
14
+ import numpy as np
15
+ import torch
16
+
17
+ from .checkpointing import CheckpointManager, directory_size
18
+ from .configuration_qyrou_arch import QyrouArchConfig
19
+ from .data import EvaluationTokens, PackedTokenStream
20
+ from .io_utils import load_run_config, sha256_file, sha256_json
21
+ from .metrics import JsonlLogger, RunningMetrics, safe_perplexity, system_telemetry
22
+ from .modeling_qyrou_arch import QyrouArchForCausalLM
23
+
24
+
25
+ class NonFiniteTrainingError(RuntimeError):
26
+ pass
27
+
28
+
29
+ def token_learning_rate(
30
+ consumed_tokens: int,
31
+ total_tokens: int,
32
+ warmup_tokens: int,
33
+ peak: float,
34
+ minimum: float,
35
+ ) -> float:
36
+ if consumed_tokens < warmup_tokens:
37
+ return peak * consumed_tokens / max(warmup_tokens, 1)
38
+ progress = (consumed_tokens - warmup_tokens) / max(total_tokens - warmup_tokens, 1)
39
+ progress = min(max(progress, 0.0), 1.0)
40
+ return minimum + 0.5 * (peak - minimum) * (1.0 + math.cos(math.pi * progress))
41
+
42
+
43
+ class QyrouArchTrainer:
44
+ def __init__(
45
+ self,
46
+ config_path: str | Path,
47
+ *,
48
+ microbatch: int,
49
+ grad_accum: int,
50
+ compile_model: bool,
51
+ compile_mode: str,
52
+ resume: str | None,
53
+ ) -> None:
54
+ self.config_path = Path(config_path).resolve()
55
+ self.cfg = load_run_config(self.config_path)
56
+ self.run_cfg = self.cfg["run"]
57
+ self.train_cfg = self.cfg["training"]
58
+ self.data_cfg = self.cfg["data"]
59
+ self.microbatch = microbatch
60
+ self.grad_accum = grad_accum
61
+ self.compile_model = compile_model
62
+ self.compile_mode = compile_mode
63
+ self.device = torch.device(self.train_cfg["device"])
64
+ self.interrupt_requested = False
65
+ self.recoveries = 0
66
+ self.lr_scale = 1.0
67
+ self.global_step = 0
68
+ self.total_tokens = 0
69
+ self.last_checkpoint_bytes: int | None = None
70
+ self.qualified_throughput = False
71
+ self.qualified_convergence = False
72
+ self.baseline_validation_loss: float | None = None
73
+ self.qualification_tps: list[float] = []
74
+ self.milestones_saved: set[float] = set()
75
+ self.metrics = RunningMetrics()
76
+ self.compiler_cache_gib = 0.0
77
+
78
+ if not torch.cuda.is_available():
79
+ raise RuntimeError("Full training requires a CUDA GPU")
80
+ if not torch.cuda.is_bf16_supported():
81
+ raise RuntimeError("The selected CUDA GPU does not support BF16")
82
+ torch.manual_seed(int(self.run_cfg["seed"]))
83
+ torch.cuda.manual_seed_all(int(self.run_cfg["seed"]))
84
+ np.random.seed(int(self.run_cfg["seed"]))
85
+ random.seed(int(self.run_cfg["seed"]))
86
+ torch.set_float32_matmul_precision("high")
87
+ torch.backends.cuda.matmul.allow_tf32 = True
88
+
89
+ token_manifest = Path(self.run_cfg["token_cache_dir"]) / "manifest.json"
90
+ self.data = PackedTokenStream(
91
+ token_manifest,
92
+ int(self.data_cfg["sequence_length"]),
93
+ int(self.cfg["model"]["pad_token_id"]),
94
+ int(self.data_cfg["shuffle_seed"]),
95
+ shuffle_block_tokens=int(self.data_cfg["shuffle_block_tokens"]),
96
+ )
97
+ self.corpus_total_tokens = self.data.total_tokens
98
+ self.eval_data_path = Path(self.run_cfg["project_dir"]) / "data" / "eval" / "wikitext103-validation.bin"
99
+ if not self.eval_data_path.exists():
100
+ raise RuntimeError(f"Missing fixed validation tokens: {self.eval_data_path}")
101
+ self.eval_data = EvaluationTokens(
102
+ self.eval_data_path,
103
+ int(self.data_cfg["sequence_length"]),
104
+ int(self.cfg["model"]["pad_token_id"]),
105
+ )
106
+ checkpoint_cfg = self.cfg["checkpoints"]
107
+ self.checkpoints = CheckpointManager(
108
+ self.run_cfg["checkpoint_dir"],
109
+ float(checkpoint_cfg["cap_gib"]),
110
+ int(checkpoint_cfg["prune_count"]),
111
+ )
112
+ self.logger = JsonlLogger(Path(self.run_cfg["log_dir"]) / "training_metrics.jsonl")
113
+
114
+ resume_path = None
115
+ if resume == "auto":
116
+ resume_path = self.checkpoints.find_latest_valid()
117
+ elif resume:
118
+ resume_path = Path(resume)
119
+ if resume_path is not None:
120
+ self.raw_model = QyrouArchForCausalLM.from_pretrained(
121
+ resume_path / "model",
122
+ trust_remote_code=False,
123
+ )
124
+ else:
125
+ self.raw_model = QyrouArchForCausalLM(QyrouArchConfig(**self.cfg["model"]))
126
+ if self.raw_model.config.vocab_size != 20_000:
127
+ raise RuntimeError("Model vocabulary must be exactly 20,000")
128
+ if (
129
+ self.raw_model.get_input_embeddings().weight.data_ptr()
130
+ != self.raw_model.get_output_embeddings().weight.data_ptr()
131
+ ):
132
+ raise RuntimeError("Input and output embeddings are not tied")
133
+ self.raw_model.to(self.device, dtype=torch.float32)
134
+ self.optimizer = torch.optim.AdamW(
135
+ self.raw_model.parameters(),
136
+ lr=float(self.train_cfg["peak_learning_rate"]),
137
+ betas=(float(self.train_cfg["beta1"]), float(self.train_cfg["beta2"])),
138
+ eps=float(self.train_cfg["epsilon"]),
139
+ weight_decay=float(self.train_cfg["weight_decay"]),
140
+ fused=True,
141
+ )
142
+ if resume_path is not None:
143
+ self._load_checkpoint(resume_path)
144
+ self.train_model = (
145
+ torch.compile(self.raw_model, mode=self.compile_mode, fullgraph=False)
146
+ if compile_model
147
+ else self.raw_model
148
+ )
149
+ signal.signal(signal.SIGINT, self._request_interrupt)
150
+ signal.signal(signal.SIGTERM, self._request_interrupt)
151
+
152
+ def _request_interrupt(self, _signum: int, _frame: Any) -> None:
153
+ self.interrupt_requested = True
154
+
155
+ def _trainer_state(self) -> dict[str, Any]:
156
+ token_manifest = Path(self.run_cfg["token_cache_dir"]) / "manifest.json"
157
+ tokenizer_path = Path(self.run_cfg["tokenizer_dir"]) / "tokenizer.json"
158
+ return {
159
+ "version": 1,
160
+ "global_step": self.global_step,
161
+ "total_tokens": self.total_tokens,
162
+ "data": self.data.state_dict(),
163
+ "metrics": self.metrics.state_dict(),
164
+ "recoveries": self.recoveries,
165
+ "lr_scale": self.lr_scale,
166
+ "microbatch": self.microbatch,
167
+ "grad_accum": self.grad_accum,
168
+ "compile_model": self.compile_model,
169
+ "compile_mode": self.compile_mode,
170
+ "qualified_throughput": self.qualified_throughput,
171
+ "qualified_convergence": self.qualified_convergence,
172
+ "baseline_validation_loss": self.baseline_validation_loss,
173
+ "milestones_saved": sorted(self.milestones_saved),
174
+ "config_sha256": sha256_file(self.config_path),
175
+ "token_manifest_sha256": sha256_file(token_manifest),
176
+ "tokenizer_sha256": sha256_file(tokenizer_path),
177
+ }
178
+
179
+ def _load_checkpoint(self, path: Path) -> None:
180
+ state = self.checkpoints.load_training_state(path)
181
+ trainer = state["trainer"]
182
+ if trainer["config_sha256"] != sha256_file(self.config_path):
183
+ raise RuntimeError("Checkpoint configuration hash differs from current run")
184
+ if trainer["token_manifest_sha256"] != sha256_file(
185
+ Path(self.run_cfg["token_cache_dir"]) / "manifest.json"
186
+ ):
187
+ raise RuntimeError("Checkpoint token-manifest hash differs from current cache")
188
+ if trainer["tokenizer_sha256"] != sha256_file(
189
+ Path(self.run_cfg["tokenizer_dir"]) / "tokenizer.json"
190
+ ):
191
+ raise RuntimeError("Checkpoint tokenizer hash differs from current tokenizer")
192
+ if trainer["microbatch"] != self.microbatch or trainer["grad_accum"] != self.grad_accum:
193
+ raise RuntimeError("Resume batch configuration differs from checkpoint")
194
+ if bool(trainer.get("compile_model", False)) != self.compile_model:
195
+ raise RuntimeError("Resume compilation setting differs from checkpoint")
196
+ if trainer.get("compile_mode", "default") != self.compile_mode:
197
+ raise RuntimeError("Resume compilation mode differs from checkpoint")
198
+ self.optimizer.load_state_dict(state["optimizer"])
199
+ self.global_step = int(trainer["global_step"])
200
+ self.total_tokens = int(trainer["total_tokens"])
201
+ self.data.load_state_dict(trainer["data"])
202
+ self.metrics.load_state_dict(trainer["metrics"])
203
+ self.recoveries = int(trainer.get("recoveries", 0))
204
+ self.lr_scale = float(trainer.get("lr_scale", 1.0))
205
+ self.qualified_throughput = bool(trainer.get("qualified_throughput", False))
206
+ self.qualified_convergence = bool(trainer.get("qualified_convergence", False))
207
+ self.baseline_validation_loss = trainer.get("baseline_validation_loss")
208
+ self.milestones_saved = set(trainer.get("milestones_saved", []))
209
+ torch.set_rng_state(state["cpu_rng"])
210
+ if state.get("cuda_rng") is not None:
211
+ torch.cuda.set_rng_state_all(state["cuda_rng"])
212
+
213
+ def _save(self, validation_loss: float | None = None) -> Path:
214
+ latest = self.checkpoints.find_latest_valid()
215
+ if latest is not None:
216
+ latest_step = int(latest.name.rsplit("-", 1)[-1])
217
+ if latest_step == self.global_step:
218
+ return latest
219
+ path = self.checkpoints.save(
220
+ model=self.raw_model,
221
+ optimizer=self.optimizer,
222
+ trainer_state=self._trainer_state(),
223
+ step=self.global_step,
224
+ total_tokens=self.total_tokens,
225
+ validation_loss=validation_loss,
226
+ expected_bytes=self.last_checkpoint_bytes,
227
+ )
228
+ self.last_checkpoint_bytes = directory_size(path)
229
+ print(f"saved_checkpoint={path}", flush=True)
230
+ return path
231
+
232
+ def _recover(self, reason: str) -> None:
233
+ maximum = int(self.train_cfg["max_automatic_recoveries"])
234
+ if self.recoveries >= maximum:
235
+ raise RuntimeError(f"Recovery limit exceeded after: {reason}")
236
+ latest = self.checkpoints.find_latest_valid()
237
+ if latest is None:
238
+ raise RuntimeError(f"Non-finite training before first recovery checkpoint: {reason}")
239
+ recovered_model = QyrouArchForCausalLM.from_pretrained(latest / "model").to(
240
+ self.device,
241
+ dtype=torch.float32,
242
+ )
243
+ self.raw_model.load_state_dict(recovered_model.state_dict())
244
+ del recovered_model
245
+ prior_recoveries = self.recoveries
246
+ prior_scale = self.lr_scale
247
+ self._load_checkpoint(latest)
248
+ self.recoveries = prior_recoveries + 1
249
+ self.lr_scale = prior_scale * 0.5
250
+ self.optimizer.zero_grad(set_to_none=True)
251
+ self.logger.log(
252
+ {
253
+ "event": "automatic_recovery",
254
+ "reason": reason,
255
+ "checkpoint": str(latest),
256
+ "recoveries": self.recoveries,
257
+ "lr_scale": self.lr_scale,
258
+ }
259
+ )
260
+
261
+ @torch.no_grad()
262
+ def evaluate(self) -> float:
263
+ self.train_model.eval()
264
+ total_loss = 0.0
265
+ total_weight = 0
266
+ for input_ids, labels, tokens in self.eval_data.batches(
267
+ batch_size=max(1, min(self.microbatch, 4)),
268
+ device=self.device,
269
+ max_tokens=int(self.train_cfg["eval_tokens"]),
270
+ ):
271
+ with torch.autocast("cuda", dtype=torch.bfloat16):
272
+ if self.compile_model:
273
+ torch.compiler.cudagraph_mark_step_begin()
274
+ output = self.train_model(
275
+ input_ids=input_ids,
276
+ labels=labels,
277
+ use_cache=False,
278
+ return_logits=False,
279
+ )
280
+ total_loss += float(output.loss) * tokens
281
+ total_weight += tokens
282
+ self.train_model.train()
283
+ return total_loss / max(total_weight, 1)
284
+
285
+ def _set_learning_rate(self) -> float:
286
+ lr = token_learning_rate(
287
+ self.total_tokens,
288
+ self.corpus_total_tokens,
289
+ int(self.train_cfg["warmup_tokens"]),
290
+ float(self.train_cfg["peak_learning_rate"]),
291
+ float(self.train_cfg["minimum_learning_rate"]),
292
+ ) * self.lr_scale
293
+ for group in self.optimizer.param_groups:
294
+ group["lr"] = lr
295
+ return lr
296
+
297
+ def _check_milestones(self) -> None:
298
+ fraction = self.total_tokens / max(self.corpus_total_tokens, 1)
299
+ for target in self.cfg["checkpoints"]["milestone_fractions"]:
300
+ target = float(target)
301
+ if fraction >= target and target not in self.milestones_saved:
302
+ self.checkpoints.save_milestone(
303
+ self.raw_model,
304
+ target,
305
+ self.global_step,
306
+ self.total_tokens,
307
+ )
308
+ self.milestones_saved.add(target)
309
+
310
+ def train(self) -> None:
311
+ self.raw_model.train()
312
+ print(
313
+ f"training_start step={self.global_step} total_tokens={self.total_tokens} "
314
+ f"corpus_tokens={self.corpus_total_tokens} microbatch={self.microbatch} "
315
+ f"grad_accum={self.grad_accum} compile={self.compile_model} "
316
+ f"compile_mode={self.compile_mode}",
317
+ flush=True,
318
+ )
319
+ if self.baseline_validation_loss is None:
320
+ self.baseline_validation_loss = self.evaluate()
321
+ self.logger.log(
322
+ {"event": "baseline_evaluation", "validation_loss": self.baseline_validation_loss}
323
+ )
324
+ print(
325
+ f"baseline_validation_loss={self.baseline_validation_loss:.6f}",
326
+ flush=True,
327
+ )
328
+ while self.data.remaining_tokens > 0:
329
+ try:
330
+ self._train_step()
331
+ except NonFiniteTrainingError as exc:
332
+ self._recover(str(exc))
333
+ continue
334
+ if self.interrupt_requested:
335
+ self._save()
336
+ self.logger.log({"event": "controlled_interrupt", "step": self.global_step})
337
+ return
338
+ final_validation = self.evaluate()
339
+ self._save(final_validation)
340
+ self._check_milestones()
341
+ self._export_final(final_validation)
342
+
343
+ def _train_step(self) -> None:
344
+ started = time.perf_counter()
345
+ self.optimizer.zero_grad(set_to_none=True)
346
+ accumulated_tokens = 0
347
+ data_wait_seconds = 0.0
348
+ loss_values: list[torch.Tensor] = []
349
+ finite_loss = torch.ones((), dtype=torch.bool, device=self.device)
350
+ failed_range: tuple[int, int] | None = None
351
+ with torch.autocast("cuda", dtype=torch.bfloat16, cache_enabled=True):
352
+ for _ in range(self.grad_accum):
353
+ data_started = time.perf_counter()
354
+ batch = self.data.next_batch(self.microbatch, self.device)
355
+ data_wait_seconds += time.perf_counter() - data_started
356
+ if batch is None:
357
+ break
358
+ if self.compile_model:
359
+ torch.compiler.cudagraph_mark_step_begin()
360
+ output = self.train_model(
361
+ input_ids=batch.input_ids,
362
+ labels=batch.labels,
363
+ use_cache=False,
364
+ return_logits=False,
365
+ )
366
+ loss = output.loss / self.grad_accum
367
+ finite_loss.logical_and_(torch.isfinite(loss.detach()))
368
+ failed_range = (batch.start_cursor, batch.end_cursor)
369
+ loss.backward()
370
+ loss_values.append(loss.detach())
371
+ accumulated_tokens += batch.real_tokens
372
+ if accumulated_tokens == 0:
373
+ return
374
+ if not bool(finite_loss):
375
+ assert failed_range is not None
376
+ raise NonFiniteTrainingError(
377
+ f"non-finite loss at token range {failed_range[0]}:{failed_range[1]}"
378
+ )
379
+ grad_norm = torch.nn.utils.clip_grad_norm_(
380
+ self.raw_model.parameters(),
381
+ float(self.train_cfg["grad_clip"]),
382
+ )
383
+ if not torch.isfinite(grad_norm):
384
+ raise NonFiniteTrainingError(f"non-finite gradient norm at step {self.global_step + 1}")
385
+ learning_rate = self._set_learning_rate()
386
+ self.optimizer.step()
387
+ torch.cuda.synchronize()
388
+ elapsed = time.perf_counter() - started
389
+ self.global_step += 1
390
+ self.total_tokens += accumulated_tokens
391
+ step_loss = float(torch.stack(loss_values).sum()) * self.grad_accum / len(loss_values)
392
+ tps = accumulated_tokens / max(elapsed, 1e-9)
393
+ self.metrics.update(step_loss, tps)
394
+ if self.global_step > 100 and not self.qualified_throughput:
395
+ self.qualification_tps.append(tps)
396
+ if self.global_step == int(self.train_cfg["qualification_steps"]):
397
+ measured = median(self.qualification_tps)
398
+ if measured < float(self.train_cfg["minimum_tps"]):
399
+ self._save()
400
+ raise RuntimeError(
401
+ f"Qualification throughput {measured:,.0f} TPS is below "
402
+ f"{self.train_cfg['minimum_tps']:,.0f}"
403
+ )
404
+ self.qualified_throughput = True
405
+ if (
406
+ not self.qualified_convergence
407
+ and self.total_tokens >= int(self.train_cfg["qualification_tokens"])
408
+ ):
409
+ validation = self.evaluate()
410
+ if validation >= float(self.baseline_validation_loss):
411
+ self._save(validation)
412
+ raise RuntimeError(
413
+ f"100M-token qualification did not improve validation loss: "
414
+ f"{self.baseline_validation_loss:.4f} -> {validation:.4f}"
415
+ )
416
+ self.qualified_convergence = True
417
+ self.logger.log(
418
+ {
419
+ "event": "convergence_qualified",
420
+ "validation_loss": validation,
421
+ "baseline_validation_loss": self.baseline_validation_loss,
422
+ }
423
+ )
424
+ if self.metrics.spike_steps >= 5000:
425
+ self._save()
426
+ raise RuntimeError("Smoothed loss rose by >10% for 5,000 consecutive steps")
427
+
428
+ validation_loss = None
429
+ if self.global_step % int(self.train_cfg["eval_every_steps"]) == 0:
430
+ validation_loss = self.evaluate()
431
+ if self.global_step % int(self.train_cfg["save_every_steps"]) == 0:
432
+ self._save(validation_loss)
433
+ self._check_milestones()
434
+ if self.global_step % int(self.train_cfg["log_every_steps"]) == 0:
435
+ if self.global_step == int(self.train_cfg["log_every_steps"]) or self.global_step % 1000 == 0:
436
+ self.compiler_cache_gib = directory_size(
437
+ Path(self.run_cfg["project_dir"]) / ".cache"
438
+ ) / 2**30
439
+ payload = {
440
+ "step": self.global_step,
441
+ "total_tokens": self.total_tokens,
442
+ "corpus_progress": self.total_tokens / self.corpus_total_tokens,
443
+ "loss": step_loss,
444
+ "loss_ema_100": self.metrics.ema_100,
445
+ "loss_ema_1000": self.metrics.ema_1000,
446
+ "perplexity": safe_perplexity(step_loss),
447
+ "learning_rate": learning_rate,
448
+ "grad_norm": float(grad_norm),
449
+ "tokens_per_sec": tps,
450
+ "median_tokens_per_sec": self.metrics.median_tps,
451
+ "validation_loss": validation_loss,
452
+ "checkpoint_gib": self.checkpoints.storage_bytes() / 2**30,
453
+ "compiler_cache_gib": self.compiler_cache_gib,
454
+ "data_wait_seconds": data_wait_seconds,
455
+ "stream_chunk": self.data.current_shard_index,
456
+ "stream_block": self.data.current_block_index,
457
+ "data_cursor": self.data.cursor,
458
+ }
459
+ payload.update(system_telemetry())
460
+ self.logger.log(payload)
461
+ print(
462
+ f"step={self.global_step:07d} "
463
+ f"chunk={self.data.current_shard_index:03d} "
464
+ f"loss={step_loss:.3f} "
465
+ f"ppl={safe_perplexity(step_loss):.3f} "
466
+ f"lr={learning_rate:.2e} "
467
+ f"grad={float(grad_norm):.3f} "
468
+ f"tok/s={tps:.1f} "
469
+ f"total_tok={self.total_tokens} "
470
+ f"gpu_mem={payload.get('gpu_allocated_gib') or 0.0:.2f}GB "
471
+ f"gpu_peak={payload.get('gpu_peak_gib') or 0.0:.2f}GB "
472
+ f"gpu_reserved={payload.get('gpu_reserved_gib') or 0.0:.2f}GB "
473
+ f"cache={self.compiler_cache_gib:.2f}GB "
474
+ f"ckpt={payload['checkpoint_gib']:.2f}GB "
475
+ f"data_wait={data_wait_seconds:.3f}s",
476
+ flush=True,
477
+ )
478
+
479
+ def _export_final(self, validation_loss: float) -> None:
480
+ destination = Path(self.run_cfg["final_dir"])
481
+ temporary = destination.with_name(f".{destination.name}.tmp")
482
+ if temporary.exists():
483
+ shutil.rmtree(temporary)
484
+ temporary.mkdir(parents=True)
485
+ self.raw_model.save_pretrained(temporary, safe_serialization=True)
486
+ shutil.copytree(
487
+ self.run_cfg["tokenizer_dir"],
488
+ temporary,
489
+ dirs_exist_ok=True,
490
+ )
491
+ (temporary / "training_summary.json").write_text(
492
+ json.dumps(
493
+ {
494
+ "global_step": self.global_step,
495
+ "total_tokens": self.total_tokens,
496
+ "validation_loss": validation_loss,
497
+ "validation_perplexity": safe_perplexity(validation_loss),
498
+ "median_tokens_per_sec": self.metrics.median_tps,
499
+ "qualified_throughput": self.qualified_throughput,
500
+ "qualified_convergence": self.qualified_convergence,
501
+ },
502
+ indent=2,
503
+ )
504
+ + "\n",
505
+ encoding="utf-8",
506
+ )
507
+ if destination.exists():
508
+ raise RuntimeError(f"Final model directory already exists: {destination}")
509
+ os.replace(temporary, destination)
source/qyrou_arch/triton_kernels.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import torch
4
+ import triton
5
+ import triton.language as tl
6
+
7
+
8
+ def _settings(n_cols: int) -> tuple[int, int]:
9
+ block_size = triton.next_power_of_2(n_cols)
10
+ if block_size > 65_536:
11
+ raise ValueError(f"Unsupported SwiGLU width: {n_cols}")
12
+ num_warps = 4 if block_size < 2_048 else 8
13
+ return block_size, num_warps
14
+
15
+
16
+ @triton.jit
17
+ def _packed_swiglu_forward_kernel(
18
+ packed_ptr,
19
+ output_ptr,
20
+ packed_stride: tl.constexpr,
21
+ output_stride: tl.constexpr,
22
+ n_cols: tl.constexpr,
23
+ block_size: tl.constexpr,
24
+ ):
25
+ row = tl.program_id(0).to(tl.int64)
26
+ offsets = tl.arange(0, block_size)
27
+ mask = offsets < n_cols
28
+ packed_row = packed_ptr + row * packed_stride
29
+ gate = tl.load(packed_row + offsets, mask=mask, other=0.0).to(tl.float32)
30
+ up = tl.load(packed_row + n_cols + offsets, mask=mask, other=0.0)
31
+ activated = (gate * tl.sigmoid(gate)).cast(up.dtype) * up
32
+ tl.store(output_ptr + row * output_stride + offsets, activated, mask=mask)
33
+
34
+
35
+ @triton.jit
36
+ def _packed_swiglu_backward_kernel(
37
+ grad_output_ptr,
38
+ packed_ptr,
39
+ grad_packed_ptr,
40
+ grad_output_stride: tl.constexpr,
41
+ packed_stride: tl.constexpr,
42
+ n_cols: tl.constexpr,
43
+ block_size: tl.constexpr,
44
+ ):
45
+ row = tl.program_id(0).to(tl.int64)
46
+ offsets = tl.arange(0, block_size)
47
+ mask = offsets < n_cols
48
+ packed_row = packed_ptr + row * packed_stride
49
+ grad_packed_row = grad_packed_ptr + row * packed_stride
50
+ grad_output = tl.load(
51
+ grad_output_ptr + row * grad_output_stride + offsets,
52
+ mask=mask,
53
+ other=0.0,
54
+ )
55
+ gate = tl.load(packed_row + offsets, mask=mask, other=0.0).to(tl.float32)
56
+ up = tl.load(packed_row + n_cols + offsets, mask=mask, other=0.0)
57
+ sigmoid_gate = tl.sigmoid(gate)
58
+ silu_gate = gate * sigmoid_gate
59
+ grad_up = grad_output * silu_gate
60
+ grad_gate = grad_output * up * (sigmoid_gate + silu_gate * (1.0 - sigmoid_gate))
61
+ tl.store(grad_packed_row + offsets, grad_gate, mask=mask)
62
+ tl.store(grad_packed_row + n_cols + offsets, grad_up, mask=mask)
63
+
64
+
65
+ class PackedSwiGLUFunction(torch.autograd.Function):
66
+ @staticmethod
67
+ def forward(ctx, packed: torch.Tensor) -> torch.Tensor:
68
+ if not packed.is_cuda:
69
+ raise ValueError("PackedSwiGLUFunction requires a CUDA tensor")
70
+ if packed.dtype not in (torch.float16, torch.bfloat16, torch.float32):
71
+ raise TypeError(f"Unsupported PackedSwiGLU dtype: {packed.dtype}")
72
+ if packed.shape[-1] % 2:
73
+ raise ValueError("Packed gate/up dimension must be even")
74
+ packed = packed.contiguous()
75
+ n_cols = packed.shape[-1] // 2
76
+ packed_2d = packed.view(-1, 2 * n_cols)
77
+ output = torch.empty((packed_2d.shape[0], n_cols), dtype=packed.dtype, device=packed.device)
78
+ block_size, num_warps = _settings(n_cols)
79
+ _packed_swiglu_forward_kernel[(packed_2d.shape[0],)](
80
+ packed_2d,
81
+ output,
82
+ packed_2d.stride(0),
83
+ output.stride(0),
84
+ n_cols=n_cols,
85
+ block_size=block_size,
86
+ num_warps=num_warps,
87
+ )
88
+ ctx.save_for_backward(packed_2d)
89
+ ctx.original_shape = packed.shape
90
+ ctx.n_cols = n_cols
91
+ ctx.block_size = block_size
92
+ ctx.num_warps = num_warps
93
+ return output.view(*packed.shape[:-1], n_cols)
94
+
95
+ @staticmethod
96
+ def backward(ctx, grad_output: torch.Tensor) -> tuple[torch.Tensor]:
97
+ (packed_2d,) = ctx.saved_tensors
98
+ grad_output_2d = grad_output.contiguous().view(-1, ctx.n_cols)
99
+ grad_packed = torch.empty_like(packed_2d)
100
+ _packed_swiglu_backward_kernel[(packed_2d.shape[0],)](
101
+ grad_output_2d,
102
+ packed_2d,
103
+ grad_packed,
104
+ grad_output_2d.stride(0),
105
+ packed_2d.stride(0),
106
+ n_cols=ctx.n_cols,
107
+ block_size=ctx.block_size,
108
+ num_warps=ctx.num_warps,
109
+ )
110
+ return (grad_packed.view(ctx.original_shape),)
source/requirements-windows.lock ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ datasets>=4,<5
2
+ transformers==5.13.0
3
+ tokenizers==0.22.2
4
+ safetensors>=0.8,<0.9
5
+ numpy==2.4.6
6
+ psutil>=6.1,<7
7
+ pytest>=8.3,<9
source/scripts/audit_shuffled_stream.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import io
5
+ import json
6
+
7
+ import numpy as np
8
+ import torch
9
+
10
+ from qyrou_arch.data import PackedTokenStream
11
+ from qyrou_arch.io_utils import load_run_config
12
+
13
+
14
+ def main() -> None:
15
+ parser = argparse.ArgumentParser()
16
+ parser.add_argument("--config", default="configs/qyrou_1_exp_base.json")
17
+ args = parser.parse_args()
18
+
19
+ cfg = load_run_config(args.config)
20
+ data_cfg = cfg["data"]
21
+ run_cfg = cfg["run"]
22
+ stream = PackedTokenStream(
23
+ f"{run_cfg['token_cache_dir']}\\manifest.json",
24
+ sequence_length=int(data_cfg["sequence_length"]),
25
+ pad_token_id=int(cfg["model"]["pad_token_id"]),
26
+ seed=int(data_cfg["shuffle_seed"]),
27
+ shuffle_block_tokens=int(data_cfg["shuffle_block_tokens"]),
28
+ )
29
+
30
+ unique = np.unique(stream.permutation)
31
+ if len(unique) != len(stream.permutation):
32
+ raise RuntimeError("Shuffled stream contains a repeated block")
33
+ if len(unique) and (int(unique[0]) != 0 or int(unique[-1]) != len(unique) - 1):
34
+ raise RuntimeError("Shuffled stream omits at least one block")
35
+ block_token_sum = sum(length for _, _, length in stream.blocks)
36
+ manifest_tokens = int(stream.manifest["total_tokens"])
37
+ if block_token_sum != manifest_tokens or stream.total_tokens != manifest_tokens:
38
+ raise RuntimeError("Shuffled block lengths do not match the token manifest")
39
+
40
+ state = stream.state_dict()
41
+ serialized = io.BytesIO()
42
+ torch.save(state, serialized)
43
+ serialized.seek(0)
44
+ restored_state = torch.load(serialized, map_location="cpu", weights_only=False)
45
+
46
+ sequence_length = int(data_cfg["sequence_length"])
47
+ boundary_cursor = max(0, stream.prefix[1] - sequence_length)
48
+ stream.seek(boundary_cursor)
49
+ restored_state["cursor"] = boundary_cursor
50
+ expected = stream.read_tokens(sequence_length * 4)
51
+
52
+ resumed = PackedTokenStream(
53
+ f"{run_cfg['token_cache_dir']}\\manifest.json",
54
+ sequence_length=sequence_length,
55
+ pad_token_id=int(cfg["model"]["pad_token_id"]),
56
+ seed=int(data_cfg["shuffle_seed"]),
57
+ shuffle_block_tokens=int(data_cfg["shuffle_block_tokens"]),
58
+ )
59
+ resumed.load_state_dict(restored_state)
60
+ replay = resumed.read_tokens(sequence_length * 4)
61
+ if not np.array_equal(expected, replay):
62
+ raise RuntimeError("Shuffled stream did not resume with the exact next tokens")
63
+
64
+ print(
65
+ json.dumps(
66
+ {
67
+ "status": "passed",
68
+ "total_tokens": stream.total_tokens,
69
+ "unique_blocks": len(stream.blocks),
70
+ "shuffle_block_tokens": stream.shuffle_block_tokens,
71
+ "permutation_sha256": stream.permutation_sha256,
72
+ "resume_across_block_boundary": "exact",
73
+ },
74
+ indent=2,
75
+ )
76
+ )
77
+
78
+
79
+ if __name__ == "__main__":
80
+ main()
source/scripts/autotune.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import gc
5
+ import json
6
+ import logging
7
+ import time
8
+ import warnings
9
+ from pathlib import Path
10
+ from statistics import mean, median
11
+
12
+ import torch
13
+
14
+ from qyrou_arch.configuration_qyrou_arch import QyrouArchConfig
15
+ from qyrou_arch.io_utils import atomic_write_json, load_run_config
16
+ from qyrou_arch.modeling_qyrou_arch import QyrouArchForCausalLM
17
+
18
+ warnings.filterwarnings(
19
+ "ignore",
20
+ message=r"Dynamo does not know how to trace the builtin .*native_specialize_impl.*",
21
+ )
22
+ logging.getLogger("torch._inductor.utils").setLevel(logging.ERROR)
23
+
24
+
25
+ def benchmark_candidate(
26
+ model_cfg: dict,
27
+ microbatch: int,
28
+ grad_accum: int,
29
+ compile_model: bool,
30
+ compile_mode: str,
31
+ steps: int,
32
+ warmup: int,
33
+ ) -> dict[str, object]:
34
+ torch.cuda.empty_cache()
35
+ torch.cuda.reset_peak_memory_stats()
36
+ model = QyrouArchForCausalLM(QyrouArchConfig(**model_cfg)).cuda().float().train()
37
+ optimizer = torch.optim.AdamW(model.parameters(), lr=6e-4, fused=True)
38
+ runner = torch.compile(model, mode=compile_mode, fullgraph=False) if compile_model else model
39
+ sequence_length = int(model_cfg["max_position_embeddings"])
40
+ input_ids = torch.randint(
41
+ 0,
42
+ int(model_cfg["vocab_size"]),
43
+ (microbatch, sequence_length),
44
+ device="cuda",
45
+ )
46
+ timings: list[float] = []
47
+ for step in range(warmup + steps):
48
+ started = time.perf_counter()
49
+ optimizer.zero_grad(set_to_none=True)
50
+ with torch.autocast("cuda", dtype=torch.bfloat16, cache_enabled=True):
51
+ for _ in range(grad_accum):
52
+ if compile_model:
53
+ torch.compiler.cudagraph_mark_step_begin()
54
+ loss = runner(
55
+ input_ids=input_ids,
56
+ labels=input_ids,
57
+ use_cache=False,
58
+ return_logits=False,
59
+ ).loss / grad_accum
60
+ loss.backward()
61
+ torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
62
+ optimizer.step()
63
+ torch.cuda.synchronize()
64
+ if step >= warmup:
65
+ timings.append(time.perf_counter() - started)
66
+ tokens_per_step = microbatch * sequence_length * grad_accum
67
+ throughputs = sorted(tokens_per_step / elapsed for elapsed in timings)
68
+
69
+ def percentile(fraction: float) -> float:
70
+ return throughputs[round((len(throughputs) - 1) * fraction)]
71
+
72
+ result = {
73
+ "microbatch": microbatch,
74
+ "grad_accum": grad_accum,
75
+ "compile": compile_model,
76
+ "compile_mode": compile_mode if compile_model else None,
77
+ "tokens_per_sec": median(throughputs),
78
+ "average_tokens_per_sec": mean(throughputs),
79
+ "p10_tokens_per_sec": percentile(0.10),
80
+ "p90_tokens_per_sec": percentile(0.90),
81
+ "average_step_seconds": mean(timings),
82
+ "measured_steps": len(timings),
83
+ "peak_gib": torch.cuda.max_memory_allocated() / 2**30,
84
+ }
85
+ del runner, optimizer, model, input_ids
86
+ if compile_model:
87
+ torch.compiler.reset()
88
+ gc.collect()
89
+ torch.cuda.empty_cache()
90
+ return result
91
+
92
+
93
+ def main() -> None:
94
+ parser = argparse.ArgumentParser()
95
+ parser.add_argument("--config", default="configs/qyrou_1_exp_base.json")
96
+ parser.add_argument("--steps", type=int, default=50)
97
+ parser.add_argument("--warmup", type=int, default=10)
98
+ parser.add_argument(
99
+ "--candidate",
100
+ nargs=2,
101
+ type=int,
102
+ metavar=("MICROBATCH", "GRAD_ACCUM"),
103
+ help="Benchmark only one candidate; useful for an isolated confirmation run.",
104
+ )
105
+ parser.add_argument("--eager-only", action="store_true")
106
+ parser.add_argument("--compiled-only", action="store_true")
107
+ parser.add_argument(
108
+ "--compile-mode",
109
+ choices=("default", "reduce-overhead", "max-autotune-no-cudagraphs"),
110
+ default="default",
111
+ )
112
+ parser.add_argument(
113
+ "--attention-backend",
114
+ choices=("cudnn", "auto", "flash"),
115
+ help="Override the configured SDPA backend for an isolated benchmark.",
116
+ )
117
+ args = parser.parse_args()
118
+ if args.eager_only and args.compiled_only:
119
+ parser.error("--eager-only and --compiled-only are mutually exclusive")
120
+ cfg = load_run_config(args.config)
121
+ torch.set_float32_matmul_precision("high")
122
+ torch.backends.cuda.matmul.allow_tf32 = True
123
+ model_cfg = dict(cfg["model"])
124
+ if args.attention_backend:
125
+ model_cfg["attention_backend"] = args.attention_backend
126
+ results = []
127
+ candidates = [args.candidate] if args.candidate else cfg["training"]["autotune_candidates"]
128
+ compile_modes = (False,) if args.eager_only else ((True,) if args.compiled_only else (False, True))
129
+ for microbatch, grad_accum in candidates:
130
+ for compile_model in compile_modes:
131
+ try:
132
+ result = benchmark_candidate(
133
+ model_cfg,
134
+ int(microbatch),
135
+ int(grad_accum),
136
+ compile_model,
137
+ args.compile_mode,
138
+ args.steps,
139
+ args.warmup,
140
+ )
141
+ result["attention_backend"] = model_cfg["attention_backend"]
142
+ except (torch.cuda.OutOfMemoryError, RuntimeError) as exc:
143
+ result = {
144
+ "microbatch": microbatch,
145
+ "grad_accum": grad_accum,
146
+ "compile": compile_model,
147
+ "error": str(exc),
148
+ }
149
+ print(json.dumps(result), flush=True)
150
+ results.append(result)
151
+ valid = [result for result in results if "tokens_per_sec" in result]
152
+ if not valid:
153
+ raise RuntimeError("No autotune candidate completed")
154
+ eager_results = [result for result in valid if not result["compile"]]
155
+ best_eager = (
156
+ max(eager_results, key=lambda result: result["tokens_per_sec"])
157
+ if eager_results
158
+ else None
159
+ )
160
+ best = max(valid, key=lambda result: result["tokens_per_sec"])
161
+ minimum_gain = float(cfg["training"]["compile_minimum_improvement"])
162
+ if (
163
+ best["compile"]
164
+ and best_eager is not None
165
+ and best["tokens_per_sec"] < best_eager["tokens_per_sec"] * (1 + minimum_gain)
166
+ ):
167
+ best = best_eager
168
+ artifact = {
169
+ "selected": best,
170
+ "best_eager": best_eager,
171
+ "results": results,
172
+ "minimum_compile_gain": minimum_gain,
173
+ }
174
+ destination = Path(cfg["run"]["project_dir"]) / "artifacts" / "autotune.json"
175
+ atomic_write_json(destination, artifact)
176
+ print(json.dumps(artifact["selected"], indent=2))
177
+
178
+
179
+ if __name__ == "__main__":
180
+ main()
source/scripts/bootstrap_windows.ps1 ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [CmdletBinding()]
2
+ param()
3
+
4
+ $ErrorActionPreference = "Stop"
5
+ $Project = Split-Path -Parent $PSScriptRoot
6
+ Set-Location $Project
7
+ $env:UV_CACHE_DIR = Join-Path $Project ".cache\uv"
8
+ $env:UV_PYTHON_INSTALL_DIR = Join-Path $Project ".tools\python"
9
+
10
+ function Assert-LastExitCode([string]$Action) {
11
+ if ($LASTEXITCODE -ne 0) {
12
+ throw "$Action failed with exit code $LASTEXITCODE"
13
+ }
14
+ }
15
+
16
+ $UvCommand = Get-Command uv -ErrorAction SilentlyContinue
17
+ if ($UvCommand) {
18
+ $Uv = $UvCommand.Source
19
+ } else {
20
+ $UvDirectory = Join-Path $Project ".tools\uv"
21
+ $Uv = Join-Path $UvDirectory "uv.exe"
22
+ if (-not (Test-Path $Uv)) {
23
+ $Installer = Join-Path $Project ".cache\uv-installer.ps1"
24
+ New-Item -ItemType Directory -Force (Split-Path $Installer) | Out-Null
25
+ $env:UV_UNMANAGED_INSTALL = $UvDirectory
26
+ $env:UV_NO_MODIFY_PATH = "1"
27
+ Invoke-WebRequest "https://astral.sh/uv/0.11.32/install.ps1" -OutFile $Installer
28
+ & powershell.exe -NoProfile -ExecutionPolicy Bypass -File $Installer
29
+ }
30
+ }
31
+
32
+ & $Uv python install 3.12
33
+ Assert-LastExitCode "Python 3.12 installation"
34
+ $NeedsVenv = -not (Test-Path ".venv\Scripts\python.exe")
35
+ if (-not $NeedsVenv) {
36
+ $PriorErrorPreference = $ErrorActionPreference
37
+ $ErrorActionPreference = "SilentlyContinue"
38
+ & ".venv\Scripts\python.exe" -c "import torch" 2>$null
39
+ $NeedsVenv = $LASTEXITCODE -ne 0
40
+ $ErrorActionPreference = $PriorErrorPreference
41
+ }
42
+ if ($NeedsVenv) {
43
+ & $Uv venv --clear --python 3.12 .venv
44
+ Assert-LastExitCode "Virtual environment creation"
45
+ }
46
+
47
+ & $Uv pip install --python ".venv\Scripts\python.exe" --index-url "https://download.pytorch.org/whl/cu130" "torch==2.12.0+cu130"
48
+ Assert-LastExitCode "CUDA PyTorch installation"
49
+ & $Uv pip install --python ".venv\Scripts\python.exe" -r requirements-windows.lock
50
+ Assert-LastExitCode "Locked dependency installation"
51
+ & $Uv pip install --python ".venv\Scripts\python.exe" "triton-windows==3.7.1.post27"
52
+ Assert-LastExitCode "Triton-Windows installation"
53
+ & $Uv pip install --python ".venv\Scripts\python.exe" --no-deps "liger-kernel==0.8.0" "cut-cross-entropy==25.1.1"
54
+ Assert-LastExitCode "Liger and cut-cross-entropy installation"
55
+ & $Uv pip install --python ".venv\Scripts\python.exe" -e .
56
+ Assert-LastExitCode "Editable project installation"
57
+ # `liger-kernel` declares the Linux distribution name `triton`; on Windows the
58
+ # compatible provider is `triton-windows`, so generic `pip check` reports a
59
+ # false missing-distribution error. The import/runtime smoke below is authoritative.
60
+
61
+ $env:TORCHINDUCTOR_CACHE_DIR = Join-Path $Project ".cache\torchinductor"
62
+ $env:TRITON_CACHE_DIR = Join-Path $Project ".cache\triton"
63
+ New-Item -ItemType Directory -Force $env:TORCHINDUCTOR_CACHE_DIR | Out-Null
64
+ New-Item -ItemType Directory -Force $env:TRITON_CACHE_DIR | Out-Null
65
+
66
+ & ".venv\Scripts\python.exe" -c "import torch, transformers, triton; print(torch.__version__, torch.version.cuda, transformers.__version__, triton.__version__)"
67
+ Assert-LastExitCode "Runtime import smoke test"
68
+ Write-Host "Qyrou-1 training environment is ready."
source/scripts/fetch_preview_assets.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import os
5
+ import urllib.request
6
+ from pathlib import Path
7
+
8
+
9
+ FILES = [
10
+ "README.md",
11
+ "config.json",
12
+ "generation_config.json",
13
+ "modeling_qyrou_arch.py",
14
+ "triton_kernels.py",
15
+ "stats/run_config.yaml",
16
+ "stats/training_state.json",
17
+ ]
18
+
19
+
20
+ def main() -> None:
21
+ parser = argparse.ArgumentParser()
22
+ parser.add_argument("--repo", default="QyrouArchLabs/QyrouArch-65M-Preview")
23
+ parser.add_argument("--revision", default="94f25e19b0efb71b4534fe890afb58f5a8e8a4e1")
24
+ parser.add_argument("--output", default="artifacts/preview_reference")
25
+ args = parser.parse_args()
26
+ token = os.environ.get("HF_TOKEN")
27
+ if not token:
28
+ raise RuntimeError("Set HF_TOKEN for this process; the script never persists it")
29
+ output = Path(args.output).resolve()
30
+ output.mkdir(parents=True, exist_ok=True)
31
+ for filename in FILES:
32
+ request = urllib.request.Request(
33
+ f"https://huggingface.co/{args.repo}/resolve/{args.revision}/{filename}",
34
+ headers={"Authorization": f"Bearer {token}"},
35
+ )
36
+ with urllib.request.urlopen(request, timeout=60) as response:
37
+ content = response.read()
38
+ destination = output / filename
39
+ destination.parent.mkdir(parents=True, exist_ok=True)
40
+ temporary = destination.with_suffix(destination.suffix + ".tmp")
41
+ temporary.write_bytes(content)
42
+ os.replace(temporary, destination)
43
+ print(destination)
44
+
45
+
46
+ if __name__ == "__main__":
47
+ main()
48
+
source/scripts/generate.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ from pathlib import Path
5
+
6
+ import torch
7
+ from transformers import PreTrainedTokenizerFast
8
+
9
+ from qyrou_arch.cache import QyrouArchHybridCache
10
+ from qyrou_arch.modeling_qyrou_arch import QyrouArchForCausalLM
11
+
12
+
13
+ def main() -> None:
14
+ parser = argparse.ArgumentParser()
15
+ parser.add_argument("model")
16
+ parser.add_argument("prompt")
17
+ parser.add_argument("--max-new-tokens", type=int, default=128)
18
+ parser.add_argument("--static-cache", action="store_true")
19
+ args = parser.parse_args()
20
+ model_path = Path(args.model)
21
+ tokenizer = PreTrainedTokenizerFast.from_pretrained(model_path)
22
+ model = QyrouArchForCausalLM.from_pretrained(model_path).cuda().to(torch.bfloat16).eval()
23
+ inputs = tokenizer(args.prompt, return_tensors="pt").to("cuda")
24
+ cache = (
25
+ QyrouArchHybridCache(model.config.num_hidden_layers, model.config.max_position_embeddings)
26
+ if args.static_cache
27
+ else QyrouArchHybridCache(model.config.num_hidden_layers)
28
+ )
29
+ with torch.inference_mode():
30
+ output = model.generate(
31
+ **inputs,
32
+ past_key_values=cache,
33
+ max_new_tokens=args.max_new_tokens,
34
+ do_sample=True,
35
+ temperature=0.8,
36
+ top_p=0.95,
37
+ use_cache=True,
38
+ pad_token_id=tokenizer.pad_token_id,
39
+ eos_token_id=tokenizer.eos_token_id,
40
+ )
41
+ print(tokenizer.decode(output[0], skip_special_tokens=False))
42
+
43
+
44
+ if __name__ == "__main__":
45
+ main()
46
+
source/scripts/gpu_smoke.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+
6
+ import torch
7
+
8
+ from qyrou_arch.cache import QyrouArchHybridCache
9
+ from qyrou_arch.configuration_qyrou_arch import QyrouArchConfig
10
+ from qyrou_arch.io_utils import load_run_config
11
+ from qyrou_arch.modeling_qyrou_arch import QyrouArchForCausalLM
12
+
13
+
14
+ def main() -> None:
15
+ parser = argparse.ArgumentParser()
16
+ parser.add_argument("--config", default="configs/qyrou_1_exp_base.json")
17
+ parser.add_argument("--sequence-length", type=int, default=128)
18
+ args = parser.parse_args()
19
+ cfg = load_run_config(args.config)
20
+ if not torch.cuda.is_available() or not torch.cuda.is_bf16_supported():
21
+ raise RuntimeError("GPU smoke requires BF16 CUDA")
22
+ torch.manual_seed(int(cfg["run"]["seed"]))
23
+ torch.cuda.manual_seed_all(int(cfg["run"]["seed"]))
24
+ torch.cuda.reset_peak_memory_stats()
25
+ model = QyrouArchForCausalLM(QyrouArchConfig(**cfg["model"])).cuda().float().train()
26
+ optimizer = torch.optim.AdamW(model.parameters(), lr=6e-4, fused=True)
27
+ tokens = torch.randint(
28
+ 5,
29
+ model.config.vocab_size,
30
+ (1, args.sequence_length),
31
+ device="cuda",
32
+ )
33
+ with torch.autocast("cuda", dtype=torch.bfloat16):
34
+ output = model(
35
+ input_ids=tokens,
36
+ labels=tokens,
37
+ use_cache=False,
38
+ return_logits=False,
39
+ )
40
+ if not torch.isfinite(output.loss):
41
+ raise RuntimeError(f"Non-finite smoke loss: {output.loss}")
42
+ output.loss.backward()
43
+ grad_norm = torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
44
+ if not torch.isfinite(grad_norm):
45
+ raise RuntimeError(f"Non-finite smoke gradient norm: {grad_norm}")
46
+ optimizer.step()
47
+ optimizer.zero_grad(set_to_none=True)
48
+
49
+ model.eval()
50
+ cache_tokens = tokens[:, :16]
51
+ with torch.inference_mode(), torch.autocast("cuda", dtype=torch.bfloat16):
52
+ full = model(cache_tokens, use_cache=False).logits
53
+ cache = QyrouArchHybridCache(model.config.num_hidden_layers)
54
+ first = model(cache_tokens[:, :10], past_key_values=cache, use_cache=True).logits
55
+ second = model(
56
+ cache_tokens[:, 10:],
57
+ past_key_values=cache,
58
+ cache_position=torch.arange(10, 16, device="cuda"),
59
+ use_cache=True,
60
+ ).logits
61
+ cached = torch.cat((first, second), dim=1)
62
+ torch.testing.assert_close(cached, full, atol=0.08, rtol=0.03)
63
+ report = {
64
+ "device": torch.cuda.get_device_name(0),
65
+ "compute_capability": torch.cuda.get_device_capability(0),
66
+ "parameters": sum(parameter.numel() for parameter in model.parameters()),
67
+ "loss": float(output.loss.detach()),
68
+ "grad_norm": float(grad_norm.detach()),
69
+ "peak_gib": torch.cuda.max_memory_allocated() / 2**30,
70
+ "cache_max_abs_error": float((cached - full).abs().max()),
71
+ }
72
+ print(json.dumps(report, indent=2))
73
+
74
+
75
+ if __name__ == "__main__":
76
+ main()