PNN GridForecast β residential grid meter, one day ahead
Β© PreciseStatistics GmbH (Prime Neural Network). Proprietary weights, published for evaluation. Loading requires the PNN evaluation or licence package, version V4.1 or later.
A small Prime Neural Network (PNN) that forecasts the net power through one residential grid meter, every 15 minutes, one day ahead, from the day-ahead weather forecast and the calendar. This is the number an energy supplier settles on. The house has a 13.3 kWp rooftop solar array (southern France), so the meter swings between buying in the evening and exporting at midday. Nothing behind the meter is an input.
Two models, each in int8 (default) and fp32
| Model | Inputs | Test MAE per 15 min, int8 | fp32 | Daily net-energy error | Sign correct |
|---|---|---|---|---|---|
hist/ (shipped) |
20 weather and calendar features + 4 from the meter's own history up to day Dβ2 | 922.7 W | 922.7 W | 9.53 kWh/d | 93.6 % |
weather/ |
20 weather and calendar features | 943.8 W | 943.8 W | 9.30 kWh/d | 93.4 % |
The numbers are those of the published files (seed 0 of each training run). Over three training
seeds the hist model averages 912 W.
Context (same 218-day test period, 2025-12-03 β 2026-07-11):
| Baseline | MAE |
|---|---|
| flat mean | 1,811 W |
| standard load profile | 1,085 W |
| gradient boosting | 981 W |
| dense neural network (same shape) | 952 W |
The PNN matches the dense network of the same shape (within seed noise) with half the hidden-layer weights.
Architecture
inputs β 64 β [PNN 64 β 64 β 64 β 32] β 1, ReLU. The input and output layers are dense; the three
hidden layers are PNN V4.1 layers at 50 % density, so each output reads 32 of its 64 inputs.
int8 (*/int8/, the default): the three PNN layers have int8 weights with one scale per output
row and one static int8 input scale each, calibrated on the training days (percentile 99.99,
chosen on the validation days). The small dense input and output layers stay fp32. On the test
period int8 gives the same MAE as fp32 to 0.1 W, and each PNN layer run on the PNN V4.1 CPU
runtime's int8 kernels equals the int8 reference (relative difference below 1e-7).
Files
| Folder | Weights | Size |
|---|---|---|
hist/int8/ |
int8 | 15.8 kB |
hist/fp32/ |
fp32 | 29.4 kB |
weather/int8/ |
int8 | 14.7 kB |
weather/fp32/ |
fp32 | 28.4 kB |
Each folder holds model.safetensors + config.json in the PNN model file format
(pnn-safetensors/1, Hugging Face safetensors with a compressed-tensors style
quantization_config). Each PNN layer stores only its own weights, shape (outputs,
connections); int8 files add weight_scale (one per row) and input_scale. config.json also
records everything needed to use the model. It states the encoding in words:
pnn.requires_pnn is the minimum PNN package version (V4.1); pnn.weight_encoding and
pnn.activation_encoding give sign, range, zero point and scale. For the int8 files: PNN-layer
weights are signed int8 in [β127, 127], zero point 0, one fp32 scale per output row; their
inputs are signed int8, zero point 0, one static fp32 scale per layer. The model itself needs:
features: the input order;preprocessing: missing values filled with the stored medians, then standardised with the stored means and standard deviations;output_scale_W: the output Γ 10,000 is the meter power in W (positive = the household buys).
Loading
import torch, torch.nn as nn
from pnn_circulant import PNNLinearCirculant # PNN package
from pnn_quant import load_pnn_safetensors
class Regressor(nn.Module): # inputs -> 64 -> [PNN 64-64-64-32] -> 1
def __init__(self, n_in, dims=(64, 64, 64, 32)):
super().__init__()
self.inp = nn.Linear(n_in, dims[0])
self.hidden = nn.ModuleList(PNNLinearCirculant(dims[i], dims[i + 1], 0.5) for i in range(3))
self.out = nn.Linear(dims[-1], 1)
def forward(self, x):
x = torch.relu(self.inp(x))
for h in self.hidden:
x = torch.relu(h(x))
return self.out(x).squeeze(-1) # x 10,000 = meter power in W
model = Regressor(n_in=24) # 20 for weather/
f = load_pnn_safetensors("hist/int8", model) # or "hist/fp32"
Loaded into a float model, int8 weights are dequantised (q Γ weight_scale). For int8 inference,
also round each PNN layer's input to int8 with that layer's f.input_scales[name]; that is what
the PNN CPU runtime does with its int8 kernels.
Training data and limitations
- Data: one household, three years of 15-minute meter readings with the day-ahead weather forecast. All times UTC. Split: chronological, the last 20 % of days as the test set.
- Scope: trained and tested on one meter. It is a demonstration of the method and of PNN layers on a small edge-sized model, not a ready model for other households. Retrain on the target meter's own data.
- Inputs: the weather features must come from the same kind of day-ahead forecast; an observed weather feed changes the error.
Contact
Prime Neural Network Β· primeneural.net Β· info@primeneural.net