Scott/Codex commited on
Commit ·
5c319bd
1
Parent(s): 4d8ed2f
Fold VRAM-first dblock mode into AGILLM4
Browse files- README.md +14 -3
- dblocks_train.py +67 -0
- nB300_agillm4_vram_dblock.py +0 -0
- relaunch_agillm4_dblock.sh +20 -0
README.md
CHANGED
|
@@ -37,6 +37,14 @@ whose released code is ViT/classification only.
|
|
| 37 |
| 4096 | 11.08 GB | 8.68 GB |
|
| 38 |
| 8192 | 22.11 GB | 19.37 GB |
|
| 39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
## Honest findings
|
| 41 |
- DiffusionBlocks and gradient-checkpointing are **substitutes** for activation
|
| 42 |
memory; with checkpointing on, the 28->7 layer saving is only ~1.1-1.3x.
|
|
@@ -44,8 +52,11 @@ whose released code is ViT/classification only.
|
|
| 44 |
- The long-context ceiling (16k+) is **attention-bound**, so the next lever is the
|
| 45 |
sublinear attention backend, not more block/CE work.
|
| 46 |
|
| 47 |
-
Status:
|
| 48 |
-
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
License: Apache-2.0 (matching the upstream method).
|
|
|
|
| 37 |
| 4096 | 11.08 GB | 8.68 GB |
|
| 38 |
| 8192 | 22.11 GB | 19.37 GB |
|
| 39 |
|
| 40 |
+
## Official-line integration snapshot (2026-05-29)
|
| 41 |
+
- `nB300_agillm4_vram_dblock.py` is the patched official trainer snapshot from the Vast box.
|
| 42 |
+
- `dblocks_train.py` is the folded-in low-VRAM training step used by `--dblock`.
|
| 43 |
+
- `relaunch_agillm4_dblock.sh` restarts the official line with `--dblock --tie_weights --attn_backend sublinear`.
|
| 44 |
+
- `--tie_weights` now means AR, SAT, and NAT share the embedding projection tensor. This drops the live parameter count from 1,213,418,242 to 716,595,202.
|
| 45 |
+
- Old untied checkpoint head matrices are intentionally skipped under tied mode; core weights still warm-start and the optimizer can rebuild.
|
| 46 |
+
- SAT now uses fused vocab-streaming CE in the dblock path, and the dblock step releases AR/SAT activations before moving to the next objective.
|
| 47 |
+
|
| 48 |
## Honest findings
|
| 49 |
- DiffusionBlocks and gradient-checkpointing are **substitutes** for activation
|
| 50 |
memory; with checkpointing on, the 28->7 layer saving is only ~1.1-1.3x.
|
|
|
|
| 52 |
- The long-context ceiling (16k+) is **attention-bound**, so the next lever is the
|
| 53 |
sublinear attention backend, not more block/CE work.
|
| 54 |
|
| 55 |
+
Status update 2026-05-29: Scott chose the VRAM-first route. The official AGILLM-4
|
| 56 |
+
training line is now the DiffusionBlocks mode folded into `nB300_agillm4.py`, with
|
| 57 |
+
`--dblock --tie_weights --attn_backend sublinear`. Checkpoint compatibility is best-effort
|
| 58 |
+
only: old untied AR/SAT/NAT head tensors are skipped when tied heads are active, and the
|
| 59 |
+
optimizer state is allowed to reset. The priority is lower VRAM over preserving every
|
| 60 |
+
old training assumption.
|
| 61 |
|
| 62 |
License: Apache-2.0 (matching the upstream method).
|
dblocks_train.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""DiffusionBlocks training mode folded into AGILLM-4 (gated by --dblock).
|
| 2 |
+
|
| 3 |
+
Block-wise EDM denoising on the real Encoder blocks, supervising AR + SAT(fixed+var)
|
| 4 |
+
+ NAT each step on ONE block, with grad-checkpointed layers and fused vocab-streaming
|
| 5 |
+
CE. Reuses the live data stream / optimizer / checkpointing of nB300_agillm4.
|
| 6 |
+
Lazy-imports nB300 inside functions to avoid a circular import.
|
| 7 |
+
"""
|
| 8 |
+
import math, random, numpy as np, torch, torch.nn as nn, torch.nn.functional as F
|
| 9 |
+
import torch.utils.checkpoint as _ck
|
| 10 |
+
from fused_ce import fused_ce
|
| 11 |
+
SD=0.5
|
| 12 |
+
def _cdf(x): return 0.5*(1+math.erf(x/math.sqrt(2)))
|
| 13 |
+
def _ppf(p): return float(torch.erfinv(torch.tensor(2*p-1.0))*math.sqrt(2))
|
| 14 |
+
def _block_sigmas(B,smin=0.002,smax=80.0,pm=-1.2,ps=1.2):
|
| 15 |
+
a,b=_cdf((math.log(smin)-pm)/ps),_cdf((math.log(smax)-pm)/ps)
|
| 16 |
+
return [float(np.exp(pm+ps*_ppf(a+(b-a)*(i/B)))) for i in range(B+1)]
|
| 17 |
+
def _edm_pre(s): s=s[:,None,None]; return SD**2/(s**2+SD**2), s*SD/(s**2+SD**2)**0.5, 1/(s**2+SD**2)**0.5
|
| 18 |
+
def _edm_w(s,wmax=5.0): return float(((s**2+SD**2)/(s*SD)**2).clamp(max=wmax).mean())
|
| 19 |
+
|
| 20 |
+
def _dblock_init(core, args):
|
| 21 |
+
B=int(getattr(args,"dblock_blocks",4)); L=len(core.blocks); sp=max(1,L//B)
|
| 22 |
+
asg=[list(range(i*sp,(i+1)*sp)) for i in range(B)]; asg[-1]=list(range((B-1)*sp,L))
|
| 23 |
+
print(f"[dblock] DiffusionBlocks mode: {L} layers -> {B} blocks {asg}")
|
| 24 |
+
print(f"[dblock] equi-prob sigma boundaries: {[round(x,3) for x in _block_sigmas(B)]}")
|
| 25 |
+
return {"B":B,"assign":asg,"bsig":_block_sigmas(B)}
|
| 26 |
+
|
| 27 |
+
def _dblock_step(core, ar_h, sat_h, nat_h, opt, scaler, args, ids, state):
|
| 28 |
+
import nB300_agillm4 as M
|
| 29 |
+
B=state["B"]; asg=state["assign"]; bs=state["bsig"]; T=ids.size(1)
|
| 30 |
+
bi=random.randrange(B); lo,hi=sorted([bs[bi],bs[bi+1]]); layers=asg[bi]
|
| 31 |
+
sig=torch.from_numpy(np.exp(np.random.uniform(math.log(max(lo,1e-4)),math.log(hi),ids.size(0))).astype("float32")).to(ids.device)
|
| 32 |
+
cs,co,ci=_edm_pre(sig); w=_edm_w(sig); SATB=M.SAT_BLOCK
|
| 33 |
+
# ---- AR: causal diffusion denoise ----
|
| 34 |
+
with M.amp(args.amp):
|
| 35 |
+
emb=core.emb(ids); zt=emb+sig[:,None,None]*torch.randn_like(emb); h=ci*zt
|
| 36 |
+
for li in layers: h=_ck.checkpoint(core.blocks[li], h, M.causal_mask(T), use_reentrant=False)
|
| 37 |
+
Dn=core.ln(cs*zt+co*h)
|
| 38 |
+
ar=w*fused_ce(Dn[:,:-1].contiguous(), ar_h.proj.weight, ids[:,1:].contiguous())
|
| 39 |
+
scaler.scale(ar).backward()
|
| 40 |
+
ar_val=float(ar.detach())
|
| 41 |
+
del emb, zt, h, Dn, ar
|
| 42 |
+
# ---- SAT: block-causal diffusion; fixed proj + variable gate ----
|
| 43 |
+
with M.amp(args.amp):
|
| 44 |
+
emb2=core.emb(ids); zt2=emb2+sig[:,None,None]*torch.randn_like(emb2); h2=ci*zt2
|
| 45 |
+
for li in layers: h2=_ck.checkpoint(core.blocks[li], h2, M.sat_mask(T), use_reentrant=False)
|
| 46 |
+
Ds=core.ln(cs*zt2+co*h2); last=Ds[:,-SATB:]
|
| 47 |
+
satf=fused_ce(last.contiguous(), sat_h.proj.weight, ids[:,1:SATB+1].contiguous())
|
| 48 |
+
satv=(M.EMIT_LAMBDA*F.cross_entropy(sat_h.gate(Ds[:,0].float()), torch.ones(ids.size(0),dtype=torch.long,device=ids.device))) if sat_h.gate is not None else 0.0
|
| 49 |
+
sat=w*(satf+satv)
|
| 50 |
+
scaler.scale(sat).backward()
|
| 51 |
+
sat_val=float(sat.detach())
|
| 52 |
+
del emb2, zt2, h2, Ds, last, satf, satv, sat
|
| 53 |
+
# ---- NAT: bidirectional mask-predict ----
|
| 54 |
+
nat_val=0.0
|
| 55 |
+
if nat_h is not None:
|
| 56 |
+
ratio=min(max(float(getattr(args,"nat_mask_ratio",0.5)),0.05),0.95)
|
| 57 |
+
with M.amp(args.amp):
|
| 58 |
+
nat_ids=ids.clone(); m=torch.rand(ids.shape,device=ids.device)<ratio
|
| 59 |
+
if not bool(m.any()): m[...,-1]=True
|
| 60 |
+
nat_ids[m]=M.BLANK; hn=core.emb(nat_ids)
|
| 61 |
+
for li in layers: hn=_ck.checkpoint(core.blocks[li], hn, None, use_reentrant=False)
|
| 62 |
+
Dnat=core.ln(hn)
|
| 63 |
+
nat=fused_ce(Dnat[m], nat_h.proj.weight, ids[m]); scaler.scale(nat).backward(); nat_val=float(nat.detach()); del nat_ids, m, hn, Dnat, nat
|
| 64 |
+
scaler.unscale_(opt)
|
| 65 |
+
nn.utils.clip_grad_norm_([p for g in opt.param_groups for p in g["params"]],1.0)
|
| 66 |
+
scaler.step(opt); scaler.update(); opt.zero_grad(set_to_none=True)
|
| 67 |
+
return ar_val+sat_val+nat_val
|
nB300_agillm4_vram_dblock.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
relaunch_agillm4_dblock.sh
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# OFFICIAL LINE: DiffusionBlocks block-wise denoising (low VRAM). Resumes newest ckpt.
|
| 3 |
+
set -Eeuo pipefail
|
| 4 |
+
cd /workspace/agillm-4
|
| 5 |
+
export TOKENIZERS_PARALLELISM=false
|
| 6 |
+
export TOKENIZER_ID="${TOKENIZER_ID:-deepseek-ai/DeepSeek-V4-Pro}"
|
| 7 |
+
export PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:512,expandable_segments:True
|
| 8 |
+
export AGILLM_ATTN_BACKEND="${AGILLM_ATTN_BACKEND:-sublinear}"
|
| 9 |
+
if [ -f /root/.cache/huggingface/token ]; then export HF_TOKEN="$(tr -d '\r\n' </root/.cache/huggingface/token)"; export HUGGING_FACE_HUB_TOKEN="$HF_TOKEN"; fi
|
| 10 |
+
SAVE_DIR=/workspace/agillm4_4090_ckpts
|
| 11 |
+
CKPT="$(ls -1t "$SAVE_DIR"/pretrain_step*.pt 2>/dev/null | head -1)"
|
| 12 |
+
[ -n "$CKPT" ] || { echo "no ckpt" >&2; exit 1; }
|
| 13 |
+
exec >> /workspace/agillm4_floor_train.log 2>&1
|
| 14 |
+
echo "RELAUNCH_AGILLM4_DBLOCK $(date -u +%Y-%m-%dT%H:%M:%SZ) resume=$CKPT --dblock blocks=${AGILLM4_DBLOCKS:-4} tie_weights=1 attn=${AGILLM_ATTN_BACKEND}"
|
| 15 |
+
exec python -u nB300_agillm4.py train --preset agillm4_floor --resume "$CKPT" \
|
| 16 |
+
--dblock --dblock_blocks "${AGILLM4_DBLOCKS:-4}" --tie_weights \
|
| 17 |
+
--batch_size 1 --block "${AGILLM4_BLOCK:-1280}" --amp --attn_backend "${AGILLM_ATTN_BACKEND}" --grad_checkpoint \
|
| 18 |
+
--optimizer paged_adamw8bit --sat_every 1 --nat_every 1 --nat_max_tokens 768 --nat_mask_ratio 0.5 \
|
| 19 |
+
--token_param_ratio 100 --save_dir "$SAVE_DIR" \
|
| 20 |
+
--save_every_sec 86400 --delta_every_steps 25000 --delta_max_keep 1 --max_ckpts 1
|