JorgeAV commited on
Commit
33997db
·
verified ·
1 Parent(s): 5c15aea

Fix TextEncoder.unfreeze_last: compatible with both AutoModel (Qwen3Model.layers) and ForCausalLM (model.model.layers)

Browse files
Files changed (1) hide show
  1. train_mrjepa.py +26 -59
train_mrjepa.py CHANGED
@@ -178,8 +178,6 @@ class VisualBackbone(nn.Module):
178
  pretrained=True,
179
  num_classes=0,
180
  )
181
- # DINOv3 via timm: forward_features returns [B, num_tokens, D]
182
- # includes CLS + 4 registers + patches
183
  self._skip = 5 # CLS + 4 reg
184
  self._use_timm = True
185
  else:
@@ -216,8 +214,8 @@ class VisualBackbone(nn.Module):
216
 
217
  def forward(self, x: torch.Tensor) -> torch.Tensor:
218
  if self._use_timm:
219
- feats = self.model.forward_features(x) # [B, N, D]
220
- return feats[:, self._skip:] # patch tokens only
221
  else:
222
  out = self.model(pixel_values=x)
223
  return out.last_hidden_state[:, self._skip:]
@@ -245,7 +243,15 @@ class TextEncoder(nn.Module):
245
  p.requires_grad = False
246
 
247
  def unfreeze_last(self, n: int):
248
- layers = self.model.model.layers # Qwen3 uses model.layers
 
 
 
 
 
 
 
 
249
  for i, lay in enumerate(layers):
250
  if i >= len(layers) - n:
251
  for p in lay.parameters():
@@ -253,7 +259,7 @@ class TextEncoder(nn.Module):
253
 
254
  def forward(self, input_ids, attention_mask):
255
  out = self.model(input_ids=input_ids, attention_mask=attention_mask)
256
- return out.last_hidden_state # [B, seq, D]
257
 
258
 
259
  # ──────────────────────────────────────────────────────────
@@ -266,7 +272,7 @@ class EvidenceMemory(nn.Module):
266
  self.queries = nn.Parameter(torch.randn(1, cfg.num_evidence_tokens, D) * 0.02)
267
  self.vis_proj = nn.Sequential(nn.Linear(cfg.backbone_dim, D), nn.LayerNorm(D), nn.GELU(), nn.Linear(D, D))
268
  self.txt_proj = nn.Sequential(nn.Linear(cfg.text_dim, D), nn.LayerNorm(D), nn.GELU(), nn.Linear(D, D))
269
- self.mod_emb = nn.Embedding(2, D) # 0=visual, 1=text
270
  self.layers = nn.ModuleList([
271
  PerceiverLayer(D, cfg.num_heads) for _ in range(cfg.num_cross_attn_layers)
272
  ])
@@ -351,9 +357,7 @@ class LatentRollout(nn.Module):
351
  self.step_emb = nn.Parameter(torch.randn(cfg.K + 1, 1, D) * 0.02)
352
  else:
353
  self.step_emb = None
354
- # Evidence dim projection to rollout dim if they differ
355
  self.ev_proj = nn.Linear(cfg.evidence_dim, D) if cfg.evidence_dim != D else nn.Identity()
