HimalayaGPT commited on
Commit
654ec22
·
verified ·
1 Parent(s): 2224ed0

Re-export SFT 49164 with HF parity fixes

Browse files
Files changed (2) hide show
  1. README.md +3 -3
  2. modeling_nanochat.py +82 -43
README.md CHANGED
@@ -6,7 +6,7 @@ tags:
6
  - trust-remote-code
7
  ---
8
 
9
- # himalaya-ai/himalayagpt-0.5b-it
10
 
11
  Exported from nanochat checkpoints with custom `transformers` remote code.
12
 
@@ -16,12 +16,12 @@ Exported from nanochat checkpoints with custom `transformers` remote code.
16
  from transformers import AutoTokenizer, AutoModelForCausalLM
17
  import torch
18
 
19
- repo = "himalaya-ai/himalayagpt-0.5b-it"
20
  tok = AutoTokenizer.from_pretrained(repo, trust_remote_code=True)
21
  model = AutoModelForCausalLM.from_pretrained(
22
  repo,
23
  trust_remote_code=True,
24
- torch_dtype=torch.bfloat16 if torch.cuda.is_available() else torch.float32,
25
  device_map="auto" if torch.cuda.is_available() else None,
26
  )
27
 
 
6
  - trust-remote-code
7
  ---
8
 
9
+ # local/nanochat-export
10
 
11
  Exported from nanochat checkpoints with custom `transformers` remote code.
12
 
 
16
  from transformers import AutoTokenizer, AutoModelForCausalLM
17
  import torch
18
 
19
+ repo = "local/nanochat-export"
20
  tok = AutoTokenizer.from_pretrained(repo, trust_remote_code=True)
21
  model = AutoModelForCausalLM.from_pretrained(
22
  repo,
23
  trust_remote_code=True,
24
+ torch_dtype="auto",
25
  device_map="auto" if torch.cuda.is_available() else None,
26
  )
27
 
modeling_nanochat.py CHANGED
@@ -11,6 +11,17 @@ def _norm(x):
11
  return F.rms_norm(x, (x.size(-1),))
12
 
13
 
 
 
 
 
 
 
 
 
 
 
 
14
  class Linear(nn.Linear):
15
  def forward(self, x):
16
  return F.linear(x, self.weight.to(dtype=x.dtype))
@@ -28,6 +39,47 @@ def _apply_rotary(x, cos, sin):
28
  return torch.cat([y1, y2], dim=-1)
29
 
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  class NanochatAttention(nn.Module):
32
  def __init__(self, config, layer_idx):
33
  super().__init__()
@@ -43,15 +95,7 @@ class NanochatAttention(nn.Module):
43
  self.ve_gate_channels = 12
44
  self.ve_gate = Linear(self.ve_gate_channels, self.n_kv_head, bias=False) if _has_ve(layer_idx, config.n_layer) else None
45
 
46
- def _layer_mask(self, t, left_window, device):
47
- if left_window < 0 or left_window >= t:
48
- return None
49
- i = torch.arange(t, device=device)
50
- causal = i[:, None] >= i[None, :]
51
- local = (i[:, None] - i[None, :]) < left_window
52
- return causal & local
53
-
54
- def forward(self, x, ve, cos_sin, left_window):
55
  bsz, seqlen, _ = x.size()
56
  q = self.c_q(x).view(bsz, seqlen, self.n_head, self.head_dim)
57
  k = self.c_k(x).view(bsz, seqlen, self.n_kv_head, self.head_dim)
@@ -68,24 +112,8 @@ class NanochatAttention(nn.Module):
68
  q = 1.2 * _norm(q)
69
  k = 1.2 * _norm(k)
70
 
71
- q = q.transpose(1, 2) # B,H,T,D
72
- k = k.transpose(1, 2)
73
- v = v.transpose(1, 2)
74
- if self.n_kv_head != self.n_head:
75
- rep = self.n_head // self.n_kv_head
76
- k = k.repeat_interleave(rep, dim=1)
77
- v = v.repeat_interleave(rep, dim=1)
78
-
79
- attn_mask = self._layer_mask(seqlen, left_window, x.device)
80
- y = F.scaled_dot_product_attention(
81
- q,
82
- k,
83
- v,
84
- attn_mask=attn_mask,
85
- dropout_p=0.0,
86
- is_causal=(attn_mask is None),
87
- )
88
- y = y.transpose(1, 2).contiguous().view(bsz, seqlen, -1)
89
  return self.c_proj(y)
90
 
91
 
@@ -105,8 +133,8 @@ class NanochatBlock(nn.Module):
105
  self.attn = NanochatAttention(config, layer_idx)
106
  self.mlp = NanochatMLP(config)
107
 
108
- def forward(self, x, ve, cos_sin, left_window):
109
- x = x + self.attn(_norm(x), ve, cos_sin, left_window)
110
  x = x + self.mlp(_norm(x))
111
  return x
112
 
