w-ahmad commited on
Commit
b4f1cee
·
verified ·
1 Parent(s): 3371ddf

Auto upload zain 2026-08-12T20:48:11.035594

Browse files
zain/Activation/exp.py CHANGED
@@ -146,11 +146,8 @@ class TinyLlamaMLP(nn.Module):
146
  self.down_proj = nn.Linear(effective_intermediate, self.hidden_size, bias=False)
147
 
148
  # Activation function (for standard variants)
149
- # For gated variants (situglu, waleed) we handle them in forward, but we still need a placeholder.
150
- # For waleed10/silu-waleed10 we need linear or silu.
151
  if self.mlp_type == "glu":
152
  if self.activation_name in ("situglu", "waleed", "situglu_low", "waleedglu_low"):
153
- # These will be handled in forward; no act_fn needed.
154
  self.act_fn = None
155
  elif self.activation_name == "waleed10":
156
  self.act_fn = GLUActivationRegistry.get("linear")
@@ -175,7 +172,7 @@ class TinyLlamaMLP(nn.Module):
175
  if self.activation_name in ("situglu_low", "waleedglu_low"):
176
  self.beta1 = 2.5
177
  self.beta2 = 4.0
178
- else: # original situglu or waleed
179
  self.beta1 = 4.0
180
  self.beta2 = 25.0
181
 
@@ -183,7 +180,7 @@ class TinyLlamaMLP(nn.Module):
183
  self.is_situglu = self.activation_name in ("situglu", "situglu_low")
184
  self.is_waleed = self.activation_name in ("waleed", "waleedglu_low")
185
  self.is_waleed10 = self.activation_name in ("waleed10", "silu-waleed10")
186
- self.has_sigmoid_gate = self.activation_name.startswith("situglu") # sigmoid in gate
187
 
188
  def forward(self, x: torch.Tensor) -> torch.Tensor:
189
  if self.mlp_type == "glu":
@@ -191,7 +188,6 @@ class TinyLlamaMLP(nn.Module):
191
  up = self.up_proj(x)
192
 
193
  if self.is_situglu or self.is_waleed:
194
- # Gated variants with tanh scaling
195
  if self.has_sigmoid_gate:
196
  gate = self.beta1 * torch.tanh(gate / self.beta1) * torch.sigmoid(gate)
197
  else:
@@ -199,7 +195,6 @@ class TinyLlamaMLP(nn.Module):
199
  up = self.beta2 * torch.tanh(up / self.beta2)
200
  hidden = gate * up
201
  else:
202
- # Standard GLU (activation applied to gate)
203
  hidden = self.act_fn(gate) * up
204
 
205
  out = self.down_proj(hidden)
@@ -208,7 +203,6 @@ class TinyLlamaMLP(nn.Module):
208
  hidden = self.act_fn(self.up_proj(x))
209
  out = self.down_proj(hidden)
210
 
211
- # Post‑clip for waleed10 variants
212
  if self.is_waleed10:
213
  out = self.waleed_beta * torch.tanh(out / self.waleed_beta)
214
 
@@ -225,11 +219,7 @@ class TinyLlamaDecoderLayer(nn.Module):
225
  self.post_attention_layernorm = LlamaRMSNorm(
226
  config.hidden_size, eps=config.rms_norm_eps
227
  )
228
- # ---------------------------------------------------------------------
229
- # NEW: zero-parameter Identity gateways for residual-stream logging.
230
- # These expose the residual tensor as named modules so the hook
231
- # registry can capture them with pattern ".*residual.*".
232
- # ---------------------------------------------------------------------
233
  self.residual_pre_attn = nn.Identity()
234
  self.residual_post_attn = nn.Identity()
235
  self.residual_post_mlp = nn.Identity()
@@ -242,7 +232,7 @@ class TinyLlamaDecoderLayer(nn.Module):
242
  position_embeddings: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
243
  **kwargs,
244
  ):
245
- # --- Attention sub-layer ---
246
  residual = hidden_states
247
  hidden_states = self.residual_pre_attn(hidden_states)
248
  hidden_states = self.input_layernorm(hidden_states)
@@ -255,7 +245,7 @@ class TinyLlamaDecoderLayer(nn.Module):
255
  hidden_states = residual + attn_out
256
  hidden_states = self.residual_post_attn(hidden_states)
257
 
258
- # --- MLP sub-layer ---
259
  residual = hidden_states
260
  hidden_states = self.post_attention_layernorm(hidden_states)
261
  hidden_states = self.mlp(hidden_states)
@@ -265,7 +255,7 @@ class TinyLlamaDecoderLayer(nn.Module):
265
 
266
 
267
  # ----------------------------------------------------------------------------
268
- # ATTENTION MASK – float mask with 0.0 / -inf (works with all backends)
269
  # ----------------------------------------------------------------------------
