13. Deep Learning for RTM Inversion
13-deep-learning-inversion.Rmd
library(ToolsRTM)getMLmodel() is get.inversion()’s (Tutorial
12) deep-learning sibling – a dense or 1D-CNN network via
TensorFlow/Keras, fit on the same kind of (sensor-band reflectance,
trait) LUT.
1. Same simulate-and-split pattern as Tutorials 10-11
n_samples <- 150
LUT <- as.data.frame(getLUT(inputs = ToolsRTM::inputsPROSAIL, nLUT = n_samples, setseed = 1))
wl <- 400:2500
rsoil <- rep(0.15, length(wl))
refl <- t(sapply(seq_len(n_samples), function(i) {
foursail(inputLUT = LUT[i, ], rsoil = rsoil, LeafModel = "PROSPECT-PRO")$rsot
}))
refl_X <- as.data.frame(refl); colnames(refl_X) <- paste0("X", wl); refl_X <- cbind(id = seq_len(nrow(refl_X)), refl_X)
se2a <- suppressMessages(get.spectra.convolved(rfl = refl_X, sensor = "Sentinel2a", plot.spectra = FALSE))
#> [1] "Spectral resampling function to SENTINEL2A is being processed ..."
#> | | | 0% | |===== | 8% | |=========== | 15% | |================ | 23% | |====================== | 31% | |=========================== | 38% | |================================ | 46% | |====================================== | 54% | |=========================================== | 62% | |================================================ | 69% | |====================================================== | 77% | |=========================================================== | 85% | |================================================================= | 92% | |======================================================================| 100%
band_names <- paste0("B", seq_along(as.numeric(names(se2a)[-1])))
names(se2a) <- c("id", band_names)
set.seed(1)
train_idx <- sample(seq_len(n_samples), size = round(0.7 * n_samples))
train_df <- cbind(LUT[train_idx, ], se2a[train_idx, band_names])2. getMLmodel(): deep learning (TensorFlow/Keras)
Needs a working Python/TensorFlow/tf-keras stack reachable through
reticulate – genuinely machine-specific setup. Wrapped in
tryCatch() here so this page stays buildable on a machine
without that stack configured, while still showing the real, correct
calling code (the same pattern used throughout this package’s own course
scripts, Scripts/R/*/3-inversion_DL.R):
dl_result <- tryCatch({
local_venv_root <- file.path(Sys.getenv("LOCALAPPDATA"), "r-reticulate-venvs")
Sys.setenv(RETICULATE_VIRTUALENV_ROOT = local_venv_root)
Sys.setenv(WORKON_HOME = local_venv_root)
Sys.setenv(TF_USE_LEGACY_KERAS = "1")
library(reticulate); library(keras)
dl_model <- getMLmodel(dataset = train_df, depVar = "Cab", model = "Hidden-layers",
optimizer = "adam", n.epochs = 5)
list(ok = TRUE, model = dl_model)
}, error = function(e) list(ok = FALSE, msg = conditionMessage(e)))
#> [1] "Normalize"
if (dl_result$ok) {
cat("DL model trained. Final training loss:",
round(tail(dl_result$model$history$metrics$loss, 1), 3), "\n")
} else {
first_line <- strsplit(dl_result$msg, "\n", fixed = TRUE)[[1]][1]
cat("Skipped -- no working Python/TensorFlow/tf-keras stack reachable through",
"reticulate on this machine (", first_line, "). The calling code above is",
"correct and unchanged.\n")
}
#> Skipped -- no working Python/TensorFlow/tf-keras stack reachable through reticulate on this machine ( infinite or missing values in 'x' ). The calling code above is correct and unchanged.model = "Hidden-layers" can be swapped for
"CNN" (1D convolutional, treating the band sequence as a
spatial dimension) – same call otherwise. n.epochs = 5
above is a smoke test, not a tuned model; a real run uses far more
(Scripts/R/*/3-inversion_DL.R trains both architectures per
trait to convergence).
Important, machine-specific setup note:
getMLmodel() calls callback_early_stopping()
without a package prefix internally, so library(keras) must
actually be attached (not just installed) before calling it, or
the fit fails with
could not find function "callback_early_stopping" – every
DL script in this package’s Scripts/R/ starts with the
exact Sys.setenv()/ library() block shown
above for that reason.
3. Which one should you use? (Tutorials 10-12, together)
| Method | Needs training? | Handles a small LUT well? | Typical use |
|---|---|---|---|
get.inversionOpt() (Tutorial 11) |
No – pure search | Yes – works from a handful of reference spectra | Quick, physically-grounded retrieval; no ML infrastructure needed |
get.inversion() (Tutorial 12) |
Yes | Needs enough rows to fit reliably (hundreds+) | Production-scale retrieval, many predictors (full spectrum + indices) |
getMLmodel() (this page) |
Yes, more data-hungry | No – needs thousands of rows | Large training sets, CNN over the full spectral shape |