git clone <this-repo>
cd experiments
make setup
This folder is a self-contained scaffold for the GeoLeaf proof-of-concept: vegetation classification (Woodland / Shrubland / Grassland) over Hong Kong, benchmarked against the AFCD THMHK 2021 habitat map. The current Run 3 baseline is territory-wide; pilot-zone field validation is planned for Tai Mo Shan and Sai Kung West.
It is designed to be developed locally on a laptop and then executed on a cloud GPU (vast.ai, RunPod, or a PolyU cluster node). Nothing here is trained or downloaded in the local sandbox.
GeoLeaf classifies natural land into three height-defined vegetation classes that the Planning Department needs but currently cannot get at scale:
| Class | Definition (canopy height) | Code |
|---|---|---|
| Woodland | > 5 m | 1 |
| Shrubland | 0.5 m – 5 m | 2 |
| Grassland | < 0.5 m | 3 |
| Other | (excluded from metrics) | 0 |
Three tiers, increasing in capability:
Reference: see GeoLeaf_PoC_Proposal_v15.pdf and
GeoLeaf_Business_Plan_v15.pdf in the parent directory.
Run 3 metrics and the submission figure are tracked under results/run3/.
# clone + cd
git clone <your-repo-url>
cd experiments
# python venv
python -m venv .venv
source .venv/bin/activate
pip install --upgrade pip wheel
pip install -r requirements.txt
# or:
make setup
Authentication (one-time per machine):
# Earth Engine (Lang CHM, GEDI fallback)
earthengine authenticate
# NASA Earthdata (GEDI L2A direct download)
# Create an account at https://urs.earthdata.nasa.gov
# Then put credentials in ~/.netrc :
echo "machine urs.earthdata.nasa.gov login <user> password <pass>" >> ~/.netrc
chmod 600 ~/.netrc
# HuggingFace (Prithvi-EO-2.0 backbone weights)
huggingface-cli login
Optional:
# Weights & Biases (auto-detected if WANDB_API_KEY is set; otherwise local TB)
export WANDB_API_KEY=xxxxxxxx
| Dataset | Resolution | Acquisition | Approx size | Licence |
|---|---|---|---|---|
| AFCD THMHK 2021 (labels) | polygon | CSDI portal (manual) | ~50 MB | HK Govt open data |
| PD Land Utilization 2024 | polygon | CSDI portal (manual) | ~100 MB | HK Govt open data |
| CEDD ALS DSM/DTM 2020 | 5 m | CSDI portal (manual) | ~500 MB | HK Govt open data |
| Sentinel-2 L2A 2024 composite | 10 m | Planetary Computer | ~460 MB HK | Copernicus open |
| Sentinel-1 RTC 2024 composite | 10 m | Planetary Computer | pending | Copernicus open |
| Lang 2023 global CHM | 10 m | Earth Engine / Zenodo | ~62 MB HK | CC BY 4.0 |
| GEDI L2A 2019– | footprint | NASA Earthdata | optional | NASA open |
The HK government layers do not currently expose a stable programmatic
download URL — scripts/download_data.py prints a clear MANUAL DOWNLOAD
banner with the source URL and target folder for those.
Run everything (skips already-downloaded sources):
make download
Three commands to get an mIoU from scratch:
make download # ~20-60 min one-time data fetch
make preprocess # ~5-15 min: co-register, tile, spatial-block split
make tier1 # ~5 min: CHM thresholds -> metrics + confusion matrix
Expected runtime:
Outputs:
outputs/tier1/metrics.jsonoutputs/tier1/confusion.npyoutputs/tier1/pred.tifmake preprocess # if not already done
make tier3 # default = A100 config
make tier3 GPU_CONFIG=config/t4.yaml # fallback for T4 16GB
Expected runtime:
T4 notes:
config/t4.yaml forces fp16, batch 4, accum 4, and the smaller Prithvi-300M
override.patch.size from 256 to 192 in config/base.yaml.Outputs:
outputs/tier3/checkpoints/{epoch_N.pt, best.pt}outputs/tier3/metrics.jsonoutputs/tier3/confusion.npyoutputs/logs/tb/ (or wandb)Resume from a checkpoint:
python scripts/tier3_lora_finetune.py \
--resume outputs/tier3/checkpoints/epoch_05.pt
Smoke-test the full graph (one batch only):
python scripts/tier3_lora_finetune.py --dry-run
Concrete cloud-GPU workflow (vast.ai shown — RunPod and PolyU clusters are analogous):
pytorch/pytorch:2.4.0-cuda12.1-cudnn9-runtime, >=60 GB disk, on-demand.ssh -p <port> root@<host>
curl -fsSL https://claude.ai/install.sh | sh # or your preferred install
claude login
Pick one:
# Option A: git
git clone <your-repo-url> /workspace/geoleaf && cd /workspace/geoleaf/experiments
# Option B: scp from your laptop
scp -P <port> -r ./experiments root@<host>:/workspace/geoleaf/
cd /workspace/geoleaf/experiments
make setup # installs requirements.txt
source .venv/bin/activate
earthengine authenticate # paste the token
huggingface-cli login
make download # one-time
make preprocess
make tier1 # smoke-check baseline
make tier3 GPU_CONFIG=config/rtx24g.yaml # 24GB GPU path used by Run 3
make figures # regenerate Figure 2 from real metrics
# from your laptop:
scp -P <port> -r root@<host>:/workspace/geoleaf/experiments/outputs ./outputs_a100/
config/base.yaml::seed (default 42). Every script
calls utils.set_seed() which seeds Python, NumPy, and PyTorch (incl.
CUDA), and forces cudnn.deterministic = True.requirements.txt; for
exact reproducibility on the cloud GPU, pip freeze > pip-lock.txt and
commit it after first successful run.After running Tier 1 and Tier 3:
make figures
Reads outputs/tier{1,2,3}/metrics.json and writes:
outputs/figures/figure_2_performance_real.png
Drop this PNG into the proposal in place of figure_2_performance_projection.png,
update the caption to say "actual measured values on the held-out
territory-wide Hong Kong spatial-block test fold", and update any in-text
numbers from the metrics JSON directly:
python -c "import json; print(json.load(open('outputs/tier3/metrics.json'))['miou'])"
experiments/
├── README.md
├── requirements.txt
├── Makefile
├── .gitignore
├── config/
│ ├── base.yaml # paths, AOI, classes, model, training
│ ├── a100.yaml # A100 batch/precision overrides
│ ├── rtx24g.yaml # RTX A5000 / 3090 / 4090 24GB overrides
│ └── t4.yaml # T4 16GB overrides
├── data/ # gitignored — populated by `make download`
├── outputs/ # gitignored — populated by experiments
├── notebooks/
│ ├── 01_data_exploration.ipynb
│ ├── 02_tier1_baseline.ipynb
│ └── 03_tier3_lora.ipynb
└── scripts/
├── download_data.py
├── preprocess.py
├── tier1_baseline.py
├── tier2_ensemble.py
├── tier3_lora_finetune.py
├── eval_metrics.py
├── make_figures.py
└── utils.py
15 commits
Python
79.2%
Jupyter Notebook
18.6%
Makefile
2.2%
git clone <this-repo>
cd experiments
make setup
This folder is a self-contained scaffold for the GeoLeaf proof-of-concept: vegetation classification (Woodland / Shrubland / Grassland) over Hong Kong, benchmarked against the AFCD THMHK 2021 habitat map. The current Run 3 baseline is territory-wide; pilot-zone field validation is planned for Tai Mo Shan and Sai Kung West.
It is designed to be developed locally on a laptop and then executed on a cloud GPU (vast.ai, RunPod, or a PolyU cluster node). Nothing here is trained or downloaded in the local sandbox.
GeoLeaf classifies natural land into three height-defined vegetation classes that the Planning Department needs but currently cannot get at scale:
| Class | Definition (canopy height) | Code |
|---|---|---|
| Woodland | > 5 m | 1 |
| Shrubland | 0.5 m – 5 m | 2 |
| Grassland | < 0.5 m | 3 |
| Other | (excluded from metrics) | 0 |
Three tiers, increasing in capability:
Reference: see GeoLeaf_PoC_Proposal_v15.pdf and
GeoLeaf_Business_Plan_v15.pdf in the parent directory.
Run 3 metrics and the submission figure are tracked under results/run3/.
# clone + cd
git clone <your-repo-url>
cd experiments
# python venv
python -m venv .venv
source .venv/bin/activate
pip install --upgrade pip wheel
pip install -r requirements.txt
# or:
make setup
Authentication (one-time per machine):
# Earth Engine (Lang CHM, GEDI fallback)
earthengine authenticate
# NASA Earthdata (GEDI L2A direct download)
# Create an account at https://urs.earthdata.nasa.gov
# Then put credentials in ~/.netrc :
echo "machine urs.earthdata.nasa.gov login <user> password <pass>" >> ~/.netrc
chmod 600 ~/.netrc
# HuggingFace (Prithvi-EO-2.0 backbone weights)
huggingface-cli login
Optional:
# Weights & Biases (auto-detected if WANDB_API_KEY is set; otherwise local TB)
export WANDB_API_KEY=xxxxxxxx
| Dataset | Resolution | Acquisition | Approx size | Licence |
|---|---|---|---|---|
| AFCD THMHK 2021 (labels) | polygon | CSDI portal (manual) | ~50 MB | HK Govt open data |
| PD Land Utilization 2024 | polygon | CSDI portal (manual) | ~100 MB | HK Govt open data |
| CEDD ALS DSM/DTM 2020 | 5 m | CSDI portal (manual) | ~500 MB | HK Govt open data |
| Sentinel-2 L2A 2024 composite | 10 m | Planetary Computer | ~460 MB HK | Copernicus open |
| Sentinel-1 RTC 2024 composite | 10 m | Planetary Computer | pending | Copernicus open |
| Lang 2023 global CHM | 10 m | Earth Engine / Zenodo | ~62 MB HK | CC BY 4.0 |
| GEDI L2A 2019– | footprint | NASA Earthdata | optional | NASA open |
The HK government layers do not currently expose a stable programmatic
download URL — scripts/download_data.py prints a clear MANUAL DOWNLOAD
banner with the source URL and target folder for those.
Run everything (skips already-downloaded sources):
make download
Three commands to get an mIoU from scratch:
make download # ~20-60 min one-time data fetch
make preprocess # ~5-15 min: co-register, tile, spatial-block split
make tier1 # ~5 min: CHM thresholds -> metrics + confusion matrix
Expected runtime:
Outputs:
outputs/tier1/metrics.jsonoutputs/tier1/confusion.npyoutputs/tier1/pred.tifmake preprocess # if not already done
make tier3 # default = A100 config
make tier3 GPU_CONFIG=config/t4.yaml # fallback for T4 16GB
Expected runtime:
T4 notes:
config/t4.yaml forces fp16, batch 4, accum 4, and the smaller Prithvi-300M
override.patch.size from 256 to 192 in config/base.yaml.Outputs:
outputs/tier3/checkpoints/{epoch_N.pt, best.pt}outputs/tier3/metrics.jsonoutputs/tier3/confusion.npyoutputs/logs/tb/ (or wandb)Resume from a checkpoint:
python scripts/tier3_lora_finetune.py \
--resume outputs/tier3/checkpoints/epoch_05.pt
Smoke-test the full graph (one batch only):
python scripts/tier3_lora_finetune.py --dry-run
Concrete cloud-GPU workflow (vast.ai shown — RunPod and PolyU clusters are analogous):
pytorch/pytorch:2.4.0-cuda12.1-cudnn9-runtime, >=60 GB disk, on-demand.ssh -p <port> root@<host>
curl -fsSL https://claude.ai/install.sh | sh # or your preferred install
claude login
Pick one:
# Option A: git
git clone <your-repo-url> /workspace/geoleaf && cd /workspace/geoleaf/experiments
# Option B: scp from your laptop
scp -P <port> -r ./experiments root@<host>:/workspace/geoleaf/
cd /workspace/geoleaf/experiments
make setup # installs requirements.txt
source .venv/bin/activate
earthengine authenticate # paste the token
huggingface-cli login
make download # one-time
make preprocess
make tier1 # smoke-check baseline
make tier3 GPU_CONFIG=config/rtx24g.yaml # 24GB GPU path used by Run 3
make figures # regenerate Figure 2 from real metrics
# from your laptop:
scp -P <port> -r root@<host>:/workspace/geoleaf/experiments/outputs ./outputs_a100/
config/base.yaml::seed (default 42). Every script
calls utils.set_seed() which seeds Python, NumPy, and PyTorch (incl.
CUDA), and forces cudnn.deterministic = True.requirements.txt; for
exact reproducibility on the cloud GPU, pip freeze > pip-lock.txt and
commit it after first successful run.After running Tier 1 and Tier 3:
make figures
Reads outputs/tier{1,2,3}/metrics.json and writes:
outputs/figures/figure_2_performance_real.png
Drop this PNG into the proposal in place of figure_2_performance_projection.png,
update the caption to say "actual measured values on the held-out
territory-wide Hong Kong spatial-block test fold", and update any in-text
numbers from the metrics JSON directly:
python -c "import json; print(json.load(open('outputs/tier3/metrics.json'))['miou'])"
experiments/
├── README.md
├── requirements.txt
├── Makefile
├── .gitignore
├── config/
│ ├── base.yaml # paths, AOI, classes, model, training
│ ├── a100.yaml # A100 batch/precision overrides
│ ├── rtx24g.yaml # RTX A5000 / 3090 / 4090 24GB overrides
│ └── t4.yaml # T4 16GB overrides
├── data/ # gitignored — populated by `make download`
├── outputs/ # gitignored — populated by experiments
├── notebooks/
│ ├── 01_data_exploration.ipynb
│ ├── 02_tier1_baseline.ipynb
│ └── 03_tier3_lora.ipynb
└── scripts/
├── download_data.py
├── preprocess.py
├── tier1_baseline.py
├── tier2_ensemble.py
├── tier3_lora_finetune.py
├── eval_metrics.py
├── make_figures.py
└── utils.py
15 commits
Python
79.2%
Jupyter Notebook
18.6%
Makefile
2.2%