summerMC commited on
Commit
892619f
·
verified ·
1 Parent(s): db83f9b

Update configuration_trm_text_ism.py

Browse files
Files changed (1) hide show
  1. configuration_trm_text_ism.py +156 -14
configuration_trm_text_ism.py CHANGED
@@ -1,17 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import math
2
  import torch
3
  import torch.nn as nn
4
  import torch.nn.functional as F
5
  from torch.utils.checkpoint import checkpoint
6
- from transformers import PreTrainedModel
7
  from transformers.generation import GenerationMixin
8
  from transformers.modeling_outputs import CausalLMOutputWithPast
9
- try:
10
- from .configuration_trm_text_ism import TRMTextISMConfig # パッケージ context (trust_remote_code)
11
- except ImportError:
12
- from configuration_trm_text_ism import TRMTextISMConfig # 直import (Colab等)
13
 
14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  def apply_rope(x, cos, sin):
16
  S = x.shape[2]
17
  c, s = cos[:, :, :S, :].to(x.dtype), sin[:, :, :S, :].to(x.dtype)
@@ -26,7 +168,7 @@ class SwiGLUMLP(nn.Module):
26
  self.gate_proj = nn.Linear(config.dim, h, bias=False)
27
  self.up_proj = nn.Linear(config.dim, h, bias=False)
28
  self.down_proj = nn.Linear(h, config.dim, bias=False)
29
- self.down_proj._scale_init = True # 残差直前の射影は深さでスケールダウン初期化
30
 
31
  def forward(self, x):
32
  return self.down_proj(F.silu(self.gate_proj(x)) * self.up_proj(x))
@@ -48,7 +190,6 @@ class TRMAttention(nn.Module):
48
  q, k, v = [t.view(B, S, self.n_heads, self.head_dim).transpose(1, 2) for t in (q, k, v)]
49
  q, k = apply_rope(q, cos, sin), apply_rope(k, cos, sin)
50
  if attn_mask is None:
51
- # packed full系列(paddingなし): flash/efficient SDPAが効く経路
52
  y = F.scaled_dot_product_attention(q, k, v, is_causal=is_causal)
53
  else:
54
  y = F.scaled_dot_product_attention(q, k, v, attn_mask=attn_mask[:, None, :, :])
@@ -78,7 +219,6 @@ class TRMTextISMForCausalLM(PreTrainedModel, GenerationMixin):
78
  def __init__(self, config):
79
  super().__init__(config)
80
  self.token_emb = nn.Embedding(config.vocab_size, config.dim)
81
- # リカレントコア: n_layers個のユニークブロック。n_layers=1 で元コードと同一挙動。
82
  self.blocks = nn.ModuleList([TRMBlock(config) for _ in range(config.n_layers)])
83
  self.norm = nn.RMSNorm(config.dim)
84
  self.lm_head = nn.Linear(config.dim, config.vocab_size, bias=False)
@@ -87,16 +227,20 @@ class TRMTextISMForCausalLM(PreTrainedModel, GenerationMixin):
87
  pos = torch.arange(config.max_seq_len).float()