270
  def _build_causal_mask(
271
  attention_mask: Optional[torch.Tensor],
@@ -273,17 +263,10 @@ def _build_causal_mask(
273
  dtype: torch.dtype,
274
  device: torch.device,
275
  ) -> torch.Tensor:
276
- """
277
- Build a 4D float attention mask for scaled_dot_product_attention.
278
- - 0.0 where attention is allowed
279
- - -inf where it is masked (causal future + padding)
280
- """
281
  min_value = torch.finfo(dtype).min
282
-
283
- # Causal mask: upper triangle (future) = -inf
284
  causal = torch.full((seq_len, seq_len), fill_value=min_value, dtype=dtype, device=device)
285
  causal = torch.triu(causal, diagonal=1)
286
- causal = causal[None, None, :, :] # (1, 1, seq_len, seq_len)
287
 
288
  if attention_mask is None:
289
  batch_size = 1
@@ -291,15 +274,11 @@ def _build_causal_mask(
291
 
292
  batch_size = attention_mask.shape[0]
293
  causal = causal.expand(batch_size, 1, seq_len, seq_len).clone()
294
-
295
- # Padding: where attention_mask == 0, set to -inf
296
- padding = attention_mask[:, None, None, :].to(device) == 0 # (batch, 1, 1, seq_len)
297
  causal = causal.masked_fill(padding, min_value)
298
-
299
  return causal
300
 
301
 
302
- # Global flag to print mask message only once
303
  _MASK_PRINTED = False
304
 
305
 
@@ -330,10 +309,7 @@ class TinyLlamaModel(LlamaPreTrainedModel):
330
  **kwargs,
331
  ):
332
  global _MASK_PRINTED
333
-
334
- return_dict = (
335
- return_dict if return_dict is not None else self.config.use_return_dict
336
- )
337
  if inputs_embeds is None:
338
  inputs_embeds = self.embed_tokens(input_ids)
339
 
@@ -346,7 +322,6 @@ class TinyLlamaModel(LlamaPreTrainedModel):
346
  hidden_states = inputs_embeds
347
  position_embeddings = self.rotary_emb(hidden_states, position_ids)
348
 
349
- # Build float causal + padding mask (print only once)
350
  seq_len = hidden_states.shape[1]
351
  causal_mask = _build_causal_mask(
352
  attention_mask, seq_len, hidden_states.dtype, hidden_states.device
@@ -402,9 +377,7 @@ class TinyLlamaForCausalLM(LlamaPreTrainedModel):
402
  return_dict: Optional[bool] = None,
403
  **kwargs,
404
  ):
405
- return_dict = (
406
- return_dict if return_dict is not None else self.config.use_return_dict
407
- )
408
  outputs = self.model(
409
  input_ids=input_ids,
410
  attention_mask=attention_mask,
@@ -459,7 +432,7 @@ class TinyLlamaForCausalLM(LlamaPreTrainedModel):
459
  # =============================================================================
460
 
461
  class StatsEngine:
462
- """Compute the unified signature for any tensor (now with range + percentiles)."""
463
 
464
  @staticmethod
465
  def compute(
@@ -474,66 +447,30 @@ class StatsEngine:
474
  else float("inf")
475
  )
476
 
477
- # --- Base statistics ---
478
- norm = tensor.norm(2).item()
479
- mean = tensor.mean().item()
480
- std = tensor.std().item()
481
- max_abs = abs_t.max().item()
482
-
483
- # --- Exact min / max / range ---
484
  t_min = tensor.min().item()
485
  t_max = tensor.max().item()
486
- t_range = t_max - t_min
487
-
488
- # --- Exact percentiles (float32 CPU for dtype safety) ---
489
- p01 = p25 = p50 = p75 = p90 = p95 = p99 = 0.0
490
- try:
491
- flat_f32 = tensor.detach().reshape(-1).to(torch.float32).cpu()
492
- if flat_f32.numel() > 0:
493
- q_vals = torch.quantile(
494
- flat_f32,
495
- torch.tensor(
496
- [0.01, 0.25, 0.5, 0.75, 0.90, 0.95, 0.99],
497
- dtype=torch.float32,
498
- ),
499
- )
500
- p01, p25, p50, p75, p90, p95, p99 = (v.item() for v in q_vals)
501
- except Exception:
502
- pass # If quantile fails (very unlikely), leave as 0.0
503
 
504
  return {
505
- "norm": norm,
506
- "mean": mean,
507
- "std": std,
508
- "max_abs": max_abs,
509
  "frac_near_dtype_limit": (
510
  (abs_t > dtype_limit).float().mean().item()
511
  if not math.isinf(dtype_limit)
512
  else 0.0
513
  ),
514
  "frac_near_user_limit": (abs_t > user_limit).float().mean().item(),
515
- # --- NEW: distributional tail metrics (exact per-tensor) ---
516
  "min": t_min,
517
  "max": t_max,
518
- "range": t_range,
519
- "p01": p01,
520
- "p25": p25,
521
- "p50": p50,
522
- "p75": p75,
523
- "p90": p90,
524
- "p95": p95,
525
- "p99": p99,
526
  }
527
 
528
 
529
  class StepAccumulator:
530
- """
531
- Stores per-tensor entries, then aggregates to layer-scope or global-scope
532
- using exact population formulas (no tensor retention).
533
- """
534
 
535
  def __init__(self):
536
- # name -> {numel, norm, mean, std, max_abs, frac_near_dtype_limit, frac_near_user_limit, min, max, range, p01..p99}
537
  self.tensors: Dict[str, Dict[str, float]] = {}
538
 
539
  def add(self, name: str, numel: int, stats: Dict[str, float]):
@@ -563,18 +500,8 @@ class StepAccumulator:
563
  a["frac_near_user_limit"] * a["numel"] + b["frac_near_user_limit"] * b["numel"]
564
  ) / total_n
565
 
566
- # Exact min/max across micro-batches (gradient accumulation)
567
  t_min = min(a.get("min", float("inf")), b.get("min", float("inf")))
568
  t_max = max(a.get("max", float("-inf")), b.get("max", float("-inf")))
569
- t_range = t_max - t_min
570
-
571
- # Percentiles: weighted average across micro-batches (best effort)
572
- def _wpct(key: str) -> float:
573
- av = a.get(key, 0.0)
574
- bv = b.get(key, 0.0)
575
- if av == 0.0 and bv == 0.0:
576
- return 0.0
577
- return (av * a["numel"] + bv * b["numel"]) / total_n
578
 
579
  return {
580
  "numel": total_n,
@@ -586,14 +513,7 @@ class StepAccumulator:
586
  "frac_near_user_limit": frac_user,
587
  "min": t_min,
588
  "max": t_max,
589
- "range": t_range,
590
- "p01": _wpct("p01"),
591
- "p25": _wpct("p25"),
592
- "p50": _wpct("p50"),
593
- "p75": _wpct("p75"),
594
- "p90": _wpct("p90"),
595
- "p95": _wpct("p95"),
596
- "p99": _wpct("p99"),
597
  }
598
 
599
  def clear(self):
@@ -605,19 +525,14 @@ class StepAccumulator:
605
  numels = [e["numel"] for e in entries.values()]
606
  total_n = sum(numels)
607
 
608
- # L2 norm
609
  norm = math.sqrt(sum(e["norm"] ** 2 for e in entries.values()))
610
- # Max abs
611
  max_abs = max(e["max_abs"] for e in entries.values())
612
- # Weighted mean
613
  mean = sum(e["mean"] * e["numel"] for e in entries.values()) / total_n
614
- # Pooled std: sqrt( E[σ² + μ²] - μ_global² )
615
  ex2 = (
616
  sum(e["numel"] * (e["std"] ** 2 + e["mean"] ** 2) for e in entries.values())
617
  / total_n
618
  )
619
  std = math.sqrt(max(0.0, ex2 - mean ** 2))
620
- # Weighted fractions
621
  frac_dtype = (
622
  sum(e["frac_near_dtype_limit"] * e["numel"] for e in entries.values())
623
  / total_n
@@ -627,10 +542,8 @@ class StepAccumulator:
627
  / total_n
628
  )
629
 
630
- # Exact bounds across all tensors in this scope
631
  t_min = min(e.get("min", float("inf")) for e in entries.values())
632
  t_max = max(e.get("max", float("-inf")) for e in entries.values())
633
- t_range = t_max - t_min
634
 
635
  return {
636
  "norm": norm,
@@ -641,9 +554,7 @@ class StepAccumulator:
641
  "frac_near_user_limit": frac_user,
642
  "min": t_min,
643
  "max": t_max,
644
- "range": t_range,
645
- # NOTE: percentiles intentionally omitted from aggregated scopes.
646
- # They are only meaningful at per-tensor scope.
647
  }
648
 
649
  def get_global_stats(self) -> Dict[str, float]:
@@ -696,10 +607,6 @@ class HookRegistry:
696
  def hook(module, inp, out):
697
  if not self.active:
698
  return
699
-
700
- # Modules can return a tensor, a tuple (take first item), or a
701
- # dict (e.g. TinyLlamaModel returns {"last_hidden_state": ...}).
702
- # Pull out the first real tensor we find; skip cleanly if none.
703
  if isinstance(out, dict):
704
  out_dict = out
705
  out = out_dict.get("last_hidden_state")
@@ -739,10 +646,7 @@ class HookRegistry:
739
 
740
 
741
  class StabilityMonitorCallback(TrainerCallback):
742
- """
743
- Full stability instrumentation: grad / param / act statistics
744
- at global, per-layer, and per-tensor scope.
745
- """
746
 
747
  def __init__(
748
  self,
@@ -756,7 +660,6 @@ class StabilityMonitorCallback(TrainerCallback):
756
  ):
757
  self.model = model
758
  self.monitor_every_n_steps = monitor_every_n_steps
759
- # NEW: default patterns now include residual gateways
760
  self.module_patterns = module_patterns or [".*mlp.*", ".*self_attn.*", ".*residual.*"]
761
  self.user_limits = user_limits or {"grad": 1.0, "param": 100.0, "act": 50.0}
762
  self.dtype_ratio = dtype_proximity_ratio
@@ -792,8 +695,6 @@ class StabilityMonitorCallback(TrainerCallback):
792
  def on_step_end(self, args, state, control, **kwargs):
793
  if not self.hooks.active:
794
  return
795
-
796
- # Parameter stats (post-optimizer step)
797
  for name, param in self.model.named_parameters():
798
  stats = StatsEngine.compute(
799
  param.data, self.user_limits["param"], self.dtype_ratio
@@ -805,7 +706,6 @@ class StabilityMonitorCallback(TrainerCallback):
805
 
806
  @staticmethod
807
  def _kind_of(name: str) -> str:
808
- """Classify a tensor key by its source: activation, gradient, or parameter."""
809
  if name.startswith("act."):
810
  return "act"
811
  if name.startswith("grad."):
@@ -825,18 +725,15 @@ class StabilityMonitorCallback(TrainerCallback):
825
  def _build_metrics(self, scope: str = "train") -> Dict[str, float]:
826
  metrics: Dict[str, float] = {}
827
 
828
- # --- Global (split by kind: act / grad / param — never pooled together) ---
829
  if self.log_scope.get("global", True):
830
  by_kind: Dict[str, Dict[str, Dict[str, float]]] = {}
831
  for k, v in self.accumulator.tensors.items():
832
  by_kind.setdefault(self._kind_of(k), {})[k] = v
833
-
834
  for kind, entries in by_kind.items():
835
  stats = self.accumulator._aggregate(entries)
836
  for kk, vv in stats.items():
837
  metrics[f"{scope}/global/{kind}/{kk}"] = vv
838
 
839
- # --- Per-layer (group by model.layers.{i}, split by kind) ---
840
  if self.log_scope.get("per_layer", True):
841
  layer_prefixes = set()
842
  for name in self.accumulator.tensors:
@@ -846,14 +743,12 @@ class StabilityMonitorCallback(TrainerCallback):
846
  if p == "layers" and i + 1 < len(parts):
847
  prefix = ".".join(parts[: i + 2])
848
  layer_prefixes.add(prefix)
849
-
850
  for prefix in layer_prefixes:
851
  by_kind: Dict[str, Dict[str, Dict[str, float]]] = {}
852
  for k, v in self.accumulator.tensors.items():
853
  clean = self._strip_kind(k)
854
  if clean.startswith(prefix + ".") or clean == prefix:
855
  by_kind.setdefault(self._kind_of(k), {})[k] = v
856
-
857
  safe = prefix.replace(".", "_")
858
  for kind, entries in by_kind.items():
859
  if not entries:
@@ -862,7 +757,6 @@ class StabilityMonitorCallback(TrainerCallback):
862
  for kk, vv in stats.items():
863
  metrics[f"{scope}/layer_{safe}/{kind}/{kk}"] = vv
864
 
865
- # --- Per-tensor (exact percentiles live here) ---
866
  if self.log_scope.get("per_tensor", False):
867
  for name, stats in self.accumulator.tensors.items():
868
  safe = name.replace(".", "_")
@@ -879,19 +773,9 @@ class StabilityMonitorCallback(TrainerCallback):
879
  self.pending_metrics = None
880
 
881
  def on_prediction_step(self, args, state, control, **kwargs):
882
- """Fires once per eval/predict batch. Trainer.evaluate() calls this
883
- for every batch in the eval loop, then calls self.log(output.metrics)
884
- (which dispatches on_log to every callback, including the wandb/
885
- tensorboard reporting callbacks) BEFORE on_evaluate() runs. So to get
886
- eval-time stats into that same on_log dispatch, we have to build
887
- pending_metrics here, not in on_evaluate — by the time on_evaluate
888
- fires, self.log() has already happened and it's too late.
889
- """
890
  if not self.monitor_during_eval:
891
  return
892
  if not self.hooks.active:
893
- # First batch of this eval pass: start a fresh accumulation and
894
- # snapshot parameter stats once (they don't change during eval).
895
  self.accumulator.clear()
896
  self.hooks.set_active(True)
897
  for name, param in self.model.named_parameters():
@@ -931,16 +815,12 @@ class TimeTrackerCallback(TrainerCallback):
931
  def on_log(self, args, state, control, logs=None, **kwargs):
932
  if logs is None:
933
  return
934
-
935
  logs["train/total_time_seconds"] = self.total_train_time
936
-
937
  if self.step_times:
938
  recent = self.step_times[-100:]
939
  logs["train/time_per_step_avg"] = sum(recent) / len(recent)
940
-
941
  if self.epoch_start is not None:
942
  logs["train/epoch_time_elapsed"] = time.perf_counter() - self.epoch_start
943
-
944
  if state.max_steps and state.global_step > 0:
945
  avg = self.total_train_time / state.global_step
946
  remaining = (state.max_steps - state.global_step) * avg
@@ -972,16 +852,13 @@ class MetricsLoggerCallback(TrainerCallback):
972
 
973
 
974
  # =============================================================================
975
- # NEW: CONTAMINATION CALLBACK
976
  # =============================================================================
977
 
978
  class ContaminationCallback(TrainerCallback):
979
  """
980
- Intentionally corrupt input_ids and labels for a window of steps,
981
- causing a controlled loss spike. Supports two modes:
982
- - "shift": every token ID is incremented by 1 (mod vocab_size)
983
- - "random": token IDs are replaced with uniform random IDs
984
- Fraction controls what proportion of tokens (per sequence) are corrupted.
985
  """
986
  def __init__(
987
  self,
@@ -1000,12 +877,9 @@ class ContaminationCallback(TrainerCallback):
1000
  self.mode = mode
1001
  self.fraction = fraction
1002
  self.seed = seed
1003
-
1004
- # Create a dedicated generator for reproducibility
1005
  self.generator = torch.Generator()
1006
  if seed is not None:
1007
  self.generator.manual_seed(seed)
1008
-
1009
  self._active = False
1010
 
1011
  def _should_corrupt(self, state) -> bool:
@@ -1017,58 +891,37 @@ class ContaminationCallback(TrainerCallback):
1017
  def on_step_begin(self, args, state, control, **kwargs):
1018
  if not self._should_corrupt(state):
1019
  return
1020
-
1021
- # The batch is passed in kwargs under key "inputs" (Trainer convention).
1022
- # We also need to grab the model's device to generate tensors on the same device.
1023
  batch = kwargs.get("inputs")
1024
- if batch is None:
1025
  return
1026
-
1027
- # We need the device – get it from the model or from the input tensors.
1028
- # We can access the model through the trainer? Not directly here.
1029
- # But we can infer device from batch tensors.
1030
- if not isinstance(batch, dict):
1031
- return
1032
-
1033
  input_ids = batch.get("input_ids")
1034
  labels = batch.get("labels")
1035
  attention_mask = batch.get("attention_mask")
1036
-
1037
  if input_ids is None or labels is None:
1038
  return
1039
 
1040
  device = input_ids.device
1041
  batch_size, seq_len = input_ids.shape
1042
 
1043
- # Build mask of positions to corrupt (based on fraction)
1044
- # We generate a mask of shape (batch_size, seq_len) with True for positions to corrupt.
1045
  if self.fraction >= 1.0:
1046
  corrupt_mask = torch.ones((batch_size, seq_len), dtype=torch.bool, device=device)
1047
  elif self.fraction <= 0.0:
1048
- return # nothing to corrupt
1049
  else:
1050
- # Generate random floats and threshold
1051
  rand = torch.rand((batch_size, seq_len), generator=self.generator, device=device)
1052
  corrupt_mask = rand < self.fraction
1053
 
1054
- # If attention_mask is present, exclude padding positions from corruption
1055
  if attention_mask is not None:
1056
- # attention_mask is 1 for real tokens, 0 for padding
1057
  padding_mask = attention_mask == 0
1058
  corrupt_mask = corrupt_mask & (~padding_mask)
1059
 
1060
  if not corrupt_mask.any():
1061
  return
1062
 
1063
- # Apply corruption
1064
  if self.mode == "shift":
1065
- # Increment by 1 modulo vocab_size
1066
- # We need to handle vocab_size properly (IDs go 0..vocab_size-1)
1067
  input_ids_corrupt = (input_ids + 1) % self.vocab_size
1068
  labels_corrupt = (labels + 1) % self.vocab_size
1069
  elif self.mode == "random":
1070
- # Replace with uniform random IDs
1071
- # Create random tensor of same shape
1072
  random_ids = torch.randint(
1073
  0, self.vocab_size, input_ids.shape,
1074
  generator=self.generator, device=device
@@ -1078,21 +931,16 @@ class ContaminationCallback(TrainerCallback):
1078
  else:
1079
  raise ValueError(f"Unknown contamination mode: {self.mode}")
1080
 
1081
- # Apply only where mask is True
1082
  input_ids.masked_scatter_(corrupt_mask, input_ids_corrupt[corrupt_mask])
1083
  labels.masked_scatter_(corrupt_mask, labels_corrupt[corrupt_mask])
1084
-
1085
- # Reassign into batch (mutated in-place)
1086
  batch["input_ids"] = input_ids
1087
  batch["labels"] = labels
1088
 
1089
- # Log a one‑time message when contamination starts
1090
  if not self._active:
1091
  print(f"[Contamination] Started at step {state.global_step} for {self.duration_steps} steps (mode={self.mode})")
1092
  self._active = True
1093
 
1094
  def on_step_end(self, args, state, control, **kwargs):
1095
- # If we just passed the end of the window, de‑activate
1096
  if self._active and state.global_step >= self.start_step + self.duration_steps:
1097
  print(f"[Contamination] Ended at step {state.global_step}")
1098
  self._active = False
@@ -1109,9 +957,8 @@ def build_dataset(
1109
  dataset_name: str = "roneneldan/TinyStories",
1110
  max_samples: Optional[int] = None,
1111
  ):
1112
- """Concatenate and chunk TinyStories for causal LM. Fast path with multiprocessing."""
1113
  ds = load_dataset(dataset_name, split=split)
1114
-
1115
  if max_samples is not None and split == "train":
1116
  ds = ds.select(range(min(max_samples, len(ds))))
1117
  print(f"[Dataset] Using first {len(ds)} samples for training (max_samples={max_samples})")
@@ -1197,11 +1044,8 @@ def create_trainer(
1197
  remove_unused_columns=False,
1198
  )
1199
 
1200
- callbacks = [
1201
- TimeTrackerCallback(),
1202
- ]
1203
 
1204
- # --- Contamination callback (optional) ---
1205
  cc = config.get("contamination", {})
1206
  if cc.get("enabled", False):
1207
  vocab_size = model.config.vocab_size
@@ -1223,14 +1067,9 @@ def create_trainer(
1223
  model=model,
1224
  monitor_every_n_steps=mc.get("monitor_every_n_steps", 10),
1225
  module_patterns=mc.get("module_patterns", [".*mlp.*", ".*self_attn.*", ".*residual.*"]),
1226
- user_limits=mc.get(
1227
- "user_limits", {"grad": 1.0, "param": 100.0, "act": 50.0}
1228
- ),
1229
  dtype_proximity_ratio=mc.get("dtype_proximity_ratio", 0.9),
1230
- log_scope=mc.get(
1231
- "log_scope",
1232
- {"global": True, "per_layer": True, "per_tensor": False},
1233
- ),
1234
  monitor_during_eval=mc.get("monitor_during_eval", False),
1235
  )
1236
  )
@@ -1248,10 +1087,8 @@ def create_trainer(
1248
  callbacks=callbacks,
1249
  )
1250
 
1251
- # Move reporting integrations to the end
1252
  try:
1253
  from transformers.integrations import get_reporting_integration_callbacks
1254
-
1255
  reporting_types = tuple(get_reporting_integration_callbacks(args.report_to))
1256
  except Exception:
1257
  reporting_types = ()
 
146
  self.down_proj = nn.Linear(effective_intermediate, self.hidden_size, bias=False)
147
 
148
  # Activation function (for standard variants)
 
 
149
  if self.mlp_type == "glu":
150
  if self.activation_name in ("situglu", "waleed", "situglu_low", "waleedglu_low"):
 
151
  self.act_fn = None
152
  elif self.activation_name == "waleed10":
153
  self.act_fn = GLUActivationRegistry.get("linear")
 
172
  if self.activation_name in ("situglu_low", "waleedglu_low"):
173
  self.beta1 = 2.5
174
  self.beta2 = 4.0
175
+ else:
176
  self.beta1 = 4.0
177
  self.beta2 = 25.0
178
 
 
180
  self.is_situglu = self.activation_name in ("situglu", "situglu_low")
181
  self.is_waleed = self.activation_name in ("waleed", "waleedglu_low")
182
  self.is_waleed10 = self.activation_name in ("waleed10", "silu-waleed10")
183
+ self.has_sigmoid_gate = self.activation_name.startswith("situglu")
184
 
185
  def forward(self, x: torch.Tensor) -> torch.Tensor:
186
  if self.mlp_type == "glu":
 
188
  up = self.up_proj(x)
189
 
190
  if self.is_situglu or self.is_waleed:
 
191
  if self.has_sigmoid_gate:
192
  gate = self.beta1 * torch.tanh(gate / self.beta1) * torch.sigmoid(gate)
193
  else:
 
195
  up = self.beta2 * torch.tanh(up / self.beta2)
196
  hidden = gate * up
197
  else:
 
198
  hidden = self.act_fn(gate) * up
199
 
200
  out = self.down_proj(hidden)
 
203
  hidden = self.act_fn(self.up_proj(x))
204
  out = self.down_proj(hidden)
205
 
 
206
  if self.is_waleed10:
207
  out = self.waleed_beta * torch.tanh(out / self.waleed_beta)
208
 
 
219
  self.post_attention_layernorm = LlamaRMSNorm(
220
  config.hidden_size, eps=config.rms_norm_eps
221
  )
222
+ # Residual stream gateways (zero params, hookable)
 
 
 
 
223
  self.residual_pre_attn = nn.Identity()
224
  self.residual_post_attn = nn.Identity()
225
  self.residual_post_mlp = nn.Identity()
 
232
  position_embeddings: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
233
  **kwargs,
234
  ):
235
+ # Attention sub-layer
236
  residual = hidden_states
237
  hidden_states = self.residual_pre_attn(hidden_states)
238
  hidden_states = self.input_layernorm(hidden_states)
 
245
  hidden_states = residual + attn_out
246
  hidden_states = self.residual_post_attn(hidden_states)
247
 
248
+ # MLP sub-layer
249
  residual = hidden_states
250
  hidden_states = self.post_attention_layernorm(hidden_states)
251
  hidden_states = self.mlp(hidden_states)
 
255
 
256
 
257
  # ----------------------------------------------------------------------------
258
+ # ATTENTION MASK
259
  # ----------------------------------------------------------------------------
260
  def _build_causal_mask(
261
  attention_mask: Optional[torch.Tensor],
 
263
  dtype: torch.dtype,
264
  device: torch.device,
265
  ) -> torch.Tensor:
 
 
 
 
 
266
  min_value = torch.finfo(dtype).min
 
 
267
  causal = torch.full((seq_len, seq_len), fill_value=min_value, dtype=dtype, device=device)
268
  causal = torch.triu(causal, diagonal=1)
269
+ causal = causal[None, None, :, :]
270
 
271
  if attention_mask is None:
272
  batch_size = 1
 
274
 
275
  batch_size = attention_mask.shape[0]
276
  causal = causal.expand(batch_size, 1, seq_len, seq_len).clone()
277
+ padding = attention_mask[:, None, None, :].to(device) == 0
 
 
278
  causal = causal.masked_fill(padding, min_value)
 
279
  return causal
280
 
281
 
 
282
  _MASK_PRINTED = False
283
 
284
 
 
309
  **kwargs,
310
  ):
311
  global _MASK_PRINTED
312
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
 
 
 
313
  if inputs_embeds is None:
314
  inputs_embeds = self.embed_tokens(input_ids)
315
 
 
322
  hidden_states = inputs_embeds
323
  position_embeddings = self.rotary_emb(hidden_states, position_ids)
324
 
 
325
  seq_len = hidden_states.shape[1]
326
  causal_mask = _build_causal_mask(
327
  attention_mask, seq_len, hidden_states.dtype, hidden_states.device
 
377
  return_dict: Optional[bool] = None,
378
  **kwargs,
379
  ):
380
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
 
 
381
  outputs = self.model(
382
  input_ids=input_ids,
383
  attention_mask=attention_mask,
 
432
  # =============================================================================
433
 
434
  class StatsEngine:
435
+ """Compute unified signature for any tensor."""
436
 
437
  @staticmethod
438
  def compute(
 
447
  else float("inf")
448
  )
449
 
 
 
 
 
 
 
 
450
  t_min = tensor.min().item()
451
  t_max = tensor.max().item()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
452
 
453
  return {
454
+ "norm": tensor.norm(2).item(),
455
+ "mean": tensor.mean().item(),
456
+ "std": tensor.std().item(),
457
+ "max_abs": abs_t.max().item(),
458
  "frac_near_dtype_limit": (
459
  (abs_t > dtype_limit).float().mean().item()
460
  if not math.isinf(dtype_limit)
461
  else 0.0
462
  ),
463
  "frac_near_user_limit": (abs_t > user_limit).float().mean().item(),
 
464
  "min": t_min,
465
  "max": t_max,
466
+ "range": t_max - t_min,
 
 
 
 
 
 
 
467
  }
468
 
469
 
470
  class StepAccumulator:
471
+ """Stores per-tensor entries, aggregates to layer/global scope."""
 
 
 
472
 
473
  def __init__(self):
 
474
  self.tensors: Dict[str, Dict[str, float]] = {}
475
 
476
  def add(self, name: str, numel: int, stats: Dict[str, float]):
 
500
  a["frac_near_user_limit"] * a["numel"] + b["frac_near_user_limit"] * b["numel"]
501
  ) / total_n
502
 
 
503
  t_min = min(a.get("min", float("inf")), b.get("min", float("inf")))
504
  t_max = max(a.get("max", float("-inf")), b.get("max", float("-inf")))
 
 
 
 
 
 
 
 
 
505
 
506
  return {
507
  "numel": total_n,
 
513
  "frac_near_user_limit": frac_user,
514
  "min": t_min,
515
  "max": t_max,
516
+ "range": t_max - t_min,
 
 
 
 
 
 
 
517
  }
518
 
519
  def clear(self):
 
525
  numels = [e["numel"] for e in entries.values()]
526
  total_n = sum(numels)
527
 
 
528
  norm = math.sqrt(sum(e["norm"] ** 2 for e in entries.values()))
 
529
  max_abs = max(e["max_abs"] for e in entries.values())
 
530
  mean = sum(e["mean"] * e["numel"] for e in entries.values()) / total_n
 
531
  ex2 = (
532
  sum(e["numel"] * (e["std"] ** 2 + e["mean"] ** 2) for e in entries.values())
533
  / total_n
534
  )
535
  std = math.sqrt(max(0.0, ex2 - mean ** 2))
 
536
  frac_dtype = (
537
  sum(e["frac_near_dtype_limit"] * e["numel"] for e in entries.values())
538
  / total_n
 
542
  / total_n
543
  )
544
 
 
545
  t_min = min(e.get("min", float("inf")) for e in entries.values())
546
  t_max = max(e.get("max", float("-inf")) for e in entries.values())
 
547
 
548
  return {
549
  "norm": norm,
 
554
  "frac_near_user_limit": frac_user,
555
  "min": t_min,
556
  "max": t_max,
557
+ "range": t_max - t_min,
 
 
558
  }
559
 
560
  def get_global_stats(self) -> Dict[str, float]:
 
607
  def hook(module, inp, out):
608
  if not self.active:
609
  return
 
 
 
 
610
  if isinstance(out, dict):
611
  out_dict = out
612
  out = out_dict.get("last_hidden_state")
 
646
 
647
 
648
  class StabilityMonitorCallback(TrainerCallback):
649
+ """Full stability instrumentation: grad / param / act statistics."""
 
 
 
650
 
651
  def __init__(
652
  self,
 
660
  ):
661
  self.model = model
662
  self.monitor_every_n_steps = monitor_every_n_steps
 
663
  self.module_patterns = module_patterns or [".*mlp.*", ".*self_attn.*", ".*residual.*"]
664
  self.user_limits = user_limits or {"grad": 1.0, "param": 100.0, "act": 50.0}
665
  self.dtype_ratio = dtype_proximity_ratio
 
695
  def on_step_end(self, args, state, control, **kwargs):
696
  if not self.hooks.active:
697
  return
 
 
698
  for name, param in self.model.named_parameters():
699
  stats = StatsEngine.compute(
700
  param.data, self.user_limits["param"], self.dtype_ratio
 
706
 
707
  @staticmethod
708
  def _kind_of(name: str) -> str:
 
709
  if name.startswith("act."):
710
  return "act"
711
  if name.startswith("grad."):
 
725
  def _build_metrics(self, scope: str = "train") -> Dict[str, float]:
726
  metrics: Dict[str, float] = {}
727
 
 
728
  if self.log_scope.get("global", True):
729
  by_kind: Dict[str, Dict[str, Dict[str, float]]] = {}
730
  for k, v in self.accumulator.tensors.items():
731
  by_kind.setdefault(self._kind_of(k), {})[k] = v
 
732
  for kind, entries in by_kind.items():
733
  stats = self.accumulator._aggregate(entries)
734
  for kk, vv in stats.items():
735
  metrics[f"{scope}/global/{kind}/{kk}"] = vv
736
 
 
737
  if self.log_scope.get("per_layer", True):
738
  layer_prefixes = set()
739
  for name in self.accumulator.tensors:
 
743
  if p == "layers" and i + 1 < len(parts):
744
  prefix = ".".join(parts[: i + 2])
745
  layer_prefixes.add(prefix)
 
746
  for prefix in layer_prefixes:
747
  by_kind: Dict[str, Dict[str, Dict[str, float]]] = {}
748
  for k, v in self.accumulator.tensors.items():
749
  clean = self._strip_kind(k)
750
  if clean.startswith(prefix + ".") or clean == prefix:
751
  by_kind.setdefault(self._kind_of(k), {})[k] = v
 
752
  safe = prefix.replace(".", "_")
753
  for kind, entries in by_kind.items():
754
  if not entries:
 
757
  for kk, vv in stats.items():
758
  metrics[f"{scope}/layer_{safe}/{kind}/{kk}"] = vv
759
 
 
760
  if self.log_scope.get("per_tensor", False):
761
  for name, stats in self.accumulator.tensors.items():
762
  safe = name.replace(".", "_")
 
773
  self.pending_metrics = None
774
 
775
  def on_prediction_step(self, args, state, control, **kwargs):
 
 
 
 
 
 
 
 
776
  if not self.monitor_during_eval:
777
  return
778
  if not self.hooks.active:
 
 
779
  self.accumulator.clear()
780
  self.hooks.set_active(True)
781
  for name, param in self.model.named_parameters():
 
815
  def on_log(self, args, state, control, logs=None, **kwargs):
816
  if logs is None:
817
  return
 
818
  logs["train/total_time_seconds"] = self.total_train_time
 
819
  if self.step_times:
820
  recent = self.step_times[-100:]
821
  logs["train/time_per_step_avg"] = sum(recent) / len(recent)
 
822
  if self.epoch_start is not None:
823
  logs["train/epoch_time_elapsed"] = time.perf_counter() - self.epoch_start
 
824
  if state.max_steps and state.global_step > 0:
825
  avg = self.total_train_time / state.global_step
826
  remaining = (state.max_steps - state.global_step) * avg
 
852
 
853
 
854
  # =============================================================================
855
+ # CONTAMINATION CALLBACK
856
  # =============================================================================
857
 
858
  class ContaminationCallback(TrainerCallback):
859
  """
860
+ Intentionally corrupt input_ids and labels for a window of steps.
861
+ Modes: "shift" (+1 mod vocab), "random" (uniform random IDs).
 
 
 
862
  """
863
  def __init__(
864
  self,
 
877
  self.mode = mode
878
  self.fraction = fraction
879
  self.seed = seed
 
 
880
  self.generator = torch.Generator()
881
  if seed is not None:
882
  self.generator.manual_seed(seed)
 
883
  self._active = False
884
 
885
  def _should_corrupt(self, state) -> bool:
 
891
  def on_step_begin(self, args, state, control, **kwargs):
892
  if not self._should_corrupt(state):
893
  return
 
 
 
894
  batch = kwargs.get("inputs")
895
+ if batch is None or not isinstance(batch, dict):
896
  return
 
 
 
 
 
 
 
897
  input_ids = batch.get("input_ids")
898
  labels = batch.get("labels")
899
  attention_mask = batch.get("attention_mask")
 
900
  if input_ids is None or labels is None:
901
  return
902
 
903
  device = input_ids.device
904
  batch_size, seq_len = input_ids.shape
905
 
 
 
906
  if self.fraction >= 1.0:
907
  corrupt_mask = torch.ones((batch_size, seq_len), dtype=torch.bool, device=device)
908
  elif self.fraction <= 0.0:
909
+ return
910
  else:
 
911
  rand = torch.rand((batch_size, seq_len), generator=self.generator, device=device)
912
  corrupt_mask = rand < self.fraction
913
 
 
914
  if attention_mask is not None:
 
915
  padding_mask = attention_mask == 0
916
  corrupt_mask = corrupt_mask & (~padding_mask)
917
 
918
  if not corrupt_mask.any():
919
  return
920
 
 
921
  if self.mode == "shift":
 
 
922
  input_ids_corrupt = (input_ids + 1) % self.vocab_size
923
  labels_corrupt = (labels + 1) % self.vocab_size
924
  elif self.mode == "random":
 
 
925
  random_ids = torch.randint(
926
  0, self.vocab_size, input_ids.shape,
927
  generator=self.generator, device=device
 
931
  else:
932
  raise ValueError(f"Unknown contamination mode: {self.mode}")
933
 
 
934
  input_ids.masked_scatter_(corrupt_mask, input_ids_corrupt[corrupt_mask])
935
  labels.masked_scatter_(corrupt_mask, labels_corrupt[corrupt_mask])
 
 
936
  batch["input_ids"] = input_ids
937
  batch["labels"] = labels
938
 
 
939
  if not self._active:
940
  print(f"[Contamination] Started at step {state.global_step} for {self.duration_steps} steps (mode={self.mode})")
941
  self._active = True
942
 
943
  def on_step_end(self, args, state, control, **kwargs):
 
944
  if self._active and state.global_step >= self.start_step + self.duration_steps:
945
  print(f"[Contamination] Ended at step {state.global_step}")
946
  self._active = False
 
957
  dataset_name: str = "roneneldan/TinyStories",
958
  max_samples: Optional[int] = None,
959
  ):
960
+ """Concatenate and chunk TinyStories for causal LM."""
961
  ds = load_dataset(dataset_name, split=split)
 
962
  if max_samples is not None and split == "train":
963
  ds = ds.select(range(min(max_samples, len(ds))))
964
  print(f"[Dataset] Using first {len(ds)} samples for training (max_samples={max_samples})")
 
1044
  remove_unused_columns=False,
1045
  )
1046
 
1047
+ callbacks = [TimeTrackerCallback()]
 
 
1048
 
 
1049
  cc = config.get("contamination", {})
1050
  if cc.get("enabled", False):
1051
  vocab_size = model.config.vocab_size
 
1067
  model=model,
1068
  monitor_every_n_steps=mc.get("monitor_every_n_steps", 10),
1069
  module_patterns=mc.get("module_patterns", [".*mlp.*", ".*self_attn.*", ".*residual.*"]),
1070
+ user_limits=mc.get("user_limits", {"grad": 1.0, "param": 100.0, "act": 50.0}),
 
 
1071
  dtype_proximity_ratio=mc.get("dtype_proximity_ratio", 0.9),
1072
+ log_scope=mc.get("log_scope", {"global": True, "per_layer": True, "per_tensor": False}),
 
 
 
1073
  monitor_during_eval=mc.get("monitor_during_eval", False),
1074
  )
1075
  )
 
