Fild's picture
DiffusionGemma tool-selector LoRA + paper (Rud Lord and the KnowledgeOS Agents)
60b165e verified
Raw
History Blame Contribute Delete
3.82 kB
#!/bin/bash
# Chain: zero-shot baseline eval -> QLoRA training (crash-resume) -> adapter eval.
# Detach with: bash -c './overnight_diffusiongemma.sh & disown'
# All paths overridable via env (override via env).
set -uo pipefail
MODEL="${MODEL:-./diffusiongemma-26B-A4B-it-4bit}"
DATA="${DATA:-./data}"
ADAPTER="${ADAPTER:-./adapters/tool-selector}"
PY="${PY:-python3}"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
TOTAL_STEPS="${TOTAL_STEPS:-250}"
MAX_RETRIES="${MAX_RETRIES:-6}"
TS=$(date +%Y%m%dT%H%M%S)
LOG="$DATA/chain_${TS}.log"
# smaller Metal command buffers: macOS's GPU interactivity watchdog kills long
# command buffers when the console session is active (kIOGPUCommandBuffer...
# ImpactingInteractivity); short buffers avoid the kill at ~25% throughput cost
export MLX_MAX_OPS_PER_BUFFER=4
export MLX_MAX_MB_PER_BUFFER=20
log() { echo "[$(date +%H:%M:%S)] $*" >> "$LOG"; }
memsnap() { log "memsnap: $(sysctl -n vm.swapusage) | $(memory_pressure 2>/dev/null | tail -1)"; }
latest_checkpoint() {
ls "$ADAPTER" 2>/dev/null | grep -E '^[0-9]{7}_adapters\.safetensors$' | sort | tail -1
}
mkdir -p "$DATA"
log "=== chain start (steps=$TOTAL_STEPS, buffer caps ops=$MLX_MAX_OPS_PER_BUFFER mb=$MLX_MAX_MB_PER_BUFFER) ==="
memsnap
# Idempotency guard: never silently retrain over a FINISHED adapter; a partial
# best-val adapter with no step checkpoints (crash inside the first save window)
# is archived and training starts fresh
if [ -f "$ADAPTER/adapters.safetensors" ] && [ -z "$(latest_checkpoint)" ]; then
BEST_STEP=$("$PY" -c "import json; print(json.load(open('$ADAPTER/best.json'))['step'])" 2>/dev/null || echo 0)
if [ "$BEST_STEP" -ge "$TOTAL_STEPS" ]; then
log "FATAL: finished adapter present (best step $BEST_STEP >= $TOTAL_STEPS) — refusing to overwrite"
exit 1
fi
log "partial adapter (best step $BEST_STEP, no checkpoints) — archiving to ${ADAPTER}.stale.$TS"
mv "$ADAPTER" "${ADAPTER}.stale.$TS"
fi
# Phase 1: zero-shot baseline on the clean test split
log "phase 1: zero-shot baseline eval"
caffeinate -ims "$PY" "$SCRIPT_DIR/diffusion_eval.py" \
--model "$MODEL" --test "$DATA/test.jsonl" \
--out "$DATA/eval_zeroshot_clean.json" --check-template >> "$LOG" 2>&1
log "phase 1 exit=$?"
memsnap
# Phase 2: training with crash-resume (Metal watchdog kills are intermittent).
# NOTE: bash 3.2 + set -u kills "${ARR[@]}" on EMPTY arrays — use guarded expansion.
TRAIN_EXIT=1
for attempt in $(seq 1 "$MAX_RETRIES"); do
CKPT=$(latest_checkpoint)
RESUME_ARGS=()
if [ -n "$CKPT" ]; then
STEP=$((10#$(echo "$CKPT" | cut -d_ -f1)))
if [ "$STEP" -ge "$TOTAL_STEPS" ]; then TRAIN_EXIT=0; log "phase 2: already complete at step $STEP"; break; fi
RESUME_ARGS=(--resume-file "$ADAPTER/$CKPT" --start-step "$STEP")
log "phase 2 attempt $attempt: resuming from step $STEP"
else
log "phase 2 attempt $attempt: fresh start"
fi
caffeinate -ims "$PY" "$SCRIPT_DIR/diffusion_lora_train.py" \
--model "$MODEL" --data "$DATA" --adapter-path "$ADAPTER" \
--steps "$TOTAL_STEPS" --val-every 25 --save-every 50 \
${RESUME_ARGS[@]+"${RESUME_ARGS[@]}"} >> "$LOG" 2>&1
TRAIN_EXIT=$?
log "phase 2 attempt $attempt exit=$TRAIN_EXIT"
[ "$TRAIN_EXIT" -eq 0 ] && break
memsnap
sleep 30
done
memsnap
# Phase 3: adapter eval (best-val adapter)
if [ "$TRAIN_EXIT" -eq 0 ] && [ -f "$ADAPTER/adapters.safetensors" ]; then
log "phase 3: adapter eval"
caffeinate -ims "$PY" "$SCRIPT_DIR/diffusion_eval.py" \
--model "$MODEL" --adapter "$ADAPTER" --test "$DATA/test.jsonl" \
--out "$DATA/eval_adapter_clean.json" >> "$LOG" 2>&1
log "phase 3 exit=$?"
else
log "phase 3 SKIPPED (train exit=$TRAIN_EXIT)"
fi
memsnap
log "=== chain done ==="