356
- # Shared predictor (weight-tied across K steps)
357
  self.predictor = nn.ModuleList([
358
  PredictorBlock(D, cfg.predictor_heads, cfg.predictor_ffn, use_gate=cfg.use_evidence_gate)
359
  for _ in range(cfg.num_predictor_layers)
@@ -374,7 +378,7 @@ class LatentRollout(nn.Module):
374
  if self.step_emb is not None:
375
  z = z + self.step_emb[k]
376
  traj.append(z)
377
- traj_t = torch.stack(traj, dim=1) # [B, K+1, N, D]
378
  B2, Kp1, N, D2 = traj_t.shape
379
  proj = self.out_proj(traj_t.reshape(-1, D2)).reshape(B2, Kp1, N, D2)
380
  return traj_t, traj[-1], proj
@@ -431,7 +435,6 @@ class SIGReg(nn.Module):
431
 
432
 
433
  class VICReg(nn.Module):
434
- """VICReg-style anti-collapse: variance + covariance regularization."""
435
  def __init__(self, var_weight=1.0, cov_weight=0.04):
436
  super().__init__()
437
  self.var_w = var_weight
@@ -439,10 +442,8 @@ class VICReg(nn.Module):
439
 
440
  def forward(self, z):
441
  if z.dim() == 3: z = z.reshape(-1, z.size(-1))
442
- # Variance: penalize if std drops below 1
443
  std = z.std(dim=0)
444
  var_loss = F.relu(1.0 - std).mean()
445
- # Covariance: penalize off-diagonal correlations
446
  z_c = z - z.mean(dim=0, keepdim=True)
447
  N = z_c.size(0)
448
  cov = (z_c.T @ z_c) / max(N - 1, 1)
@@ -460,7 +461,6 @@ class JEPALoss(nn.Module):
460
  self.vicreg = VICReg(cfg.vicreg_var, cfg.vicreg_cov) if cfg.use_vicreg else None
461
 
462
  def forward(self, pred_traj, target_traj, task_loss, gen_loss=None):
463
- # Prediction loss (skip z0)
464
  p = pred_traj[:, 1:]
465
  t = target_traj[:, 1:]
466
  if self.cfg.loss_fn == "smooth_l1":
@@ -515,8 +515,8 @@ class DiscriminativeHead(nn.Module):
515
  q = self.pool_q.expand(B, -1, -1)
516
  z_n = self.pool_n(z_final)
517
  pooled, _ = self.pool_attn(q, z_n, z_n)
518
- pooled = pooled.squeeze(1) # [B, D]
519
- opt = self.opt_proj(opt_emb) # [B, O, D]
520
  z_exp = pooled.unsqueeze(1).expand_as(opt)
521
  combined = torch.cat([z_exp, opt, z_exp * opt], dim=-1)
522
  logits = self.scorer(combined).squeeze(-1)
@@ -557,7 +557,6 @@ class ScienceQADataset(Dataset):
557
  question = row["question"]
558
  hint = row.get("hint", "")
559
 
560
- # Build text: question + hint + options
561
  text = question
562
  if hint:
563
  text += f" Context: {hint}"
@@ -585,18 +584,13 @@ def collate_fn(batch, transform, tokenizer, max_len, max_opts, device_hint="cpu"
585
  all_choices.append(s["choices"])
586
  answers.append(s["answer"])
587
 
588
- # Image preprocessing
589
  if hasattr(transform, '__call__'):
590
- # timm transform
591
  pixel_values = torch.stack([transform(img) for img in images])
592
  else:
593
- # HF processor
594
  pixel_values = transform(images=images, return_tensors="pt")["pixel_values"]
595
 
596
- # Text tokenization
597
  tok = tokenizer(texts, padding="max_length", truncation=True, max_length=max_len, return_tensors="pt")
598
 
599
- # Options: encode each option text
600
  opt_texts = []
601
  opt_mask = []
602
  for choices in all_choices:
@@ -637,13 +631,11 @@ class MRJEPAModel(nn.Module):
637
  self._use_rollout = cfg.K > 0
638
 
639
  def encode_options(self, opt_ids, opt_mask_attn):
640
- """Encode option texts via text encoder, return CLS embeddings."""
641
  with torch.no_grad():
642
- h = self.txt(opt_ids, opt_mask_attn).float() # [B*O, seq, D]
643
- # Mean pooling over non-padding tokens
644
  mask_expanded = opt_mask_attn.unsqueeze(-1).float()
645
  pooled = (h * mask_expanded).sum(1) / mask_expanded.sum(1).clamp(min=1)
646
- return pooled # [B*O, D]
647
 
648
  def forward(self, pixel_values, input_ids, attention_mask,
649
  opt_input_ids, opt_attention_mask, opt_mask, labels, batch_size, **kw):
@@ -651,18 +643,14 @@ class MRJEPAModel(nn.Module):
651
  B = batch_size
652
  O = self.cfg.max_options
653
 
654
- # 1) Visual features (frozen)
655
  with torch.no_grad():
656
- vis_tok = self.vis(pixel_values).float() # [B, N_patches, D_vis]
657
 
658
- # 2) Text features (frozen)
659
  with torch.no_grad():
660
- txt_tok = self.txt(input_ids, attention_mask).float() # [B, seq, D_txt]
661
 
662
- # 3) Evidence memory
663
- evidence, _, ev_mask = self.evidence(vis_tok, txt_tok, attention_mask) # [B, N_ev, D_ev]
664
 
665
- # 4) Rollout
666
  if self._use_rollout:
667
  traj, z_final, z_proj = self.rollout(evidence)
668
  else:
@@ -674,18 +662,15 @@ class MRJEPAModel(nn.Module):
674
  traj = z0.unsqueeze(1)
675
  z_proj = self.rollout.out_proj(z0).unsqueeze(1)
676
 
677
- # 5) Target encoder (JEPA targets)
678
  target_proj = None
679
  if self._use_jepa and self.training:
680
  target_proj = self.target(vis_tok.detach(), txt_tok.detach(), attention_mask.detach())
681
 
682
- # 6) Encode options & score
683
- opt_emb = self.encode_options(opt_input_ids, opt_attention_mask) # [B*O, D]
684
- opt_emb = opt_emb.view(B, O, -1) # [B, O, D]
685
- logits = self.disc(z_final, opt_emb, opt_mask) # [B, O]
686
  task_loss = F.cross_entropy(logits, labels)
687
 
688
- # 7) JEPA loss
689
  if self._use_jepa and self.training and target_proj is not None:
690
  losses = self.jepa_loss(z_proj, target_proj, task_loss)
691
  else:
@@ -709,7 +694,6 @@ def train(cfg: Config):
709
 
710
  os.makedirs(cfg.output_dir, exist_ok=True)
711
 
712
- # ── Trackio ──
713
  try:
714
  import trackio
715
  tracker = trackio.init(name=cfg.run_name, project="MR-JEPA")
@@ -718,18 +702,15 @@ def train(cfg: Config):
718
  log.warning(f"Trackio init failed: {e}")
719
  tracker = None
720
 
721
- # ── Model ──
722
  log.info("Building model...")
723
  model = MRJEPAModel(cfg)
724
 
725
- # Count params
726
  total_p = sum(p.numel() for p in model.parameters())
727
  train_p = sum(p.numel() for p in model.parameters() if p.requires_grad)
728
  log.info(f"Total params: {total_p:,} | Trainable: {train_p:,} ({100*train_p/total_p:.1f}%)")
729
 
730
  model = model.to(device)
731
 
732
- # ── Data ──
733
  transform = model.vis.get_transform()
734
  tokenizer = model.txt.tokenizer
735
 
@@ -746,7 +727,6 @@ def train(cfg: Config):
746
  eval_dl = DataLoader(eval_ds, batch_size=cfg.batch_size, shuffle=False, num_workers=cfg.num_workers,
747
  collate_fn=coll, pin_memory=True)
748
 
749
- # ── Optimizer ──
750
  trainable = [p for p in model.parameters() if p.requires_grad]
751
  optimizer = AdamW(trainable, lr=cfg.lr, weight_decay=cfg.weight_decay)
752
  total_steps = cfg.epochs * len(train_dl) // cfg.grad_accum
@@ -760,7 +740,6 @@ def train(cfg: Config):
760
 
761
  scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda)
762
 
763
- # ── Training ──
764
  log.info(f"Starting training: {cfg.epochs} epochs, {len(train_dl)} batches/epoch, "
765
  f"grad_accum={cfg.grad_accum}, total_steps≈{total_steps}")
766
 
@@ -776,7 +755,6 @@ def train(cfg: Config):
776
  optimizer.zero_grad()
777
 
778
  for batch_idx, batch in enumerate(train_dl):
779
- # Move to device
780
  batch = {k: v.to(device) if isinstance(v, torch.Tensor) else v for k, v in batch.items()}
781
 
782
  with torch.autocast(device_type="cuda", dtype=amp_dtype, enabled=cfg.bf16 and device.type == "cuda"):
@@ -793,7 +771,6 @@ def train(cfg: Config):
793
  model.update_target(global_step, total_steps)
794
  global_step += 1
795
 
796
- # Metrics
797
  for k, v in losses.items():
798
  if isinstance(v, torch.Tensor):
799
  epoch_losses[k].append(v.item())
@@ -801,7 +778,6 @@ def train(cfg: Config):
801
  epoch_correct += correct
802
  epoch_total += batch["batch_size"]
803
 
804
- # Log every 50 batches
805
  if batch_idx % 50 == 0:
806
  avg = {k: np.mean(v[-50:]) for k, v in epoch_losses.items()}
807
  acc = epoch_correct / max(epoch_total, 1) * 100
@@ -825,7 +801,6 @@ def train(cfg: Config):
825
  })
