Skip to content

Commit ffafa53

Browse files
simple lstm
1 parent 664c0be commit ffafa53

5 files changed

Lines changed: 197 additions & 49 deletions

File tree

cybench/runs/run_de_agera5fpar_lstm.sh

Lines changed: 175 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
#!/usr/bin/env bash
22
#
3-
# Germany-only colleague-style LSTM: AgERA5 + FPAR, avg_pool tokenizer, no static context.
4-
# Runs the same screening → walk-forward pipeline as the SLURM benchmark (other torch models).
3+
# Germany colleague-style LSTM: AgERA5 + FPAR, avg_pool tokenizer, no static context.
4+
# Default: submit screening → walk-forward via SLURM (same pipeline as other torch models).
55
#
66
# Usage (from repo root):
77
# cybench/runs/run_de_agera5fpar_lstm.sh all
88
# cybench/runs/run_de_agera5fpar_lstm.sh screening --batch my_de_lstm_v1
9-
# cybench/runs/run_de_agera5fpar_lstm.sh walk_forward --frozen-dir /path/to/.../2017_2018_2019_2020_2021
10-
# cybench/runs/run_de_agera5fpar_lstm.sh all --crop maize --device cpu --hp-trials 5 --dry-run
9+
# cybench/runs/run_de_agera5fpar_lstm.sh all --local # run poetry on this machine
10+
# cybench/runs/run_de_agera5fpar_lstm.sh all --cpu --dry-run # SLURM on CPU partition
1111
#
1212
set -euo pipefail
1313

@@ -18,23 +18,28 @@ Usage: run_de_agera5fpar_lstm.sh <screening|walk_forward|all> [options]
1818
Phases:
1919
screening HPO on train/val, final fit on train+val, evaluate on held-out test block
2020
walk_forward Rolling forecasts using frozen hyperparameters from screening
21-
all screening, then walk_forward (auto-discovers latest screening artifacts)
21+
all screening, then walk-forward (SLURM: wf chained with afterok on screening)
2222
2323
Options:
2424
--batch NAME experiment.name / output folder (default: baselines_de_agera5fpar_lstm_v1)
2525
--crop CROP wheat | maize (default: maize)
2626
--country CC ISO country code (default: DE)
2727
--horizon H end_of_sequence (default: middle-of-season → run dirs *_mid_season_*)
28-
--device DEV cuda | cpu (default: cuda)
2928
--hp-trials N Optuna trials in screening (default: 20)
30-
--frozen-dir PATH Screening split folder with optimal_model.yaml (walk_forward only)
31-
--collect After walk_forward, run collect_walk_forward_results.py
32-
--dry-run Print commands without executing
29+
--local Run poetry/python on this machine (default: submit SLURM jobs)
30+
--cpu SLURM: main partition, no GPU (CYBENCH_FORCE_CPU=1)
31+
--no-dependency SLURM "all": submit walk-forward without afterok on screening
32+
--frozen-dir PATH Walk-forward only, local mode: explicit screening artifact folder
33+
--collect After walk-forward, run collect_walk_forward_results.py (local only)
34+
--dry-run Print planned commands without executing
3335
-h, --help Show this help
3436
35-
Output (relative to repo root):
36-
../output/<batch>/maize_DE_lstm_lf_screening_mid_season_<timestamp>/
37-
../output/<batch>/maize_DE_lstm_lf_walk_forward_mid_season_<timestamp>/
37+
SLURM (default):
38+
Logs: output/screening/ and output/walk_forward/ under repo root
39+
Results: ../output/<batch>/maize_DE_lstm_lf_{screening,walk_forward}_mid_season_<timestamp>/
40+
41+
Local (--local):
42+
Same Hydra overrides; writes directly under ../output/<batch>/
3843
EOF
3944
}
4045

@@ -48,6 +53,9 @@ HP_TRIALS=20
4853
FROZEN_DIR=""
4954
COLLECT=false
5055
DRY_RUN=false
56+
USE_SLURM=true
57+
SLURM_CPU=false
58+
SLURM_NO_DEPENDENCY=false
5159

5260
if [[ $# -lt 1 ]]; then
5361
usage
@@ -79,6 +87,9 @@ while [[ $# -gt 0 ]]; do
7987
--device) DEVICE=$2; shift 2 ;;
8088
--hp-trials) HP_TRIALS=$2; shift 2 ;;
8189
--frozen-dir) FROZEN_DIR=$2; shift 2 ;;
90+
--local) USE_SLURM=false; shift ;;
91+
--cpu) SLURM_CPU=true; DEVICE=cpu; shift ;;
92+
--no-dependency) SLURM_NO_DEPENDENCY=true; shift ;;
8293
--collect) COLLECT=true; shift ;;
8394
--dry-run) DRY_RUN=true; shift ;;
8495
-h|--help) usage; exit 0 ;;
@@ -92,11 +103,17 @@ done
92103

