Upload scripts/mix64k_h200.sh with huggingface_hub
Browse files- scripts/mix64k_h200.sh +343 -0
scripts/mix64k_h200.sh
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# =============================================================================
|
| 3 |
+
# 32k -> 64k extension on the MIXED corpus. 8x H200, cp=4 dp=2.
|
| 4 |
+
#
|
| 5 |
+
# THE EXPERIMENT
|
| 6 |
+
# The 2026-09-12 run extended to 64k on 100% b5 (every doc >= 65,536 tokens)
|
| 7 |
+
# and destroyed retrieval: 67/65/52/29 -> 45/17/2/1 at 8k/16k/32k/64k, while
|
| 8 |
+
# training loss FELL 2.89 -> 1.14. Data composition is the leading suspect
|
| 9 |
+
# (100% long-context data is documented to cause severe degradation).
|
| 10 |
+
# This run changes ONE variable: the data is now 50% b5 / 30% b3 / 20% b1.
|
| 11 |
+
# LR, schedule shape, parent and window are all held identical so a null
|
| 12 |
+
# result is attributable.
|
| 13 |
+
#
|
| 14 |
+
# PARENT is ext64k step 695 -- the SAME starting point the failed cont64k run
|
| 15 |
+
# used, so the only changed variable is the data. Its own measured scores
|
| 16 |
+
# (67/65/52/29, logs/ext64k_len_*/summary.csv on HF) are the step-0 baseline.
|
| 17 |
+
# Starting instead from the 32k model would top out at 1,714 cumulative 64k
|
| 18 |
+
# steps, short of the 1,894 where the cliff actually appeared -- the run could
|
| 19 |
+
# miss the failure regime entirely and prove nothing.
|
| 20 |
+
#
|
| 21 |
+
# WHY cp=4 dp=2 AND NOT cp=8
|
| 22 |
+
# 16,384 tok/rank MEASURED 108,493 MB, which fits H200's 143 GB with room.
|
| 23 |
+
# dp=2 halves the micro-steps per optimizer step (8 -> 4) instead of adding
|
| 24 |
+
# ring hops: ~6.75 s/step projected vs 7.81 measured for cp=8 on H100.
|
| 25 |
+
#
|
| 26 |
+
# SEGMENTED, BECAUSE EVAL NEEDS THE GPUS
|
| 27 |
+
# CP_MAX_STEPS is ALWAYS the full 1714 -- never the segment target. That
|
| 28 |
+
# keeps the WSD schedule identical across segments and keeps the guard at
|
| 29 |
+
# bluscriptCP.cpp:1006 (warmup+stable >= max_steps-1) satisfied. A watcher
|
| 30 |
+
# stops the trainer when the segment's checkpoint lands. Resumes pin
|
| 31 |
+
# CP_REWARMUP_ANCHOR=0; its default (-1) re-anchors warmup to start_step and
|
| 32 |
+
# would re-ramp the LR at every resume.
|
| 33 |
+
#
|
| 34 |
+
# WHAT KILLED THE LAST RUN'S POST-MORTEM
|
| 35 |
+
# CP_CKPT_KEEP=2 pruned every checkpoint before the collapse, so the last
|
| 36 |
+
# good model was unrecoverable and the failure could not be bisected. Here a
|
| 37 |
+
# janitor preserves rank_0 of EVERY save (3.75 GB; eval only ever loads
|
| 38 |
+
# rank_0 at NP=1) under a prefix the pruner cannot match, and uploads on a
|
| 39 |
+
# coarser cadence. Worst case you lose 100 steps.
|
| 40 |
+
#
|
| 41 |
+
# COLLAPSE DETECTION IS FREE AND CONTINUOUS
|
| 42 |
+
# The cliff is visible in training loss long before any eval: the failed run
|
| 43 |
+
# went 2.886 -> 2.642 -> 2.185 across steps 1100-1249 on a 50-step mean.
|
| 44 |
+
# detect_cliff() compares the current 50-step mean to the 200-700 baseline.
|
| 45 |
+
# Loss CANNOT prove retrieval is intact -- that is the whole lesson -- so the
|
| 46 |
+
# evals still run; the detector only decides WHICH checkpoints to evaluate.
|
| 47 |
+
# =============================================================================
|
| 48 |
+
set -uo pipefail
|
| 49 |
+
cd "$(dirname "$0")"
|
| 50 |
+
ROOT="$(cd ../.. && pwd)"
|
| 51 |
+
export LD_LIBRARY_PATH="$ROOT/Tensor-Implementations/lib:$ROOT/Profiler/lib:${LD_LIBRARY_PATH:-}"
|
| 52 |
+
export OMPI_ALLOW_RUN_AS_ROOT=1 OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1
|
| 53 |
+
export PATH=/usr/local/cuda/bin:$PATH
|
| 54 |
+
|
| 55 |
+
PY=${PY:-/root/Adhi/BluTrain/envv/bin/python3}
|
| 56 |
+
HF=${HF:-/root/Adhi/BluTrain/envv/bin/hf}
|
| 57 |
+
EXE=./build/bluscriptCP_exec
|
| 58 |
+
CK=checkpoints_bluscriptcp/rank_0
|
| 59 |
+
LOG=mix64k_logs; mkdir -p "$LOG"
|
| 60 |
+
M="$LOG/master.log"
|
| 61 |
+
REPO=unparallelled/BluScriptCP
|
| 62 |
+
|
| 63 |
+
T=65536; NP=8; CPSZ=4; GPUS=0,1,2,3,4,5,6,7 # dp = NP/CPSZ = 2
|
| 64 |
+
ORIG=32768; SCALE=2 # 2 * 32768 == 65536
|
| 65 |
+
STEPS=${STEPS:-1300} # past the cliff with margin; mix50 allows 1716
|
| 66 |
+
WARM=100; TAIL=2; FREQ=100; KEEP=2
|
| 67 |
+
STABLE=$(( STEPS - WARM - TAIL )) # plateau throughout, like the old ext64k
|
| 68 |
+
GB=524288
|
| 69 |
+
DATA=Data_Loader/Data/mix50
|
| 70 |
+
PARENT="$CK/ext64k_step695.ckpt"
|
| 71 |
+
PARENT_SZ=5225752094
|
| 72 |
+
HAY="$PWD/Data_Loader/Data/b2_8192_16384/val_shard_00000.bin"
|
| 73 |
+
EVAL_A=${EVAL_A:-695} # A/B point vs the failed ext64k
|
| 74 |
+
EVAL_B=${EVAL_B:-1200} # collapse check
|
| 75 |
+
UPLOAD_EVERY=${UPLOAD_EVERY:-300}
|
| 76 |
+
|
| 77 |
+
say(){ echo "[$(date -u +%H:%M:%S)] $*" | tee -a "$M" >&2; }
|
| 78 |
+
die(){ say "FATAL: $*"; exit 1; }
|
| 79 |
+
|
| 80 |
+
# ---------------------------------------------------------------- preflight --
|
| 81 |
+
$PY -c "import tiktoken" 2>/dev/null || die "tiktoken missing (eval would score nothing, silently)"
|
| 82 |
+
[ -x "$EXE" ] || die "$EXE not built"
|
| 83 |
+
STAMP=build/objects/.cp_rope_flags
|
| 84 |
+
[ -f "$STAMP" ] || die "no build-flag stamp; rebuild: make CP_FUSED_ROPE=1 CP_ATTN_SPLIT=1 SM_ARCH=90 bluscript-cp -j\$(nproc)"
|
| 85 |
+
grep -q 'CP_FUSED_ROPE=1' "$STAMP" && grep -q 'CP_ATTN_SPLIT=1' "$STAMP" || \
|
| 86 |
+
die "binary lacks the split kernels ($(cat "$STAMP")). CP_ATTN_FUSION=split will throw at the first forward. Rebuild: make CP_FUSED_ROPE=1 CP_ATTN_SPLIT=1 SM_ARCH=90 bluscript-cp -j\$(nproc)"
|
| 87 |
+
[ -f "$PARENT" ] || die "parent missing: $PARENT"
|
| 88 |
+
[ "$(stat -c%s "$PARENT")" = "$PARENT_SZ" ] || die "parent wrong size: $(stat -c%s "$PARENT") (want $PARENT_SZ)"
|
| 89 |
+
[ -f "$HAY" ] || die "haystack missing: $HAY"
|
| 90 |
+
[ -d "$DATA" ] || die "$DATA missing -- run mix_buckets.py first"
|
| 91 |
+
[ $((SCALE*ORIG)) -eq $T ] || die "YaRN invariant: $SCALE * $ORIG != $T"
|
| 92 |
+
[ $((NP % CPSZ)) -eq 0 ] || die "NP=$NP not divisible by CP_SIZE=$CPSZ"
|
| 93 |
+
[ $(( WARM + STABLE )) -lt $(( STEPS - 1 )) ] || die "illegal WSD: $WARM+$STABLE >= $STEPS-1"
|
| 94 |
+
# the manifest counts batches the way DataLoaderLite actually consumes them;
|
| 95 |
+
# total_tokens//global_batch silently overshoots and the loader wraps in silence
|
| 96 |
+
if [ -f "$DATA/MANIFEST.txt" ]; then
|
| 97 |
+
USABLE=$(awk '/^usable_steps/{print $2}' "$DATA/MANIFEST.txt")
|
| 98 |
+
[ "$STEPS" -le "$USABLE" ] || die "STEPS=$STEPS exceeds usable_steps=$USABLE -- the loader would WRAP silently"
|
| 99 |
+
say "manifest: usable_steps=$USABLE, running $STEPS"
|
| 100 |
+
else
|
| 101 |
+
say "WARN: no MANIFEST.txt in $DATA -- cannot rule out a silent loader wrap"
|
| 102 |
+
fi
|
| 103 |
+
FREEG=$(df -BG --output=avail / | tail -1 | tr -dc 0-9)
|
| 104 |
+
[ "$FREEG" -ge 120 ] || die "only ${FREEG} GB free; need ~120 (30 GB live ckpts + ~64 GB preserved rank_0)"
|
| 105 |
+
|
| 106 |
+
say "=== 32k -> 64k on the MIXED corpus (8x H200, cp=$CPSZ dp=$((NP/CPSZ))) ==="
|
| 107 |
+
say "parent: ext64k step 695 (64k model, 67/65/52/29) -- weights-only: its 4-rank ZeRO sidecars cannot load at world_size 8"
|
| 108 |
+
say "data: $DATA $(awk '/^mix /{printf "%s=%s ", $2, $4}' "$DATA/MANIFEST.txt" 2>/dev/null)"
|
| 109 |
+
say "YaRN: SCALE=$SCALE ORIG=$ORIG -> $((SCALE*ORIG)) == CP_T=$T OK"
|
| 110 |
+
say "WSD: warmup $WARM, stable $STABLE, tail $TAIL -> plateau at 1.8e-4 throughout"
|
| 111 |
+
say "evals: $EVAL_A (mid-run health), $EVAL_B (the failed run cliffed at its step 1199)"
|
| 112 |
+
say "disk: ${FREEG} GB free"
|
| 113 |
+
|
| 114 |
+
ARCH="CP_N_EMBD=1536 CP_N_LAYER=36 CP_N_HEAD=12 CP_N_KVHEAD=4 CP_FFN=4096 CP_WEIGHT_TYING=0"
|
| 115 |
+
BASE="CP_SIZE=$CPSZ CP_ATTN_MODE=ring CP_ATTN_FUSION=split CP_T=$T \
|
| 116 |
+
YARN_SCALE=$SCALE YARN_ORIG_MAXPOS=$ORIG \
|
| 117 |
+
CP_ZERO=1 CP_ZERO_V2=1 CP_ZERO_SYMMETRIC=1 CP_BF16=1 CP_OPTIMIZER=muon \
|
| 118 |
+
CP_WARMUP=$WARM CP_REWARMUP=$WARM CP_REWARMUP_PEAK=0.3 \
|
| 119 |
+
CP_STABLE=$STABLE CP_CKPT=1 CP_CKPT_FREQ=$FREQ CP_CKPT_KEEP=$KEEP"
|
| 120 |
+
|
| 121 |
+
# ------------------------------------------------------------- upload helper --
|
| 122 |
+
# A missing source is a LOUD failure and every upload is confirmed by the byte
|
| 123 |
+
# size HF reports back. The old helper was `[ -e "$1" ] || return 0`, which
|
| 124 |
+
# returned SUCCESS when the file was absent -- that is how 17 GB of checkpoints
|
| 125 |
+
# were lost with nothing in the log.
|
| 126 |
+
up_verify(){ # src dst
|
| 127 |
+
local src=$1 dst=$2
|
| 128 |
+
[ -f "$src" ] || { say " MISSING SOURCE (NOT UPLOADED): $src"; return 1; }
|
| 129 |
+
$HF upload "$REPO" "$src" "$dst" >>"$LOG/upload.log" 2>&1 || { say " UPLOAD FAILED $dst"; return 1; }
|
| 130 |
+
$PY - "$REPO" "$src" "$dst" <<'PYEOF'
|
| 131 |
+
import sys, os
|
| 132 |
+
from huggingface_hub import HfApi
|
| 133 |
+
repo, loc, rem = sys.argv[1], sys.argv[2], sys.argv[3]
|
| 134 |
+
want = os.path.getsize(loc)
|
| 135 |
+
got = {f.rfilename: (f.size or 0) for f in HfApi().repo_info(repo, files_metadata=True).siblings}.get(rem)
|
| 136 |
+
print((" VERIFIED " if got == want else " *** MISMATCH/ABSENT ") + f"{rem} local={want:,} remote={got}")
|
| 137 |
+
sys.exit(0 if got == want else 1)
|
| 138 |
+
PYEOF
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
# ---------------------------------------------------------------- janitor ----
|
| 142 |
+
# Preserve rank_0 of EVERY save before CP_CKPT_KEEP can prune it, and upload on
|
| 143 |
+
# a coarser cadence (HF deletes do not reclaim LFS storage in this repo).
|
| 144 |
+
start_janitor(){
|
| 145 |
+
( mkdir -p "$LOG/keep"
|
| 146 |
+
while sleep 45; do
|
| 147 |
+
RN=$(cat "$LOG/runnum" 2>/dev/null) || continue
|
| 148 |
+
for f in $CK/blumodelcp_run${RN}_step_*.ckpt; do
|
| 149 |
+
[ -e "$f" ] || continue
|
| 150 |
+
case "$f" in *keep_*) continue;; esac
|
| 151 |
+
S=$(basename "$f" | grep -oE 'step_[0-9]+' | cut -d_ -f2)
|
| 152 |
+
[ -n "$S" ] || continue
|
| 153 |
+
K="$CK/keep_step_${S}.ckpt"
|
| 154 |
+
if [ ! -f "$K" ]; then
|
| 155 |
+
cp "$f" "$K" 2>/dev/null && echo "$S" >> "$LOG/keep/preserved.txt"
|
| 156 |
+
if [ $(( S % UPLOAD_EVERY )) -eq 0 ]; then
|
| 157 |
+
up_verify "$K" "checkpoints/mix64k_step_${S}.ckpt" >>"$M" 2>&1 &
|
| 158 |
+
fi
|
| 159 |
+
fi
|
| 160 |
+
done
|
| 161 |
+
# drop orphaned sidecars: CP_CKPT_KEEP prunes .ckpt but never .zero_g*,
|
| 162 |
+
# the leak that filled a 193 GB disk on 2026-09-09
|
| 163 |
+
for s in checkpoints_bluscriptcp/rank_*/*.zero_g*; do
|
| 164 |
+
[ -e "$s" ] || continue
|
| 165 |
+
[ -f "${s%.zero_g*}.ckpt" ] || rm -f "$s"
|
| 166 |
+
done
|
| 167 |
+
done ) & JANITOR=$!
|
| 168 |
+
}
|
| 169 |
+
stop_janitor(){ kill "${JANITOR:-}" 2>/dev/null; wait "${JANITOR:-}" 2>/dev/null; }
|
| 170 |
+
trap 'stop_janitor' EXIT
|
| 171 |
+
|
| 172 |
+
# ------------------------------------------------------------ cliff detector --
|
| 173 |
+
# Verdict from the 50-step rolling mean vs the 200-700 baseline. The failed run:
|
| 174 |
+
# baseline ~2.87, then 2.642 (steps 1150-1199) and 2.185 (1200-1249). Per-step
|
| 175 |
+
# SD is ~0.16, so a 50-step mean has SE ~0.023 and a 0.20 drop is ~9 sigma --
|
| 176 |
+
# this cannot fire on noise.
|
| 177 |
+
detect_cliff(){ # prints: CLEAN|CLIFF <onset_step> <baseline> <current>
|
| 178 |
+
$PY - "$LOG/train.log" <<'PYEOF'
|
| 179 |
+
import re, sys, statistics as st
|
| 180 |
+
rows = [(int(a), float(b)) for a, b in
|
| 181 |
+
re.findall(r"^step +(\d+) \| loss: ([0-9.]+)", open(sys.argv[1], errors="ignore").read(), re.M)]
|
| 182 |
+
# NODATA, never CLEAN, when the baseline cannot be computed. Reporting "clean"
|
| 183 |
+
# because the evidence is missing is the failure direction that matters here --
|
| 184 |
+
# b4cont/train.log does exactly this, since it resumes at step 750 and has no
|
| 185 |
+
# steps in the 200-700 baseline window.
|
| 186 |
+
if len(rows) < 250:
|
| 187 |
+
print("NODATA 0 0 0"); raise SystemExit
|
| 188 |
+
d = dict(rows)
|
| 189 |
+
base = [d[s] for s in range(200, 701) if s in d]
|
| 190 |
+
if len(base) < 100:
|
| 191 |
+
print("NODATA 0 0 0"); raise SystemExit
|
| 192 |
+
B = st.mean(base)
|
| 193 |
+
THRESH = B - 0.20
|
| 194 |
+
onset = 0
|
| 195 |
+
for s in range(750, max(d) + 1):
|
| 196 |
+
w = [d[k] for k in range(s - 49, s + 1) if k in d]
|
| 197 |
+
if len(w) == 50 and st.mean(w) < THRESH:
|
| 198 |
+
onset = s; break
|
| 199 |
+
cur = st.mean([d[k] for k in range(max(d) - 49, max(d) + 1) if k in d] or [B])
|
| 200 |
+
print(f"{'CLIFF' if onset else 'CLEAN'} {onset} {B:.3f} {cur:.3f}")
|
| 201 |
+
PYEOF
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
# ------------------------------------------------------------------ probe ----
|
| 205 |
+
if [ ! -f "$LOG/probe.ok" ]; then
|
| 206 |
+
# grad_accum=1 means global_batch == B*T*dp_size, NOT T. At dp=2 the minimum
|
| 207 |
+
# micro-batch is 131,072 tokens; passing T alone dies in the divisibility
|
| 208 |
+
# guard (bluscriptCP.cpp:1462) before the first step, and MPI_ABORT eats the
|
| 209 |
+
# message so the log shows only "MPI_ABORT ... errorcode 1".
|
| 210 |
+
say "PHASE 1: 3-step probe ($(( T / CPSZ )) tok/rank, global_batch $(( T * NP / CPSZ )))"
|
| 211 |
+
env $ARCH $BASE CP_STABLE=0 CP_CKPT=0 \
|
| 212 |
+
CP_INIT_FROM="$PWD/$PARENT" CP_DATA_ROOT="$PWD/$DATA" \
|
| 213 |
+
CP_B=1 CP_GLOBAL_BATCH=$(( T * NP / CPSZ )) CP_MAX_STEPS=3 \
|
| 214 |
+
CUDA_VISIBLE_DEVICES=$GPUS mpirun --allow-run-as-root -np $NP "$EXE" \
|
| 215 |
+
> "$LOG/probe.log" 2>&1
|
| 216 |
+
RC=$?
|
| 217 |
+
# The probe must PROVE it trained. An earlier version only grepped for OOM,
|
| 218 |
+
# so a probe that aborted on every rank was called "PROBE OK" and the real
|
| 219 |
+
# run died 73 s later.
|
| 220 |
+
grep -qaiE 'out of memory|bad_alloc' "$LOG/probe.log" && { say "PROBE OOM at cp=$CPSZ"; exit 1; }
|
| 221 |
+
grep -qaE 'terminate called|std::runtime_error|Signal: Aborted' "$LOG/probe.log" && {
|
| 222 |
+
say "PROBE CRASHED:"; grep -aA1 'terminate called' "$LOG/probe.log" | grep -a 'what()' | head -1 >&2; exit 1; }
|
| 223 |
+
NSTEP=$(grep -acE '^step +[0-9]+ \|' "$LOG/probe.log")
|
| 224 |
+
PEAK=$(grep -aoE 'mem: [0-9]+MB' "$LOG/probe.log" | tail -1)
|
| 225 |
+
[ "$RC" -eq 0 ] && [ "$NSTEP" -ge 1 ] && [ -n "$PEAK" ] || {
|
| 226 |
+
say "PROBE FAILED (rc=$RC steps=$NSTEP peak='$PEAK')"; tail -15 "$LOG/probe.log" >&2; exit 1; }
|
| 227 |
+
say "PROBE OK: $NSTEP steps, peak $PEAK"
|
| 228 |
+
touch "$LOG/probe.ok"
|
| 229 |
+
fi
|
| 230 |
+
|
| 231 |
+
# ------------------------------------------------------------- run a segment --
|
| 232 |
+
run_seg(){ # $1 = target step
|
| 233 |
+
local TGT="$1"
|
| 234 |
+
[ -f "$LOG/seg_${TGT}.done" ] && { say "segment to $TGT already done"; return 0; }
|
| 235 |
+
local MODE
|
| 236 |
+
if [ -f "$LOG/runnum" ]; then
|
| 237 |
+
MODE="CP_CKPT_RESUME=$(cat "$LOG/runnum") CP_REWARMUP_ANCHOR=0"
|
| 238 |
+
say "resuming to step $TGT"
|
| 239 |
+
else
|
| 240 |
+
MODE="CP_INIT_FROM=$PWD/$PARENT CP_CKPT_NEW_RUN=1"
|
| 241 |
+
say "starting fresh from $(basename "$PARENT"), target step $TGT"
|
| 242 |
+
fi
|
| 243 |
+
( for i in $(seq 1 6000); do
|
| 244 |
+
RN=$(cat "$LOG/runnum" 2>/dev/null || \
|
| 245 |
+
grep -aoE 'CP_Training_log[0-9]+' "$LOG/train.log" 2>/dev/null | tail -1 | grep -oE '[0-9]+')
|
| 246 |
+
[ -n "$RN" ] || { sleep 20; continue; }
|
| 247 |
+
echo "$RN" > "$LOG/runnum"
|
| 248 |
+
n=$(ls checkpoints_bluscriptcp/rank_*/blumodelcp_run${RN}_step_${TGT}.ckpt 2>/dev/null | wc -l)
|
| 249 |
+
if [ "$n" -ge "$NP" ]; then
|
| 250 |
+
sleep 5
|
| 251 |
+
for p in $(pgrep -f 'mpirun.*bluscriptCP_exec') $(pgrep -f 'build/bluscriptCP_exec'); do kill "$p" 2>/dev/null; done
|
| 252 |
+
exit 0
|
| 253 |
+
fi
|
| 254 |
+
sleep 20
|
| 255 |
+
done ) & WPID=$!
|
| 256 |
+
env $ARCH $BASE $MODE CP_DATA_ROOT="$PWD/$DATA" \
|
| 257 |
+
CP_B=1 CP_GLOBAL_BATCH=$GB CP_MAX_STEPS=$STEPS \
|
| 258 |
+
CUDA_VISIBLE_DEVICES=$GPUS mpirun --allow-run-as-root -np $NP "$EXE" \
|
| 259 |
+
>> "$LOG/train.log" 2>&1
|
| 260 |
+
kill $WPID 2>/dev/null; wait $WPID 2>/dev/null
|
| 261 |
+
grep -qaiE 'out of memory|bad_alloc' "$LOG/train.log" && die "OOM"
|
| 262 |
+
local RN; RN=$(cat "$LOG/runnum" 2>/dev/null) || die "no run number"
|
| 263 |
+
local SRC="blumodelcp_run${RN}_step_${TGT}.ckpt"
|
| 264 |
+
local n; n=$(ls checkpoints_bluscriptcp/rank_*/"$SRC" 2>/dev/null | wc -l)
|
| 265 |
+
[ "$n" -ge "$NP" ] || die "step $TGT checkpoint missing ($n/$NP ranks)"
|
| 266 |
+
local SAFE="mix64k${TGT}_${SRC}"
|
| 267 |
+
[ -f "$CK/$SAFE" ] || cp "$CK/$SRC" "$CK/$SAFE"
|
| 268 |
+
echo "$SAFE" > "$LOG/seg_${TGT}.ckpt"; touch "$LOG/seg_${TGT}.done"
|
| 269 |
+
say "step $TGT reached (run $RN), preserved as $SAFE, $(grep -aE '^step ' "$LOG/train.log" | tail -1 | grep -oE 'loss: [0-9.]+')"
|
| 270 |
+
up_verify "$CK/$SAFE" "checkpoints/mix64k_step_${TGT}.ckpt" >>"$M" 2>&1 &
|
| 271 |
+
}
|
| 272 |
+
|
| 273 |
+
# --------------------------------------------------------------- run an eval --
|
| 274 |
+
eval_at(){ # $1 = label $2 = checkpoint basename in rank_0
|
| 275 |
+
local TAG="$1" CKF="$2"
|
| 276 |
+
[ -f "$CK/$CKF" ] || { say "eval $TAG: $CKF not present, SKIP"; return 1; }
|
| 277 |
+
[ -f "$LOG/eval_${TAG}.done" ] && { say "eval $TAG already done"; return 0; }
|
| 278 |
+
say "EVAL $TAG -- 4 lengths, one GPU each ($CKF)"
|
| 279 |
+
for LEN in 8192 16360 32744 65512; do
|
| 280 |
+
( GEN_DATA_ROOT=$DATA HAY="$HAY" PY=$PY OUT=mix64k_${TAG}_len_$LEN TASKS="niah_single" \
|
| 281 |
+
CKPTS="m$TAG" N=100 PROMPT_LEN=$LEN MAX_NEW=24 T=$T \
|
| 282 |
+
DEPTHS="0.0,0.25,0.5,0.75,1.0" YARN_ORIG=$ORIG YARN=$SCALE \
|
| 283 |
+
GPUS=$(( (LEN==8192)?0:(LEN==16360)?1:(LEN==32744)?2:3 )) JOBS_PER_GPU=1 \
|
| 284 |
+
BF16=1 GREEDY=1 CKPT_EXTRA="m$TAG=$CKF" ./run_longeval.sh \
|
| 285 |
+
>> "$LOG/eval_${TAG}_$LEN.log" 2>&1 ) &
|
| 286 |
+
done
|
| 287 |
+
wait
|
| 288 |
+
for LEN in 8192 16360 32744 65512; do
|
| 289 |
+
[ -s "mix64k_${TAG}_len_$LEN/summary.csv" ] \
|
| 290 |
+
&& say " $TAG len $LEN -> $(sed -n 2p mix64k_${TAG}_len_$LEN/summary.csv)" \
|
| 291 |
+
|| say " $TAG len $LEN -> NO RESULT"
|
| 292 |
+
done
|
| 293 |
+
touch "$LOG/eval_${TAG}.done"
|
| 294 |
+
}
|
| 295 |
+
|
| 296 |
+
# =============================== the experiment ==============================
|
| 297 |
+
start_janitor
|
| 298 |
+
|
| 299 |
+
run_seg "$EVAL_A" || die "segment to $EVAL_A failed"
|
| 300 |
+
eval_at "$EVAL_A" "$(cat "$LOG/seg_${EVAL_A}.ckpt")"
|
| 301 |
+
say "baseline for all of these = this run's own parent, measured: 67.0/65.0/52.0/29.0"
|
| 302 |
+
|
| 303 |
+
run_seg "$EVAL_B" || die "segment to $EVAL_B failed"
|
| 304 |
+
eval_at "$EVAL_B" "$(cat "$LOG/seg_${EVAL_B}.ckpt")"
|
| 305 |
+
|
| 306 |
+
read -r VERDICT ONSET BASE_L CUR_L <<<"$(detect_cliff)"
|
| 307 |
+
say "cliff detector: $VERDICT (onset=$ONSET baseline=$BASE_L current=$CUR_L)"
|
| 308 |
+
|
| 309 |
+
[ "$VERDICT" = "NODATA" ] && die "cliff detector has no baseline (needs steps 200-700 in $LOG/train.log). Refusing to call this run clean on missing evidence -- inspect the loss curve by hand."
|
| 310 |
+
|
| 311 |
+
if [ "$VERDICT" = "CLIFF" ]; then
|
| 312 |
+
# BRANCH A -- collapsed. Evaluate the last preserved checkpoint BEFORE onset,
|
| 313 |
+
# which is exactly what CP_CKPT_KEEP=2 destroyed last time.
|
| 314 |
+
# ONSET is the END of the first 50-step window under threshold, so the window
|
| 315 |
+
# opened at ONSET-49 and the last save known-clean is below that.
|
| 316 |
+
PRE=$(( ((ONSET - 49) / FREQ) * FREQ ))
|
| 317 |
+
say "BRANCH A: collapse detected at step $ONSET -- evaluating the pre-cliff checkpoint at $PRE"
|
| 318 |
+
if [ -f "$CK/keep_step_${PRE}.ckpt" ]; then
|
| 319 |
+
eval_at "pre${PRE}" "keep_step_${PRE}.ckpt"
|
| 320 |
+
up_verify "$CK/keep_step_${PRE}.ckpt" "checkpoints/mix64k_pre_step_${PRE}.ckpt" >>"$M" 2>&1
|
| 321 |
+
else
|
| 322 |
+
say " keep_step_${PRE}.ckpt missing; preserved steps: $(sort -n "$LOG/keep/preserved.txt" 2>/dev/null | tr '\n' ' ')"
|
| 323 |
+
fi
|
| 324 |
+
say "RESULT: the mix did NOT prevent the collapse. Data composition is not the (only) cause."
|
| 325 |
+
else
|
| 326 |
+
# BRANCH B -- clean at EVAL_B. Run out the mix and evaluate the endpoint.
|
| 327 |
+
say "BRANCH B: no cliff by $EVAL_B (the pure-b5 run was already at val 2.00 here) -- continuing to $STEPS"
|
| 328 |
+
run_seg "$STEPS" || die "segment to $STEPS failed"
|
| 329 |
+
eval_at "$STEPS" "$(cat "$LOG/seg_${STEPS}.ckpt")"
|
| 330 |
+
read -r V2 O2 B2 C2 <<<"$(detect_cliff)"
|
| 331 |
+
say "cliff detector after full pass: $V2 (onset=$O2 baseline=$B2 current=$C2)"
|
| 332 |
+
if [ "$V2" = "CLIFF" ]; then
|
| 333 |
+
PRE=$(( ((O2 - 49) / FREQ) * FREQ ))
|
| 334 |
+
say "collapse appeared later, at $O2 -- evaluating the pre-cliff checkpoint at $PRE"
|
| 335 |
+
[ -f "$CK/keep_step_${PRE}.ckpt" ] && eval_at "pre${PRE}" "keep_step_${PRE}.ckpt"
|
| 336 |
+
else
|
| 337 |
+
say "RESULT: a FULL pass on the mix with no cliff. The pure-b5 run collapsed at ~1150."
|
| 338 |
+
fi
|
| 339 |
+
fi
|
| 340 |
+
|
| 341 |
+
stop_janitor
|
| 342 |
+
say "=== preserved checkpoints: $(ls $CK/keep_step_*.ckpt 2>/dev/null | wc -l) ==="
|
| 343 |
+
say "=== DONE -- see $M; every upload above was byte-verified against HF ==="
|