Diamegs commited on
Commit
3cbb136
·
verified ·
1 Parent(s): 4a88a7b

Update modeling code

Browse files
Files changed (2) hide show
  1. configuration_pit.py +10 -0
  2. modeling_pit.py +141 -42
configuration_pit.py CHANGED
@@ -12,13 +12,23 @@ class PITConfig(PretrainedConfig):
12
  n_layer: int = 20,
13
  n_head: int = 32,
14
  n_embd: int = 4096,
 
 
15
  **kwargs,
16
  ):
 
17
  super().__init__(**kwargs)
18
  self.vocab_size = vocab_size
19
  self.n_layer = n_layer
20
  self.n_head = n_head
21
  self.n_embd = n_embd
 
 
 
 
 
 
 
22
  # Standard aliases expected by transformers internals
23
  self.num_hidden_layers = n_layer
24
  self.hidden_size = n_embd
 
12
  n_layer: int = 20,
13
  n_head: int = 32,
14
  n_embd: int = 4096,
15
+ use_cache: bool = True,
16
+ tie_word_embeddings: bool = True,
17
  **kwargs,
18
  ):
19
+ kwargs.pop("tie_word_embeddings", None)
20
  super().__init__(**kwargs)
21
  self.vocab_size = vocab_size
22
  self.n_layer = n_layer
23
  self.n_head = n_head
24
  self.n_embd = n_embd
25
+ # KV caching during generation; the base PretrainedConfig does not
26
+ # define this, so the model reads it from here.
27
+ self.use_cache = use_cache
28
+ # lm_head shares its weight with the input embedding. transformers v5
29
+ # treats an *absent* `tie_word_embeddings` as False and would leave the
30
+ # two untied, so state it explicitly.
31
+ self.tie_word_embeddings = tie_word_embeddings
32
  # Standard aliases expected by transformers internals
33
  self.num_hidden_layers = n_layer
34
  self.hidden_size = n_embd
modeling_pit.py CHANGED
@@ -3,12 +3,17 @@ PIT (Point-In-Time) GPT model — self-contained for trust_remote_code=True load
3
 
4
  Architecture: decoder-only Transformer with RoPE, RMSNorm on Q/K, squared-ReLU
5
  MLP, and weight-tied input/output embeddings.
 
 
 
 
