Yuanwen Yue, Stefano Puliti†, Damien Robert†, Atakan Topaloğlu, Binbin Xiang, Maciej Wielgosz, Jan Dirk Wegner, Rasmus Astrup, Christian Rupprecht, Konrad Schindler
ETH Zurich, University of Oxford, Norwegian Institute of Bioeconomy Research (NIBIO), University of Zurich
†Equal contribution
ForPT is a self-supervised pretrained point cloud model for forests. We pretrain a LitePT backbone on a large corpus of unlabeled airborne (ALS), UAV (ULS) and mobile (MLS) laser scans covering diverse forest ecosystems, and evaluate it on four forestry tasks: semantic segmentation, instance segmentation, tree species classification and tree age regression. Self-supervised pretraining accelerates convergence, improves performance when annotations are scarce, and transfers better than task-specific supervised pretraining.
ForPT relies on LitePT. Follow the instructions in the LitePT repository to install the environment for LitePT. Then install the few additional packages used by ForPT:
pip install pandas matplotlib seaborn tqdmAll data should be placed in ForPT/data, organized as expected by the configs:
ForPT/data/
├── forpt_ssl_corpus/ # unlabeled pretraining corpus (ALS / ULS / MLS)
│ └── knn_track/ # labeled subset for kNN evaluation during pretraining
├── forinstancev2_sem/ # FOR-instanceV2 (semantic segmentation)
│ ├── train/ val/ test/
│ └── train_{5,10,20,50}.txt # label-efficient training subsets
├── forspecies20k/ # FOR-species20K (species classification)
│ ├── processed/ # full-resolution trees: <species>/<tree>.txt (x,y,z), test/, train/val/test splits
│ └── processed_8192/ # same trees downsampled to 8,192 points, plus train_{1,5,10,20,50}p.txt subsets
└── forage/ # tree age regression
├── FORage_tree_metadata_train_val.csv # official FORage train/val metadata
├── FORage_tree_metadata_test.csv # hidden test set: sampleID, file (no age)
├── pc/<name>/coord.npy # one point cloud per tree (test: <name> = sampleID)
├── forage_{train,val,test}_8192points_uniform.pth # cached 8,192-point samples
└── train_{5,10,20,50}p.txt # label-efficient training subsets
We provide the preprocessed datasets on Hugging Face:
| Dataset | Task | Download | Local folder |
|---|---|---|---|
| ForPT corpus | Self-supervised pretraining | - | forpt_ssl_corpus |
| FOR-instanceV2 | Semantic segmentation | prs-eth/forpt-forinstancev2-sem | forinstancev2_sem |
| Instance segmentation | - | forinstancev2_ins |
|
| FOR-species20K | Species classification | prs-eth/forpt-forspecies20k | forspecies20k |
| FORage | Age regression | prs-eth/forpt-forage | forage |
Point clouds are packed into .tar files, while split lists, metadata CSVs and the FORage .pth caches are stored as-is. To download everything and unpack it into the layout above, run the following from the ForPT root (requires pip install -U huggingface_hub):
mkdir -p data && cd data
# FOR-instanceV2 (~14 GB): train.tar, val.tar, test.tar
hf download prs-eth/forpt-forinstancev2-sem --repo-type dataset --local-dir forinstancev2_sem
# FOR-species20K (~168 GB): one tar per species folder in processed/ and processed_8192/
hf download prs-eth/forpt-forspecies20k --repo-type dataset --local-dir forspecies20k
# FORage (~13 GB): pc.tar
hf download prs-eth/forpt-forage --repo-type dataset --local-dir forage
# Unpack every tar next to itself, then delete the archive
find forinstancev2_sem forspecies20k forage -name "*.tar" | while read f; do
tar -xf "$f" -C "$(dirname "$f")" && rm "$f"
done
rm -rf {forinstancev2_sem,forspecies20k,forage}/{.cache,.gitattributes}FOR-species20K is the largest download. The full-resolution processed/ folder is only used by the *-allpts configs. If you only need the 8,192-point version used by all other configs, add --include "processed_8192/*" to its download command. Interrupted downloads resume when you rerun the same command.
| Model | Params | Pretraining data | Config | Checkpoint |
|---|---|---|---|---|
| ForPT (LitePT-S) | 12.4M | ForPT corpus | link | Download |
| Task | Benchmark | Model | Config | Checkpoint |
|---|---|---|---|---|
| Semantic segmentation | FOR-instanceV2 | LitePT-scratch | link | Download |
| ForPT-linear probing | link | Download | ||
| ForPT-decoder probing | link | Download | ||
| ForPT-finetune | link | Download | ||
| Instance segmentation | FOR-instanceV2 | Coming soon | - | - |
| Species classification | FOR-species20K | LitePT-scratch | link | Download |
| ForPT-linear probing | link | Download | ||
| ForPT-finetune | link | Download | ||
| Age regression | FORage | LitePT-scratch | link | Download |
| ForPT-linear probing | link | Download | ||
| ForPT-finetune | link | Download |
All checkpoints are hosted on prs-eth/ForPT under the <task>/<config name>/model/ layout. To download them into ForPT/weights, where the configs expect them (e.g. weights/00_ssl_pretrain/pretrain-forpt/model/epoch_30.pth), run from the ForPT root:
hf download prs-eth/ForPT --local-dir weights --exclude ".gitattributes" --exclude "README.md"Configs are grouped by task under configs/. The -d flag of scripts/train.sh selects the config folder, and experiments are saved to exp/${FOLDER}/${EXP_NAME}.
Config names follow the pattern {task}-{init}-{protocol}-{labels}:
- Initialization and protocol
litepts-scratch: LitePT-S trained from scratch (supervised baseline)forpt-lin: linear probing on frozen ForPT featuresforpt-dec: initialize from ForPT, freeze the encoder and train the decoderforpt-ft: initialize from ForPT and fine-tune the full model{sem,ins,species,age}-pretrain-lin: linear probing on a backbone pretrained with supervision on another task
- Labels:
p001,p005,p010,p020,p050andp100train on 1%–100% of the training labels.allpts(species only) trains on train+val at full point density.
We pretrain on 16 nodes with 4 GPUs each. On a SLURM cluster, launch the script once per node; the node rank and master address are read from SLURM:
srun --nodes=16 --ntasks-per-node=1 sh scripts/train.sh -g 4 -m 16 -d 00_ssl_pretrain -c pretrain-forpt -n pretrain-forptbatch_size in the configs is the total across all GPUs, so running on fewer GPUs increases the batch per GPU.
All downstream experiments use a single node with 4 GPUs.
# LitePT-scratch
sh scripts/train.sh -g 4 -d 01_forinstancev2_sem -c semseg-litepts-scratch-p100 -n semseg-litepts-scratch-p100
# ForPT-linear probing
sh scripts/train.sh -g 4 -d 01_forinstancev2_sem -c semseg-forpt-lin-p100 -n semseg-forpt-lin-p100
# ForPT-decoder probing
sh scripts/train.sh -g 4 -d 01_forinstancev2_sem -c semseg-forpt-dec-p100 -n semseg-forpt-dec-p100
# ForPT-finetune
sh scripts/train.sh -g 4 -d 01_forinstancev2_sem -c semseg-forpt-ft-p100 -n semseg-forpt-ft-p100# LitePT-scratch
sh scripts/train.sh -g 4 -d 03_forspecies20k -c cls-litepts-scratch-allpts -n cls-litepts-scratch-allpts
# ForPT-linear probing
sh scripts/train.sh -g 4 -d 03_forspecies20k -c cls-forpt-lin-allpts -n cls-forpt-lin-allpts
# ForPT-finetune
sh scripts/train.sh -g 4 -d 03_forspecies20k -c cls-forpt-ft-allpts -n cls-forpt-ft-allpts# LitePT-scratch
sh scripts/train.sh -g 4 -d 04_forage -c age-litepts-scratch-p100 -n age-litepts-scratch-p100
# ForPT-linear probing
sh scripts/train.sh -g 4 -d 04_forage -c age-forpt-lin-p100 -n age-forpt-lin-p100
# ForPT-finetune
sh scripts/train.sh -g 4 -d 04_forage -c age-forpt-ft-p100 -n age-forpt-ft-p100To run another protocol or label fraction, change the config name, e.g. -c semseg-forpt-lin-p010 for linear probing with 10% of the labels.
- Release pretrained models.
- Release pretraining code and configs.
- Release semantic segmentation, species classification and age regression code.
- Release instance segmentation code.
This work uses parts of the code from LitePT, Pointcept, Sonata, ForestFormer3D, and FORage. We thank the authors for making their code publicly available.
If you find this project useful, please cite:
@article{yue2026forpt,
title={Toward a foundation model for forest point clouds},
author={Yue, Yuanwen and Puliti, Stefano and Robert, Damien and Topalo{\u{g}}lu, Atakan and Xiang, Binbin and Wielgosz, Maciej and Wegner, Jan Dirk and Astrup, Rasmus and Rupprecht, Christian and Schindler, Konrad},
journal={arXiv preprint arXiv:2609.24787},
year={2026}
}