826
  except: pass
827
 
828
- # ── Eval ──
829
  train_acc = epoch_correct / max(epoch_total, 1) * 100
830
  eval_acc = evaluate(model, eval_dl, device, cfg)
831
  log.info(f"=== Epoch {epoch} done | Train acc: {train_acc:.1f}% | Eval acc: {eval_acc:.1f}% ===")
@@ -844,7 +819,6 @@ def train(cfg: Config):
844
 
845
  log.info(f"Training complete. Best eval accuracy: {best_acc:.1f}%")
846
 
847
- # ── Push to Hub ──
848
  if cfg.push_to_hub:
849
  push_results(cfg, best_acc)
850
 
@@ -885,7 +859,6 @@ def save_checkpoint(model, cfg, epoch, acc, is_best=False):
885
 
886
 
887
  def push_results(cfg, best_acc):
888
- """Push results summary to Hub."""
889
  try:
890
  from huggingface_hub import HfApi
891
  api = HfApi()
@@ -916,7 +889,6 @@ def push_results(cfg, best_acc):
916
  repo_id=cfg.hub_model_id,
917
  repo_type="model",
918
  )
919
- # Upload best checkpoint
920
  best_ckpt = os.path.join(cfg.output_dir, "checkpoint_best.pt")
921
  if os.path.exists(best_ckpt):