6
  """
7
 
8
  import torch
9
  import torch.nn as nn
10
  import torch.nn.functional as F
11
- from transformers import PreTrainedModel
 
12
  from transformers.modeling_outputs import CausalLMOutputWithPast
13
 
14
  from .configuration_pit import PITConfig
@@ -23,24 +28,23 @@ class Rotary(nn.Module):
23
  super().__init__()
24
  self.dim = dim
25
  self.base = base * scaling_factor
26
- self.seq_len_cached: int | None = None
27
- self.cos_cached: torch.Tensor | None = None
28
- self.sin_cached: torch.Tensor | None = None
29
-
30
- def forward(self, x: torch.Tensor):
31
- seq_len = x.shape[1]
32
- if seq_len != self.seq_len_cached:
33
- self.seq_len_cached = seq_len
34
- # Compute inv_freq on-the-fly on the correct device — never stored
35
- # as a buffer so device_map="auto" / meta-device loading can't break it.
36
- inv_freq = 1.0 / (self.base ** (
37
- torch.arange(0, self.dim, 2, device=x.device, dtype=torch.float32) / self.dim
 
 
38
  ))
39
- t = torch.arange(seq_len, device=x.device, dtype=torch.float32)
40
- freqs = torch.outer(t, inv_freq)
41
- self.cos_cached = freqs.cos().bfloat16()
42
- self.sin_cached = freqs.sin().bfloat16()
43
- return self.cos_cached[None, :, None, :], self.sin_cached[None, :, None, :]
44
 
45
 
46
  def _apply_rotary_emb(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
@@ -50,8 +54,9 @@ def _apply_rotary_emb(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) ->
50
 
51
 
52
  class CausalSelfAttention(nn.Module):
53
- def __init__(self, config: PITConfig):
54
  super().__init__()
 
55
  self.n_head = config.n_head
56
  self.n_embd = config.n_embd
57
  self.head_dim = config.n_embd // config.n_head
@@ -60,19 +65,30 @@ class CausalSelfAttention(nn.Module):
60
  self.c_v = nn.Linear(config.n_embd, config.n_embd, bias=False)
61
  self.c_proj = nn.Linear(config.n_embd, config.n_embd, bias=False)
62
  self.c_proj.weight.data.zero_()
63
- self.rotary = Rotary(self.head_dim)
64
 
65
- def forward(self, x: torch.Tensor) -> torch.Tensor:
 
 
 
 
 
 
 
 
66
  B, T, C = x.size()
67
  q = self.c_q(x).view(B, T, self.n_head, self.head_dim)
68
  k = self.c_k(x).view(B, T, self.n_head, self.head_dim)
69
  v = self.c_v(x).view(B, T, self.n_head, self.head_dim)
70
- cos, sin = self.rotary(q)
71
  q = _apply_rotary_emb(F.rms_norm(q, (q.size(-1),)), cos, sin)
72
  k = _apply_rotary_emb(F.rms_norm(k, (k.size(-1),)), cos, sin)
73
- y = F.scaled_dot_product_attention(
74
- q.transpose(1, 2), k.transpose(1, 2), v.transpose(1, 2), is_causal=True
75
- )
 
 
 
 
 
76
  return self.c_proj(y.transpose(1, 2).contiguous().view_as(x))
77
 
78
 
@@ -88,13 +104,23 @@ class MLP(nn.Module):
88
 
89
 
90
  class Block(nn.Module):
91
- def __init__(self, config: PITConfig):
92
  super().__init__()
93
- self.attn = CausalSelfAttention(config)
94
  self.mlp = MLP(config)
95
 
96
- def forward(self, x: torch.Tensor) -> torch.Tensor:
97
- x = x + self.attn(F.rms_norm(x, (x.size(-1),)))
 
 
 
 
 
 
 
 
 
 
98
  x = x + self.mlp(F.rms_norm(x, (x.size(-1),)))
99
  return x
100
 
@@ -103,7 +129,7 @@ class Block(nn.Module):
103
  # HuggingFace PreTrainedModel wrapper
104
  # ---------------------------------------------------------------------------
105
 
106
- class PITForCausalLM(PreTrainedModel):
107
  """
108
  Point-In-Time GPT wrapped as a HuggingFace CausalLM.
109
 
@@ -123,17 +149,25 @@ class PITForCausalLM(PreTrainedModel):
123
 
124
  config_class = PITConfig
125
  _no_split_modules = ["Block"]
126
- _supports_cache_class = False
127
- # Weight tying: lm_head and transformer.wte share parameters.
128
- _tied_weights_keys = ["lm_head.weight", "transformer.wte.weight"]
 
 
 
 
 
129
 
130
  def __init__(self, config: PITConfig):
131
  super().__init__(config)
132
  self.transformer = nn.ModuleDict({
133
  "wte": nn.Embedding(config.vocab_size, config.n_embd),
134
- "h": nn.ModuleList([Block(config) for _ in range(config.n_layer)]),
135
  })
136
  self.lm_head = nn.Linear(config.n_embd, config.vocab_size, bias=False)
 
 
 
137
  # Tie weights (re-tied after load_state_dict via tie_weights())
138
  self.transformer["wte"].weight = self.lm_head.weight
139
  self.post_init()
@@ -152,20 +186,86 @@ class PITForCausalLM(PreTrainedModel):
152
  def set_output_embeddings(self, value: nn.Linear) -> None:
153
  self.lm_head = value
154
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
  # -- forward -------------------------------------------------------------
156
 
