ForeBlocks DARTS Guide
ForeBlocks includes a staged neural architecture search subsystem for time-series forecasting. The DARTS stack is built around DARTSTrainer, but it is not just a single differentiable loop. It combines:
- search-space sampling
- zero-cost candidate screening
- bilevel DARTS training on promoted candidates
- conversion to a discrete fixed model
- final retraining and optional result analysis
Install
DARTS requires the darts extra plus the base foreblocks package:
pip install "foreblocks[darts]"For foreblocks imports (ForecastingModel, Trainer, create_dataloaders, etc.) see the Imports Reference.
When to use DARTS here
Use the DARTS subsystem when:
- your operation pool is already fairly well defined, but you do not know which combinations work best
- you want a cheaper staged NAS workflow instead of fully training every candidate
- you want to inspect alpha evolution, promoted candidates, and final retrained metrics
Do not start here if:
- your base training loop is not working yet
- you have not verified tensor shapes with a normal
ForecastingModelrun - you are still deciding between totally different problem formulations
Quick start
from darts import DARTSTrainer
trainer = DARTSTrainer(
input_dim=5,
hidden_dims=[32, 64, 128],
forecast_horizon=24,
seq_length=48,
device="auto",
)
results = trainer.multi_fidelity_search(
train_loader=train_loader,
val_loader=val_loader,
test_loader=test_loader,
num_candidates=20,
search_epochs=20,
final_epochs=80,
max_samples=32,
top_k=5,
)
best_model = results["final_model"]
trainer.save_best_model("best_darts_model.pth")Useful knobs:
max_samplesnum_batchesfast_modeablationn_randomrandom_sigmaseed
train_darts_model(...)
from darts import DARTSTrainConfig
train_config = DARTSTrainConfig(
epochs=30,
arch_learning_rate=3e-3,
model_learning_rate=1e-3,
use_bilevel_optimization=True,
)
search_results = trainer.train_darts_model(
model=candidate,
train_loader=train_loader,
val_loader=val_loader,
train_config=train_config,
)Use this after the mixed architecture has converged enough that you want a discrete artifact for final training or export.
train_final_model(...)
final_results = trainer.train_final_model(
model=fixed_model,
train_loader=train_loader,
val_loader=val_loader,
test_loader=test_loader,
epochs=100,
)Result structure
The end-to-end search dictionary includes:
final_modelcandidatestop_candidatestrained_candidatesbest_candidatefinal_resultssearch_configstatswhencollect_stats=True
In practice, the most useful objects to inspect are:
results["best_candidate"]results["final_results"]["final_metrics"]results["trained_candidates"]results["candidates"]if you are debugging promotion behavior
Configuration surface
The config dataclasses in darts.config map cleanly onto the staged workflow:
| Config | What it controls |
|---|---|
DARTSSearchSpaceConfig | op pool, architecture modes, hidden dims, cells, nodes, family grouping |
DARTSTrainConfig | bilevel search phase, regularization, pruning, temperature schedule |
FinalTrainConfig | fixed-model retraining budget and optimizer behavior |
MultiFidelitySearchConfig | candidate count, promotion size, epoch budgets, worker/stats settings |
AblationSearchConfig | zero-cost weighting ablations |
RobustPoolSearchConfig | sensitivity to alternative op-pool definitions |
Default search-space ingredients
The default architecture modes are:
encoder_decoderencoder_onlydecoder_only
The default operation pool (darts.config.DEFAULT_OPS, grouped by family in DEFAULT_OP_FAMILIES) includes:
IdentityResidualMLP,GRN,TimeMixer,NBeats(mlpfamily)TimeConv,TCN,ConvMixer,MultiScaleConv,PyramidConv(convfamily)Fourier,Wavelet,DLinear,TimesNet(frequencyfamily)PatchEmbed,InvertedAttention(attentionfamily)SwiGLU,GeGLU,GatedGELU(gated_ffnfamily)SSM(ssmfamily — a lightweight S4D-style diagonal state-space block)
Every operation's constructor, family, and efficiency prior is defined once in darts.architecture.ops.registry.OP_REGISTRY; the search-space config and MixedOp both derive from it, so it is the single place to add a new op.
Recommended tuning order
When the search behaves poorly, change things in this order:
- shrink or clarify the search space
- verify zero-cost metrics are ranking plausible candidates
- reduce
top_kand search budgets only after phase 2 looks sensible - tune bilevel parameters like
arch_learning_rate,model_learning_rate, andarchitecture_update_freq - only then add more advanced regularization or pruning rules
Useful early knobs:
hidden_dimscell_rangenode_rangemin_opsmax_opsnum_candidatestop_ksearch_epochs
Practical tips
- Run a tiny end-to-end search first, even if the result quality is poor. That verifies your search loop, result structure, and save/load path.
- Keep
num_candidates,top_k, andsearch_epochssmall while you are validating the search space. - Use
evaluate_zero_cost_metrics(...)directly before trusting a longer search run. - Treat
collect_stats=Trueas a benchmarking tool, not a default setting for every run. - Save the best discrete model with
trainer.save_best_model(...)once you have a search worth keeping.