AbstractPhil commited on
Commit
afde2d1
Β·
verified Β·
1 Parent(s): a0c035e

Update train_cell_cifar_10_resnet.py

Browse files
Files changed (1) hide show
  1. train_cell_cifar_10_resnet.py +72 -5
train_cell_cifar_10_resnet.py CHANGED
@@ -29,6 +29,31 @@ import torchvision.transforms as T
29
 
30
  device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  # ── Conv building blocks ─────────────────────────────────────────
33
 
34
  class ConvBNAct(nn.Module):
@@ -228,6 +253,18 @@ print(f" Label smooth: {LABEL_SMOOTH}")
228
  criterion = nn.CrossEntropyLoss(label_smoothing=LABEL_SMOOTH)
229
  opt = torch.optim.AdamW(model.parameters(), lr=LR, weight_decay=WD)
230
 
 
 
 
 
 
 
 
 
 
 
 
 
231
  def cosine_lr(epoch):
232
  t = epoch / float(max(1, EPOCHS - 1))
233
  min_ratio = 1e-5 / LR
@@ -242,15 +279,35 @@ print(f"{'=' * 70}")
242
  best_acc = 0
243
  t0 = time.time()
244
 
 
 
 
 
245
  for epoch in range(1, EPOCHS + 1):
246
  model.train()
247
  correct, total = 0, 0
248
  loss_sum = 0
 
249
 
250
- for images, labels in tqdm(train_loader, desc=f"Ep {epoch:3d}", leave=False):
251
  images, labels = images.to(device), labels.to(device)
252
  out = model(images)
253
- loss = criterion(out['logits'], labels)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
 
255
  opt.zero_grad(set_to_none=True)
256
  loss.backward()
@@ -259,17 +316,21 @@ for epoch in range(1, EPOCHS + 1):
259
 
260
  correct += (out['logits'].argmax(-1) == labels).sum().item()
261
  total += images.shape[0]
262
- loss_sum += loss.item()
 
 
263
 
264
  sched.step()
265
  train_acc = correct / total
266
  avg_loss = loss_sum / len(train_loader)
 
267
 
268
  # Val
269
  model.eval()
270
  val_correct, val_total = 0, 0
271
  pcc = torch.zeros(10)
272
  pct = torch.zeros(10)
 
273
 
274
  with torch.no_grad():
275
  for images, labels in test_loader:
@@ -283,7 +344,14 @@ for epoch in range(1, EPOCHS + 1):
283
  pcc[c] += (preds[m] == labels[m]).sum().item()
284
  pct[c] += m.sum().item()
285
 
 
 
 
 
 
 
286
  val_acc = val_correct / val_total
 
287
  star = ''
288
  if val_acc > best_acc:
289
  best_acc = val_acc
@@ -292,7 +360,6 @@ for epoch in range(1, EPOCHS + 1):
292
  lr = opt.param_groups[0]['lr']
293
 
294
  if epoch <= 5 or epoch % 5 == 0 or epoch == EPOCHS:
295
- # Cell spectral diagnostics
296
  with torch.no_grad():
297
  S2 = out['cell2']['S_orig'].mean(dim=(0, 1))
298
  S3 = out['cell3']['S_orig'].mean(dim=(0, 1))
@@ -300,7 +367,7 @@ for epoch in range(1, EPOCHS + 1):
300
  s3_str = ', '.join(f'{v:.2f}' for v in S3.tolist()[:3]) + f'...{S3[-1]:.2f}'
301
 
302
  print(f" ep{epoch:3d} loss={avg_loss:.4f} acc={val_acc:.1%}{star} "
303
- f"train={train_acc:.1%} lr={lr:.6f}")
304
  print(f" S2=[{s2_str}] S3=[{s3_str}]")
305
 
306
  if epoch <= 3 or epoch % 20 == 0 or epoch == EPOCHS:
 
29
 
30
  device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
31
 
32
+ # ── Soft Hand CV ─────────────────────────────────────────────────
33
+
34
+ @torch.no_grad()
35
+ def compute_target_cv(V, D, n_trials=20, n_samples=200, device='cuda'):
36
+ """Geometric attractor CV for (V, D) on S^{D-1}.
37
+ Random unit vectors, pentachoron sampling, averaged.
38
+ """
39
+ cvs = []
40
+ for _ in range(n_trials):
41
+ pts = F.normalize(torch.randn(V, D, device=device), dim=-1)
42
+ cv = cv_of(pts, n_samples=n_samples)
43
+ if cv > 0:
44
+ cvs.append(cv)
45
+ return sum(cvs) / len(cvs) if cvs else 0.0
46
+
47
+ import math as _math
48
+
49
+ def cv_proximity(measured, target, sigma=0.15):
50
+ """Gaussian proximity: 1.0 at target, decays with distance."""
51
+ return _math.exp(-(measured - target) ** 2 / (2 * sigma ** 2))
52
+
53
+ def soft_hand_weights(proximity, boost=0.5, penalty_weight=0.3):
54
+ """Near target: boost primary, penalty→0. Far: primary baseline, penalty active."""
55
+ return 1.0 + boost * proximity, penalty_weight * (1.0 - proximity)
56
+
57
  # ── Conv building blocks ─────────────────────────────────────────
