Zero-shot anomaly detection with Timestral A project built on the Timestral time series foundation model detects anomalies in hourly electricity demand for five European countries from 2015 to September 2020 without any training on the target data, using only two API calls. The 50,400-row dataset from Open Power System Data, sourced from ENTSO-E grid operator publications, contains the March 2020 COVID-19 lockdowns as a real anomaly; the project shows a fixed 25,000 MW threshold flagged 15% of all hours before the lockdown (every night and weekend) yet missed weekday-afternoon lockdown demand of 34,000 MW, while a 2-standard-deviation weekly moving average flagged 0% of hours before and during the lockdown. The zero-shot approach matters because fixed thresholds and moving averages require per-country, per-season tuning and cannot separate a lockdown from a Sunday. This project finds anomalies in real data with a time series foundation model. You do not need experience in machine learning to use it. The input is the hourly electricity demand load of five European countries, from 2015 to September 2020. The unit is megawatts MW . The data comes from Open Power System Data https://data.open-power-system-data.org/time series/ . This source collects the data that the European grid operators publish on the ENTSO-E Transparency Platform. Sample rows: | time UTC | Germany | France | Italy | Spain | Great Britain | |---|---|---|---|---|---| | 2020-03-02 06:00 | 65,838 | 66,270 | 35,176 | 29,200 | 38,178 | | 2020-03-02 07:00 | 66,800 | 68,659 | 40,259 | 31,353 | 42,669 | | 2020-03-02 08:00 | 67,032 | 70,363 | 41,955 | 32,759 | 44,722 | | 2020-03-02 09:00 | 67,977 | 70,442 | 42,109 | 33,539 | 44,679 | | 2020-03-02 10:00 | 68,588 | 70,845 | 42,817 | 33,390 | 45,250 | The data has 50,400 rows, one row for each hour. Each column is one time series: one number for each hour. The data contains a real anomaly: the COVID-19 lockdowns of March 2020. We use it to examine the results. The chart shows Italy before and after the start of its lockdown on 10 March 2020. It also shows two usual methods to find anomalies. A fixed threshold. The rule is: "If the demand is less than 25,000 MW, send an alert." This rule cannot find the difference between a lockdown and a Sunday: - The demand changes much during each day. At night it is 21,000-25,000 MW. At noon it is approximately 44,000 MW. - The demand also changes during each week. On weekends it is much lower than on weekdays. - Before the lockdown, the rule sent an alert for 15% of all hours: each night and each weekend. - During the lockdown, the demand on weekday afternoons was 34,000 MW. The rule did not send an alert for these hours. If you increase the threshold, you get more false alerts. If you decrease it, the rule does not find the lockdown. Also, each country, season and year needs a different threshold. A moving average. The rule is: "If an hour is more than 2 standard deviations from the average of the last week, send an alert." This rule sent an alert for 0% of the hours, before and during the lockdown: - The daily change in demand is large. Thus the standard deviation is approximately 6,000 MW, and the band is approximately 24,000 MW wide. No hour goes out of this band. - The average decreases only one week after the demand decreases. - A shorter window or a narrower band follows the daily cycle. Then the rule sends an alert each morning. You can make a better model yourself. For example, you can use an average for each hour of the week, a seasonal decomposition, a holiday calendar and a trend. But you must adjust this model for each time series. You must do this work again for each metric that you monitor. Thus you need a model that knows the usual pattern for each hour, each weekday and each season. The model must learn this pattern from the data only. A large language model LLM learns from a very large quantity of text. This occurs one time, before you use the model. Then you give the model a prompt, and the model writes the next text. A time series foundation model TSFM uses the same method for numbers. It learns from a very large set of different time series. Then you give it the history of a new time series, and it forecasts the next values. This is "zero-shot" forecasting: you do not train, fit or adjust the model on your data. | | LLM | TSFM | |---|---|---| | Learns from | a large quantity of text | a large set of time series | | Input | a prompt | the history: a list of numbers | | Output | the next words | the next values, with a range that shows the uncertainty | | Training on your data | none | none | Thus you do not need experience in machine learning. You do not design features, select a model or adjust parameters. This project uses only two calls: python from timestral import TimestralForecaster forecaster = TimestralForecaster.from pretrained "mistralai/Timestral-209M" result = forecaster.forecast history, prediction length=24 history: series, hours array result.quantile 0.5 the best estimate for each of the next 24 hours result.quantile 0.99 the value that the model is 99% sure the demand stays below This project uses Timestral https://mistral.ai/news/timestral/ , the TSFM from Mistral. The model has 209 million parameters, and the download is 837 MB. Public holidays decrease the demand, and we know their dates before they occur. Thus we give the holidays to the model as an extra input. The model gets all other information from the history. We ran all the commands on a MacBook Air M1, 2020 with 16 GB of RAM. The model ran on the CPU, without a GPU. Each command first reads the data file 124 MB, 50,400 rows . Then it gives a part of the history to the model. In this table, one value is the load of one country for one hour. The model also gets one holiday value 0 or 1 for each of these hours. | Command | Function | Input to the model | Output of the model | Time | |---|---|---|---|---| | main.py forecast | Forecasts the next 30 days for 5 countries | 5 forecasts, each from 32,768 hours: 163,840 values | 3,600 hours 720 × 5 | 42 s | | main.py backtest | Makes 30 day-ahead forecasts for 5 countries | 150 forecasts, each from 2,048 hours: 307,200 values | 3,600 hours 720 × 5 | 54 s | | main.py anomalies | Makes one year of week-ahead forecasts for 5 countries | 260 forecasts, each from 2,048 hours: 532,480 values | 43,680 hours 8,736 × 5 | 96 s | This project uses the same model in three different ways. You give the model the history until now. The model gives each hour of the next 30 days, with a range for each hour. php flowchart LR H "History