sol-max-record / harness /scripts /post_step150_continue.sh
simonycl's picture
Upload folder using huggingface_hub
9589849 verified
Raw
History Blame Contribute Delete
14.6 kB
#!/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"