semiSupervisedPipeline() implements ssel’s row-promotion
layer. It promotes selected prediction rows as pseudo-labels, refits the
supervised helper family, and uses a separate OOF gauge to accept or
revert a candidate round. Pseudo-labeling is a broad semi-supervised
pattern (van Engelen and
Hoos 2020), but the range ratio, budget, gauge, and
state transitions below are package-defined policies. They are not
tri-training, co-training regression, held-out validation, or calibrated
predictive uncertainty.
Let \(s=0,1,\ldots\) index rounds, \(r\) a response, \(u\) a prediction-row key, \(m\) a learner, and \(d\) a dataset. The first retained input CSV alone defines two ordered row-level vectors:
\[L=(u:\texttt{set}="train").\]
\[U=(u:\texttt{set}="predict").\]
These are vectors, not mathematical sets: order, duplicates, and missing keys remain. Consequently, \(|U|=\operatorname{length}(U)\) counts rows, while a later promotion budget spends unique keys. Other input CSVs are not checked to have matching pools.
allResponses should name every response column so
non-target responses are removed before fitting. Missing names are
ignored; an omitted response can therefore remain and be numerically
coerced as a predictor.
When .path.iter is supplied, exactly one target response
is required. The function reads lexically ordered
metrics_<integer>.csv files, keeps that response’s
ensemble.RMSE rows, and chooses the first row attaining the
global maximum \(R^2\). Its iteration
number selects one datasets_<iterStar> directory.
This is an augmented-dataset snapshot selection. It does not consume
a stitched model, rerun a chain sweep, or provide an iteration-zero
fallback. Without .path.iter, iterStar in the
return value is NA_integer_.
For original prediction key \(u\), response \(r\), and round \(s\), collect all available projected ordinary base-method predictions across configured datasets:
\[A_{ur}^{(s)}= \{f_{mdr}^{(s)}(u)\}_{\mathrm{available}}.\]
\[S_{ur}^{(s)}=\max A_{ur}^{(s)}-\min A_{ur}^{(s)}.\]
A pair is eligible only when at least two distinct method names are
available; multiple datasets from one method do not satisfy this
condition. Let \(\bar
f_{ur,last}^{(s)}(u)\) be the last matching ordinary
ensemble.RMSE value in response-file concatenation and
update order. ssel defines
\[D_{ur}^{(s)}= \frac{S_{ur}^{(s)}} {\max\{1,|\bar f_{ur,last}^{(s)}(u)|\}}.\]
Promotion uses the strict comparison \(D_{ur}^{(s)}<\tau\). Equal finite base predictions give \(D=0\); a missing member makes the score missing. The score is dimensionless, but the fixed denominator floor makes it non-invariant to response rescaling and translation. It is neither a probability nor an uncertainty calibration.
Let \(c=\texttt{promotionCap}\), let \(P\) be the accepted pseudo-label table, and let \(K(P)\) be its unique keys. The remaining key budget is
\[B_s=\lfloor c|U|\rfloor-|K(P)|.\]
Candidate response pairs are ordered by ascending score.
First-encountered new keys consume the budget; all eligible response
pairs for retained keys remain. Pseudo-label values are
all matching ordinary ensemble.RMSE rows.
There is no median or cross-dataset reduction, so one pair can
contribute several label rows.
This distinction is visible in the return value:
history$promoted counts candidate pairs, whereas accepted
history$cumulative and the returned promoted
table count label rows. Because \(|U|\)
counts rows but spending counts unique keys, duplicate prediction keys
change the effective cap.
For response \(r\), let \(H_r^{(s)}\) contain reconstructed OOF residual rows whose key occurs in the original labelled vector \(L\). Rows from all datasets and duplicates retain their multiplicity. The response score is squared sample correlation, and the round gauge is
\[g_s=\operatorname{mean}_r \{\operatorname{cor}(y,\hat y_{OOF})^2\}.\]
Missing response-level scores are omitted; surviving responses
receive equal weight rather than row-count weight. This gauge uses
oofEnsemble()’s independent cell-local inverse-RMSE
reconstruction. It is not held-out risk, a fully nested OOF estimate, or
a guarantee of generalization gain.
Let \(b_{s-1}\) be the historical best gauge and \(\varepsilon=\texttt{epsRevert}\). A candidate is rejected only when
\[g_s<b_{s-1}-\varepsilon.\]
Equality is accepted. On acceptance, \(b_s=\max\{b_{s-1},g_s\}\), so the accepted
fitted state may be up to \(\varepsilon\) below the historical maximum.
Returned finalR2 is that historical maximum; it is not
necessarily the final fitted state’s gauge.
The history actions are:
| Action | Meaning |
|---|---|
baseline |
supervised round-zero gauge |
accept |
candidate labels retained and historical best updated |
empty-stop |
no eligible pair passed the strict score gate |
saturation-stop |
no positive/new-key budget remained |
REVERT-stop |
candidate gauge crossed the absolute reversion threshold |
Exhausting maxRounds adds no terminal row. Reversion
materially refits the accepted pool, but it does not recompute the
restoration gauge. The function returns history, accepted
promoted label rows (or NULL),
baselineR2, historical-best finalR2, and
iterStar; it does not return a fitted model.
This complete example creates its own inputs and working directory.
root <- tempfile("ssel-semi-")
datasets_dir <- file.path(root, "datasets")
dir.create(datasets_dir, recursive = TRUE)
n_train <- 30L
assembled <- data.frame(
SampleID = c(paste0("S", seq_len(n_train)), "P1", "P2"),
set = c(rep("train", n_train), "predict", "predict"),
x = seq_len(n_train + 2L),
z = rep(c(0, 1), length.out = n_train + 2L),
target = c(
1.5 * seq_len(n_train) + rep(c(-0.4, 0.2, 0.6), 10),
NA, NA
)
)
utils::write.csv(
assembled, file.path(datasets_dir, "demo.csv"),
row.names = FALSE, na = ""
)
old_quiet <- getOption("aR.quiet")
options(aR.quiet = TRUE)
result <- semiSupervisedPipeline(
.path.datasets = datasets_dir,
.path.work = file.path(root, "work"),
key = "SampleID",
responses = "target",
allResponses = "target",
datasets = "demo",
methods = c("lm", "glm"),
seed = 2026,
tau = 0.1,
maxRounds = 1,
epsRevert = Inf,
promotionCap = 0.5
)
options(aR.quiet = old_quiet)
result$history
result$promoted
c(baseline = result$baselineR2, historical_best = result$finalR2)
# The one-round policy promotes at most one of the two original keys.
# iterStar is NA because no chain iteration directory was supplied.
unlink(root, recursive = TRUE)Every baseline, candidate, and restoration fit rebuilds the current
round with the same seed. After the loop, each final TEST split uses the
fitted TRAIN feature schema and the original prediction keys. A serial
trainRegressionModel(TRAIN = FALSE) pass refreshes cell
metrics and responses, then the shared prediction-summary calculation
and aggregateResponses() refresh summaries and exports for
that complete pool, including promoted keys.
Fitted models and OOF residuals, offsets and optimism diagnostics remain those of the accepted full round. Delivery refresh does not fit or evaluate again.
NaN and later comparison can fail.These are method limits, not optional fallback modes. Detailed input schemas, side effects, and failure timing are part of the function reference.