922
  api.upload_file(
@@ -946,8 +918,8 @@ def parse_args():
946
  p.add_argument("--no_jepa", action="store_true")
947
  p.add_argument("--no_rollout", action="store_true")
948
  p.add_argument("--no_evidence_gate", action="store_true")
949
- p.add_argument("--no_sigreg", action="store_true", help="Disable SIGReg anti-collapse")
950
- p.add_argument("--use_vicreg", action="store_true", help="Enable VICReg anti-collapse")
951
  p.add_argument("--purist", action="store_true")
952
  p.add_argument("--sigreg_weight", type=float, default=0.1)
953
  p.add_argument("--max_train_samples", type=int, default=0)
@@ -982,11 +954,6 @@ def main():
982
 
983
  if args.no_rollout:
984
  cfg.K = 0
985
- # JEPA requires a multi-step trajectory to supervise; with K=0 there is
986
- # only z₀, so JEPA loss is undefined. This is intentional — the no_rollout
987
- # ablation tests whether iterative refinement + JEPA together add value
988
- # vs. a single-pass baseline. To test JEPA loss in isolation, use --no_jepa
989
- # with K>0 instead.
990
  cfg.use_jepa = False
991
 
992
  if args.purist:
 
178
  pretrained=True,
179
  num_classes=0,
180
  )
 
 