93104
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
94105
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
106+
SLURM_DIR="${REPO_ROOT}/cybench/runs/slurm"
107+
SUBMIT_ARRAY="${SLURM_DIR}/submit_array.sh"
95108
cd "${REPO_ROOT}"
96109

97110
OUTPUT_ROOT="${REPO_ROOT}/../output"
98111
BATCH_DIR="${OUTPUT_ROOT}/${BATCH}"
99-
if [[ "${DRY_RUN}" != true ]]; then
112+
MANIFEST_DIR="${SLURM_DIR}/manifests/${BATCH}"
113+
MANIFEST="${MANIFEST_DIR}/benchmark_jobs_gpu.txt"
114+
OVERRIDES_FILE="${MANIFEST_DIR}/extra_overrides.txt"
115+
116+
if [[ "${DRY_RUN}" != true && "${USE_SLURM}" == false ]]; then
100117
mkdir -p "${BATCH_DIR}"
101118
OUTPUT_ROOT="$(cd "${OUTPUT_ROOT}" && pwd)"
102119
BATCH_DIR="${OUTPUT_ROOT}/${BATCH}"
@@ -112,8 +129,29 @@ run_cmd() {
112129
fi
113130
}
114131

115-
# Shared Hydra overrides (must match across screening and walk-forward).
116-
shared_overrides() {
132+
# Dataset/model overrides shared by SLURM and local runs (crop/country/model set elsewhere on SLURM).
133+
write_extra_overrides_file() {
134+
mkdir -p "${MANIFEST_DIR}"
135+
cat > "${OVERRIDES_FILE}" <<EOF
136+
dataset/temporal=no_aggregate
137+
model/torch_model/temporal_encoder/tokenizer=avg_pool
138+
model.torch_model.embed_dim=8
139+
~dataset.temporal.sources.ndvi
140+
~dataset.temporal.sources.soil_moisture
141+
dataset.temporal.sources.meteo.select=[tmin,tmax,tavg,prec,rad,et0,vpd]
142+
+process={name:select_context,drop:[year,sos_sin,sos_cos,eos_sin,eos_cos,loc_x,loc_y,loc_z,awc,bulk_density,drainage_class_1,drainage_class_2,drainage_class_3,drainage_class_4,drainage_class_5,drainage_class_6],keep:null}
143+
EOF
144+
}
145+
146+
write_job_manifest() {
147+
mkdir -p "${MANIFEST_DIR}"
148+
cat > "${MANIFEST}" <<EOF
149+
# ${CROP} ${COUNTRY} colleague-style lstm_lf (AgERA5 + FPAR, avg_pool, no static)
150+
${CROP} ${COUNTRY} lstm_lf torch yes no yes
151+
EOF
152+
}
153+
154+
shared_local_overrides() {
117155
cat <<EOF
118156
dataset/crop=${CROP}
119157
dataset.country=${COUNTRY}
@@ -134,8 +172,8 @@ experiment.device=${DEVICE}
134172
EOF
135173
}
136174

137-
read_overrides() {
138-
mapfile -t OVERRIDES < <(shared_overrides)
175+
read_local_overrides() {
176+
mapfile -t OVERRIDES < <(shared_local_overrides)
139177
}
140178

141179
horizon_tag() {
@@ -161,37 +199,37 @@ find_frozen_screening_dir() {
161199
return 1
162200
}
163201

164-
run_screening() {
165-
read_overrides
202+
run_local_screening() {
203+
read_local_overrides
166204
local -a cmd=(poetry run python cybench/runs/run_experiments.py)
167205
cmd+=("${OVERRIDES[@]}")
168206
cmd+=(
169207
validation=screening
170208
+hp_search=bayesian
171209
"hp_search.n_trials=${HP_TRIALS}"
172210
)
173-
echo "== Screening | ${CROP}/${COUNTRY} | batch=${BATCH} | device=${DEVICE} | out=${BATCH_DIR}"
211+
echo "== Local screening | ${CROP}/${COUNTRY} | batch=${BATCH} | device=${DEVICE}"
174212
run_cmd "${cmd[@]}"
175213
}
176214

177-
run_walk_forward() {
215+
run_local_walk_forward() {
178216
local frozen=$1
179217
if [[ ! -f "${frozen}/optimal_model.yaml" ]]; then
180218
echo "Missing optimal_model.yaml in ${frozen}" >&2
181219
exit 1
182220
fi
183-
read_overrides
221+
read_local_overrides
184222
local -a cmd=(poetry run python cybench/runs/run_experiments.py)
185223
cmd+=("${OVERRIDES[@]}")
186224
cmd+=(
187225
validation=walk_forward
188226
"validation.frozen_screening_dir=${frozen}"
189227
)
190-
echo "== Walk-forward | ${CROP}/${COUNTRY} | batch=${BATCH} | frozen=${frozen}"
228+
echo "== Local walk-forward | ${CROP}/${COUNTRY} | frozen=${frozen}"
191229
run_cmd "${cmd[@]}"
192230
}
193231

194-
run_collect() {
232+
run_local_collect() {
195233
local -a cmd=(
196234
poetry run python cybench/runs/analysis/collect_walk_forward_results.py
197235
--batch "${BATCH}"
@@ -201,32 +239,120 @@ run_collect() {
201239
run_cmd "${cmd[@]}"
202240
}
203241

204-
case "${PHASE}" in
205-
screening)
206-
run_screening
207-
;;
208-
walk_forward)
209-
if [[ -z "${FROZEN_DIR}" ]]; then
210-
FROZEN_DIR=$(find_frozen_screening_dir)
211-
fi
212-
run_walk_forward "${FROZEN_DIR}"
213-
if [[ "${COLLECT}" == true ]]; then
214-
run_collect
215-
fi
216-
;;
217-
all)
218-
run_screening
219-
if [[ "${DRY_RUN}" == true ]]; then
220-
echo "(dry-run: would auto-discover frozen screening dir for walk-forward)"
221-
run_cmd echo walk_forward skipped in dry-run after screening
222-
else
223-
FROZEN_DIR=$(find_frozen_screening_dir)
224-
run_walk_forward "${FROZEN_DIR}"
242+
submit_slurm_phase() {
243+
local phase=$1
244+
local dependency=${2:-}
245+
local -a cmd=(
246+
"${SUBMIT_ARRAY}" "${phase}" "${MANIFEST}"
247+
--batch "${BATCH}"
248+
--array 0
249+
--group gpu
250+
)
251+
if [[ "${SLURM_CPU}" == true ]]; then
252+
cmd+=(--cpu)
253+
else
254+
cmd+=(--gpu)
255+
fi
256+
if [[ -n "${dependency}" ]]; then
257+
cmd+=(--dependency "${dependency}")
258+
fi
259+
echo "== SLURM ${phase} | ${CROP}/${COUNTRY} | batch=${BATCH} | horizon=${HORIZON}"
260+
if [[ "${DRY_RUN}" == true ]]; then
261+
echo " export PREDICTION_HORIZON=${HORIZON}"
262+
echo " export HP_TRIALS=${HP_TRIALS}"
263+
echo " export CYBENCH_EXTRA_OVERRIDES_FILE=${OVERRIDES_FILE}"
264+
run_cmd "${cmd[@]}"
265+
SLURM_LAST_JOB_ID=""
266+
return 0
267+
fi
268+
if ! command -v sbatch >/dev/null 2>&1; then
269+
echo "sbatch not found. Use --local or run on a cluster login node." >&2
270+
exit 1
271+
fi
272+
local out
273+
out=$("${cmd[@]}" 2>&1)
274+
echo "${out}"
275+
SLURM_LAST_JOB_ID=$(echo "${out}" | awk -F= '/^job_id=/{print $2}')
276+
if [[ -z "${SLURM_LAST_JOB_ID}" ]]; then
277+
echo "Failed to parse SLURM job id from submit_array output" >&2
278+
exit 1
279+
fi
280+
}
281+
282+
SLURM_LAST_JOB_ID=""
283+
284+
prepare_slurm_assets() {
285+
write_job_manifest
286+
write_extra_overrides_file
287+
export PREDICTION_HORIZON="${HORIZON}"
288+
export HP_TRIALS="${HP_TRIALS}"
289+
export CYBENCH_EXTRA_OVERRIDES_FILE="${OVERRIDES_FILE}"
290+
export CYBENCH_EXPERIMENT_NAME="${BATCH}"
291+
echo "Manifest: ${MANIFEST}"
292+
echo "Overrides: ${OVERRIDES_FILE}"
293+
echo "Output batch: ../output/${BATCH}/"
294+
}
295+
296+
run_slurm_screening() {
297+
prepare_slurm_assets
298+
submit_slurm_phase screening
299+
}
300+
301+
run_slurm_walk_forward() {
302+
local dependency=${1:-}
303+
prepare_slurm_assets
304+
submit_slurm_phase walk_forward "${dependency}"
305+
}
306+
307+
run_slurm_all() {
308+
local wf_dep=""
309+
prepare_slurm_assets
310+
submit_slurm_phase screening
311+
if [[ "${SLURM_NO_DEPENDENCY}" != true && -n "${SLURM_LAST_JOB_ID}" ]]; then
312+
wf_dep="afterok:${SLURM_LAST_JOB_ID}"
313+
fi
314+
submit_slurm_phase walk_forward "${wf_dep}"
315+
if [[ "${COLLECT}" == true ]]; then
316+
echo ""
317+
echo "Note: --collect is not submitted as a SLURM job. After walk-forward completes, run:"
318+
echo " poetry run python cybench/runs/analysis/collect_walk_forward_results.py \\"
319+
echo " --batch ${BATCH} --output-dir ${OUTPUT_ROOT}/paper_${BATCH}"
320+
fi
321+
}
322+
323+
if [[ "${USE_SLURM}" == true ]]; then
324+
case "${PHASE}" in
325+
screening) run_slurm_screening ;;
326+
walk_forward) run_slurm_walk_forward ;;
327+
all) run_slurm_all ;;
328+
esac
329+
else
330+
case "${PHASE}" in
331+
screening)
332+
run_local_screening
333+
;;
334+
walk_forward)
335+
if [[ -z "${FROZEN_DIR}" ]]; then
336+
FROZEN_DIR=$(find_frozen_screening_dir)
337+
fi
338+
run_local_walk_forward "${FROZEN_DIR}"
225339
if [[ "${COLLECT}" == true ]]; then
226-
run_collect
340+
run_local_collect
227341
fi
228-
fi
229-
;;
230-
esac
342+
;;
343+
all)
344+
run_local_screening
345+
if [[ "${DRY_RUN}" == true ]]; then
346+
echo "(dry-run: would auto-discover frozen screening dir for walk-forward)"
347+
else
348+
FROZEN_DIR=$(find_frozen_screening_dir)
349+
run_local_walk_forward "${FROZEN_DIR}"
350+
if [[ "${COLLECT}" == true ]]; then
351+
run_local_collect
352+
fi
353+
fi
354+
;;
355+
esac
356+
fi
231357

