2026-07-01 09:19:42 +08:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
# Run all non-wrapper evaluation scripts for every completed experiment under
|
|
|
|
|
# runs/. The script is written for Linux servers with bash 4.2.
|
|
|
|
|
|
|
|
|
|
cd "$(dirname "${BASH_SOURCE[0]}")"
|
|
|
|
|
|
|
|
|
|
PYTHON_BIN="${PYTHON_BIN:-python}"
|
|
|
|
|
DEVICE="${DEVICE:-cuda}"
|
|
|
|
|
EVAL_SPLIT="${EVAL_SPLIT:-test}"
|
|
|
|
|
NUM_WORKERS="${NUM_WORKERS:-4}"
|
|
|
|
|
NUM_WORKERS_AUC="${NUM_WORKERS_AUC:-}"
|
|
|
|
|
BATCH_SIZE="${BATCH_SIZE:-}"
|
|
|
|
|
DATASET_SUBSET_SIZE="${DATASET_SUBSET_SIZE:-}"
|
|
|
|
|
DRY_RUN="${DRY_RUN:-0}"
|
|
|
|
|
|
|
|
|
|
# These attribution jobs can be expensive, but they are part of the evaluation
|
|
|
|
|
# surface in this repository. Set either variable to 0 to leave that family out.
|
|
|
|
|
RUN_EXTRA_INFO_ATTRIBUTION="${RUN_EXTRA_INFO_ATTRIBUTION:-1}"
|
|
|
|
|
RUN_SINGLE_DISEASE_MORTALITY_ATTRIBUTION="${RUN_SINGLE_DISEASE_MORTALITY_ATTRIBUTION:-1}"
|
|
|
|
|
|
|
|
|
|
common_args_base() {
|
|
|
|
|
printf '%s\n' --run_path "$1" --eval_split "${EVAL_SPLIT}" --num_workers "${NUM_WORKERS}"
|
|
|
|
|
if [[ -n "${BATCH_SIZE}" ]]; then
|
|
|
|
|
printf '%s\n' --batch_size "${BATCH_SIZE}"
|
|
|
|
|
fi
|
|
|
|
|
if [[ -n "${DATASET_SUBSET_SIZE}" ]]; then
|
|
|
|
|
printf '%s\n' --dataset_subset_size "${DATASET_SUBSET_SIZE}"
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
common_args_with_device() {
|
|
|
|
|
common_args_base "$1"
|
|
|
|
|
printf '%s\n' --device "${DEVICE}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auc_args() {
|
|
|
|
|
if [[ -n "${NUM_WORKERS_AUC}" ]]; then
|
|
|
|
|
printf '%s\n' --num_workers_auc "${NUM_WORKERS_AUC}"
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
has_completed_dir() {
|
|
|
|
|
local dir="$1"
|
|
|
|
|
shift
|
|
|
|
|
[[ -d "${dir}" ]] || return 1
|
|
|
|
|
local required
|
|
|
|
|
for required in "$@"; do
|
|
|
|
|
[[ -s "${dir}/${required}" ]] || return 1
|
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
run_command() {
|
|
|
|
|
echo " run: $*"
|
|
|
|
|
if [[ "${DRY_RUN}" == "1" ]]; then
|
|
|
|
|
return 0
|
|
|
|
|
fi
|
|
|
|
|
"$@"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
run_dir_result_if_missing() {
|
|
|
|
|
local label="$1"
|
|
|
|
|
local result_dir="$2"
|
|
|
|
|
local required_1="$3"
|
|
|
|
|
local required_2="$4"
|
|
|
|
|
shift 4
|
|
|
|
|
|
|
|
|
|
if has_completed_dir "${result_dir}" "${required_1}" "${required_2}"; then
|
|
|
|
|
echo " skip ${label}: found ${result_dir}"
|
|
|
|
|
return 0
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
run_command "$@"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
run_has_extra_info() {
|
|
|
|
|
"${PYTHON_BIN}" - "$1" <<'PY'
|
|
|
|
|
import json
|
|
|
|
|
import sys
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
cfg_path = Path(sys.argv[1]) / "train_config.json"
|
|
|
|
|
try:
|
|
|
|
|
cfg = json.loads(cfg_path.read_text(encoding="utf-8"))
|
|
|
|
|
except Exception:
|
|
|
|
|
raise SystemExit(1)
|
|
|
|
|
|
|
|
|
|
extra = cfg.get("extra_info_types", [])
|
|
|
|
|
raise SystemExit(0 if isinstance(extra, list) and len(extra) > 0 else 1)
|
|
|
|
|
PY
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-01 10:47:09 +08:00
|
|
|
run_is_all_future() {
|
|
|
|
|
"${PYTHON_BIN}" - "$1" <<'PY'
|
|
|
|
|
import json
|
|
|
|
|
import sys
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
cfg_path = Path(sys.argv[1]) / "train_config.json"
|
|
|
|
|
try:
|
|
|
|
|
cfg = json.loads(cfg_path.read_text(encoding="utf-8"))
|
|
|
|
|
except Exception:
|
|
|
|
|
raise SystemExit(1)
|
|
|
|
|
|
|
|
|
|
mode = str(cfg.get("model_target_mode", "next_token")).lower()
|
|
|
|
|
raise SystemExit(0 if mode == "all_future" else 1)
|
|
|
|
|
PY
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-01 09:19:42 +08:00
|
|
|
for run_path in runs/*; do
|
|
|
|
|
[[ -d "${run_path}" ]] || continue
|
|
|
|
|
|
|
|
|
|
echo "==> ${run_path}"
|
|
|
|
|
if [[ ! -f "${run_path}/train_config.json" ]]; then
|
|
|
|
|
echo " skip run: missing train_config.json"
|
|
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
if [[ ! -s "${run_path}/best_model.pt" ]]; then
|
|
|
|
|
echo " skip run: missing best_model.pt"
|
|
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
common=()
|
|
|
|
|
while IFS= read -r arg; do common+=("${arg}"); done < <(common_args_with_device "${run_path}")
|
|
|
|
|
|
|
|
|
|
auc_extra=()
|
|
|
|
|
while IFS= read -r arg; do auc_extra+=("${arg}"); done < <(auc_args)
|
|
|
|
|
|
|
|
|
|
run_dir_result_if_missing \
|
|
|
|
|
"evaluate_auc.py" \
|
|
|
|
|
"${run_path}" \
|
|
|
|
|
"df_both.csv" \
|
|
|
|
|
"df_auc_unpooled.csv" \
|
|
|
|
|
"${PYTHON_BIN}" evaluate_auc.py "${common[@]}" "${auc_extra[@]}"
|
|
|
|
|
|
|
|
|
|
run_dir_result_if_missing \
|
|
|
|
|
"evaluate_auc_v2.py" \
|
|
|
|
|
"${run_path}" \
|
|
|
|
|
"df_auc_landmark.csv" \
|
|
|
|
|
"df_auc_landmark_unpooled.csv" \
|
|
|
|
|
"${PYTHON_BIN}" evaluate_auc_v2.py "${common[@]}" "${auc_extra[@]}"
|
|
|
|
|
|
2026-07-01 10:47:09 +08:00
|
|
|
if ! run_is_all_future "${run_path}"; then
|
|
|
|
|
echo " skip attribution evaluations: model_target_mode is not all_future"
|
|
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
|
2026-07-01 09:19:42 +08:00
|
|
|
if [[ "${RUN_EXTRA_INFO_ATTRIBUTION}" == "1" ]]; then
|
|
|
|
|
if run_has_extra_info "${run_path}"; then
|
|
|
|
|
run_dir_result_if_missing \
|
|
|
|
|
"evaluate_extra_info_attribution.py" \
|
2026-07-01 10:16:51 +08:00
|
|
|
"${run_path}/extra_info_attribution_${EVAL_SPLIT}" \
|
2026-07-01 09:19:42 +08:00
|
|
|
"manifest.json" \
|
2026-07-01 10:16:51 +08:00
|
|
|
"summary_extra_info_disease_parameters.csv" \
|
|
|
|
|
"${PYTHON_BIN}" evaluate_extra_info_attribution.py "${common[@]}"
|
2026-07-01 09:19:42 +08:00
|
|
|
else
|
|
|
|
|
echo " skip evaluate_extra_info_attribution.py: run has no extra-info types"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [[ "${RUN_SINGLE_DISEASE_MORTALITY_ATTRIBUTION}" == "1" ]]; then
|
|
|
|
|
run_dir_result_if_missing \
|
|
|
|
|
"evaluate_single_disease_mortality_attribution.py" \
|
|
|
|
|
"${run_path}/single_disease_mortality_parameters_${EVAL_SPLIT}_all_diseases" \
|
|
|
|
|
"manifest.json" \
|
|
|
|
|
"summary_by_disease_age_sex.csv" \
|
|
|
|
|
"${PYTHON_BIN}" evaluate_single_disease_mortality_attribution.py "${common[@]}"
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
echo "All missing evaluations are complete."
|