157
  def forward(
158
  self,
159
  input_ids: torch.Tensor | None = None,
160
  attention_mask: torch.Tensor | None = None,
 
 
161
  labels: torch.Tensor | None = None,
 
 
162
  **kwargs,
163
  ) -> CausalLMOutputWithPast:
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  x = self.transformer["wte"](input_ids)
 
 
 
165
  for block in self.transformer["h"]:
166
- x = block(x)
167
  x = F.rms_norm(x, (x.size(-1),))
168
- logits = self.lm_head(x).float()
 
 
 
 
169
 
170
  loss = None
171
  if labels is not None:
@@ -175,9 +275,8 @@ class PITForCausalLM(PreTrainedModel):
175
  ignore_index=-100,
176
  )
177
 
178
- return CausalLMOutputWithPast(loss=loss, logits=logits)
179
-
180
- def prepare_inputs_for_generation(
181
- self, input_ids: torch.Tensor, **kwargs
182
- ) -> dict:
183
- return {"input_ids": input_ids}
 
3
 
4
  Architecture: decoder-only Transformer with RoPE, RMSNorm on Q/K, squared-ReLU
5
  MLP, and weight-tied input/output embeddings.
6
+
7
+ Generation is KV-cached: past keys and values are kept in a `DynamicCache`, so
8
+ each step runs attention with a single query position against the cache instead
9
+ of re-running the whole prefix.
10
  """
11
 
12
  import torch
13
  import torch.nn as nn
14
  import torch.nn.functional as F
15
+ from transformers import GenerationMixin, PreTrainedModel
16
+ from transformers.cache_utils import Cache, DynamicCache
17
  from transformers.modeling_outputs import CausalLMOutputWithPast
18
 
19
  from .configuration_pit import PITConfig
 
28
  super().__init__()
29
  self.dim = dim
30
  self.base = base * scaling_factor
31
+ self.inv_freq: torch.Tensor | None = None
32
+
33
+ def forward(self, position_ids: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
34
+ """Absolute positions [B, T] -> (cos, sin), each of shape [B, T, 1, dim // 2].
35
+
36
+ Positions are passed in rather than derived from the sequence length so
37
+ that a cached decode step rotates the new token by its *absolute*
38
+ position, not by 0.
39
+ """
40
+ if self.inv_freq is None or self.inv_freq.device != position_ids.device:
41
+ # Computed on-the-fly on the correct device — never stored as a
42
+ # buffer so device_map="auto" / meta-device loading can't break it.
43
+ self.inv_freq = 1.0 / (self.base ** (
44
+ torch.arange(0, self.dim, 2, device=position_ids.device, dtype=torch.float32) / self.dim
45
  ))
46
+ freqs = position_ids.float()[:, :, None] * self.inv_freq
47
+ return freqs.cos().bfloat16()[:, :, None, :], freqs.sin().bfloat16()[:, :, None, :]
 
 
 
48
 
49
 
50
  def _apply_rotary_emb(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
 
54
 
55
 
56
  class CausalSelfAttention(nn.Module):
57
+ def __init__(self, config: PITConfig, layer_idx: int = 0):
58
  super().__init__()
59
+ self.layer_idx = layer_idx
60
  self.n_head = config.n_head
61
  self.n_embd = config.n_embd
62
  self.head_dim = config.n_embd // config.n_head
 
65
  self.c_v = nn.Linear(config.n_embd, config.n_embd, bias=False)
66
  self.c_proj = nn.Linear(config.n_embd, config.n_embd, bias=False)
67
  self.c_proj.weight.data.zero_()
 
68
 
69
+ def forward(
70
+ self,
71
+ x: torch.Tensor,
72
+ cos: torch.Tensor,
73
+ sin: torch.Tensor,
74
+ attn_mask: torch.Tensor | None = None,
75
+ is_causal: bool = False,
76
+ past_key_values: Cache | None = None,
77
+ ) -> torch.Tensor:
78
  B, T, C = x.size()
79
  q = self.c_q(x).view(B, T, self.n_head, self.head_dim)
80
  k = self.c_k(x).view(B, T, self.n_head, self.head_dim)
81
  v = self.c_v(x).view(B, T, self.n_head, self.head_dim)
 
82
  q = _apply_rotary_emb(F.rms_norm(q, (q.size(-1),)), cos, sin)
83
  k = _apply_rotary_emb(F.rms_norm(k, (k.size(-1),)), cos, sin)
84
+
85
+ # [B, T, H, D] -> [B, H, T, D], the layout the cache and SDPA expect.
86
+ q, k, v = q.transpose(1, 2), k.transpose(1, 2), v.transpose(1, 2)
87
+ if past_key_values is not None:
88
+ # RoPE is already applied, so cached keys stay valid for later steps.
89
+ k, v = past_key_values.update(k, v, self.layer_idx)
90
+
91
+ y = F.scaled_dot_product_attention(q, k, v, attn_mask=attn_mask, is_causal=is_causal)
92
  return self.c_proj(y.transpose(1, 2).contiguous().view_as(x))
93
 
94
 
 
104
 
105
 
106
  class Block(nn.Module):
107
+ def __init__(self, config: PITConfig, layer_idx: int = 0):
108
  super().__init__()
109
+ self.attn = CausalSelfAttention(config, layer_idx)
110
  self.mlp = MLP(config)
111
 
112
+ def forward(
113
+ self,
114
+ x: torch.Tensor,
115
+ cos: torch.Tensor,
116
+ sin: torch.Tensor,
117
+ attn_mask: torch.Tensor | None = None,
118
+ is_causal: bool = False,
119
+ past_key_values: Cache | None = None,
120
+ ) -> torch.Tensor:
121
+ x = x + self.attn(
122
+ F.rms_norm(x, (x.size(-1),)), cos, sin, attn_mask, is_causal, past_key_values
123
+ )
124
  x = x + self.mlp(F.rms_norm(x, (x.size(-1),)))
125
  return x
126
 
 
129
  # HuggingFace PreTrainedModel wrapper
130
  # ---------------------------------------------------------------------------
131
 
132
+ class PITForCausalLM(PreTrainedModel, GenerationMixin):
133
  """
134
  Point-In-Time GPT wrapped as a HuggingFace CausalLM.
135
 
 
149
 
150
  config_class = PITConfig
151
  _no_split_modules = ["Block"]
152
+ _supports_cache_class = True # only read by transformers < 4.45
153
+ # `bool(attention_mask.all())` below forces a host sync, and the KV cache is
154
+ # dynamically sized — neither is fullgraph-compilable.
155
+ _can_compile_fullgraph = False
156
+ # Weight tying: lm_head and transformer.wte share parameters. transformers
157
+ # v5 expects {tied key: source key}; older versions iterate it as a list of
158
+ # tied keys, which yields "lm_head.weight" — also correct.
159
+ _tied_weights_keys = {"lm_head.weight": "transformer.wte.weight"}
160
 
161
  def __init__(self, config: PITConfig):
162
  super().__init__(config)
163
  self.transformer = nn.ModuleDict({
164
  "wte": nn.Embedding(config.vocab_size, config.n_embd),
165
+ "h": nn.ModuleList([Block(config, i) for i in range(config.n_layer)]),
166
  })
167
  self.lm_head = nn.Linear(config.n_embd, config.vocab_size, bias=False)
168
+ # Parameter-free, so it adds nothing to the state dict. Shared by every
169
+ # block: cos/sin depend only on position, not on the layer.
170
+ self.rotary = Rotary(config.n_embd // config.n_head)
171
  # Tie weights (re-tied after load_state_dict via tie_weights())
172
  self.transformer["wte"].weight = self.lm_head.weight
173
  self.post_init()
 
186
  def set_output_embeddings(self, value: nn.Linear) -> None:
187
  self.lm_head = value
188
 
189
+ # -- attention masking ---------------------------------------------------
190
+
191
+ @staticmethod
192
+ def _causal_mask(
193
+ attention_mask: torch.Tensor | None,
194
+ q_len: int,
195
+ past_len: int,
196
+ device: torch.device,
197
+ ) -> tuple[torch.Tensor | None, bool]:
198
+ """Return the (attn_mask, is_causal) pair to hand to SDPA.
199
+
200
+ The two `None` cases are the fast paths — SDPA can pick a fused kernel
201
+ only when no explicit mask is materialised:
202
+ • one query token: it may attend to the entire cache, no mask needed;
203
+ • uncached prefill: plain `is_causal=True`.
204
+ Anything else (chunked prefill on top of a cache, or padded batches)
205
+ needs an explicit mask, because `is_causal=True` aligns to the *top
206
+ left* of a non-square score matrix and would mask the cache away.
207
+ """
208
+ if attention_mask is not None:
209
+ if attention_mask.dim() == 4:
210
+ return attention_mask, False # already prepared by the caller
211
+ if bool(attention_mask.all()):
212
+ attention_mask = None # all-ones carries no information
213
+
214
+ if attention_mask is None:
215
+ if q_len == 1:
216
+ return None, False
217
+ if past_len == 0:
218
+ return None, True
219
+
220
+ kv_len = past_len + q_len
221
+ q_pos = torch.arange(past_len, kv_len, device=device)[:, None]
222
+ kv_pos = torch.arange(kv_len, device=device)[None, :]
223
+ mask = (kv_pos <= q_pos)[None, None] # [1, 1, q_len, kv_len]
224
+ if attention_mask is not None:
225
+ mask = mask & attention_mask[:, None, None, :].bool()
226
+ # A left-padded row can end up fully masked, which makes softmax
227
+ # produce NaN. Let those (discarded) pad rows attend freely.
228
+ mask = mask | ~mask.any(-1, keepdim=True)
229
+ return mask, False
230
+
231
  # -- forward -------------------------------------------------------------
232
 
233
  def forward(
234
  self,
235
  input_ids: torch.Tensor | None = None,
236
  attention_mask: torch.Tensor | None = None,
237
+ position_ids: torch.Tensor | None = None,
238
+ past_key_values: Cache | None = None,
239
  labels: torch.Tensor | None = None,
240
+ use_cache: bool | None = None,
241
+ logits_to_keep: int | torch.Tensor = 0,
242
  **kwargs,
243
  ) -> CausalLMOutputWithPast:
244
+ # Training/eval passes `labels` and never reuses the cache, so don't pay
245
+ # for it unless the caller explicitly asks.
246
+ if use_cache is None:
247
+ use_cache = self.config.use_cache and labels is None
248
+ if use_cache and past_key_values is None:
249
+ past_key_values = DynamicCache()
250
+ past_len = past_key_values.get_seq_length() if past_key_values is not None else 0
251
+
252
+ T = input_ids.shape[1]
253
+ if position_ids is None:
254
+ position_ids = torch.arange(past_len, past_len + T, device=input_ids.device)
255
+ position_ids = position_ids.view(-1, T)
256
+
257
  x = self.transformer["wte"](input_ids)
258
+ cos, sin = self.rotary(position_ids)
259
+ attn_mask, is_causal = self._causal_mask(attention_mask, T, past_len, x.device)
260
+
261
  for block in self.transformer["h"]:
262
+ x = block(x, cos, sin, attn_mask, is_causal, past_key_values)
263
  x = F.rms_norm(x, (x.size(-1),))
264
+
265
+ # Only the last position matters while generating; computing the full
266
+ # [B, T, vocab_size] logits during prefill costs hundreds of MB.
267
+ keep = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
268
+ logits = self.lm_head(x[:, keep]).float()
269
 
270
  loss = None
271
  if labels is not None:
 
275
  ignore_index=-100,
276
  )
277
 
278
+ return CausalLMOutputWithPast(
279
+ loss=loss,
280
+ logits=logits,
281
+ past_key_values=past_key_values if use_cache else None,
282
+ )
 
Free AI Image Generator No sign-up. Instant results. Open Now