232358
echo "Done."

cybench/runs/slurm/screening.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,5 +67,6 @@ COMMON=(
6767
configure_parallelism COMMON
6868
EXTRA=()
6969
configure_hpo_extras EXTRA
70+
append_extra_overrides_file EXTRA
7071

7172
poetry run python cybench/runs/run_experiments.py "${COMMON[@]}" "${EXTRA[@]}"

cybench/runs/slurm/slurm_common.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,20 @@ configure_hpo_extras() {
303303
fi
304304
}
305305

306+
# Optional Hydra overrides file (one override per line). Set CYBENCH_EXTRA_OVERRIDES_FILE.
307+
append_extra_overrides_file() {
308+
local -n _extra=$1
309+
local line
310+
if [[ -z "${CYBENCH_EXTRA_OVERRIDES_FILE:-}" || ! -f "${CYBENCH_EXTRA_OVERRIDES_FILE}" ]]; then
311+
return 0
312+
fi
313+
while IFS= read -r line || [[ -n "${line}" ]]; do
314+
line="${line//$'\r'/}"
315+
[[ -z "${line}" || "${line}" =~ ^[[:space:]]*# ]] && continue
316+
_extra+=("${line}")
317+
done < "${CYBENCH_EXTRA_OVERRIDES_FILE}"
318+
}
319+
306320
# Find .../<test_years>/optimal_model.yaml under the latest horizon-tagged screening run.
307321
find_frozen_screening_dir() {
308322
local crop=$1 country=$2 model_slug=$3

cybench/runs/slurm/submit_array.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,12 @@ if [[ "${FORCE_CPU}" == true ]]; then
187187
fi
188188

189189
SBATCH_EXPORT="ALL,JOB_MANIFEST=${JOB_MANIFEST},PREDICTION_HORIZON=${PREDICTION_HORIZON},CYBENCH_EXPERIMENT_NAME=${CYBENCH_EXPERIMENT_NAME}"
190+
if [[ -n "${HP_TRIALS:-}" ]]; then
191+
SBATCH_EXPORT+=",HP_TRIALS=${HP_TRIALS}"
192+
fi
193+
if [[ -n "${CYBENCH_EXTRA_OVERRIDES_FILE:-}" ]]; then
194+
SBATCH_EXPORT+=",CYBENCH_EXTRA_OVERRIDES_FILE=${CYBENCH_EXTRA_OVERRIDES_FILE}"
195+
fi
190196
if [[ "${FORCE_CPU}" == true ]]; then
191197
SBATCH_EXPORT+=",CYBENCH_FORCE_CPU=1"
192198
fi

cybench/runs/slurm/walk_forward.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,6 @@ EXTRA=()
5555
if [[ "${FEATURE_DESIGN}" == "yes" && "${FRAMEWORK}" == "pandas" ]]; then
5656
EXTRA+=(+feature_selection=mrmr)
5757
fi
58+
append_extra_overrides_file EXTRA
5859

5960
poetry run python cybench/runs/run_experiments.py "${COMMON[@]}" "${EXTRA[@]}"

0 commit comments

Comments
 (0)