181
  self._skip = 5 # CLS + 4 reg
182
  self._use_timm = True
183
  else:
 
214
 
215
  def forward(self, x: torch.Tensor) -> torch.Tensor:
216
  if self._use_timm:
217
+ feats = self.model.forward_features(x)
218
+ return feats[:, self._skip:]
219
  else:
220
  out = self.model(pixel_values=x)
221
  return out.last_hidden_state[:, self._skip:]
 
243
  p.requires_grad = False
244
 
245
  def unfreeze_last(self, n: int):
246
+ # Compatible with both AutoModel (Qwen3Model with .layers)
247
+ # and AutoModelForCausalLM (Qwen3ForCausalLM with .model.layers)
248
+ if hasattr(self.model, 'layers'):
249
+ layers = self.model.layers # AutoModel returns Qwen3Model directly
250
+ elif hasattr(self.model, 'model') and hasattr(self.model.model, 'layers'):
251
+ layers = self.model.model.layers # ForCausalLM wraps in .model
252
+ else:
253
+ log.warning("Could not find transformer layers in text encoder for unfreezing")
254
+ return
255
  for i, lay in enumerate(layers):
256
  if i >= len(layers) - n:
257
  for p in lay.parameters():
 
259
 
260
  def forward(self, input_ids, attention_mask):
261
  out = self.model(input_ids=input_ids, attention_mask=attention_mask)
262
+ return out.last_hidden_state
263
 
264
 
265
  # ──────────────────────────────────────────────────────────
 
272
  self.queries = nn.Parameter(torch.randn(1, cfg.num_evidence_tokens, D) * 0.02)
273
  self.vis_proj = nn.Sequential(nn.Linear(cfg.backbone_dim, D), nn.LayerNorm(D), nn.GELU(), nn.Linear(D, D))
274
  self.txt_proj = nn.Sequential(nn.Linear(cfg.text_dim, D), nn.LayerNorm(D), nn.GELU(), nn.Linear(D, D))
275
+ self.mod_emb = nn.Embedding(2, D)
276
  self.layers = nn.ModuleList([
277
  PerceiverLayer(D, cfg.num_heads) for _ in range(cfg.num_cross_attn_layers)
278
  ])
 
357
  self.step_emb = nn.Parameter(torch.randn(cfg.K + 1, 1, D) * 0.02)
358
  else:
359
  self.step_emb = None
 
360
  self.ev_proj = nn.Linear(cfg.evidence_dim, D) if cfg.evidence_dim != D else nn.Identity()
 
