Tomorrow's air: a dilated CNN against the forecasts it has to beat
A stack of dilated convolutions can read a whole week of sensor data with a handful of layers. We tested whether that makes it a better day-ahead forecaster for office CO2 than the simple forecasts any building engineer already has, on 13 real rooms with the holidays held out.
Abstract
A temporal convolutional network (TCN) stacks causal convolutions whose dilation doubles at every layer, so eight layers with two-tap kernels see 256 hours, more than a week of hourly data. We test the idea as a day-ahead forecaster: at midnight, read the last 168 hours of a room's CO2 and predict the next 24. Our comparison set is seven forecasts that need no training, ridge and linear models, and a GRU. The data is the VTT SCOTT dataset: 13 offices in Finland, 2019, trained on February to August and tested on October to December, holidays included.1
The TCN works. With a calendar input it scores a mean absolute error of 15.5 ppm against 17.3 ppm for the best baseline (the mean of last week's days of the same type), 10% better and better in 12 of 13 rooms. Most of the gain is on days the office stays empty. On ordinary workdays the best baseline and every network land within about 1.5 ppm of each other, because whether people come in is not in last week's data. A one-layer GRU did better still, 14.9 ppm and better than the baseline in all 13 rooms. The calendar input cut the error on weekday holidays from 18.9 to 6.2 ppm and cost nothing on ordinary workdays; its worst errors came on the days between Christmas and New Year, when the national calendar said "work" and the offices were empty.
A day ahead
Building services at our Kyoto site ventilate rooms on a schedule set the evening before. Ventilate too little and air goes stale by mid-morning; ventilate an empty room and you pay to heat or cool outside air for nobody. Every room has a CO2 sensor, so the obvious question is whether tomorrow's readings can be forecast well enough to set that schedule.
CO2 is the right signal for this job and the wrong one for most others. ASHRAE's position is that indoor CO2 concentrations "are not overall indicators of IAQ", and that the familiar 1,000 ppm figure "is at best an indicator of outdoor air ventilation rate per person".2 What CO2 does track is people: an average adult doing office work exhales about 0.0052 litres a second,3 so in a room with fixed ventilation it rises when the room fills and decays when it empties. Forecasting CO2 a day ahead is mostly forecasting occupancy.
In 2023 one of us proposed a model for it: a stack of dilated causal convolutions in the style of WaveNet, reading a week and writing a day. It was tested on synthetic data and looked promising, but it was never compared with anything. This note rebuilds it and makes the comparison. The hypotheses, from that report, made testable:
- H1. The TCN beats the simple forecasts (same hour last week, yesterday, and so on) and a linear model on the same inputs.
- H2. Telling it which future hours are working days fixes public holidays without costing accuracy on ordinary days.
- H3. Normalising each input window fixes its failure on levels it has never seen.
- H4. A week of history beats one to three days.
- H5. It beats a recurrent network, which the report expected to struggle with long seasonality.
A week in eight layers
A one-dimensional convolution with kernel size k computes each output from k adjacent inputs. Make it causal by padding on the left only, so the output at hour t depends on hours up to t and never later; that is what lets the same network forecast. Stacking plain layers grows the window by k − 1 per layer, so seeing 168 hours with k = 2 would take 167 layers. WaveNet's answer was dilation: layer i skips 2i − 1 inputs between its taps, so the dilations run 1, 2, 4, 8 and the window doubles with every layer (Figure 1).4 Bai, Kolter and Koltun called the general recipe, causal dilated convolutions with residual connections, a temporal convolutional network, "not as a label for a truly new architecture, but as a simple descriptive term".5 Borovykh and colleagues applied it to forecasting in 2017.6
Each layer with dilation d adds (k − 1)d hours of history,5 so L layers with dilations 1 to 2L−1 see the window of Equation 1:
Eight layers give 256 hours, which covers the 168 the forecast reads. We checked it on the network
itself: fed 400 hours, the gradient of the stack's last position is non-zero for exactly the last 256
(check_rf.py).
The 2023 report also said this design "would only require the model to learn 2 × 8 = 16 kernel values". That is true for a network with one filter per layer, which could learn very little. Its own architecture had 32 filters per layer, and a layer with Cin inputs and Cout filters has 2CinCout + Cout parameters: 96 for the first layer, 2,080 for each of the other seven, and 64 for the 1 × 1 convolution that lets the first residual connection add up. That makes 14,720 in the convolutions, and 19,073 in our rebuild of the whole model with its dense layers (the report's adds its input without the 1 × 1 convolution, 64 fewer). Still small: the point of dilation is not that it needs few weights, but that the count grows with log2 of the window instead of with the window.
We use residual blocks as the report did (one dilated convolution, ReLU and dropout, plus the block's input) and test two ways of turning the stack into 24 forecasts. The report's 2023 head runs a small dense network at every time step and keeps the last 24 positions. Because the convolutions are causal, the forecast for each hour then sees data only up to the same hour the day before. In the report's calendar version, each of those 24 values is paired with its own hour's workday flag and passed through a second small dense network, hour by hour. Our direct head flattens the last 24 positions, adds the 24 future workday flags and hour-of-day features, and maps them to all 24 hours at once, so every hour sees the whole history and the whole of tomorrow's calendar.
What it has to beat
Hyndman and Athanasopoulos open their benchmark chapter with "some forecasting methods are extremely simple and surprisingly effective", and close it with the rule that a new method should be compared with them: "If not, the new method is not worth considering."7 On 1,045 monthly series from the M3 competition, Makridakis and colleagues found that "even Naive 2 (a seasonal Random Walk (RW) benchmark) is more accurate than half of the ML methods", LSTMs included.8 On long-horizon benchmarks, one-layer linear models have beaten far more complex transformers.9 So before any network, we built the forecasts a building engineer could write in an afternoon. All but the room profile see only the same 168 hours as the networks, plus the calendar:
- Same hour last week and same hour yesterday: seasonal naive forecasts with periods of 168 and 24 hours.7
- Last day of the same type: copy the most recent workday if tomorrow is a workday, or the most recent non-working day if it isn't.
- Mean and median of same-type days: average every day of tomorrow's type in the last week, hour by hour.
- Room profile: the median of each hour of the day over the training months, separately for workdays and other days.
- Ridge regression on the 168 hours of CO2, with and without tomorrow's calendar, and a linear model trained exactly like the networks, which also sees the past workday flags (see below).
The networks are trained on absolute error, with Adam and early stopping on a validation month. Every score in this note is mean absolute error (MAE) over the 24 hours of a forecast day, averaged over days.
The clean version
The 2023 report tested on synthetic "office CO2": a flat baseline at night, a double hump on workdays (a truncated sine with a lunch dip), a low single hump at weekends and on statutory holidays, and noise. We rebuilt it from the report's description (not its code, which didn't survive) with Gaussian noise of σ = 0.2 on a scale where the workday peak is about 15, over three years of Ontario holidays: train 2021–22, validate the first half of 2023, test the second half, Thanksgiving included.
Synthetic data has a property real data lacks: we know the best possible score. A forecast that knew the true profile would be wrong only by the noise, and a forecast that copies a noisy past day carries two lots of noise:
Table view
| Method | MAE | 95% interval | Weekday holidays |
|---|---|---|---|
| Same hour last week | 0.310 | 0.252–0.383 | 2.83 |
| Same hour yesterday | 0.977 | 0.806–1.161 | 0.23 |
| Last day of the same type | 0.224 | 0.221–0.227 | 0.23 |
| Ridge regression | 0.373 | 0.317–0.443 | 2.61 |
| Ridge + calendar | 0.281 | 0.245–0.324 | 1.78 |
| TCN, 2023 design | 0.259 | 0.210–0.320 | 2.67 |
| TCN | 0.296 | 0.246–0.356 | 2.64 |
| TCN, 2023 design + calendar | 0.183 | 0.168–0.203 | 1.04 |
| TCN + calendar | 0.225 | 0.216–0.234 | 0.62 |
| GRU + calendar | 0.169 | 0.167–0.172 | 0.17 |
Figure 2 shows the result. Copying the last day of the same type scores 0.224, on its expected value; no copy of a single day can do much better. The report's TCN with its calendar input scores 0.183, below anything copying can reach, so it has learned the profile rather than repeating a day, though it stays 14% above the floor. Its holidays are better than without the calendar (1.04 against 2.67) but far from right. Our direct head does worse here, 0.225. The GRU comes closest to the floor, 0.169, and gets holidays right too (0.17). Without a calendar every trained model misses the holidays, since nothing in the past says one is coming. That is the report's own conclusion, and it holds.
The report also claimed something subtler: that a network with no calendar can infer weekends by counting. After five workdays comes a weekend; after two weekend days comes a workday. Figure 3 tests it by cutting the history the TCN sees. With 24 hours it gets Mondays and Saturdays wrong. With 48 or 72 hours it fixes Mondays, since it can see the weekend that just ended, but still calls every Saturday a workday, because two or three workdays look the same whether tomorrow is Friday or Saturday. With 120 hours Saturdays are mostly fixed, and with 168 every day of the week is close to the floor (Mondays, at 0.39, are the worst). That is what the report described: the model "can correctly account for weekends if it knows how many weekdays have just occurred. Otherwise, it assumes the next day is a weekday." Saturday after three days of history is the "otherwise". With a calendar input the counting is unnecessary, and history length stops mattering.
Table view
| History | Mon | Tue | Wed | Thu | Fri | Sat | Sun |
|---|---|---|---|---|---|---|---|
| 24 h | 2.50 | 0.31 | 0.31 | 0.30 | 0.30 | 2.61 | 0.40 |
| 72 h | 0.26 | 0.24 | 0.25 | 0.31 | 0.33 | 2.59 | 0.24 |
| 168 h | 0.39 | 0.23 | 0.22 | 0.20 | 0.23 | 0.22 | 0.21 |
| 72 h + calendar | 0.24 | 0.19 | 0.20 | 0.20 | 0.21 | 0.24 | 0.22 |
Thirteen real offices
The VTT SCOTT IAQ dataset is a year of indoor-air measurements from 13 rooms at VTT's premises in Northern Finland: 11 office cubicles for two to four people and two 12-person meeting rooms, recorded every minute through 2019, published under CC BY 4.0.1 In the files each room has one CO2 node and two to four motion (PIR) nodes. Its authors used it to forecast 1 to 15 minutes ahead;10 we use it for a day ahead.
We converted the timestamps to Finnish local time and averaged each hour that had at least 30 minutes of
readings. Gaps of up to three hours were filled by straight lines; any day with a longer gap in its
forecast or the week before it was dropped. Most rooms report from January and all of them by 11 February,
where training starts. We trained on hourly windows to 31 August (including the July holiday, when most rooms sit empty), chose models on September, and
tested on midnight forecasts from 1 October to 31 December: 780 room-days. One model is trained for all
13 rooms. Public holidays come from the holidays package's Finnish calendar,11
which counts Christmas Eve as a holiday.
Real rooms are much quieter than the synthetic series. At night every room's median sits between 414 and 420 ppm. On a median test workday, CO2 rises about 124 ppm from its lowest to its highest hour, and the median between 10:00 and 16:00 on workdays ranges from 429 ppm in one room to 588 ppm in another. People are also less regular than a sine wave: they arrive late, work from elsewhere, go to meetings. A forecast that holds last week's night-time median all day scores 36.8 ppm on workdays; that is the scale to judge everything else against.
Results
Table view
| Method | MAE, ppm | 95% interval | Kind |
|---|---|---|---|
| Same hour last week | 25.10 | 22.40–27.71 | baseline |
| Same hour yesterday | 23.03 | 20.15–25.94 | baseline |
| Last day of the same type | 18.40 | 16.29–20.51 | baseline |
| Room profile from training | 18.02 | 15.94–20.08 | baseline |
| Mean of same-type days | 17.32 | 15.43–19.02 | baseline |
| Median of same-type days | 17.39 | 15.54–19.21 | baseline |
| Ridge + calendar | 19.68 | 18.32–21.17 | linear |
| Linear + calendar, L1 loss | 18.13 | 16.61–19.81 | linear |
| TCN, 2023 design | 16.66 | 15.08–18.31 | neural |
| TCN, 2023 design + calendar | 16.13 | 14.46–17.82 | neural |
| TCN | 15.92 | 14.15–17.71 | neural |
| TCN + calendar | 15.51 | 13.71–17.40 | neural |
| GRU + calendar | 14.93 | 13.12–16.77 | neural |
Figure 4 has the main result. The simple forecasts fall into two groups. Copying the same hour last week (25.1 ppm) or yesterday (23.0) is poor, because neither knows that tomorrow is a different kind of day. Anything that uses the calendar does much better: the last same-type day scores 18.4, the room's training profile 18.0, and averaging last week's same-type days 17.3 ppm. That last one is the forecast to beat. Ridge regression on the raw week does worse than every calendar-aware one, 19.7 ppm even with the calendar.
Every TCN beats it. The report's own design with its calendar input scores 16.1 ppm, 1.2 better (interval 0.0–2.4). Our direct head scores 15.5, 1.8 ppm better than the baseline (95% interval 0.8–3.0, resampling whole dates) or 10%, and is better in 12 of the 13 rooms. Adding the motion sensors as an input channel changed nothing (15.5), and neither did normalising each window (15.5). Across five training seeds the standard deviation is 0.10–0.18 ppm (0.46 for the report's calendar design, the least stable model here), and repeating a run on the GPU can move it by about 0.1, so differences smaller than that are noise.
| Method | Workday (467) | First workday back (87) | Weekend (208) | Weekday holiday (44) | 23–31 Dec workday (50) | All (780) |
|---|---|---|---|---|---|---|
| Mean of same-type days | 21.6 | 21.6 | 9.3 | 5.6 | 23.4 | 17.3 |
| Ridge + calendar | 22.9 | 24.8 | 12.1 | 21.6 | 21.3 | 19.7 |
| TCN, 2023 design | 20.8 | 22.8 | 7.5 | 19.5 | 15.1 | 16.7 |
| TCN, 2023 design + calendar | 20.5 | 21.9 | 6.3 | 14.8 | 19.5 | 16.1 |
| TCN | 20.6 | 22.1 | 5.6 | 18.9 | 14.8 | 15.9 |
| TCN + calendar | 20.6 | 23.1 | 5.2 | 6.2 | 20.7 | 15.5 |
| GRU + calendar | 20.3 | 23.1 | 4.5 | 3.5 | 19.7 | 14.9 |
Figure 5 says where the gain comes from. On ordinary workdays the TCN is 0.9 ppm better than the baseline and the interval (−0.1 to 2.0) includes zero; on weekends it is 4.1 ppm better, nearly halving the error. The baseline copies last week's weekend, sensor wobble and the odd visitor included, while the network has learned that an empty room is flat. On workdays nobody does much better than 20 ppm, about 45% below the flat forecast. Whether a particular person comes in tomorrow is not in last week's data, and no architecture can find it there.
The network or the loss?
One difference between the TCN and ridge regression is not architecture. The networks are trained on absolute error, which aims at the median; ridge minimises squared error, which aims at the mean, and CO2 has spikes that pull the mean around. To separate the two we trained a plain linear model, the same map from 168 hours to 24 that ridge learns, with the networks' loss, optimiser and early stopping. It scores 18.1 ppm with the calendar, against 19.5 ppm for the same linear model on squared error. Conversely the TCN trained on squared error scores 16.4 instead of 15.5. So the loss is worth about a point either way, and with the same loss the TCN still beats the linear model by 2.6 ppm. The convolutions are doing real work.
The calendar input
The report found that adding a workday variable "had the effect of decreasing the accuracy of the non-holiday predictions". We see a version of that, and a reason. Without the calendar, the TCN's error on weekday holidays is 18.9 ppm: it forecasts an ordinary working day. With it, 6.2 ppm. Figure 6 shows the week of Finnish Independence Day, a Friday. The report's own way of adding the calendar, one flag per forecast hour at the very last layer, does much less: 19.5 to 14.8 ppm, an interval that includes no change. The report called its holiday forecasts "improved, yet imperfect", and that reproduces.
On first workdays back after a weekend or holiday, though, the calendar version was 2.9 ppm worse (interval 0.3 to 6.0). Four-fifths of that came from 27 and 30 December. Both are working days on the Finnish calendar, and on both the offices stayed empty: average CO2 was 420 and 399 ppm. The model with the calendar believed it and forecast a working day; the model without it saw a week of quiet and forecast more quiet (Figure 7). Leave out 23–31 December and the calendar costs nothing on ordinary workdays (+0.1 ppm, interval −0.3 to +0.5) and about 1 ppm on the remaining first days back (interval 0.1 to 2.0), a small cost we can't explain. On the calendar "workdays" of that week it costs 5.9 ppm.
So H2 mostly holds, with a condition. The network does what the calendar says, so the calendar has to say what the building will do. A national holiday list is not a site calendar: the week between Christmas and New Year, bridge days, company shutdowns and the summer holiday all need to be in it.
How much history
On the real rooms, with the calendar, cutting the history from 168 hours to 72 or 120 costs only 0.2–0.3 ppm, within the noise. Cutting to 24 or 48 hours costs 1.3–1.4 ppm (Figure 8). Tomorrow depends on the last few days, not on last Tuesday. That fits the synthetic result: once the calendar is given, the long receptive field is no longer needed to work out what kind of day is next, only to estimate the room's level.
Table view
| History | MAE, ppm | 95% interval |
|---|---|---|
| 24 h | 16.84 | 14.61–19.02 |
| 48 h | 17.03 | 14.87–19.40 |
| 72 h | 15.89 | 13.98–17.89 |
| 120 h | 15.80 | 13.86–17.76 |
| 168 h | 15.58 | 13.78–17.44 |
Levels it has never seen
The report's TCN failed on a series whose level drifted outside anything in training. The standard fix is to normalise each window by its own statistics and undo it on the output, as RevIN does.12 We use the simplest form: subtract the input week's mean and add it back to the forecast. We tested it on synthetic series whose level wandered between 0 and 10 in training. For the test half-year, one series stayed in that range and the other moved to 15–25 (Figure 9).
| Method | Familiar level (0–10) | Unseen level (15–25) |
|---|---|---|
| Last day of the same type | 0.44 | 0.63 |
| Ridge + calendar | 0.37 | 0.90 |
| Ridge + calendar, window norm | 0.38 | 0.53 |
| TCN + calendar | 0.31 | 3.57 |
| TCN + calendar, window norm | 0.28 | 0.46 |
Scaled with fixed training statistics, the TCN does well on the familiar series (0.31) and is by far the worst on the unseen one, 3.6 against 0.6 for copying a same-type day: the report's failure, reproduced. Subtracting each window's mean brings it to 0.46, the best on that series, and to 0.28 on the familiar one. On the real rooms the same change made no difference (15.5 against 15.5 ppm), because October to December sat at the same levels as training. H3 holds, and since the fix costs nothing when levels are stable, we would always use it.
The recurrent network
The report motivated the TCN by saying recurrent networks suffer from vanishing gradients, which "restricts the length of seasonality that the models can capture". That is the problem gated recurrent units were designed to solve: the LSTM was built so error could flow across long gaps without vanishing,13 and the GRU's gates serve the same purpose.14 Bai and colleagues did find TCNs ahead of LSTMs and GRUs on most of their benchmarks, though not all, and none of them was forecasting.5
On our data a single GRU layer with 64 units, reading the same 168 hours and the same calendar, scores 14.9 ppm: 0.6 ppm better than the TCN (interval 0.3–0.9), better than the baseline in all 13 rooms, and no worse on any kind of day, holidays most of all (3.5 against 6.2 ppm). It has a quarter of the parameters, 33,688 against 125,560, and trains in about the same time. H5 fails on this data.
Verdict
| Hypothesis | Verdict | Evidence |
|---|---|---|
| H1 · beats simple forecasts and a linear model | Yes | Direct head 15.5 vs 17.3 ppm (best baseline); the 2023 design 16.1; linear with the same loss 18.1 |
| H2 · a calendar fixes holidays at no cost | Mostly, if the calendar is right | Holidays 18.9 → 6.2 ppm; ordinary workdays +0.1; other first days back +1.0; 23–31 Dec calendar workdays +5.9 |
| H3 · window norm fixes unseen levels | Yes (synthetic) | Unseen level 3.57 → 0.46; no effect on the real rooms |
| H4 · a week beats 1–3 days | Beats 1–2 days; 3 days ties | With the calendar, vs 168 h: 24 h +1.3, 48 h +1.4, 72 h +0.3 (interval includes 0). Without one (synthetic only), Saturdays need 5 days |
| H5 · beats a recurrent network | No | GRU 14.9 ppm, 0.6 better (interval 0.3–0.9); also closest to the synthetic noise floor |
Figure 10 sums up. The 2023 idea was sound. A dilated causal CNN does learn a week of office rhythm with a few layers: on the clean series it beats anything that copies the past, and on real rooms it beats every forecast that needs no training. Two of its claims didn't survive: that it needs only 16 weights, and that it beats recurrent networks. The real limit is not the architecture but what's in the data. Most of the remaining error on working days is whether people turn up, and the biggest single failure was a calendar that didn't match the building.
What we do with this
For the day-ahead ventilation schedule, YS·FLOOR now works to four rules:
- Start with the same-type-day mean. It needs no training, takes a few lines to write, and set a bar that ridge regression failed to clear. Publish every model's score against it.
- Feed the site calendar, not the national one. Bridge days, shutdowns and the week between Christmas and New Year belong in the input. A model follows its calendar, wrong or not.
- Use a small network if the gain is worth it to you. Here it is 10–14%, nearly all on empty days, which is where ventilation is wasted. Try a GRU alongside the TCN; on this data it won.
- Train on absolute error if you score on it, and normalise each window. The loss was worth about a point here, as much as a change of architecture; window normalisation cost nothing on these rooms and rescued the synthetic test, so it is cheap insurance.
Where this started
This note grew out of a 2023 internal report by one of us, "Time Series Forecasting Using 1D CNN". It set out the dilation argument well, built the model in Keras, and found three things we reproduced: the network infers weekends from what it has seen, it misses holidays without a calendar, and it fails on levels outside training. It borrowed most of its diagrams from a Unit8 blog post,15 and it scored its forecasts without anything to compare the scores with. Our runs use its architecture as "TCN, 2023 design". The one result we could not reproduce is its loss of detail on ordinary days after adding the workday variable. With its head rebuilt as its diagram draws it, the calendar improved ordinary days on the synthetic series (0.17 against 0.22) and made no difference on the real rooms. We don't have its code or training settings, so we can't say where the loss came from.
Limits of this note
- One building, one year, 13 rooms in Northern Finland. Rooms with different occupancy (labs, cleanrooms, classrooms) may favour a different model. Three months of test data contain four weekday holidays, so the holiday results rest on 44 room-days.
- Rooms contribute between 31 and 92 test days, depending on their gaps, so the pooled scores lean towards the best-covered rooms; the per-room comparison (12 and 13 of 13) is the check on that. The "best baseline" was picked on the test set; the mean and median of same-type days differ by 0.07 ppm, so the choice doesn't matter.
- The rooms share a building, holidays and weather, so we resample whole dates for every interval. That is more honest than resampling room-days but still treats dates as independent.
- We tuned little: one width and depth per network, and fixed learning rate and dropout. A tuned TCN might close the gap to the GRU, and a tuned GRU might widen it. We didn't try transformers or pretrained forecasting models.
- Models are trained on windows starting every third hour rather than every hour. We checked: training on every hour scored 16.2 ppm against 15.6, no better.
- The synthetic series is our reconstruction of the 2023 report's description, not its code or data.
- No model saw outdoor temperature, room bookings or anything else that knows who is coming. Those would likely help more than any change of architecture, and we haven't tested them.
References
- P. Räsänen, J. Koivusaari, J. Kallio, J. Rehu, J. Ronkainen, J. Tervonen, J. Peltola. VTT SCOTT IAQ Dataset (v01). Zenodo, 2020. CC BY 4.0. doi:10.5281/zenodo.3774723.
- ASHRAE. ASHRAE Position Document on Indoor Carbon Dioxide. Approved 12 February 2025 (© 2022, 2025), pp. 2, 7. ashrae.org/about/position-documents.
- A. Persily, L. de Jonge. "Carbon dioxide generation rates for building occupants." Indoor Air 27(5):868–879, 2017. doi:10.1111/ina.12383.
- A. van den Oord et al. "WaveNet: A Generative Model for Raw Audio." arXiv preprint, 2016. arXiv:1609.03499.
- S. Bai, J. Z. Kolter, V. Koltun. "An Empirical Evaluation of Generic Convolutional and Recurrent Networks for Sequence Modeling." arXiv preprint, 2018, §3. arXiv:1803.01271.
- A. Borovykh, S. Bohte, C. W. Oosterlee. "Conditional Time Series Forecasting with Convolutional Neural Networks." arXiv preprint, 2017 (v5, 2018). arXiv:1703.04691.
- R. J. Hyndman, G. Athanasopoulos. Forecasting: Principles and Practice, 3rd ed., OTexts, 2021, §5.2. otexts.com/fpp3. Accessed 2026-10-17.
- S. Makridakis, E. Spiliotis, V. Assimakopoulos. "Statistical and Machine Learning forecasting methods: Concerns and ways forward." PLOS ONE 13(3):e0194889, 2018. doi:10.1371/journal.pone.0194889.
- A. Zeng, M. Chen, L. Zhang, Q. Xu. "Are Transformers Effective for Time Series Forecasting?" Proc. AAAI 37(9):11121–11128, 2023. doi:10.1609/aaai.v37i9.26317.
- J. Kallio, J. Tervonen, P. Räsänen, R. Mäkynen, J. Koivusaari, J. Peltola. "Forecasting office indoor CO2 concentration using machine learning with a one-year dataset." Building and Environment 187:107409, 2021. doi:10.1016/j.buildenv.2020.107409.
- Vacanza team and contributors. holidays, version 0.106, MIT licence. github.com/vacanza/holidays.
- T. Kim, J. Kim, Y. Tae, C. Park, J.-H. Choi, J. Choo. "Reversible Instance Normalization for Accurate Time-Series Forecasting against Distribution Shift." ICLR 2022. openreview.net/forum?id=cGDAkQo1C0p.
- S. Hochreiter, J. Schmidhuber. "Long Short-Term Memory." Neural Computation 9(8):1735–1780, 1997. doi:10.1162/neco.1997.9.8.1735.
- J. Chung, C. Gulcehre, K. Cho, Y. Bengio. "Empirical Evaluation of Gated Recurrent Neural Networks on Sequence Modeling." NIPS 2014 Deep Learning Workshop. arXiv:1412.3555. The gated unit is from K. Cho et al., EMNLP 2014, doi:10.3115/v1/D14-1179.
- F. Lässig. "Temporal Convolutional Networks and Forecasting." Unit8 blog, 6 July 2021. unit8.com.
Reproduce
Data: the VTT SCOTT IAQ dataset, CC BY 4.0.1 The 140 MB archive reduces to a
600 kB file of hourly CO2 and motion per room, which we ship, so you can skip the download.
That file is our reduction of VTT's data (hourly means, local time), redistributed under the same
CC BY 4.0 licence.
You need Python 3.10+, NumPy, pandas, PyTorch and holidays. On a GTX 1060 every experiment
runs in about two hours; a CPU works but takes several times longer. GPU convolutions are not bit-for-bit
repeatable, so expect differences of about 0.1 ppm.
bash code/ys-en-0006/run_all.sh # everything below, in order
python code/ys-en-0006/prep_vtt.py VTT_SCOTT_IAQ_dataset.zip results/vtt_hourly.csv.gz
python code/ys-en-0006/exp_synth.py main # also: hist, trend
python code/ys-en-0006/exp_vtt.py main # also: hist, loss, check
python code/ys-en-0006/analyse.py results/vtt_main.npz sametype_mean
python code/ys-en-0006/check_rf.py # Equation 1, parameter counts
python code/ys-en-0006/build_figures.py # rebuild the figures in this page
prep_vtt.py: minute readings to hourly rooms;vtt_hourly.csv.gz, its outputsynth.py: the synthetic office seriesfc.py: windows, baselines, ridge, TCN, GRU and training;exp.py: the shared runnerexp_synth.pyandexp_vtt.py: every experiment in this noteanalyse.py: scores by day type and date-resampled intervals, and the descriptive numbers about the rooms (vtt_stats.json);results/*.npzhold every test forecastcheck_rf.pyand its output: Equation 1 and the parameter counts, checked on the networksbuild_figures.pyandsvgchart.py: figure generationLICENSE: the scripts are MIT-licensed. Use them freely, keep the notice; they come with no warranty
Changelog
- Rev 1. First publication.