edaphos 1.2.0 — Pillar 4 foundation model reaches the user
Summary
Promotes Pillar 4 from a self-supervised-training scaffold to a
ready-to-use transfer-learning pipeline. Three capabilities land
together: a downstream fine-tuning API, a public pretrained encoder
hosted on Zenodo, and GPU (Apple Silicon MPS / NVIDIA CUDA) dispatch.
Downstream fine-tuning API
Two new entry points attach a classification or regression head on
top of any MoCo v2 / SimCLR encoder and train it against a labelled
patch set:
foundation_fit_classifier()— linear probe (frozen backbone)
or full fine-tuning with a two-group learning-rate schedule
(Kornblith, Shlens and Le 2019).predict(..., type = "class")
ortype = "prob".foundation_fit_regressor()— same API for continuous
targets; target normalisation is handled internally (center + scale
at fit time, un-scale at predict) and a Huber loss option is
provided for robustness against pedogenic outliers.
Both dispatch via device = c("cpu", "mps", "cuda") and fall back
to CPU with a clear message when the requested backend is
unavailable.
Public pretrained encoder: edaphos-cerrado-moco-v1
The first published Pillar 4 foundation model is live on Zenodo:
- DOI: 10.5281/zenodo.19701276
- Record: https://zenodo.org/records/19701276
- License: Creative Commons Attribution 4.0 International
It is a MoCo v2 (He et al. 2020; Chen et al. 2020) encoder
pretrained for 20 000 InfoNCE steps on an Apple Silicon M1 Max (MPS
backend) over 50 000 16×16 raster patches sampled from a core
Cerrado AoI (longitude −53 to −43, latitude −23 to −10). Input
channels (31 total) are a multi-source stack aligned to a 0.01
degree (~1 km) grid:
| Source | Channels |
|---|---|
| SoilGrids 250m (0–5 cm mean) | SOC, clay, sand, pH(H₂O), bulk density |
| WorldClim 2.1 (Brazil country pack) | 12 monthly precipitation + 12 monthly mean temperature |
| SRTM 30 arc-second | elevation, slope |
Architecture: 5-block convolutional backbone → 64-dimensional
feature → 2-layer MLP projection head (proj_dim = 32). Training
used a queue of 4096 negatives, InfoNCE temperature 0.07, momentum
0.999, Adam learning rate 3e-4, batch size 64. Final InfoNCE loss
~1.64 (from an initial ~7.68 — a 4.7× reduction).
Weights distribution infrastructure
Three new functions consume the encoder registry:
foundation_weights_list()— catalogue of every published
encoder (name, DOI, SHA-256, AoI, channel count, feature
dimension,edaphosversion).foundation_weights_download()— fetches from Zenodo, verifies
the SHA-256 digest, caches under
tools::R_user_dir("edaphos", "cache") / weights /.foundation_weights_load()— rebuilds the in-memory
edaphos_foundation_mocowrapper with the state dict loaded and
shape metadata populated.
moco <- foundation_weights_load("edaphos-cerrado-moco-v1")
fit <- foundation_fit_classifier(moco, labelled_patches, soil_order,
epochs = 40L, device = "mps")Reproducibility
Two scripts under data-raw/ rebuild the deposit from scratch on
any R ≥ 4.3 + torch ≥ 0.16 machine:
pretrain_cerrado_prepare.R— downloads the SoilGrids, WorldClim
and SRTM tiles; crops them to the AoI; aligns them to the
analysis grid; samples 50 000 patches.pretrain_cerrado_train.R— runs 20 000 MoCo v2 steps on the
requested backend; writes the state dict + metadata + loss
history; computes the SHA-256 for registry verification.
The seed is fixed (seed = 2026L) so re-running on the same
hardware produces byte-identical artefacts. The seed, the AoI, the
variable list, the alignment grid and the training hyperparameters
are all recorded in the Zenodo deposit's metadata.json.
A companion data-raw/zenodo_upload.R is the reproducible
create / publish / discard pipeline that produced this deposit and
will produce every subsequent one.
Bug fixes
foundation_moco_embed()now forces the encoder intoeval()
mode before the forward pass, so BatchNorm uses its saved
running_mean/running_varrather than batch-level statistics.
Previously the embeddings depended on the current batch
composition and disagreed with any reloaded copy of the same
encoder — the bug that would have made the Zenodo round-trip
miscompare.foundation_tile_source_soilgrids()now accepts both the
human-readable depth strings ("0-5cm","5-15cm", …) documented
in its@paramblock and the integer form
geodata::soil_world()expects internally.
Quality
- 49 new tests (
test-foundation-finetune,
test-foundation-weights) land green. - Full Foundation test suite: 94 / 94 green.
R CMD check --as-cran: 0 errors / 0 warnings / 1 harmless NOTE.