@@ -142,17 +170,22 @@ class NanochatBackbone(nn.Module):
142
  self.register_buffer("sin", torch.empty(1), persistent=False)
143
  self._refresh_rotary()
144
 
145
- def _refresh_rotary(self):
146
- head_dim = self.config.n_embd // self.config.n_head
147
- weight = self.transformer.wte.weight
148
- device = weight.device
149
- dtype = weight.dtype
150
- t = torch.arange(self.rotary_seq_len, dtype=torch.float32, device=device)
151
- ch = torch.arange(0, head_dim, 2, dtype=torch.float32, device=device)
152
- inv = 1.0 / (100000 ** (ch / head_dim))
153
- freqs = torch.outer(t, inv)
154
  cos = freqs.cos()[None, :, None, :].to(dtype=dtype)
155
  sin = freqs.sin()[None, :, None, :].to(dtype=dtype)
 
 
 
 
 
 
 
 
 
156
  self.cos = cos
157
  self.sin = sin
158
 
@@ -170,11 +203,19 @@ class NanochatBackbone(nn.Module):
170
 
171
  def forward(self, input_ids):
172
  bsz, seqlen = input_ids.shape
173
- if self.cos.device != input_ids.device or self.cos.dtype != self.transformer.wte.weight.dtype:
174
- self._refresh_rotary()
 
 
 
 
 
 
 
175
  cos_sin = self.cos[:, :seqlen], self.sin[:, :seqlen]
176
 
177
  x = self.transformer["wte"](input_ids)
 
178
  x = _norm(x)
179
  if seqlen > 1:
180
  gate = self.smear_lambda.to(x.dtype) * torch.sigmoid(self.smear_gate(x[:, 1:, :24]))
@@ -186,7 +227,7 @@ class NanochatBackbone(nn.Module):
186
  for i, block in enumerate(self.transformer["h"]):
187
  x = self.resid_lambdas[i] * x + self.x0_lambdas[i] * x0
188
  ve = self.value_embeds[str(i)](input_ids).to(x.dtype) if str(i) in self.value_embeds else None
189
- x = block(x, ve, cos_sin, self.window_sizes[i][0])
190
  if i == backout_layer:
191
  x_backout = x
192
 
@@ -213,8 +254,6 @@ class NanochatForCausalLM(PreTrainedModel):
213
  def all_tied_weights_keys(self):
214
  # Compatibility shim for some transformers/accelerate versions that
215
  # access `model.all_tied_weights_keys` during device_map inference.
216
- # Newer codepaths call `.keys()` on this object, so expose a dict-like
217
- # mapping regardless of the underlying list/tuple representation.
218
  return {k: None for k in getattr(self, "_tied_weights_keys", [])}
219
 
220
  def get_input_embeddings(self):
 
11
  return F.rms_norm(x, (x.size(-1),))
12
 
13
 
14
+ def _detect_compute_dtype(device):
15
+ if device.type == "cuda":
16
+ idx = device.index
17
+ if idx is None:
18
+ idx = torch.cuda.current_device()
19
+ major, minor = torch.cuda.get_device_capability(idx)
20
+ if (major, minor) >= (8, 0):
21
+ return torch.bfloat16
22
+ return torch.float32
23
+
24
+
25
  class Linear(nn.Linear):
26
  def forward(self, x):
27
  return F.linear(x, self.weight.to(dtype=x.dtype))
 
39
  return torch.cat([y1, y2], dim=-1)
40
 
41
 
42
+ def _sdpa_attention(q, k, v, window_size, enable_gqa):
43
+ # q/k/v are (B, H, T, D)
44
+ t_q = q.size(2)
45
+ t_k = k.size(2)
46
+ left_window = window_size[0]
47
+
48
+ # Full causal attention when the window covers full context.
49
+ if (left_window < 0 or left_window >= t_q) and t_q == t_k:
50
+ return F.scaled_dot_product_attention(q, k, v, is_causal=True, enable_gqa=enable_gqa)
51
+
52
+ # Single-token decode path.
53
+ if t_q == 1:
54
+ if left_window >= 0 and left_window < t_k:
55
+ start = max(0, t_k - (left_window + 1))
56
+ k = k[:, :, start:, :]
57
+ v = v[:, :, start:, :]
58
+ return F.scaled_dot_product_attention(q, k, v, is_causal=False, enable_gqa=enable_gqa)
59
+
60
+ # Build explicit causal (+ optional sliding-window) mask.
61
+ device = q.device
62
+ row_idx = (t_k - t_q) + torch.arange(t_q, device=device).unsqueeze(1)
63
+ col_idx = torch.arange(t_k, device=device).unsqueeze(0)
64
+ mask = col_idx <= row_idx
65
+ if left_window >= 0 and left_window < t_k:
66
+ mask = mask & ((row_idx - col_idx) <= left_window)
67
+
68
+ return F.scaled_dot_product_attention(q, k, v, attn_mask=mask, enable_gqa=enable_gqa)
69
+
70
+
71
+ def _flash_attn_func(q, k, v, causal=False, window_size=(-1, -1)):
72
+ if not causal:
73
+ raise NotImplementedError("Nanochat HF export currently supports only causal attention")
74
+ # SDPA fallback mirroring nanochat.flash_attention semantics.
75
+ q = q.transpose(1, 2)
76
+ k = k.transpose(1, 2)
77
+ v = v.transpose(1, 2)
78
+ enable_gqa = q.size(1) != k.size(1)
79
+ y = _sdpa_attention(q, k, v, window_size=window_size, enable_gqa=enable_gqa)
80
+ return y.transpose(1, 2)
81
+
82
+
83
  class NanochatAttention(nn.Module):