1087
  callbacks=callbacks,
1088
  )
1089
 
 
1090
  try:
1091
  from transformers.integrations import get_reporting_integration_callbacks
 
1092
  reporting_types = tuple(get_reporting_integration_callbacks(args.report_to))
1093
  except Exception:
1094
  reporting_types = ()
zain/Activation/sweep.py CHANGED
@@ -39,14 +39,13 @@ def parse_variant(variant: str):
39
  if prefix not in ('glu', 'mlp'):
40
  raise ValueError(f"Invalid prefix: '{prefix}'. Must be 'glu' or 'mlp'.")
41
 
42
- # The last part might be layers like "10L"
43
  last = parts[-1]
44
  if last.endswith('L') and last[:-1].isdigit():
45
  layers = int(last[:-1])
46
- activation = '-'.join(parts[1:-1]) # everything between prefix and layers
47
  else:
48
  layers = None
49
- activation = '-'.join(parts[1:]) # everything after prefix
50
 
51
  if not activation:
52
  raise ValueError(f"Missing activation name in variant: '{variant}'")
@@ -84,32 +83,20 @@ def main():
84
  tokenizer.pad_token = tokenizer.eos_token
85
 
86
  msl = base["model"].get("max_position_embeddings", 512)
87
- train_ds = build_dataset(
88
- tokenizer,
89
- max_seq_len=msl,
90
- split="train",
91
- max_samples=None
92
- )
93
- eval_ds = build_dataset(
94
- tokenizer,
95
- max_seq_len=msl,
96
- split="validation",
97
- max_samples=None
98
- )
99
 