361
  self.predictor = nn.ModuleList([
362
  PredictorBlock(D, cfg.predictor_heads, cfg.predictor_ffn, use_gate=cfg.use_evidence_gate)
363
  for _ in range(cfg.num_predictor_layers)
 
378
  if self.step_emb is not None:
379
  z = z + self.step_emb[k]
380
  traj.append(z)
381
+ traj_t = torch.stack(traj, dim=1)
382
  B2, Kp1, N, D2 = traj_t.shape
383
  proj = self.out_proj(traj_t.reshape(-1, D2)).reshape(B2, Kp1, N, D2)
384
  return traj_t, traj[-1], proj
 
435
 
436
 
437
  class VICReg(nn.Module):
 
438
  def __init__(self, var_weight=1.0, cov_weight=0.04):
439
  super().__init__()
440
  self.var_w = var_weight
 
442
 
443
  def forward(self, z):
444
  if z.dim() == 3: z = z.reshape(-1, z.size(-1))
 
445
  std = z.std(dim=0)
446
  var_loss = F.relu(1.0 - std).mean()
 
447
  z_c = z - z.mean(dim=0, keepdim=True)
448
  N = z_c.size(0)
449
  cov = (z_c.T @ z_c) / max(N - 1, 1)
 
461
  self.vicreg = VICReg(cfg.vicreg_var, cfg.vicreg_cov) if cfg.use_vicreg else None
462
 
463
  def forward(self, pred_traj, target_traj, task_loss, gen_loss=None):
 
464
  p = pred_traj[:, 1:]
465
  t = target_traj[:, 1:]
466
  if self.cfg.loss_fn == "smooth_l1":
 
515
  q = self.pool_q.expand(B, -1, -1)
516
  z_n = self.pool_n(z_final)
517
  pooled, _ = self.pool_attn(q, z_n, z_n)
518
+ pooled = pooled.squeeze(1)
519
+ opt = self.opt_proj(opt_emb)
520
  z_exp = pooled.unsqueeze(1).expand_as(opt)
521
  combined = torch.cat([z_exp, opt, z_exp * opt], dim=-1)
522
  logits = self.scorer(combined).squeeze(-1)
 
557
  question = row["question"]
558
  hint = row.get("hint", "")
559
 
 
560
  text = question
561
  if hint:
562
  text += f" Context: {hint}"
 
584
  all_choices.append(s["choices"])
585
  answers.append(s["answer"])
586
 
 
587
  if hasattr(transform, '__call__'):
 
588
  pixel_values = torch.stack([transform(img) for img in images])
589
  else:
 
590
  pixel_values = transform(images=images, return_tensors="pt")["pixel_values"]
591
 
 
592
  tok = tokenizer(texts, padding="max_length", truncation=True, max_length=max_len, return_tensors="pt")
593
 
 
594
  opt_texts = []
595
  opt_mask = []
596
  for choices in all_choices:
 
631
  self._use_rollout = cfg.K > 0
632
 
633
  def encode_options(self, opt_ids, opt_mask_attn):
 
634
  with torch.no_grad():
635
+ h = self.txt(opt_ids, opt_mask_attn).float()
 
636
  mask_expanded = opt_mask_attn.unsqueeze(-1).float()
637
  pooled = (h * mask_expanded).sum(1) / mask_expanded.sum(1).clamp(min=1)
638
+ return pooled
639
 
640
  def forward(self, pixel_values, input_ids, attention_mask,
641
  opt_input_ids, opt_attention_mask, opt_mask, labels, batch_size, **kw):
 
643
  B = batch_size
644
  O = self.cfg.max_options
645
 
 
646
  with torch.no_grad():
647
+ vis_tok = self.vis(pixel_values).float()
648
 
 
649
  with torch.no_grad():
650
+ txt_tok = self.txt(input_ids, attention_mask).float()
651
 
652
+ evidence, _, ev_mask = self.evidence(vis_tok, txt_tok, attention_mask)
 
653
 
 
654
  if self._use_rollout:
655
  traj, z_final, z_proj = self.rollout(evidence)
656
  else:
 
662
  traj = z0.unsqueeze(1)
663
  z_proj = self.rollout.out_proj(z0).unsqueeze(1)
664
 
 
665
  target_proj = None
666
  if self._use_jepa and self.training:
667
  target_proj = self.target(vis_tok.detach(), txt_tok.detach(), attention_mask.detach())
668
 
669
+ opt_emb = self.encode_options(opt_input_ids, opt_attention_mask)
670
+ opt_emb = opt_emb.view(B, O, -1)
671
+ logits = self.disc(z_final, opt_emb, opt_mask)
 
672
  task_loss = F.cross_entropy(logits, labels)
673
 
 
674
  if self._use_jepa and self.training and target_proj is not None:
675
  losses = self.jepa_loss(z_proj, target_proj, task_loss)
676
  else:
 
694
 
695
  os.makedirs(cfg.output_dir, exist_ok=True)
696
 
 
697
  try:
698
  import trackio
699
  tracker = trackio.init(name=cfg.run_name, project="MR-JEPA")
 
702
  log.warning(f"Trackio init failed: {e}")
703
  tracker = None
704
 
 
705
  log.info("Building model...")
706
  model = MRJEPAModel(cfg)
707
 
 
708
  total_p = sum(p.numel() for p in model.parameters())
709
  train_p = sum(p.numel() for p in model.parameters() if p.requires_grad)
710
  log.info(f"Total params: {total_p:,} | Trainable: {train_p:,} ({100*train_p/total_p:.1f}%)")
711
 
712
  model = model.to(device)
713
 
 
714
  transform = model.vis.get_transform()
715
  tokenizer = model.txt.tokenizer
716
 
 
727
  eval_dl = DataLoader(eval_ds, batch_size=cfg.batch_size, shuffle=False, num_workers=cfg.num_workers,
728
  collate_fn=coll, pin_memory=True)
729
 
 
730
  trainable = [p for p in model.parameters() if p.requires_grad]
731
  optimizer = AdamW(trainable, lr=cfg.lr, weight_decay=cfg.weight_decay)
732
  total_steps = cfg.epochs * len(train_dl) // cfg.grad_accum
 
740
 
741
  scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda)
742
 
 
743
  log.info(f"Starting training: {cfg.epochs} epochs, {len(train_dl)} batches/epoch, "
744
  f"grad_accum={cfg.grad_accum}, total_steps≈{total_steps}")
745
 
 
755
  optimizer.zero_grad()
756
 
757
  for batch_idx, batch in enumerate(train_dl):
 
758
  batch = {k: v.to(device) if isinstance(v, torch.Tensor) else v for k, v in batch.items()}
759
 
760
  with torch.autocast(device_type="cuda", dtype=amp_dtype, enabled=cfg.bf16 and device.type == "cuda"):
 
771
  model.update_target(global_step, total_steps)
772
  global_step += 1
773
 
 
774
  for k, v in losses.items():
775
  if isinstance(v, torch.Tensor):
776
  epoch_losses[k].append(v.item())
 
778
  epoch_correct += correct
779
  epoch_total += batch["batch_size"]
780
 
 
781
  if batch_idx % 50 == 0:
782
  avg = {k: np.mean(v[-50:]) for k, v in epoch_losses.items()}
783
  acc = epoch_correct / max(epoch_total, 1) * 100
 
801
  })
