File size: 6,072 Bytes
80e070e | 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 | """
Band-combination sweep: train + evaluate each combo, save checkpoints, collect
results. Runs on the patches in --data-dir (default all_data/patches). Patch size
is auto-detected by train.py / evaluate.py, so the SAME runner works for the
64 px (30 m) set now and the 128 px (10 m) set later — just point --data-dir at
the new patches.
python run_experiments.py --gpu 1
python run_experiments.py --gpu 1 --only f17_proven,f17_standard,f17_optical
python run_experiments.py --gpu 1 --data-dir all_data/patches --force
Each combo writes all_data/checkpoint_<name>/ and all_data/result_<name>/.
Already-finished combos are skipped (resume-friendly) unless --force.
At the end it prints + saves the comparison table (all_data/runs_comparison.csv).
"""
import os
import sys
import argparse
import subprocess
HERE = os.path.dirname(os.path.abspath(__file__))
RUN_GPU = os.path.join(HERE, "run_gpu.sh")
# name -> band list (None = all 35 bands). Edit / extend freely.
COMBOS = {
# --- three 17-feature variants (the focus); first is the current best 17 ---
"f17_proven": ["TPI", "ROUGHNESS", "LST_winter", "NDWI", "LST_summer", "REL_RELIEF",
"ASPECT", "LST_anomaly", "ELEV_COP", "B2", "HILLSHADE", "B8A", "NDMI", "B12", "B3", "B4", "B8"],
"f17_standard": ["TPI", "SLOPE", "LST_winter", "NDVI", "LST_summer", "REL_RELIEF",
"ASPECT", "LST_anomaly", "ELEV_COP", "B2", "HILLSHADE", "B8A", "NDMI", "B11", "B3", "B4", "B8"],
"f17_optical": ["B2", "B3", "B4", "B8", "B8A", "B11", "B12", "NDVI", "NDWI", "NDMI", "NDSI", "BSI",
"ELEV_COP", "SLOPE", "TPI", "LST_winter", "LST_summer"],
# --- extra combos to round out the sweep ---
"f17_terrain": ["TPI", "ROUGHNESS", "TRI", "SLOPE", "ASPECT", "HILLSHADE", "CURVATURE", "REL_RELIEF",
"ELEV_COP", "LST_winter", "LST_summer", "LST_anomaly", "B2", "B4", "B8", "NDWI", "NDVI"],
"f17_no_thermal": ["TPI", "ROUGHNESS", "SLOPE", "ASPECT", "REL_RELIEF", "HILLSHADE", "CURVATURE",
"ELEV_COP", "B2", "B3", "B4", "B8", "B8A", "B11", "B12", "NDWI", "NDVI"],
"f16_balanced": ["TPI", "ROUGHNESS", "REL_RELIEF", "ELEV_COP", "SLOPE", "ASPECT", "LST_winter", "LST_summer",
"NDWI", "NDVI", "NDMI", "B2", "B3", "B4", "B8", "B12"],
"f20_rich": ["TPI", "ROUGHNESS", "LST_winter", "NDWI", "LST_summer", "REL_RELIEF", "ASPECT", "LST_anomaly",
"ELEV_COP", "B2", "HILLSHADE", "B8A", "NDMI", "B12", "B3", "B4", "B8", "NDVI", "SLOPE", "B11"],
"f14_lean": ["TPI", "ROUGHNESS", "LST_winter", "NDWI", "LST_summer", "REL_RELIEF", "ASPECT",
"ELEV_COP", "B2", "B8A", "NDMI", "B12", "B4", "B8"],
"f12_core": ["TPI", "ROUGHNESS", "LST_winter", "LST_summer", "REL_RELIEF", "ASPECT",
"ELEV_COP", "B2", "B8", "NDWI", "NDMI", "B12"],
# --- reference baseline (auto-skips if already trained) ---
"all": None,
}
def dirs_for(name):
ck = "all_data/checkpoint" if name == "all" else f"all_data/checkpoint_{name}"
rs = "all_data/result" if name == "all" else f"all_data/result_{name}"
return os.path.join(HERE, ck), os.path.join(HERE, rs)
def run_step(script, env, log_path):
os.makedirs(os.path.dirname(log_path), exist_ok=True)
with open(log_path, "w") as lf:
r = subprocess.run([RUN_GPU, script], cwd=HERE, env=env,
stdout=lf, stderr=subprocess.STDOUT)
return r.returncode
def run_one(name, bands, gpu, data_dir, force):
ckpt_dir, result_dir = dirs_for(name)
weights = os.path.join(ckpt_dir, "best_model.weights.h5")
metrics = os.path.join(result_dir, "metrics.json")
env = os.environ.copy()
env["CUDA_VISIBLE_DEVICES"] = str(gpu)
env["DATA_DIR"] = data_dir
env["RUN_NAME"] = name
env.pop("BANDS", None)
if bands is None:
env.pop("BAND_LIST", None)
else:
env["BAND_LIST"] = ",".join(bands)
nb = "all" if bands is None else len(bands)
if force or not os.path.exists(weights):
print(f"\n[TRAIN] {name} ({nb} bands) GPU {gpu}")
if run_step("train.py", env, os.path.join(ckpt_dir, "train_log.txt")) != 0:
print(f" TRAIN FAILED -> {ckpt_dir}/train_log.txt")
return False
else:
print(f"\n[skip train] {name} (weights exist)")
if force or not os.path.exists(metrics):
print(f"[EVAL ] {name}")
if run_step("evaluate.py", env, os.path.join(result_dir, "eval_log.txt")) != 0:
print(f" EVAL FAILED -> {result_dir}/eval_log.txt")
return False
else:
print(f"[skip eval ] {name} (metrics exist)")
return True
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--gpu", default="1", help="CUDA device index (default 1)")
ap.add_argument("--data-dir", default="all_data/patches",
help="patches root (default all_data/patches)")
ap.add_argument("--only", default=None,
help="comma list of combo names to run (default: all in COMBOS)")
ap.add_argument("--force", action="store_true",
help="re-run even if checkpoint/metrics already exist")
args = ap.parse_args()
names = list(COMBOS)
if args.only:
wanted = [n.strip() for n in args.only.split(",")]
bad = [n for n in wanted if n not in COMBOS]
if bad:
sys.exit(f"Unknown combo(s): {bad}\nAvailable: {list(COMBOS)}")
names = wanted
print(f"Running {len(names)} combo(s) on GPU {args.gpu}, data={args.data_dir}:")
print(" " + ", ".join(names))
ok, failed = [], []
for n in names:
(ok if run_one(n, COMBOS[n], args.gpu, args.data_dir, args.force) else failed).append(n)
print(f"\nDone. ok={ok} failed={failed}")
print("\n" + "=" * 60 + "\n RESULTS\n" + "=" * 60)
subprocess.run([sys.executable, os.path.join(HERE, "results_table.py")], cwd=HERE)
if __name__ == "__main__":
main()
|