100
  results = []
101
 
102
  for variant in args.variants:
103
  prefix, act, layers = parse_variant(variant)
104
 
105
- # Validation: mlp-situglu and mlp-waleed are banned
106
  if prefix == "mlp" and act in ("situglu", "waleed", "situglu_low", "waleedglu_low"):
107
  raise ValueError(
108
  f"Activation '{act}' requires a gated architecture (GLU). "
109
  f"Please use 'glu-{act}' instead."
110
  )
111
 
112
- # Build config overrides
113
  cfg = copy.deepcopy(base)
114
  cfg["model"]["mlp_type"] = prefix
115
  cfg["model"]["activation"] = act
@@ -123,16 +110,13 @@ def main():
123
  actual_layers = cfg["model"]["num_hidden_layers"]
124
  variant_label = f"{prefix}-{act}-{actual_layers}L"
125
 
126
- # Unique output directory
127
  out_dir = Path(cfg["training"]["output_dir"]).parent / f"{variant_label}_run"
128
  cfg["training"]["output_dir"] = str(out_dir)
129
 
130
- # Re-seed for reproducibility across variants
131
  set_seed(seed)
132
 
133
  print(f"\n{'='*60}\n>>> Variant: {variant_label} | Out: {out_dir}\n{'='*60}")
134
 
