Download scripts/benchmark.sh from lewislululu/jevon: direct link, hf CLI and curl.
- Browser
- Download file 4.36 kB
-
https://huggingface.co/lewislululu/jevon/resolve/main/scripts/benchmark.sh
- Command line
-
hf download hf://lewislululu/jevon/scripts/benchmark.sh
-
curl -L -o benchmark.sh https://huggingface.co/lewislululu/jevon/resolve/main/scripts/benchmark.sh
4.36 kB
| # Full evaluation suite for a trained checkpoint. | |
| # | |
| # scripts/benchmark.sh runs/jevon-final [arcade-data-dir] [weights] | |
| # | |
| # `weights` selects which checkpoint the whole suite is run against, and | |
| # defaults to balanced.pt -- the one chosen on min(maze_action, snake_action) | |
| # rather than on aggregate eval loss, which the boolean questions dominate. | |
| # Falls back to best.pt for runs trained before that existed. Every number in | |
| # RESULTS.md comes from the single checkpoint named here, so the evaluation, | |
| # the probe and all the play configs cannot silently disagree about which | |
| # model they measured. | |
| # | |
| # Writes eval.json, probe.json and play/*.json under the run directory, then | |
| # regenerates RESULTS.md from them. | |
| set -euo pipefail | |
| RUN="${1:-runs/jevon-final}" | |
| ARCADE="${2:-}" | |
| WEIGHTS="${3:-}" | |
| PY="${PYTHON:-$(dirname "$0")/../../.venv/bin/python}" | |
| [ -x "$PY" ] || PY=python3 | |
| PLAY="$RUN/play" | |
| mkdir -p "$PLAY" | |
| if [ -z "$WEIGHTS" ]; then | |
| if [ -f "$RUN/balanced.pt" ]; then WEIGHTS=balanced.pt; else WEIGHTS=best.pt; fi | |
| fi | |
| [ -f "$RUN/$WEIGHTS" ] || { echo "no such checkpoint: $RUN/$WEIGHTS" >&2; exit 1; } | |
| echo "==> benchmarking $RUN/$WEIGHTS" | |
| echo "==> question-level metrics and calibration" | |
| "$PY" scripts/evaluate.py --checkpoint "$RUN" --weights "$WEIGHTS" \ | |
| --splits data/frozen/val.jsonl,data/frozen/test.jsonl,data/frozen/ood.jsonl \ | |
| --out "$RUN/eval.json" | |
| echo "==> planner field probe" | |
| "$PY" scripts/probe.py --checkpoint "$RUN" --weights "$WEIGHTS" \ | |
| --out "$RUN/probe.json" | |
| play () { # play <name> <game> <controller> <extra...> | |
| local name="$1" game="$2" ctrl="$3"; shift 3 | |
| echo "==> play: $name" | |
| local trace=() | |
| # --trace-max-steps shortens the replay, never the episode, so the summary is | |
| # still measured over the full run. Without it a random walk on a 31x31 board | |
| # contributes its whole 3844-step cap and the arcade data directory grows to | |
| # megabytes of JSON nobody will scrub through. record.sh in the arcade repo | |
| # uses the same cap; they must agree or the two repos ship different replays. | |
| [ -n "$ARCADE" ] && trace=(--trace-out "$ARCADE/${name}.json" --trace-max-steps 400) | |
| "$PY" scripts/play.py --checkpoint "$RUN" --weights "$WEIGHTS" \ | |
| --game "$game" --controller "$ctrl" \ | |
| --out "$PLAY/${name}_summary.json" "${trace[@]}" "$@" | |
| [ -n "$ARCADE" ] && cp "$PLAY/${name}_summary.json" "$ARCADE/" || true | |
| } | |
| MAZE_ARGS=(--episodes 3 --maze-sizes 11,21,31) | |
| SNAKE_ARGS=(--episodes 6 --snake-sizes 12) | |
| play maze_model maze model "${MAZE_ARGS[@]}" | |
| # Same network, same absence of search or memory, but sampling from the | |
| # distribution instead of taking its argmax. Now that only legal moves are | |
| # offered, a greedy controller cannot bang into a wall, but it can still cycle: | |
| # the planner is stateless, so a two-cell oscillation repeats forever. Sampling | |
| # breaks that with no memory at all, if the policy is calibrated enough for its | |
| # second choice to be worth taking. Whether it is, is the point of measuring. | |
| play maze_model_sampled maze model "${MAZE_ARGS[@]}" --temperature 1.0 | |
| play maze_model_field maze model-field "${MAZE_ARGS[@]}" | |
| play maze_model_memory maze model+memory "${MAZE_ARGS[@]}" | |
| play maze_reference maze reference "${MAZE_ARGS[@]}" | |
| play maze_random maze random "${MAZE_ARGS[@]}" | |
| # The control for maze_model_memory, and the reason that row cannot be read on | |
| # its own. Identical bookkeeping, no network: whatever this solves, the memory | |
| # solved. On a small board near-exhaustive exploration is enough to match the | |
| # model on solve rate, so the two separate on path length, not on arrival. | |
| play maze_random_memory maze random+memory "${MAZE_ARGS[@]}" | |
| play snake_model snake model "${SNAKE_ARGS[@]}" | |
| play snake_reference snake reference "${SNAKE_ARGS[@]}" | |
| play snake_random snake random "${SNAKE_ARGS[@]}" | |
| echo "==> RESULTS.md" | |
| "$PY" scripts/make_results.py --run "$RUN" --eval "$RUN/eval.json" \ | |
| --probe "$RUN/probe.json" --play-dir "$PLAY" | |
| # The README headline is generated from the same JSON, so it cannot disagree | |
| # with RESULTS.md. Only refreshed when benchmarking the run the README is | |
| # about; benchmarking an experimental arm must not rewrite the front page. | |
| if [ "$RUN" = "runs/jevon-final" ]; then | |
| echo "==> README headline" | |
| "$PY" scripts/readme_table.py --run "$RUN" | |
| fi | |