58
 
59
  class ConvBNAct(nn.Module):
 
253
  criterion = nn.CrossEntropyLoss(label_smoothing=LABEL_SMOOTH)
254
  opt = torch.optim.AdamW(model.parameters(), lr=LR, weight_decay=WD)
255
 
256
+ # Soft hand config
257
+ SIGMA = 0.15
258
+ BOOST = 0.5
259
+ PENALTY_WEIGHT = 0.3
260
+ CV_MEASURE_EVERY = 50 # batches
261
+
262
+ print(f"\nComputing target CV for V=16, D=16 on S^15...")
263
+ target_cv = compute_target_cv(16, 16, n_trials=20, device=device)
264
+ print(f" target_cv = {target_cv:.4f}")
265
+ print(f" soft hand: Οƒ={SIGMA}, boost={BOOST}, penalty={PENALTY_WEIGHT}")
266
+ print(f" CV measured every {CV_MEASURE_EVERY} batches")
267
+
268
  def cosine_lr(epoch):
269
  t = epoch / float(max(1, EPOCHS - 1))
270
  min_ratio = 1e-5 / LR
 
279
  best_acc = 0
280
  t0 = time.time()
281
 
282
+ # Soft hand state
283
+ last_cv = target_cv
284
+ last_prox = 1.0
285
+
286
  for epoch in range(1, EPOCHS + 1):
287
  model.train()
288
  correct, total = 0, 0
289
  loss_sum = 0
290
+ ep_prox_sum, ep_prox_n = 0, 0
291
 
292
+ for batch_idx, (images, labels) in enumerate(tqdm(train_loader, desc=f"Ep {epoch:3d}", leave=False)):
293
  images, labels = images.to(device), labels.to(device)
294
  out = model(images)
295
+ ce_loss = criterion(out['logits'], labels)
296
+
297
+ # Measure CV periodically from cell M rows
298
+ with torch.no_grad():
299
+ if batch_idx % CV_MEASURE_EVERY == 0:
300
+ # Sample from cell3 M rows (deeper features)
301
+ M_sample = out['cell3']['M'][0, 0] # (V, D)
302
+ current_cv = cv_of(M_sample, n_samples=100)
303
+ if current_cv > 0:
304
+ last_cv = current_cv
305
+ last_prox = cv_proximity(last_cv, target_cv, SIGMA)
306
+
307
+ # Soft hand adaptive weights
308
+ primary_w, cv_w = soft_hand_weights(last_prox, BOOST, PENALTY_WEIGHT)
309
+ cv_l = (last_cv - target_cv) ** 2
310
+ loss = primary_w * ce_loss + cv_w * cv_l
311
 
312
  opt.zero_grad(set_to_none=True)
313
  loss.backward()
 
316
 
317
  correct += (out['logits'].argmax(-1) == labels).sum().item()
318
  total += images.shape[0]
319
+ loss_sum += ce_loss.item()
320
+ ep_prox_sum += last_prox
321
+ ep_prox_n += 1
322
 
323
  sched.step()
324
  train_acc = correct / total
325
  avg_loss = loss_sum / len(train_loader)
326
+ avg_prox = ep_prox_sum / max(1, ep_prox_n)
327
 
328
  # Val
329
  model.eval()
330
  val_correct, val_total = 0, 0
331
  pcc = torch.zeros(10)
332
  pct = torch.zeros(10)
333
+ val_cv_samples = []
334
 
335
  with torch.no_grad():
336
  for images, labels in test_loader:
 
344
  pcc[c] += (preds[m] == labels[m]).sum().item()
345
  pct[c] += m.sum().item()
346
 
347
+ # Measure val CV
348
+ for i in range(min(4, out['cell3']['M'].shape[0])):
349
+ cv_val = cv_of(out['cell3']['M'][i, 0], n_samples=100)
350
+ if cv_val > 0:
351
+ val_cv_samples.append(cv_val)
352
+
353
  val_acc = val_correct / val_total
354
+ val_cv = sum(val_cv_samples) / len(val_cv_samples) if val_cv_samples else 0.0
355
  star = ''
356
  if val_acc > best_acc:
357
  best_acc = val_acc
 
360
  lr = opt.param_groups[0]['lr']
361
 
362
  if epoch <= 5 or epoch % 5 == 0 or epoch == EPOCHS:
 
363
  with torch.no_grad():
364
  S2 = out['cell2']['S_orig'].mean(dim=(0, 1))
365
  S3 = out['cell3']['S_orig'].mean(dim=(0, 1))
 
367
  s3_str = ', '.join(f'{v:.2f}' for v in S3.tolist()[:3]) + f'...{S3[-1]:.2f}'
368
 
369
  print(f" ep{epoch:3d} loss={avg_loss:.4f} acc={val_acc:.1%}{star} "
370
+ f"train={train_acc:.1%} cv={val_cv:.4f} prox={avg_prox:.3f} lr={lr:.6f}")
371
  print(f" S2=[{s2_str}] S3=[{s3_str}]")
372
 
373
  if epoch <= 3 or epoch % 20 == 0 or epoch == EPOCHS: