Upload source/files/needle_sweep.sh with huggingface_hub
Browse files- source/files/needle_sweep.sh +254 -0
source/files/needle_sweep.sh
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# =============================================================================
|
| 3 |
+
# needle_sweep.sh -- run the retrieval probe across an extension-factor row set
|
| 4 |
+
# for ONE model, then score it. Forward-only, cache-only (no fine-tuning).
|
| 5 |
+
#
|
| 6 |
+
# Reuses the existing CP_EVAL_PPL + CP_EVAL_TOKENS_BIN path unchanged: the .bin
|
| 7 |
+
# holds synthetic trials, and per-position mean_nll in the CSV gives retrieval
|
| 8 |
+
# NLL at the answer span. See needle_gen.py for the fixed-slot construction.
|
| 9 |
+
#
|
| 10 |
+
# GROUP-SLOT FAN-OUT: slots = NGPU / CP_SIZE, so EVERY row parallelises --
|
| 11 |
+
# T<=128k -> CP=1 -> 8 concurrent | 256k -> CP=2 -> 4 | 512k -> CP=4 -> 2
|
| 12 |
+
# Each group is pinned to a contiguous device subset so cudaSetDevice(rank) binds
|
| 13 |
+
# rank->GPU correctly. Concurrency changes only WHEN work happens, never what is
|
| 14 |
+
# computed (independent processes, separate CSVs), so no determinism argument is
|
| 15 |
+
# needed -- unlike the search's EvaluatorPool.
|
| 16 |
+
#
|
| 17 |
+
# RESUMABLE: one marker per (T, cache, variant, block) in $STATE_DIR.
|
| 18 |
+
#
|
| 19 |
+
# Launch (114M 4k base, NTK, all five factors):
|
| 20 |
+
# MODEL_KEY=114m_run1 CKPT_RUN=1 \
|
| 21 |
+
# MODEL_ARCH="CP_N_EMBD=768 CP_N_LAYER=12 CP_N_HEAD=12 CP_N_KVHEAD=4 CP_FFN=2048 CP_WEIGHT_TYING=1" \
|
| 22 |
+
# FACTORS="8 16 32" CACHES="ntk" \
|
| 23 |
+
# nohup bash Tests/bluscriptcp/needle_sweep.sh > needle_114m.log 2>&1 &
|
| 24 |
+
#
|
| 25 |
+
# The single searched cell (48M@16x) is a separate invocation:
|
| 26 |
+
# MODEL_KEY=48m_run200 CKPT_RUN=200 MODEL_ARCH="..." FACTORS="16" \
|
| 27 |
+
# CACHES="searched" SEARCHED_LAMBDA=longrope_best_48m_s16_repr.txt bash ...
|
| 28 |
+
# =============================================================================
|
| 29 |
+
set -euo pipefail
|
| 30 |
+
cd "$(dirname "$0")/../.." # -> repo root
|
| 31 |
+
|
| 32 |
+
MODEL_KEY=${MODEL_KEY:?set MODEL_KEY (label for the results rows, e.g. 114m_run1)}
|
| 33 |
+
# INIT_FROM=<path/to/base.ckpt> loads WEIGHTS ONLY instead of resuming a run.
|
| 34 |
+
# Needed whenever the checkpoint's optimizer state cannot be read back in the
|
| 35 |
+
# EVAL configuration: a ZeRO/Muon-trained base evaluated forward-only builds a
|
| 36 |
+
# plain nn::AdamW, which would try to parse ShardedMuon's state and fail. Also
|
| 37 |
+
# the only way to evaluate a base trained at a different world size, since ZeRO
|
| 38 |
+
# state is sharded to the size that wrote it. A forward-only probe needs no
|
| 39 |
+
# optimizer state at all, so this is the correct loader for it.
|
| 40 |
+
INIT_FROM=${INIT_FROM:-}
|
| 41 |
+
if [ -n "$INIT_FROM" ]; then
|
| 42 |
+
[ -f "$INIT_FROM" ] || { echo "INIT_FROM: no such file: $INIT_FROM" >&2; exit 1; }
|
| 43 |
+
CKPT_RUN=${CKPT_RUN:-0}
|
| 44 |
+
CKPT_ENV="CP_INIT_FROM=$INIT_FROM"
|
| 45 |
+
else
|
| 46 |
+
CKPT_RUN=${CKPT_RUN:?set CKPT_RUN (or INIT_FROM=<path>)}
|
| 47 |
+
CKPT_ENV="CP_CKPT_RESUME=$CKPT_RUN"
|
| 48 |
+
fi
|
| 49 |
+
MODEL_ARCH=${MODEL_ARCH:?set MODEL_ARCH (must match how CKPT_RUN was trained)}
|
| 50 |
+
|
| 51 |
+
FACTORS=${FACTORS:-"8 16 32 64 128"} # extension factors s; T = PHYS_T * s
|
| 52 |
+
CACHES=${CACHES:-"ntk"} # ntk | yarn | pi | searched
|
| 53 |
+
# YARN_SCALE: evaluate under the trainer's YaRN cache instead of the LongRoPE lambda
|
| 54 |
+
# ramp. These are DIFFERENT ways to reach the same T and a checkpoint must be scored
|
| 55 |
+
# under the one it was TRAINED with, or the numbers are meaningless -- measured:
|
| 56 |
+
# run7 (extended at YARN_SCALE=4, m=1.1386) scored under the lambda ramp (scale=1.0)
|
| 57 |
+
# had its retrieval FLOOR collapse from 4.17 to 11.34 nats, i.e. it failed even with
|
| 58 |
+
# the needle 12 tokens from the probe. bluscriptCP.cpp:914 states the intended matrix:
|
| 59 |
+
# base ckpt + YARN_SCALE=1 (naive extrapolation @ T)
|
| 60 |
+
# extended ckpt + YARN_SCALE=4 (YaRN @ T)
|
| 61 |
+
# Set YARN_SCALE to take that route; leave it empty for the LongRoPE route.
|
| 62 |
+
YARN_SCALE=${YARN_SCALE:-}
|
| 63 |
+
SEARCHED_LAMBDA=${SEARCHED_LAMBDA:-} # required when CACHES contains "searched"
|
| 64 |
+
PHYS_T=${PHYS_T:-4096}
|
| 65 |
+
HEAD_DIM=${HEAD_DIM:-64}
|
| 66 |
+
ROPE_THETA=${ROPE_THETA:-500000}
|
| 67 |
+
BLOCKS=${BLOCKS:-5}
|
| 68 |
+
NEEDLE_LEN=${NEEDLE_LEN:-8}
|
| 69 |
+
SEED=${SEED:-0}
|
| 70 |
+
GPUS=${GPUS:-0,1,2,3,4,5,6,7}
|
| 71 |
+
FILLER=${FILLER:-Data_Loader/Data/edufineweb_val_00000.bin}
|
| 72 |
+
DATA_ROOT=${DATA_ROOT:-$PWD/Data_Loader/Data}
|
| 73 |
+
NEEDLE_DIR=${NEEDLE_DIR:-needles}
|
| 74 |
+
# Absolute offset for the probe+answer span (needle_gen --probe-pos). Unset = the
|
| 75 |
+
# end of the trial, the historical layout. Set it to decouple needle-to-probe
|
| 76 |
+
# DISTANCE from the needle's ABSOLUTE POSITION, which the default layout welds
|
| 77 |
+
# into a single variable. Filenames do NOT encode it, so give each probe position
|
| 78 |
+
# its own NEEDLE_DIR/CSV_DIR/STATE_DIR.
|
| 79 |
+
PROBE_POS=${PROBE_POS:-}
|
| 80 |
+
CSV_DIR=${CSV_DIR:-needle_csv}
|
| 81 |
+
STATE_DIR=${STATE_DIR:-needle_state}
|
| 82 |
+
RESULTS=${RESULTS:-needle_results.csv}
|
| 83 |
+
# Per-rank forward-only logits are (T/CP)*vocab*4 bytes. Keep <= this so a 49 GB
|
| 84 |
+
# card holds logits + activations. Pre-flight 2b must confirm it (and that logits
|
| 85 |
+
# are fp32 -- a bf16 model triggers an as_type copy that doubles the peak).
|
| 86 |
+
MAX_TOK_PER_RANK=${MAX_TOK_PER_RANK:-131072}
|
| 87 |
+
|
| 88 |
+
EXE=./build/bluscriptCP_exec
|
| 89 |
+
export LD_LIBRARY_PATH=BluTrain/Tensor-Implementations/lib:BluTrain/Profiler/lib:${LD_LIBRARY_PATH:-}
|
| 90 |
+
IFS=',' read -r -a GPUARR <<< "$GPUS"
|
| 91 |
+
NGPU=${#GPUARR[@]}
|
| 92 |
+
KV=$(sed -E 's/.*CP_N_KVHEAD=([0-9]+).*/\1/' <<< "$MODEL_ARCH")
|
| 93 |
+
mkdir -p "$NEEDLE_DIR" "$CSV_DIR" "$STATE_DIR"
|
| 94 |
+
log(){ echo "[$(date '+%F %H:%M:%S')] $*"; }
|
| 95 |
+
|
| 96 |
+
# ---- pre-flight -------------------------------------------------------------
|
| 97 |
+
[ -x "$EXE" ] || { echo "ERROR: $EXE missing -- build first"; exit 1; }
|
| 98 |
+
[ -f "$FILLER" ] || { echo "ERROR: filler stream $FILLER not found"; exit 1; }
|
| 99 |
+
# Detect a live evaluator by RESOLVED EXECUTABLE, not by command-line text. `pgrep -f
|
| 100 |
+
# bluscriptCP_exec` also matches any shell whose argv merely CONTAINS that string --
|
| 101 |
+
# including the wrapper that launches this script -- which produced a false "already
|
| 102 |
+
# running" abort. `pgrep -x` is no good either: comm truncates to 15 chars and
|
| 103 |
+
# "bluscriptCP_exec" is 16. Comparing /proc/PID/exe is exact.
|
| 104 |
+
exec_running(){
|
| 105 |
+
local want; want=$(readlink -f "$EXE") || return 1
|
| 106 |
+
local p
|
| 107 |
+
for p in /proc/[0-9]*; do
|
| 108 |
+
[ "$(readlink -f "$p/exe" 2>/dev/null)" = "$want" ] && { echo "${p#/proc/}"; return 0; }
|
| 109 |
+
done
|
| 110 |
+
return 1
|
| 111 |
+
}
|
| 112 |
+
if pids=$(exec_running); then
|
| 113 |
+
# ALLOW_CONCURRENT_EXE=1 permits a DELIBERATE overlap -- e.g. probing a mid-training
|
| 114 |
+
# checkpoint while the pretrain keeps running. Only use it when the memory headroom is
|
| 115 |
+
# known: a forward-only probe is ~10-15 GB against a training job's ~47 GB on an 80 GB
|
| 116 |
+
# card. The context_rot_2026-08-19 copies of this script already carried this flag; the
|
| 117 |
+
# tracked one never got it.
|
| 118 |
+
if [ "${ALLOW_CONCURRENT_EXE:-0}" = "1" ]; then
|
| 119 |
+
log "WARNING: $EXE already running (pid $pids); ALLOW_CONCURRENT_EXE=1 -- proceeding"
|
| 120 |
+
else
|
| 121 |
+
echo "ERROR: $EXE already running (pid $pids) -- this sweep would oversubscribe GPUs $GPUS"
|
| 122 |
+
echo " set ALLOW_CONCURRENT_EXE=1 to override when the overlap is intended"
|
| 123 |
+
exit 1
|
| 124 |
+
fi
|
| 125 |
+
fi
|
| 126 |
+
case " $CACHES " in *" searched "*)
|
| 127 |
+
[ -n "$SEARCHED_LAMBDA" ] || { echo "ERROR: CACHES has 'searched' but SEARCHED_LAMBDA is unset"; exit 1; }
|
| 128 |
+
[ -f "$SEARCHED_LAMBDA" ] || { echo "ERROR: SEARCHED_LAMBDA '$SEARCHED_LAMBDA' not found"; exit 1; };;
|
| 129 |
+
esac
|
| 130 |
+
|
| 131 |
+
# CP degree per row, from the plan's CP table (memory-driven, not a free choice).
|
| 132 |
+
cp_for_T(){
|
| 133 |
+
local T=$1 cp=1
|
| 134 |
+
while [ $(( T / cp )) -gt "$MAX_TOK_PER_RANK" ]; do cp=$(( cp * 2 )); done
|
| 135 |
+
echo "$cp"
|
| 136 |
+
}
|
| 137 |
+
# Verified topology only: pure ring>=4 is known-broken in this repo.
|
| 138 |
+
topo_for(){
|
| 139 |
+
local cp=$1
|
| 140 |
+
if [ "$cp" = "1" ]; then echo ""
|
| 141 |
+
elif [ $(( KV % cp )) -eq 0 ]; then echo "CP_ATTN_MODE=ulysses"
|
| 142 |
+
elif [ $(( cp % KV )) -eq 0 ]; then echo "CP_ATTN_MODE=hybrid CP_ULYSSES_SIZE=$KV"
|
| 143 |
+
else echo "UNSUPPORTED"; fi
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
# lambda file per (cache, head_dim, theta, s). NTK/PI are theta-independent closed
|
| 147 |
+
# forms; YaRN depends on base via find_correction_range, so it MUST be regenerated
|
| 148 |
+
# per theta. All are head_dim-dependent (vector length = head_dim/2).
|
| 149 |
+
lambda_file(){
|
| 150 |
+
local cache=$1 s=$2
|
| 151 |
+
if [ "$cache" = "searched" ]; then echo "$SEARCHED_LAMBDA"; return; fi
|
| 152 |
+
local out="$NEEDLE_DIR/lam_${cache}_hd${HEAD_DIM}_th${ROPE_THETA}_s${s}.txt"
|
| 153 |
+
if [ ! -f "$out" ]; then
|
| 154 |
+
python3 - "$cache" "$s" "$out" "$HEAD_DIM" "$ROPE_THETA" "$PHYS_T" <<'PY'
|
| 155 |
+
import sys, importlib.util
|
| 156 |
+
cache, s, out, hd, theta, orig = (sys.argv[1], int(sys.argv[2]), sys.argv[3],
|
| 157 |
+
int(sys.argv[4]), float(sys.argv[5]), float(sys.argv[6]))
|
| 158 |
+
spec = importlib.util.spec_from_file_location('lrs', 'Tests/bluscriptcp/longrope_search.py')
|
| 159 |
+
lrs = importlib.util.module_from_spec(spec); spec.loader.exec_module(lrs)
|
| 160 |
+
half, lam_max = hd // 2, s * 1.25
|
| 161 |
+
if cache == 'ntk': lam = lrs.seed_NTK(hd, s, lam_max)
|
| 162 |
+
elif cache == 'pi': lam = lrs.seed_PI(half, s, lam_max)
|
| 163 |
+
elif cache == 'yarn': lam = lrs.seed_YaRN(hd, s, orig, 32.0, 1.0, theta, lam_max)
|
| 164 |
+
else: sys.exit(f"unknown cache '{cache}'")
|
| 165 |
+
lrs.write_factors(out, lam, 0, s, int(orig * s))
|
| 166 |
+
PY
|
| 167 |
+
fi
|
| 168 |
+
echo "$out"
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
# Overridable so a REFINED distance grid can be added around a crossing. Set
|
| 172 |
+
# VARIANTS to the UNION of old and new points: already-completed ones are skipped
|
| 173 |
+
# by their state markers, so only the new points actually run, and the scorer then
|
| 174 |
+
# reports the whole grid in one table.
|
| 175 |
+
VARIANTS=${VARIANTS:-"p0 p0.25 p0.5 p0.75 p1 absent null"}
|
| 176 |
+
log "needle sweep: model=$MODEL_KEY run=$CKPT_RUN factors=[$FACTORS] caches=[$CACHES] blocks=$BLOCKS"
|
| 177 |
+
log " GPUS=$GPUS (n=$NGPU) kv=$KV head_dim=$HEAD_DIM theta=$ROPE_THETA"
|
| 178 |
+
|
| 179 |
+
for S in $FACTORS; do
|
| 180 |
+
T=$(( PHYS_T * S ))
|
| 181 |
+
CP=$(cp_for_T "$T")
|
| 182 |
+
TOPO=$(topo_for "$CP")
|
| 183 |
+
[ "$TOPO" = "UNSUPPORTED" ] && { echo "ERROR: no verified topology for CP=$CP with kv=$KV at T=$T"; exit 1; }
|
| 184 |
+
[ "$CP" -le "$NGPU" ] || { echo "ERROR: T=$T needs CP=$CP but only $NGPU GPU(s) given"; exit 1; }
|
| 185 |
+
N=$([ "$T" -le 131072 ] && echo 8 || echo 4) # pinned trials-per-block (plan A1)
|
| 186 |
+
SLOTS=$(( NGPU / CP ))
|
| 187 |
+
log "== s=$S T=$T CP=$CP ${TOPO:-(single-GPU)} N=$N slots=$SLOTS =="
|
| 188 |
+
|
| 189 |
+
# .bin files depend only on (T, variant, block) -- shared across models and caches.
|
| 190 |
+
for V in $VARIANTS; do for B in $(seq 0 $((BLOCKS-1))); do
|
| 191 |
+
[ -f "$NEEDLE_DIR/needle_T${T}_${V}_b${B}.bin" ] && continue
|
| 192 |
+
python3 Tests/bluscriptcp/needle_gen.py --T "$T" --variant "$V" --block "$B" \
|
| 193 |
+
--trials "$N" --needle-len "$NEEDLE_LEN" --filler "$FILLER" \
|
| 194 |
+
--seed "$SEED" --out-dir "$NEEDLE_DIR" \
|
| 195 |
+
${PROBE_POS:+--probe-pos "$PROBE_POS"} >/dev/null
|
| 196 |
+
done; done
|
| 197 |
+
|
| 198 |
+
for CACHE in $CACHES; do
|
| 199 |
+
LAM=$(lambda_file "$CACHE" "$S")
|
| 200 |
+
# The lambda vector is length-specific: reject a mismatch loudly rather than
|
| 201 |
+
# scoring a cache tuned for another length (the T == S_search invariant).
|
| 202 |
+
SS=$(awk '/^S_search/{print $2}' "$LAM")
|
| 203 |
+
[ "$SS" = "$T" ] || { echo "ERROR: $LAM has S_search=$SS but this row is T=$T"; exit 1; }
|
| 204 |
+
|
| 205 |
+
# ---- job list, then a group-slot loop ----
|
| 206 |
+
jobs=()
|
| 207 |
+
for V in $VARIANTS; do for B in $(seq 0 $((BLOCKS-1))); do jobs+=("$V:$B"); done; done
|
| 208 |
+
running=0; g=0
|
| 209 |
+
for job in "${jobs[@]}"; do
|
| 210 |
+
V=${job%%:*}; B=${job##*:}
|
| 211 |
+
MK="$STATE_DIR/${MODEL_KEY}.T${T}.${CACHE}.${V}.b${B}.done"
|
| 212 |
+
OUTCSV="$CSV_DIR/ppl_needle_T${T}_${V}_b${B}.csv"
|
| 213 |
+
[ -f "$MK" ] && continue
|
| 214 |
+
# pin this group to its own contiguous device subset
|
| 215 |
+
devs=$(IFS=,; echo "${GPUARR[*]:$((g*CP)):$CP}")
|
| 216 |
+
if [ -n "$YARN_SCALE" ]; then
|
| 217 |
+
ROPE_ENV="YARN_SCALE=$YARN_SCALE" # trainer's YaRN cache
|
| 218 |
+
else
|
| 219 |
+
ROPE_ENV="CP_LONGROPE_FACTORS=$LAM" # LongRoPE lambda ramp
|
| 220 |
+
fi
|
| 221 |
+
(
|
| 222 |
+
env $MODEL_ARCH CP_SIZE=$CP $TOPO \
|
| 223 |
+
CP_EVAL_PPL=1 CP_T=$T CP_B=1 \
|
| 224 |
+
CP_EVAL_WINDOWS=$N CP_EVAL_SKIP_WINDOWS=0 \
|
| 225 |
+
CP_EVAL_TOKENS_BIN="$NEEDLE_DIR/needle_T${T}_${V}_b${B}.bin" \
|
| 226 |
+
$ROPE_ENV $CKPT_ENV \
|
| 227 |
+
CP_EVAL_OUT="$OUTCSV" CP_DATA_ROOT="$DATA_ROOT" \
|
| 228 |
+
CUDA_VISIBLE_DEVICES=$devs mpirun -np $CP "$EXE" \
|
| 229 |
+
> "$CSV_DIR/${MODEL_KEY}_T${T}_${CACHE}_${V}_b${B}.log" 2>&1 \
|
| 230 |
+
&& touch "$MK"
|
| 231 |
+
) &
|
| 232 |
+
g=$(( (g + 1) % SLOTS )); running=$(( running + 1 ))
|
| 233 |
+
if [ "$running" -ge "$SLOTS" ]; then wait -n; running=$(( running - 1 )); fi
|
| 234 |
+
done
|
| 235 |
+
wait
|
| 236 |
+
|
| 237 |
+
# every job must have produced its marker, or scoring would read a stale CSV
|
| 238 |
+
miss=0
|
| 239 |
+
for V in $VARIANTS; do for B in $(seq 0 $((BLOCKS-1))); do
|
| 240 |
+
[ -f "$STATE_DIR/${MODEL_KEY}.T${T}.${CACHE}.${V}.b${B}.done" ] || {
|
| 241 |
+
echo " MISSING: $V b$B -- see $CSV_DIR/${MODEL_KEY}_T${T}_${CACHE}_${V}_b${B}.log"; miss=1; }
|
| 242 |
+
done; done
|
| 243 |
+
[ "$miss" = "0" ] || { echo "ERROR: s=$S cache=$CACHE incomplete; not scoring"; exit 1; }
|
| 244 |
+
|
| 245 |
+
log " scoring s=$S cache=$CACHE -> $RESULTS"
|
| 246 |
+
python3 Tests/bluscriptcp/needle_score.py \
|
| 247 |
+
--needle-dir "$NEEDLE_DIR" --csv-dir "$CSV_DIR" --T "$T" --blocks "$BLOCKS" \
|
| 248 |
+
--model "$MODEL_KEY" --cache "$CACHE" --factor "$S" \
|
| 249 |
+
--head-dim "$HEAD_DIM" --rope-theta "$ROPE_THETA" --out "$RESULTS" \
|
| 250 |
+
--p-fracs "$(tr ' ' '\n' <<< "$VARIANTS" | grep '^p' | paste -sd,)"
|
| 251 |
+
done
|
| 252 |
+
done
|
| 253 |
+
|
| 254 |
+
log "SWEEP DONE -> $RESULTS (CSVs in $CSV_DIR, markers in $STATE_DIR)"
|