Performs K-fold cross-validation to evaluate the equilibrium mapping of the NMF-FFB model.
For each fold, nmf.ffb is fitted on the training samples,
yielding an equilibrium mapping \(\hat Y_1 = M_{\mathrm{model}} Y_2\).
The held-out endogenous variables \(Y_1\) are then predicted from \(Y_2\)
using this mapping, and the mean absolute error (MAE) over all entries in the
test block is computed. The returned value is the average MAE across folds.
This implements the hyperparameter selection strategy described in the paper: hyperparameters are chosen by predictive cross-validation rather than direct inspection of the internal structural matrices.
method = "fiml". When method = "fiml" is passed
(through ...), the column-wise scheme above is not run: the
equilibrium prediction \(M_{\mathrm{model}} Y_2\) is a reduced-form
quantity, and the reduced form is the same with and without feedback, so
predicting held-out columns cannot select \(\Theta_1\) (that is what the
BIC path and the bootstrap-calibrated LR test in nmf.ffb /
nmf.ffb.inference are for). The only tunable quantity is the
stage-1 rank, and the call is delegated to the element-wise CV of the
feed-forward fit, nmfkc.ecv(Y = Y1, A = Y2, rank = rank,
...), whose object is returned ($objfunc holds the CV error per
rank; rank may be a vector, default 1:min(3, P1)).
C1.L1 and C2.L1 are ignored in that case.
Usage
nmf.ffb.cv(
Y1,
Y2,
rank = NULL,
X.init = "nndsvd",
X.L2.ortho = 100,
C1.L1 = 1,
C2.L1 = 0.1,
epsilon = 1e-06,
maxit = 5000,
...
)Arguments
- Y1
A non-negative numeric matrix of endogenous variables with rows = variables (P1), columns = samples (N).
- Y2
A non-negative numeric matrix of exogenous variables with rows = variables (P2), columns = samples (N). Must satisfy
ncol(Y1) == ncol(Y2).- rank
Integer; rank (number of latent factors) passed to
nmf.ffb. IfNULL,nmf.ffbdecides the effective rank (via...ornrow(Y2)).- X.init
Initialization strategy for
X, forwarded tonmf.ffb. One of"nndsvd"(default),"kmeans","kmeansar","runif", a numeric \(P_1 \times Q\) matrix, orNULL(alias for"nndsvd"). Seenmf.ffbfor details.- X.L2.ortho
L2 orthogonality penalty for
X.- C1.L1
L1 sparsity penalty for
C1(\(\Theta_1\)).- C2.L1
L1 sparsity penalty for
C2(\(\Theta_2\)).- epsilon
Convergence threshold for
nmf.ffb.- maxit
Maximum number of iterations for
nmf.ffb.- ...
Additional arguments passed to
nmf.ffb(except forrank,seed,div,shuffle, which are handled here). Also accepts:nfolds(number of folds, default 5;divalso accepted),seed(master random seed, defaultNULL),shuffle(logical, defaultTRUE),cores(integer; number of parallel workers for the fold loop, defaultgetOption("mc.cores", 1L)). Fold partitions and per-fold seeds are drawn before the loop and eachnmf.ffbfit self-seeds, so results are identical to the sequential run for anycores.
Value
A numeric scalar: mean MAE across CV folds (the fits use
method = "mu" unless method is given). With
method = "fiml", the nmfkc.ecv object of the
stage-1 rank sweep.
Examples
Y <- t(iris[, -5])
Y1 <- Y[1:2, ]
Y2 <- Y[3:4, ]
mae <- nmf.ffb.cv(Y1, Y2, rank = 2, maxit = 500, nfolds = 3)
#> Warning: maximum iterations (500) reached...
#> Warning: maximum iterations (500) reached...
#> Warning: maximum iterations (500) reached...
mae
#> [1] 1.656582
# \donttest{
# rank selection for the likelihood-based estimator
ecv <- nmf.ffb.cv(Y1, Y2, rank = 1:2, method = "fiml", nfolds = 3)
#> Performing Element-wise CV for Q = 1,2 (3-fold)...
ecv$objfunc
#> Q=1 Q=2
#> 2.915778 2.915701
# }