84
  def __init__(self, config, layer_idx):
85
  super().__init__()
 
95
  self.ve_gate_channels = 12
96
  self.ve_gate = Linear(self.ve_gate_channels, self.n_kv_head, bias=False) if _has_ve(layer_idx, config.n_layer) else None
97
 
98
+ def forward(self, x, ve, cos_sin, window_size):
 
 
 
 
 
 
 
 
99
  bsz, seqlen, _ = x.size()
100
  q = self.c_q(x).view(bsz, seqlen, self.n_head, self.head_dim)
101
  k = self.c_k(x).view(bsz, seqlen, self.n_kv_head, self.head_dim)
 
112
  q = 1.2 * _norm(q)
113
  k = 1.2 * _norm(k)
114
 
115
+ y = _flash_attn_func(q, k, v, causal=True, window_size=window_size)
116
+ y = y.contiguous().view(bsz, seqlen, -1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  return self.c_proj(y)
118
 
119
 
 
133
  self.attn = NanochatAttention(config, layer_idx)
134
  self.mlp = NanochatMLP(config)
135
 
136
+ def forward(self, x, ve, cos_sin, window_size):
137
+ x = x + self.attn(_norm(x), ve, cos_sin, window_size)
138
  x = x + self.mlp(_norm(x))
139
  return x
140
 
 
170
  self.register_buffer("sin", torch.empty(1), persistent=False)
171
  self._refresh_rotary()
172
 
173
+ def _precompute_rotary_embeddings(self, seq_len, head_dim, device, dtype):
174
+ channel_range = torch.arange(0, head_dim, 2, dtype=torch.float32, device=device)
175
+ inv_freq = 1.0 / (100000 ** (channel_range / head_dim))
176
+ t = torch.arange(seq_len, dtype=torch.float32, device=device)
177
+ freqs = torch.outer(t, inv_freq)
 
 
 
 
178
  cos = freqs.cos()[None, :, None, :].to(dtype=dtype)
179
  sin = freqs.sin()[None, :, None, :].to(dtype=dtype)
180
+ return cos, sin
181
+
182
+ def _refresh_rotary(self, device=None, dtype=None):
183
+ head_dim = self.config.n_embd // self.config.n_head
184
+ if device is None:
185
+ device = self.transformer.wte.weight.device
186
+ if dtype is None:
187
+ dtype = self.transformer.wte.weight.dtype
188
+ cos, sin = self._precompute_rotary_embeddings(self.rotary_seq_len, head_dim, device=device, dtype=dtype)
189
  self.cos = cos
190
  self.sin = sin
191
 
 
203
 
204
  def forward(self, input_ids):
205
  bsz, seqlen = input_ids.shape
206
+ compute_dtype = _detect_compute_dtype(input_ids.device)
207
+ if self.cos.device != input_ids.device or self.cos.dtype != compute_dtype:
208
+ self._refresh_rotary(device=input_ids.device, dtype=compute_dtype)
209
+
210
+ if seqlen > self.cos.size(1):
211
+ raise ValueError(
212
+ f"Sequence length {seqlen} exceeds rotary cache length {self.cos.size(1)}. "
213
+ "Re-export with larger sequence_len if needed."
214
+ )
215
  cos_sin = self.cos[:, :seqlen], self.sin[:, :seqlen]
216
 
217
  x = self.transformer["wte"](input_ids)
218
+ x = x.to(compute_dtype)
219
  x = _norm(x)
220
  if seqlen > 1:
221
  gate = self.smear_lambda.to(x.dtype) * torch.sigmoid(self.smear_gate(x[:, 1:, :24]))
 
227
  for i, block in enumerate(self.transformer["h"]):
228
  x = self.resid_lambdas[i] * x + self.x0_lambdas[i] * x0
229
  ve = self.value_embeds[str(i)](input_ids).to(x.dtype) if str(i) in self.value_embeds else None
230
+ x = block(x, ve, cos_sin, self.window_sizes[i])
231
  if i == backout_layer:
232
  x_backout = x
233
 
 
254
  def all_tied_weights_keys(self):
255
  # Compatibility shim for some transformers/accelerate versions that
256
  # access `model.all_tied_weights_keys` during device_map inference.
 
 
257
  return {k: None for k in getattr(self, "_tied_weights_keys", [])}
258
 
259
  def get_input_embeddings(self):