135
- # Instantiate model
136
  config = TinyLlamaConfig(**cfg["model"])
137
  model = TinyLlamaForCausalLM(config)
138
  model = model.to(torch.bfloat16)
@@ -143,11 +127,9 @@ def main():
143
  run_name = f"LM-{variant_label}-{param_str}-{timestamp}"
144
  cfg["training"]["run_name"] = run_name
145
 
146
- # Also update hub_model_id to include variant and layer count
147
  hub_id_base = cfg["training"].get("hub_model_id", "tiny-llama-lab")
148
  cfg["training"]["hub_model_id"] = f"{hub_id_base}-{variant_label}"
149
 
150
- # Ensure a fresh WandB run – remove any global WANDB_RUN_ID
151
  os.environ.pop("WANDB_RUN_ID", None)
152
 
153
  trainer = create_trainer(model, tokenizer, cfg, train_ds, eval_ds)
@@ -156,7 +138,7 @@ def main():
156
  trainer.train()
157
  metrics = trainer.evaluate()
158
  results.append({
159
- "variant": variant_label, # now always includes layer count
160
  "eval_loss": metrics.get("eval_loss"),
161
  "out": str(out_dir),
162
  "run_name": run_name,
@@ -165,10 +147,8 @@ def main():
165
  if args.push or cfg["training"].get("push_to_hub", False):
166
  trainer.push_to_hub()
167
  finally:
168
- # Explicitly finish WandB run to avoid re‑using the same run
169
  wandb.finish()
170
 
171
- # Save summary
172
  summary = Path(base["training"]["output_dir"]).parent / "sweep_summary.json"
173
  summary.write_text(json.dumps(results, indent=2))
174
  print("\nSweep complete:")
 
39
  if prefix not in ('glu', 'mlp'):
40
  raise ValueError(f"Invalid prefix: '{prefix}'. Must be 'glu' or 'mlp'.")
41
 
 
42
  last = parts[-1]
43
  if last.endswith('L') and last[:-1].isdigit():
44
  layers = int(last[:-1])
45
+ activation = '-'.join(parts[1:-1])
46
  else:
47
  layers = None
48
+ activation = '-'.join(parts[1:])
49
 
50
  if not activation:
51
  raise ValueError(f"Missing activation name in variant: '{variant}'")
 
83
  tokenizer.pad_token = tokenizer.eos_token
84
 
85
  msl = base["model"].get("max_position_embeddings", 512)
86
+ train_ds = build_dataset(tokenizer, max_seq_len=msl, split="train", max_samples=None)
87
+ eval_ds = build_dataset(tokenizer, max_seq_len=msl, split="validation", max_samples=None)
 
 
 
 
 
 
 
 
 
 
88
 
89
  results = []
90
 
91
  for variant in args.variants:
92
  prefix, act, layers = parse_variant(variant)
93
 
 
94
  if prefix == "mlp" and act in ("situglu", "waleed", "situglu_low", "waleedglu_low"):
95
  raise ValueError(
96
  f"Activation '{act}' requires a gated architecture (GLU). "
97
  f"Please use 'glu-{act}' instead."
98
  )
99
 
 
100
  cfg = copy.deepcopy(base)
101
  cfg["model"]["mlp_type"] = prefix
102
  cfg["model"]["activation"] = act
 
110
  actual_layers = cfg["model"]["num_hidden_layers"]
111
  variant_label = f"{prefix}-{act}-{actual_layers}L"
112
 
 
113
  out_dir = Path(cfg["training"]["output_dir"]).parent / f"{variant_label}_run"
114
  cfg["training"]["output_dir"] = str(out_dir)
115
 
 
116
  set_seed(seed)
117
 
118
  print(f"\n{'='*60}\n>>> Variant: {variant_label} | Out: {out_dir}\n{'='*60}")
119
 
 
120
  config = TinyLlamaConfig(**cfg["model"])
121
  model = TinyLlamaForCausalLM(config)
122
  model = model.to(torch.bfloat16)
 
127
  run_name = f"LM-{variant_label}-{param_str}-{timestamp}"
128
  cfg["training"]["run_name"] = run_name
129
 
 
130
  hub_id_base = cfg["training"].get("hub_model_id", "tiny-llama-lab")
131
  cfg["training"]["hub_model_id"] = f"{hub_id_base}-{variant_label}"
132
 
 
133
  os.environ.pop("WANDB_RUN_ID", None)
134
 
135
  trainer = create_trainer(model, tokenizer, cfg, train_ds, eval_ds)
 
138
  trainer.train()
139
  metrics = trainer.evaluate()
140
  results.append({
141
+ "variant": variant_label,
142
  "eval_loss": metrics.get("eval_loss"),
143
  "out": str(out_dir),
144
  "run_name": run_name,
 
147
  if args.push or cfg["training"].get("push_to_hub", False):
148
  trainer.push_to_hub()
149
  finally:
 
150
  wandb.finish()
151
 
 
152
  summary = Path(base["training"]["output_dir"]).parent / "sweep_summary.json"
153
  summary.write_text(json.dumps(results, indent=2))
154
  print("\nSweep complete:")
zain/Activation/train.py CHANGED
@@ -18,12 +18,11 @@ def main():
18
  with open(args.config) as f:
19
  cfg = yaml.safe_load(f)
20
 
21
- # Explicit seed before any randomness
22
  seed = cfg.get("training", {}).get("seed", 42)
23
  set_seed(seed)
24
 
25
  # -------------------------------------------------------------------------
26
- # NEW: Force run into a specific WandB project (read from config).
27
  # -------------------------------------------------------------------------
28
  wandb_project = cfg.get("training", {}).get("wandb_project")
29
  if wandb_project:
@@ -34,13 +33,11 @@ def main():
34
  model_cfg = cfg["model"]
35
  train_cfg = cfg.get("training", {})
36
 
37
- # Tokenizer
38
  tok_name = model_cfg.pop("tokenizer_name", "meta-llama/Llama-2-7b-hf")
39
  tokenizer = AutoTokenizer.from_pretrained(tok_name)
40
  if tokenizer.pad_token is None:
41
  tokenizer.pad_token = tokenizer.eos_token
42
 
43
- # Model
44
  tiny_config = TinyLlamaConfig(**model_cfg)
45
  model = TinyLlamaForCausalLM(tiny_config)
46
  model = model.to(torch.bfloat16)
@@ -48,26 +45,13 @@ def main():
48
  n_params = sum(p.numel() for p in model.parameters()) / 1e6
49
  print(f"Model: {n_params:.2f}M params | MLP type: {tiny_config.mlp_type} | Activation: {tiny_config.activation}")
50
 
51
- # Data
52
  msl = model_cfg.get("max_position_embeddings", 512)
53
- train_ds = build_dataset(
54
- tokenizer,
55
- max_seq_len=msl,
56
- split="train",
57
- max_samples=None
58
- )
59
- eval_ds = build_dataset(
60
- tokenizer,
61
- max_seq_len=msl,
62
- split="validation",
63
- max_samples=None
64
- )
65
 
66
- # Train
67
  trainer = create_trainer(model, tokenizer, cfg, train_ds, eval_ds)
68
  trainer.train()
69
 
70
- # Save & push
71
  out = train_cfg.get("output_dir", "./out")
72
  trainer.save_model(out)
73
  if args.push or train_cfg.get("push_to_hub", False):
 
18
  with open(args.config) as f:
19
  cfg = yaml.safe_load(f)
20
 
 
21
  seed = cfg.get("training", {}).get("seed", 42)
22
  set_seed(seed)
23
 
24
  # -------------------------------------------------------------------------
25
+ # LOCK WANDB PROJECT: read from config, force into environment.
26
  # -------------------------------------------------------------------------
27
  wandb_project = cfg.get("training", {}).get("wandb_project")
28
  if wandb_project:
 
33
  model_cfg = cfg["model"]
34
  train_cfg = cfg.get("training", {})
35
 
 
36
  tok_name = model_cfg.pop("tokenizer_name", "meta-llama/Llama-2-7b-hf")
37
  tokenizer = AutoTokenizer.from_pretrained(tok_name)
38
  if tokenizer.pad_token is None:
39
  tokenizer.pad_token = tokenizer.eos_token
40
 
 
41
  tiny_config = TinyLlamaConfig(**model_cfg)
42
  model = TinyLlamaForCausalLM(tiny_config)
43
  model = model.to(torch.bfloat16)
 
45
  n_params = sum(p.numel() for p in model.parameters()) / 1e6
46
  print(f"Model: {n_params:.2f}M params | MLP type: {tiny_config.mlp_type} | Activation: {tiny_config.activation}")
47
 
 
48
  msl = model_cfg.get("max_position_embeddings", 512)
49
+ train_ds = build_dataset(tokenizer, max_seq_len=msl, split="train", max_samples=None)
50
+ eval_ds = build_dataset(tokenizer, max_seq_len=msl, split="validation", max_samples=None)
 
 
 
 
 
 
 
 
 
 
51
 
 
52
  trainer = create_trainer(model, tokenizer, cfg, train_ds, eval_ds)
53
  trainer.train()
54
 
 
55
  out = train_cfg.get("output_dir", "./out")
56
  trainer.save_model(out)
57
  if args.push or train_cfg.get("push_to_hub", False):