File size: 14,581 Bytes
9589849 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 | #!/usr/bin/env bash
set -euo pipefail
workspace="/mnt/pvc/users/simon/agentptb/runs/sol-max-s1/workspace"
eval_marker="$workspace/state/step150-eval-complete"
eval_session="agentptb-step150-eval-watcher"
train_session="agentptb-sft-continuation"
target_steps=1800
source_output="$workspace/outputs/sft-agent-mix-clean-full-v1"
continuation_log="$workspace/logs/sft-agent-mix-clean-continuation-launch.log"
full_config="$workspace/configs/sft-agent-mix-clean-full-step1650-resume.toml"
selective_config="$workspace/configs/sft-agent-mix-clean-full-step1650-selective-resume.toml"
gpu_optim_config="$workspace/configs/sft-agent-mix-clean-full-step1650-gpuoptim-resume.toml"
noact_config="$workspace/configs/sft-agent-mix-clean-full-step1650-noactoffload-resume.toml"
mb10_config="$workspace/configs/sft-agent-mix-clean-full-mb10-resume.toml"
mb12_config="$workspace/configs/sft-agent-mix-clean-full-mb12-resume.toml"
bench_pid=""
cleanup_bench() {
if [[ -n "$bench_pid" ]]; then
# Target the whole setsid process group even if the uv group leader has
# already exited; torchrun/workers can otherwise survive an OOM launcher.
kill -TERM -- "-$bench_pid" 2>/dev/null || true
for _ in {1..12}; do
kill -0 -- "-$bench_pid" 2>/dev/null || break
sleep 5
done
kill -KILL -- "-$bench_pid" 2>/dev/null || true
wait "$bench_pid" 2>/dev/null || true
fi
}
run_benchmark() {
local config="$1" output="$2" log="$3"
mkdir -p "$output/checkpoints" "$output/weights"
if [[ ! -e "$output/checkpoints/step_150" ]]; then
cp -Rl "$source_output/checkpoints/step_150" "$output/checkpoints/step_150"
fi
if [[ ! -e "$output/weights/step_150" ]]; then
cp -Rl "$source_output/weights/step_150" "$output/weights/step_150"
fi
if [[ -f "$output/checkpoints/step_152/trainer/.metadata" \
&& -f "$output/weights/step_152/STABLE" \
&& -f "$log" ]] \
&& grep -q 'Step 152 |' "$log"; then
return 0
fi
cd /root/work/a/prime-rl
setsid bash -c 'exec "$@"' _ \
env CUDA_VISIBLE_DEVICES=4,5,6,7 \
TMPDIR=/tmp/agentptb-s1 \
PYTHONUNBUFFERED=1 \
uv run --no-sync sft @ "$config" >"$log" 2>&1 &
bench_pid=$!
local ok=false
for _ in {1..180}; do
if [[ -f "$output/checkpoints/step_152/trainer/.metadata" \
&& -f "$output/weights/step_152/STABLE" ]] \
&& grep -q 'Step 152 |' "$log"; then
ok=true
break
fi
kill -0 "$bench_pid" 2>/dev/null || break
sleep 10
done
cleanup_bench
bench_pid=""
[[ "$ok" == true ]]
}
read_metrics() {
python - "$1" "$2" "$3" <<'PY'
import json
import math
import re
import sys
text = open(sys.argv[1], errors="replace").read()
metrics_path = sys.argv[2]
batch_size = int(sys.argv[3])
matches = re.findall(
r"Step 152 \|.*?Loss ([^ |]+) \| Grad\. Norm ([^ |]+) \| "
r"LR ([^ |]+) \| Throughput [0-9.]+ tokens/s.*?Peak Mem\. ([0-9.]+)/",
text,
)
if not matches:
print("0 999 nan nan nan 0")
else:
loss, grad, lr, memory = matches[-1]
step_metrics = {}
try:
with open(metrics_path) as handle:
for line in handle:
row = json.loads(line)
if row.get("step") == 152:
step_metrics.update(row)
effective_step_seconds = (
float(step_metrics["time/step"])
- float(step_metrics.get("time/save_ckpt", 0.0))
)
throughput = batch_size * 32768 / effective_step_seconds
except (OSError, ValueError, KeyError, ZeroDivisionError, json.JSONDecodeError):
effective_step_seconds = math.nan
throughput = 0.0
values = [
float(loss),
float(grad),
float(lr),
float(throughput),
float(memory),
effective_step_seconds,
]
valid = int(
all(math.isfinite(value) for value in values)
and 1.9e-5 <= values[2] <= 2.1e-5
and values[4] <= 170
and values[5] > 0
)
print(f"{throughput:.3f}", memory, loss, grad, lr, valid)
PY
}
trap cleanup_bench EXIT INT TERM
while [[ ! -f "$eval_marker" ]]; do
sleep 30
done
while tmux has-session -t "$eval_session" 2>/dev/null; do
sleep 10
done
[[ -f "$source_output/checkpoints/step_150/trainer/.metadata" ]]
[[ -f "$source_output/weights/step_150/STABLE" ]]
if curl --fail --silent http://127.0.0.1:8200/health >/dev/null 2>&1; then
echo "refusing to train while inference still owns port 8200" >&2
exit 1
fi
tmux has-session -t "$train_session" 2>/dev/null && {
echo "continuation session already exists" >&2
exit 1
}
baseline_save=false
baseline_throughput=0
baseline_memory=999
baseline_loss=nan
baseline_grad=nan
baseline_lr=nan
baseline_valid=0
if run_benchmark \
"$workspace/configs/sft-baseline-bench-resume150.toml" \
"$workspace/outputs/baseline-bench" \
"$workspace/logs/sft-baseline-bench.log"; then
baseline_save=true
read -r baseline_throughput baseline_memory baseline_loss baseline_grad baseline_lr baseline_valid \
< <(read_metrics "$workspace/logs/sft-baseline-bench.log" \
"$workspace/outputs/baseline-bench/metrics.jsonl" 128)
fi
mb10_save=false
mb10_throughput=0
mb10_memory=999
mb10_loss=nan
mb10_grad=nan
mb10_lr=nan
mb10_valid=0
if run_benchmark \
"$workspace/configs/sft-mb10-bench-resume150.toml" \
"$workspace/outputs/mb10-bench" \
"$workspace/logs/sft-mb10-bench.log"; then
mb10_save=true
read -r mb10_throughput mb10_memory mb10_loss mb10_grad mb10_lr mb10_valid \
< <(read_metrics "$workspace/logs/sft-mb10-bench.log" \
"$workspace/outputs/mb10-bench/metrics.jsonl" 120)
fi
mb12_save=false
mb12_throughput=0
mb12_memory=999
mb12_loss=nan
mb12_grad=nan
mb12_lr=nan
mb12_valid=0
if run_benchmark \
"$workspace/configs/sft-mb12-bench-resume150.toml" \
"$workspace/outputs/mb12-bench" \
"$workspace/logs/sft-mb12-bench.log"; then
mb12_save=true
read -r mb12_throughput mb12_memory mb12_loss mb12_grad mb12_lr mb12_valid \
< <(read_metrics "$workspace/logs/sft-mb12-bench.log" \
"$workspace/outputs/mb12-bench/metrics.jsonl" 96)
fi
selective_save=false
selective_throughput=0
selective_memory=999
selective_loss=nan
selective_grad=nan
selective_lr=nan
selective_valid=0
if run_benchmark \
"$workspace/configs/sft-ac-selective-bench-resume150.toml" \
"$workspace/outputs/ac-selective-bench" \
"$workspace/logs/sft-ac-selective-bench.log"; then
selective_save=true
read -r selective_throughput selective_memory selective_loss selective_grad selective_lr selective_valid \
< <(read_metrics "$workspace/logs/sft-ac-selective-bench.log" \
"$workspace/outputs/ac-selective-bench/metrics.jsonl" 128)
fi
gpu_save=false
gpu_throughput=0
gpu_memory=999
gpu_loss=nan
gpu_grad=nan
gpu_lr=nan
gpu_valid=0
if run_benchmark \
"$workspace/configs/sft-optim-gpu-bench-resume150.toml" \
"$workspace/outputs/optim-gpu-bench" \
"$workspace/logs/sft-optim-gpu-bench.log"; then
gpu_save=true
read -r gpu_throughput gpu_memory gpu_loss gpu_grad gpu_lr gpu_valid \
< <(read_metrics "$workspace/logs/sft-optim-gpu-bench.log" \
"$workspace/outputs/optim-gpu-bench/metrics.jsonl" 128)
fi
noact_save=false
noact_throughput=0
noact_memory=999
noact_loss=nan
noact_grad=nan
noact_lr=nan
noact_valid=0
if run_benchmark \
"$workspace/configs/sft-noactoffload-bench-resume150.toml" \
"$workspace/outputs/noactoffload-bench" \
"$workspace/logs/sft-noactoffload-bench.log"; then
noact_save=true
read -r noact_throughput noact_memory noact_loss noact_grad noact_lr noact_valid \
< <(read_metrics "$workspace/logs/sft-noactoffload-bench.log" \
"$workspace/outputs/noactoffload-bench/metrics.jsonl" 128)
fi
chosen="$full_config"
best_throughput="$baseline_throughput"
chosen_batch_size=128
mb10_eligible=0
mb12_eligible=0
selective_eligible=0
gpu_eligible=0
noact_eligible=0
if [[ "$baseline_valid" == 1 && "$mb10_valid" == 1 ]] \
&& awk -v new="$mb10_throughput" -v base="$baseline_throughput" \
'BEGIN { exit !(new >= base * 1.03) }'; then
mb10_eligible=1
chosen="$mb10_config"
chosen_batch_size=120
best_throughput="$mb10_throughput"
fi
if [[ "$baseline_valid" == 1 && "$mb12_valid" == 1 ]] \
&& awk -v new="$mb12_throughput" -v base="$baseline_throughput" \
'BEGIN { exit !(new >= base * 1.03) }'; then
mb12_eligible=1
if awk -v new="$mb12_throughput" -v old="$best_throughput" \
'BEGIN { exit !(new > old) }'; then
chosen="$mb12_config"
chosen_batch_size=96
best_throughput="$mb12_throughput"
fi
fi
if [[ "$baseline_valid" == 1 && "$selective_valid" == 1 ]] \
&& awk -v new="$selective_throughput" -v base="$baseline_throughput" \
'BEGIN { exit !(new >= base * 1.03) }'; then
selective_eligible=1
if awk -v new="$selective_throughput" -v old="$best_throughput" \
'BEGIN { exit !(new > old) }'; then
chosen="$selective_config"
chosen_batch_size=128
best_throughput="$selective_throughput"
fi
fi
if [[ "$baseline_valid" == 1 && "$gpu_valid" == 1 ]] \
&& awk -v new="$gpu_throughput" -v base="$baseline_throughput" \
'BEGIN { exit !(new >= base * 1.03) }'; then
gpu_eligible=1
if awk -v new="$gpu_throughput" -v old="$best_throughput" \
'BEGIN { exit !(new > old) }'; then
chosen="$gpu_optim_config"
chosen_batch_size=128
best_throughput="$gpu_throughput"
fi
fi
if [[ "$baseline_valid" == 1 && "$noact_valid" == 1 ]] \
&& awk -v new="$noact_throughput" -v base="$baseline_throughput" \
'BEGIN { exit !(new >= base * 1.03) }'; then
noact_eligible=1
if awk -v new="$noact_throughput" -v old="$best_throughput" \
'BEGIN { exit !(new > old) }'; then
chosen="$noact_config"
chosen_batch_size=128
best_throughput="$noact_throughput"
fi
fi
trap - EXIT INT TERM
# Choose the terminal step only from measured execution throughput and remaining
# wall clock. This decision is deliberately independent of evaluation reward.
# Reserve 18 hours for checkpoint scanning, serving, the canonical 1,178-episode
# stock/custom measurement, and recovery from transient infrastructure failures.
# A 5% step-time penalty plus 1.5 seconds/update for amortized checkpoint overhead
# keeps a two-update benchmark from making the long-run projection optimistic.
remaining_seconds="$(python - "$workspace/DEADLINE" <<'PY'
import sys
import time
print(max(0, int(float(open(sys.argv[1]).read().strip()) - time.time())))
PY
)"
planning_throughput="$best_throughput"
if ! awk -v value="$planning_throughput" \
'BEGIN { exit !(value ~ /^[0-9]+([.][0-9]+)?$/ && value >= 1000) }'; then
# The active run sustained roughly 27.5k tokens/s. Use a lower fallback if
# the isolated reference benchmark failed before emitting a usable metric.
planning_throughput=26000
fi
final_eval_reserve_seconds=$((18 * 3600))
startup_guard_seconds=1800
read -r target_steps projected_step_seconds < <(
python - "$remaining_seconds" "$planning_throughput" \
"$final_eval_reserve_seconds" "$startup_guard_seconds" \
"$chosen_batch_size" <<'PY'
import sys
remaining = int(sys.argv[1])
throughput = float(sys.argv[2])
reserve = int(sys.argv[3])
startup_guard = int(sys.argv[4])
batch_size = int(sys.argv[5])
tokens_per_update = batch_size * 32768
projected_step_seconds = tokens_per_update / throughput * 1.05 + 1.5
available = max(0, remaining - reserve - startup_guard)
updates = int(available / projected_step_seconds)
# Step 150 is the fixed branch point. About step 2,050 is two full passes over
# the prepared mixture under batch 128. Preserve the same total-token cap when
# a mechanically faster benchmark selects a nearby global batch size.
token_equivalent_cap = 150 + int((2050 - 150) * 128 / batch_size)
target = max(151, min(token_equivalent_cap, 150 + updates))
print(target, f"{projected_step_seconds:.3f}")
PY
)
printf 'baseline_checkpoint_save_succeeded=%s\nbaseline_throughput=%s\nbaseline_peak_memory_gib=%s\nbaseline_loss=%s\nbaseline_grad_norm=%s\nbaseline_lr=%s\nbaseline_valid=%s\nmb10_checkpoint_save_succeeded=%s\nmb10_throughput=%s\nmb10_peak_memory_gib=%s\nmb10_loss=%s\nmb10_grad_norm=%s\nmb10_lr=%s\nmb10_valid=%s\nmb10_eligible=%s\nmb12_checkpoint_save_succeeded=%s\nmb12_throughput=%s\nmb12_peak_memory_gib=%s\nmb12_loss=%s\nmb12_grad_norm=%s\nmb12_lr=%s\nmb12_valid=%s\nmb12_eligible=%s\nselective_checkpoint_save_succeeded=%s\nselective_throughput=%s\nselective_peak_memory_gib=%s\nselective_loss=%s\nselective_grad_norm=%s\nselective_lr=%s\nselective_valid=%s\nselective_eligible=%s\ngpu_optim_checkpoint_save_succeeded=%s\ngpu_optim_throughput=%s\ngpu_optim_peak_memory_gib=%s\ngpu_optim_loss=%s\ngpu_optim_grad_norm=%s\ngpu_optim_lr=%s\ngpu_optim_valid=%s\ngpu_optim_eligible=%s\nnoact_checkpoint_save_succeeded=%s\nnoact_throughput=%s\nnoact_peak_memory_gib=%s\nnoact_loss=%s\nnoact_grad_norm=%s\nnoact_lr=%s\nnoact_valid=%s\nnoact_eligible=%s\nchosen_config=%s\nchosen_batch_size=%s\nremaining_seconds_at_plan=%s\nplanning_throughput=%s\nprojected_step_seconds=%s\nfinal_eval_reserve_seconds=%s\nstartup_guard_seconds=%s\ntarget_steps=%s\n' \
"$baseline_save" "$baseline_throughput" "$baseline_memory" "$baseline_loss" "$baseline_grad" "$baseline_lr" "$baseline_valid" \
"$mb10_save" "$mb10_throughput" "$mb10_memory" "$mb10_loss" "$mb10_grad" "$mb10_lr" "$mb10_valid" "$mb10_eligible" \
"$mb12_save" "$mb12_throughput" "$mb12_memory" "$mb12_loss" "$mb12_grad" "$mb12_lr" "$mb12_valid" "$mb12_eligible" \
"$selective_save" "$selective_throughput" "$selective_memory" "$selective_loss" "$selective_grad" "$selective_lr" "$selective_valid" "$selective_eligible" \
"$gpu_save" "$gpu_throughput" "$gpu_memory" "$gpu_loss" "$gpu_grad" "$gpu_lr" "$gpu_valid" "$gpu_eligible" \
"$noact_save" "$noact_throughput" "$noact_memory" "$noact_loss" "$noact_grad" "$noact_lr" "$noact_valid" "$noact_eligible" "$chosen" "$chosen_batch_size" \
"$remaining_seconds" "$planning_throughput" "$projected_step_seconds" \
"$final_eval_reserve_seconds" "$startup_guard_seconds" "$target_steps" \
>"$workspace/state/training-variant-benchmarks.txt"
tmux has-session -t "$train_session" 2>/dev/null && {
echo "continuation session already exists" >&2
exit 1
}
tmux new-session -d -s "$train_session" \
"cd /root/work/a/prime-rl && env CUDA_VISIBLE_DEVICES=4,5,6,7 TMPDIR=/tmp/agentptb-s1 PYTHONUNBUFFERED=1 uv run --no-sync sft @ '$chosen' --max-steps '$target_steps' > '$continuation_log' 2>&1"
printf 'config=%s\nbatch_size=%s\nmax_steps=%s\n' "$chosen" "$chosen_batch_size" "$target_steps" \
>"$workspace/state/continuation-config.txt"
touch "$workspace/state/continuation-launched"
|