Example: Deep Compartment Model (DCM)
A two-compartment oral model where a small neural network replaces the analytical covariate model. The network reads subject-level covariates (WT, CRCL) and outputs a multiplicative modulator for each PK parameter — the baseline thetas carry the absolute scale, the NN learns the covariate-driven deviation on top.
This is the baseline × NN-modulator architecture from Janssen et al. 2022 (CPT:PSP, DOI 10.1002/psp4.12808).
DCM requires the nn cargo feature. The default ferx-r build includes it. If fit$neural_networks is empty after fitting, rebuild with --features nn.
Model file
[parameters]
theta TVCL(4.0, 0.1, 100.0)
theta TVV1(40.0, 1.0, 500.0)
theta TVQ (8.0, 0.1, 100.0)
theta TVV2(80.0, 1.0, 500.0)
theta TVKA(1.0, 0.01, 10.0)
omega ETA_CL ~ 0.15
omega ETA_V1 ~ 0.15
omega ETA_Q ~ 0.08
omega ETA_V2 ~ 0.08
omega ETA_KA ~ 0.20
sigma PROP_ERR ~ 0.04 (sd)
[covariate_nn TYPICAL_PK]
inputs = [WT, CRCL]
outputs = [CL, V1, Q, V2, KA]
hidden = [8, 8]
activation = tanh
output_activation = softplus
[individual_parameters]
CL = TVCL * TYPICAL_PK.CL * exp(ETA_CL)
V1 = TVV1 * TYPICAL_PK.V1 * exp(ETA_V1)
Q = TVQ * TYPICAL_PK.Q * exp(ETA_Q)
V2 = TVV2 * TYPICAL_PK.V2 * exp(ETA_V2)
KA = TVKA * TYPICAL_PK.KA * exp(ETA_KA)
[structural_model]
pk two_cpt_oral(cl=CL, v1=V1, q=Q, v2=V2, ka=KA)
[error_model]
DV ~ proportional(PROP_ERR)
[fit_options]
method = focei
maxiter = 200
optimizer = lbfgs
The [covariate_nn] block
The [covariate_nn NAME] block declares a feed-forward MLP:
| Key | Description |
|---|---|
inputs |
Covariate column names from the dataset |
outputs |
Names used in [individual_parameters] as NAME.output |
hidden |
List of hidden-layer widths |
activation |
Hidden activation (tanh, relu, softplus) |
output_activation |
Output activation — softplus keeps outputs positive, matching the expected PK parameter scale |
At initialisation (Glorot weights), softplus(0) ≈ 0.69 and all outputs are near 1.0, so the baseline thetas dominate and the NN starts as a near-identity modulator.
Fitting
library(ferx)
ex <- ferx_example("warfarin_dcm")
fit <- ferx_fit(ex$model, ex$data,
settings = list(optimizer = "lbfgs"))
print(fit)Use optimizer = "lbfgs" (or "bobyqa") for NN-bearing models. The default SLSQP optimizer has a known issue with mu-referenced NN models — it silently no-ops at iteration 1. This is tracked in ferx-core.
Inspecting the neural network
fit$neural_networks is a list of one element per [covariate_nn] block:
nn <- fit$neural_networks[[1]]
nn$name # "TYPICAL_PK"
nn$shape # layer widths including input/output
nn$n_weights # total trainable parameters (~141 for [2,8,8,5])
nn$input_names # ["WT", "CRCL"]
nn$output_names # ["CL", "V1", "Q", "V2", "KA"]
nn$weights_offset # index into the flat theta vector where weights startWhen does DCM help?
DCM is worth the extra parameters (~141 weights for [2, 8, 8, 5]) only when covariates are genuinely predictive and the analytical covariate model cannot capture the relationship.
The bundled script fits a DCM and a no-covariate two-compartment baseline on the same dataset and compares AIC and OFV, with covariate-importance heuristics and individual profile plots:
source(system.file("examples/ex_warfarin_dcm.R", package = "ferx"))Reference
Janssen A. et al. (2022). Deep compartment models: A deep learning approach for the reliable prediction of time-series data in pharmacokinetic modeling. CPT Pharmacometrics Syst Pharmacol 11:934–945. DOI 10.1002/psp4.12808.