802
  except: pass
803
 
 
804
  train_acc = epoch_correct / max(epoch_total, 1) * 100
805
  eval_acc = evaluate(model, eval_dl, device, cfg)
806
  log.info(f"=== Epoch {epoch} done | Train acc: {train_acc:.1f}% | Eval acc: {eval_acc:.1f}% ===")
 
819
 
820
  log.info(f"Training complete. Best eval accuracy: {best_acc:.1f}%")
821
 
 
822
  if cfg.push_to_hub:
823
  push_results(cfg, best_acc)
824
 
 
859
 
860
 
861
  def push_results(cfg, best_acc):
 
862
  try:
863
  from huggingface_hub import HfApi
864
  api = HfApi()
 
889
  repo_id=cfg.hub_model_id,
890
  repo_type="model",
891
  )
 
892
  best_ckpt = os.path.join(cfg.output_dir, "checkpoint_best.pt")
893
  if os.path.exists(best_ckpt):
894
  api.upload_file(
 
918
  p.add_argument("--no_jepa", action="store_true")
919
  p.add_argument("--no_rollout", action="store_true")
920
  p.add_argument("--no_evidence_gate", action="store_true")
921
+ p.add_argument("--no_sigreg", action="store_true")
922
+ p.add_argument("--use_vicreg", action="store_true")
923
  p.add_argument("--purist", action="store_true")
924
  p.add_argument("--sigreg_weight", type=float, default=0.1)
925
  p.add_argument("--max_train_samples", type=int, default=0)
 
954
 
955
  if args.no_rollout:
956
  cfg.K = 0
 
 
 
 
 
957
  cfg.use_jepa = False
958
 
959
  if args.purist: