A reproducible benchmark for energy disaggregation
One aggregate power signal in. Appliance-level estimates out. We benchmark 16 NILM models across 3 datasets and 2 resolutions — on accuracy, efficiency, and generalization — and find that generalization is the wall.
Schematic of the NILM task — appliance MAEs are NILMFormer's intra-building scores on UK-DALE. Real predictions below.
Non-Intrusive Load Monitoring (NILM) decomposes a household's aggregate power signal into appliance-level estimates. Despite many architectural advances, progress is hindered by the lack of a reproducible benchmark that evaluates models under varying compute budgets and at the resolutions that matter for real-time feedback (1 min) and utility-scale planning (15 min).
NILMBench2026 systematically evaluates sixteen models across regression accuracy, event detection, computational cost, and generalization on UK-DALE, REDD and REFIT. Two insights emerge: (1) model performance is highly context-dependent, varying with appliance type and temporal resolution; and (2) more critically, most models fail to generalize across buildings — especially when test appliances exhibit power characteristics unseen during training.
We also modernize the NILMTK ecosystem: every legacy model is re-implemented in PyTorch behind a unified API, with Docker + uv for one-command reproducibility. NILMBench2026 offers a platform — and actionable insights — to steer NILM toward robust, deployable solutions.
Foundational toolkits standardized data parsers and baselines, but overlooked the constraints that decide real-world adoption. NILMBench2026 adds the first rigorous evaluation of efficiency, utility-scale resolution, and cross-domain generalization — on a modernized, reproducible software stack.
| Feature | NILMTK '14 | Contrib '19 | NILMBench2026 |
|---|---|---|---|
| Deployability | ✗ | ✗ | Docker + uv |
| Models | 2 | 9 | 16 |
| Resolutions | variable | 1-min | 1-min & 15-min |
| Efficiency (FLOPs / time) | ✗ | ✗ | ✓ |
| Cross-building generalization | ✗ | ✓ | ✓ |
| Cross-dataset generalization | ✗ | ✗ | ✓ |
| Software stack | Python 2.7 | TensorFlow 1.x | PyTorch + uv |
NILMBench2026 is the benchmark runner around NILMTK
data and nilmtk-contrib models.
It freezes real-data protocols, provenance and leaderboard generation behind one CLI. Use uv
for development or build the pinned CPU/CUDA container family. Pick a tab.
# Python 3.11 benchmark environment uv venv --python 3.11 source .venv/bin/activate uv pip install "nilmbench[benchmark] @ git+https://github.com/nilmtk/nilmbench.git" nilmbench list
# Until the first public image release, build the pinned CUDA runner locally git clone https://github.com/nilmtk/nilmtk-contrib.git git clone https://github.com/nilmtk/nilmbench.git cd nilmbench docker buildx build --load --build-context contrib=../nilmtk-contrib \ -f docker/Dockerfile.cuda -t nilmbench:cuda . docker run --rm --gpus all nilmbench:cuda doctor
from nilmtk.api import API
from nilmtk.disaggregate import Mean
from nilmtk_contrib.torch import (
Seq2PointTorch, Seq2Seq, RNN, WindowGRU, DAE,
NILMFormer, TCN, ConvLSTM, MSDC, Reformer, # +5 modern architectures
)
experiment = {
'power': {'mains': ['active'], 'appliance': ['active']},
'sample_rate': 60, # 60s real-time | 900s utility-scale
'appliances': ['fridge', 'washing machine', 'microwave', 'kettle'],
'methods': {
'NILMFormer': NILMFormer({'n_epochs': 50, 'batch_size': 256}),
'Seq2Point': Seq2PointTorch({'n_epochs': 50, 'batch_size': 256}),
'TCN': TCN({'n_epochs': 50, 'batch_size': 256}),
'Mean': Mean({}),
},
'train': {'datasets': {'UKDALE': {'path': 'ukdale.h5',
'buildings': {1: {'start_time': '2013-04-01', 'end_time': '2013-05-01'}}}}},
'test': {'datasets': {'UKDALE': {'path': 'ukdale.h5',
'buildings': {4: {'start_time': '2013-05-01', 'end_time': '2013-05-08'}}}},
'metrics': ['mae', 'f1score']},
}
results = API(experiment) # trains, tests & scores every model — cross-building (T2)# One provenance-recorded real-data smoke nilmbench run --task corrected-t1-redd --model Seq2Point \ --appliance fridge --seed 42 --epochs 3 --max-samples 8192 \ --sequence-length 299 --device cuda --results results/candidates # Generate the site artifacts only from immutable result bundles nilmbench leaderboard --results results/published \ --output leaderboard.json --csv leaderboard.csv
Every legacy Keras/TF model re-implemented in modern PyTorch under one common train/infer interface.
Docker + uv lock every version into isolated environments — results that actually reproduce.
All architectures conform to the NILMTK Experiment API. Swap, add, or compare with a dict entry.
TCN, ConvLSTM, MSDC, Reformer, and NILMFormer extend the suite to reflect SOTA temporal modeling.
Benchmarks move fields. ImageNet moved vision, GLUE moved language, the KDD Cup moved applied ML. NILM has never had a shared, sealed, generalization-first leaderboard — NILMBench2026 is engineered to be exactly that. Add a model in one class, add a metric in one function, and submit to a public board scored on held-out buildings and datasets.
from nilmtk.disaggregate import Disaggregator
class MyNILM(Disaggregator): # implement the interface
def __init__(self, params):
self.MODEL_NAME = 'MyNILM'
self.models = {}
self.sequence_length = params.get('sequence_length', 99)
self.n_epochs = params.get('n_epochs', 50)
def partial_fit(self, train_main, train_appliances, **kw):
... # train one PyTorch model per appliance
def disaggregate_chunk(self, test_main):
... # return {appliance: predicted_power}
return predictions
# Register it — the only line your experiment changes
experiment['methods']['MyNILM'] = MyNILM({'n_epochs': 50})# nilmtk/losses.py — define once, reference by name
def sae(app_gt, app_pred): # Signal Aggregate Error
return abs(app_pred.sum() - app_gt.sum()) / app_gt.sum()
def nep(app_gt, app_pred): # Normalized Error in Power
return (app_gt - app_pred).abs().sum() / app_gt.abs().sum()
# Score every model on it — just name it in the experiment
experiment['test']['metrics'] = ['mae', 'f1score', 'sae', 'nep']
# Same harness, same splits, same pre-processing for everyone —
# so reported gains are architectural, not implementation luck.One class, a couple of methods. Any PyTorch model plugs into the same train / infer contract as the 16 built-ins.
Sealed T1 / T2 / T3 splits and fixed test windows — identical data pipeline for every entry, by construction.
Your results are checked and your model takes its place on the public, generalization-first leaderboard.
This page records the fixed BuildSys 2026 study. The living board grows as new provenance-checked result bundles are published, without rewriting the paper.
From foundational baselines to current state-of-the-art, spanning recurrent, convolutional, attention-based, and hybrid designs — integrated behind one unified PyTorch API.
Generalization is tested at three levels — from the best case (same home) to the hardest case (a different country). Performance falls off a cliff as the domain shifts.
Train and test on disjoint time segments from the same home — temporal generalization, the best-case baseline.
Train and test on different homes within a dataset. The realistic deployment test — and where most models break.
Zero-shot transfer across countries & grids (110/230V). The hardest test — a symmetric, bidirectional collapse.
Selected for building diversity, temporal continuity, and open access — the properties needed to validate generalization. Single-building and pay-walled repositories are excluded.
Excluded: AMPds, iAWE, BLUED, DRED (single-building → cannot test cross-building generalization); PecanStreet (not freely available at full scale).
Cells are heat-mapped exactly as in the paper — green is good, red is bad. Bold = best, underline = second-best per column. Scroll tables horizontally.
Real predictions on real UK-DALE / REDD / REFIT traces — the receipts behind the headline findings.




The best architecture depends on the appliance's electrical signature — CNNs excel at sparse, high-power events; Transformers handle complex multi-state loads. Real deployment likely needs an ensemble.
Most models fail on unseen buildings and datasets. The drop from intra- to cross-building is steep and symmetric across directions — the core barrier to real-world adoption.
Always predicting "off" yields a deceptively low MAE while missing every activation. Event metrics like F1 are essential for sparse, bursty appliances.
The trade-off is non-monotonic. Architectural inductive bias beats raw compute: a 69K-param TCN rivals the heavyweight NILMFormer on cross-dataset tasks.
Real-world impact. On the UK-DALE fridge, moving from RNN (~43 W MAE) to RNN Attn. Cl. (~18 W) corrects roughly 219 kWh / year of energy attribution — a difference that matters for consumer feedback and billing systems.
Based on the failure modes the benchmark surfaces, we chart six directions to move NILM from controlled accuracy to deployable robustness.
Align source/target latent distributions so models survive new grids & appliances without per-home labels.
Masked modeling on large unlabeled mains data to learn abstract, transferable load representations.
Classification branches stabilize sparse, high-power loads — extend to probabilistic state modeling.
Local stats + exogenous priors (time-of-day, occupancy) to disambiguate at low resolution.
GANs / diffusion to synthesize adversarial appliance signatures and broaden training diversity.
Maintained, OOD-focused leaderboards — GLUE/ImageNet for energy — to reward robustness over marginal gains.
If the benchmark or modernized toolkit helps your research, please cite the paper.
@inproceedings{kuloor2026nilmbench,
title = {NILMBench2026: A Benchmark for Energy Disaggregation},
author = {Kuloor, Aayush and Singh, Anurag and Dhru, Harsh and Batra, Nipun},
booktitle = {Proceedings of the 13th ACM International Conference on Systems for
Energy-Efficient Buildings, Cities, and Transportation (BuildSys '26)},
year = {2026},
doi = {10.1145/3744256.3812587},
publisher = {ACM},
address = {Banff, AB, Canada}
}