Minato Namikaze
Deploy to Hugging Face Spaces
2aed081
Raw
History Blame Contribute Delete
2.79 kB
import torch
import torch.nn as nn
def compute_pos_weight(loader, device):
"""
Walk the training data once to compute the empirical positive-class weight.
pos_weight = n_negative / n_positive (per BCEWithLogitsLoss convention).
Falls back to 10.0 if the data gives extreme values.
"""
n_pos = n_neg = 0
for batch in loader:
labels = batch["hal_label"].view(-1)
n_pos += (labels > 0.5).sum().item()
n_neg += (labels < 0.5).sum().item()
if n_pos == 0:
return torch.tensor([10.0]).to(device)
weight = n_neg / n_pos
# Clamp to [2, 20] β€” avoids degenerate regimes
weight = max(2.0, min(20.0, weight))
return torch.tensor([weight]).to(device)
def train_epoch(model, loader, optimizer, scheduler, pos_weight, grad_accum_steps=4):
"""
Train for one epoch.
Args
────
model : AgentSightModel
loader : DataLoader (batch_size=1, one trajectory per item)
optimizer : AdamW
scheduler : LR scheduler (stepped once per *gradient update*, not per batch)
pos_weight : tensor([float]) for BCEWithLogitsLoss
grad_accum_steps : accumulate gradients over N trajectories before stepping
(effective batch size = grad_accum_steps)
"""
model.train()
device = next(model.parameters()).device
bce = nn.BCEWithLogitsLoss(pos_weight=pos_weight.to(device))
total_loss = 0.0
optimizer.zero_grad()
for step_idx, batch in enumerate(loader):
input_ids = batch["input_ids"].squeeze(0).to(device)
attention_mask = batch["attention_mask"].squeeze(0).to(device)
hal_labels = batch["hal_label"].squeeze(0).float().to(device)
# Guard against out-of-vocabulary tokens (rare but seen in practice)
vocab_size = model.encoder.config.vocab_size
input_ids = torch.clamp(input_ids, 0, vocab_size - 1)
hal_logits = model(input_ids, attention_mask)
loss = bce(hal_logits, hal_labels) / grad_accum_steps
loss.backward()
total_loss += loss.item() * grad_accum_steps # un-scale for logging
# Gradient update every grad_accum_steps trajectories
if (step_idx + 1) % grad_accum_steps == 0:
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
optimizer.step()
if scheduler is not None:
scheduler.step()
optimizer.zero_grad()
# Flush remaining gradients
if len(loader) % grad_accum_steps != 0:
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
optimizer.step()
if scheduler is not None:
scheduler.step()
optimizer.zero_grad()
return total_loss / len(loader) if loader else 0.0