fractus-cte / fractus /train /ar_loss.py
thefinalboss's picture
Upload fractus/train/ar_loss.py with huggingface_hub
783922b verified
Raw History Blame
2.77 kB
"""AR-facing training extras: scheduled-sampling mix, SS_PROB ramp,
anti-repeat log-prob (penalize echoing the last input token).
Used by scripts/fast4gpu_boost_v4.py. Shapes-compatible with the v3 CE path.
"""
from __future__ import annotations
import torch
import torch.nn.functional as F
from torch.utils.checkpoint import checkpoint
def ss_prob_at(
tokens_done: int,
start: float = 0.2,
end: float = 0.5,
ramp_tokens: int = 50_000_000,
) -> float:
"""Linear ramp start → end over ramp_tokens. Clamped to [start, end]."""
if ramp_tokens <= 0:
return float(end)
t = min(1.0, max(0.0, tokens_done / float(ramp_tokens)))
return float(start + (end - start) * t)
def mix_scheduled_sampling(
chunk: torch.Tensor,
sampled: torch.Tensor,
ss_prob: float,
) -> torch.Tensor:
"""Replace a fraction of *previous* tokens with model samples.
Position 0 is never replaced (need a real start). `sampled[:, t]` is the
model's draw at t; we feed it as input at t+1 (standard SS).
"""
B, C = chunk.shape
use_ss = torch.rand(B, C, device=chunk.device) < ss_prob
use_ss[:, 0] = False
prev = torch.cat([chunk[:, :1], sampled[:, :-1]], dim=1)
return torch.where(use_ss, prev, chunk)
def _chunk_repeat_logit(hi: torch.Tensor, weight: torch.Tensor, pi: torch.Tensor) -> torch.Tensor:
# RAW logit of the echo class, not logprob. Tied-head + residual makes
# p(echo)≈1 at init so log p saturates at 0 and d(log p)/dθ ≈ 0.
logits = F.linear(hi, weight).float()
return logits.gather(-1, pi.view(-1, 1)).sum()
def chunked_repeat_logprob(
h: torch.Tensor,
weight: torch.Tensor,
prev_ids: torch.Tensor,
ce_chunk: int = 8192,
) -> torch.Tensor:
"""Mean logit of class prev_ids[i] at row i.
Name kept for call-sites. Add λ · this to the loss to suppress
last-token echo. Memory-flat like chunked CE.
"""
assert h.dim() == 2 and prev_ids.dim() == 1 and h.shape[0] == prev_ids.shape[0]
n = h.shape[0]
if n == 0:
return h.new_zeros((), dtype=torch.float32)
total = None
for i in range(0, n, ce_chunk):
hi = h[i : i + ce_chunk]
pi = prev_ids[i : i + ce_chunk]
if torch.is_grad_enabled() and n > ce_chunk:
part = checkpoint(_chunk_repeat_logit, hi, weight, pi, use_reentrant=False)
else:
part = _chunk_repeat_logit(hi, weight, pi)
total = part if total is None else total + part
return total / n
def echo_frac(generated: list[int], last_prompt_id: int) -> float:
"""Fraction of greedy tokens equal to the last prompt token."""
if not generated:
return 0.0
return sum(1 for t in generated if t == last_prompt_id) / len(generated)