88
  theta = 1.0 / (10000.0 ** (torch.arange(0, config.head_dim // 2).float() / (config.head_dim // 2)))
89
  f = torch.outer(pos, theta)
90
- # persistent=False: configから再計算できるのでcheckpointに焼かない
91
- self.register_buffer("rope_cos", f.cos().view(1, 1, config.max_seq_len, -1), persistent=False)
92
- self.register_buffer("rope_sin", f.sin().view(1, 1, config.max_seq_len, -1), persistent=False)
 
 
 
 
 
93
  self.post_init()
94
 
95
  def _init_weights(self, module):
96
  if isinstance(module, nn.Linear):
97
  std = 0.02
98
  if getattr(module, "_scale_init", False):
99
- # 実効深度 = n_layers * recurrence_steps ぶん残差が積み上がるのでスケールダウン
100
  eff_depth = self.config.n_layers * self.config.recurrence_steps
101
  std = 0.02 / math.sqrt(2 * max(1, eff_depth))
102
  nn.init.normal_(module.weight, mean=0.0, std=std)
@@ -138,7 +282,6 @@ class TRMTextISMForCausalLM(PreTrainedModel, GenerationMixin):
138
  B, S = input_ids.shape
139
  x = self.token_emb(input_ids)
140
 
141
- # paddingが無ければ causal flash 経路。あれば明示マスク(生成・可変長用)。
142
  if attention_mask is None:
143
  m, is_causal = None, True
144
  else:
@@ -148,7 +291,6 @@ class TRMTextISMForCausalLM(PreTrainedModel, GenerationMixin):
148
 
149
  c, s = self.rope_cos, self.rope_sin
150
 
151
- # recurrent depth: コア(n_layers層)を recurrence_steps 回まわす
152
  for _ in range(self.config.recurrence_steps):
153
  for blk in self.blocks:
154
  if self.gradient_checkpointing and self.training:
 
1
+ """
2
+ TRM-text-ISM: 単一ファイル構成。
3
+
4
+ なぜ1ファイルにしたか:
5
+ config と modeling を別ファイル + relative import (`from .config import ...`) に
6
+ 分けると、`save_pretrained()` → `push_to_hub()` → `from_pretrained(trust_remote_code=True)`
7
+ という往復で `ModuleNotFoundError` を起こす既知の不具合がある
8
+ (huggingface/transformers issue #40496, 2025-08)。
9
+ Falcon/ChatGLM2など実運用のHubモデルの多くも、複数ファイル構成を避けて
10
+ configとmodelingを1ファイルに収めることでこれを回避している。
11
+
12
+ このファイルだけを `modeling_trm_text_ism.py` としてHubに置けば、
13
+ Colabでの直importでも、Hubのtrust_remote_code経由でも、同一コードパスで動く。
14
+ """
15
  import math
16
  import torch
17
  import torch.nn as nn
18
  import torch.nn.functional as F
19
  from torch.utils.checkpoint import checkpoint
20
+ from transformers import PreTrainedModel, PretrainedConfig
21
  from transformers.generation import GenerationMixin
22
  from transformers.modeling_outputs import CausalLMOutputWithPast
 
 
 
 
23
 
24
 
25
+ # ============================== Config ==============================
26
+
27
+ class TRMTextISMConfig(PretrainedConfig):
28
+ """
29
+ TRM-text (ISM) config.
30
+
31
+ アーキテクチャ: RMSNorm + SwiGLU + RoPE + gated residual の `TRMBlock` を
32
+ `n_layers` 層積み、それを `recurrence_steps` 回ループ(recurrent-depth)。
33
+ n_layers=1 で「1ブロックをrecurrence_steps回」という最初の形と同一挙動。
34
+
35
+ 制約: n_heads * head_dim == dim (qkvがdimにしか射影しないためMHAのみ)。
36
+ """
37
+
38
+ model_type = "trm_text_ism"
39
+ auto_map = {
40
+ "AutoConfig": "modeling_trm_text_ism.TRMTextISMConfig",
41
+ "AutoModelForCausalLM": "modeling_trm_text_ism.TRMTextISMForCausalLM",
42
+ }
43
+
44
+ def __init__(
45
+ self,
46
+ vocab_size: int = 151936,
47
+ dim: int = 2048,
48
+ n_layers: int = 1,
49
+ n_heads: int = 16,
50
+ head_dim: int = 128,
51
+ mlp_ratio: float = 2.6875,
52
+ mlp_hidden_size: int | None = 5632,
53
+ recurrence_steps: int = 4,
54
+ max_seq_len: int = 2048,
55
+ residual_scale: float = 1.0,
56
+ tie_word_embeddings: bool = False,
57
+ pad_token_id: int | None = None,
58
+ bos_token_id: int | None = None,
59
+ eos_token_id: int | None = None,
60
+ **kwargs,
61
+ ):
62
+ self.vocab_size = vocab_size
63
+ self.dim = dim
64
+ self.n_layers = n_layers
65
+ self.n_heads = n_heads
66
+ self.head_dim = head_dim
67
+ self.mlp_ratio = mlp_ratio
68
+ self.mlp_hidden_size = mlp_hidden_size
69
+ self.recurrence_steps = recurrence_steps
70
+ self.max_seq_len = max_seq_len
71
+ self.residual_scale = residual_scale
72
+ kwargs["use_cache"] = False # KVキャッシュ未実装。generate()のcache分岐を踏ませない
73
+
74
+ super().__init__(
75
+ tie_word_embeddings=tie_word_embeddings,
76
+ pad_token_id=pad_token_id,
77
+ bos_token_id=bos_token_id,
78
+ eos_token_id=eos_token_id,
79
+ **kwargs,
80
+ )
81
+
82
+ @property
83
+ def hidden_size(self) -> int:
84
+ return self.dim
85
+
86
+ @property
87
+ def num_attention_heads(self) -> int:
88
+ return self.n_heads
89
+
90
+ @property
91
+ def num_hidden_layers(self) -> int:
92
+ return self.n_layers
93
+
94
+ @property
95
+ def num_key_value_heads(self) -> int:
96
+ return self.n_heads
97
+
98
+ @property
99
+ def _mlp_hidden(self) -> int:
100
+ return self.mlp_hidden_size or int(self.dim * self.mlp_ratio)
101
+
102
+ def param_breakdown(self) -> dict:
103
+ d, h, V = self.dim, self._mlp_hidden, self.vocab_size
104
+ attn = 3 * d * d + d * d
105
+ mlp = 3 * d * h
106
+ norms = 2 * d
107
+ gates = 2 * d
108
+ per_block = attn + mlp + norms + gates
109
+ blocks = self.n_layers * per_block
110
+ final_norm = d
111
+ token_emb = V * d
112
+ lm_head = 0 if self.tie_word_embeddings else V * d
113
+ non_emb = blocks + final_norm
114
+ total = non_emb + token_emb + lm_head
115
+ return {
116
+ "token_emb": token_emb, "lm_head": lm_head, "per_block": per_block,
117
+ "blocks_total": blocks, "final_norm": final_norm,
118
+ "non_embedding": non_emb, "total": total,
119
+ "embedding_share": (token_emb + lm_head) / total,
120
+ }
121
+
122
+ def num_parameters(self, include_embeddings: bool = True) -> int:
123
+ b = self.param_breakdown()
124
+ return b["total"] if include_embeddings else b["non_embedding"]
125
+
126
+ def __post_init_check__(self):
127
+ assert self.n_heads * self.head_dim == self.dim, (
128
+ f"n_heads*head_dim ({self.n_heads}*{self.head_dim}) != dim ({self.dim})."
129
+ )
130
+
131
+
132
+ TRM_TEXT_PRESETS: dict[str, dict] = {
133
+ "debug": dict(dim=512, n_layers=4, n_heads=8, head_dim=64,
134
+ mlp_hidden_size=1408, recurrence_steps=4, max_seq_len=1024),
135
+ "950m": dict(dim=2048, n_layers=7, n_heads=16, head_dim=128,
136
+ mlp_hidden_size=5632, recurrence_steps=4, max_seq_len=2048),
137
+ "1b": dict(dim=2048, n_layers=8, n_heads=16, head_dim=128,
138
+ mlp_hidden_size=5632, recurrence_steps=4, max_seq_len=2048),
139
+ "1b-single": dict(dim=3072, n_layers=1, n_heads=24, head_dim=128,
140
+ mlp_hidden_size=8192, recurrence_steps=4, max_seq_len=2048),
141
+ "1.3b": dict(dim=2304, n_layers=10, n_heads=18, head_dim=128,
142
+ mlp_hidden_size=6144, recurrence_steps=4, max_seq_len=2048),
143
+ }
144
+
145
+
146
+ def trm_text_config(preset: str = "1b", **overrides) -> TRMTextISMConfig:
147
+ if preset not in TRM_TEXT_PRESETS:
148
+ raise KeyError(f"unknown preset {preset!r}. choices: {list(TRM_TEXT_PRESETS)}")
149
+ cfg_kwargs = {**TRM_TEXT_PRESETS[preset], **overrides}
150
+ cfg = TRMTextISMConfig(**cfg_kwargs)
151
+ cfg.__post_init_check__()
152
+ return cfg
153
+
154
+
155
+ # ============================== Model ==============================
156
+
157
  def apply_rope(x, cos, sin):
158
  S = x.shape[2]
159
  c, s = cos[:, :, :S, :].to(x.dtype), sin[:, :, :S, :].to(x.dtype)
 
168
  self.gate_proj = nn.Linear(config.dim, h, bias=False)
169
  self.up_proj = nn.Linear(config.dim, h, bias=False)
170
  self.down_proj = nn.Linear(h, config.dim, bias=False)
171
+ self.down_proj._scale_init = True
172
 
173
  def forward(self, x):
174
  return self.down_proj(F.silu(self.gate_proj(x)) * self.up_proj(x))
 
190
  q, k, v = [t.view(B, S, self.n_heads, self.head_dim).transpose(1, 2) for t in (q, k, v)]
191
  q, k = apply_rope(q, cos, sin), apply_rope(k, cos, sin)
192
  if attn_mask is None:
 
193
  y = F.scaled_dot_product_attention(q, k, v, is_causal=is_causal)
194
  else:
195
  y = F.scaled_dot_product_attention(q, k, v, attn_mask=attn_mask[:, None, :, :])
 
219
  def __init__(self, config):
220
  super().__init__(config)
221
  self.token_emb = nn.Embedding(config.vocab_size, config.dim)
 
222
  self.blocks = nn.ModuleList([TRMBlock(config) for _ in range(config.n_layers)])
223
  self.norm = nn.RMSNorm(config.dim)
224
  self.lm_head = nn.Linear(config.dim, config.vocab_size, bias=False)
 
227
  pos = torch.arange(config.max_seq_len).float()
228
  theta = 1.0 / (10000.0 ** (torch.arange(0, config.head_dim // 2).float() / (config.head_dim // 2)))
229
  f = torch.outer(pos, theta)
230
+ # persistent=True (デフォルト): from_pretrained low_cpu_mem_usage 経路
231
+ # モデルが meta device 上に一旦構築され、その後 state_dict から重みがロードされる。
232
+ # persistent=False のバッファは state_dict に乗らないため、このロード経路では
233
+ # meta device 上の未初期化値のまま残ってしまい、cos()/sin() の出力が
234
+ # 1e+34 のような異常値になってNaNが全体に伝播する事故が起きた。
235
+ # config から再計算可能な値であっても、ロード安全性のため persistent のままにする。
236
+ self.register_buffer("rope_cos", f.cos().view(1, 1, config.max_seq_len, -1))
237
+ self.register_buffer("rope_sin", f.sin().view(1, 1, config.max_seq_len, -1))
238
  self.post_init()
239
 
240
  def _init_weights(self, module):
241
  if isinstance(module, nn.Linear):
242
  std = 0.02
243
  if getattr(module, "_scale_init", False):
 
244
  eff_depth = self.config.n_layers * self.config.recurrence_steps
245
  std = 0.02 / math.sqrt(2 * max(1, eff_depth))
246
  nn.init.normal_(module.weight, mean=0.0, std=std)
 
282
  B, S = input_ids.shape
283
  x = self.token_emb(input_ids)
284
 
 
285
  if attention_mask is None:
286
  m, is_causal = None, True
287
  else:
 
291
 
292
  c, s = self.rope_cos, self.rope_sin
293
 
 
294
  for _ in range(self.config.recurrence_steps):
295
  for blk in self.blocks:
296
  if self.gradient_checkpointing and self.training: