File size: 6,129 Bytes
b2108ae
e5e340b
 
700a890
e5e340b
 
 
c0809e5
700a890
7cecffc
4d6d97a
340176f
700a890
8d3eb2c
700a890
 
e5e340b
2b898e9
 
700a890
34960fe
 
700a890
34960fe
e5e340b
 
91a8ee3
 
 
 
 
 
700a890
91a8ee3
 
700a890
91a8ee3
 
 
 
700a890
 
 
91a8ee3
700a890
 
e5e340b
7cecffc
e5e340b
700a890
e5e340b
700a890
e5e340b
700a890
e5e340b
700a890
e5e340b
 
 
700a890
a4e353d
2b898e9
a4e353d
8d3eb2c
c0f7916
700a890
 
2b898e9
0a98de7
08750ad
1d5eb19
700a890
08750ad
700a890
8c26465
700a890
e5e340b
3127dc9
700a890
 
3127dc9
700a890
8c26465
700a890
 
 
8c26465
 
 
700a890
8c26465
f0efcd9
 
8c26465
e5e340b
d511c44
 
700a890
1258ec5
700a890
c65f546
700a890
 
1258ec5
700a890
 
ef2ab0c
 
1258ec5
700a890
ef2ab0c
 
700a890
1258ec5
c0f7916
700a890
65275be
 
c2b17c7
78d4c43
700a890
 
7cecffc
d511c44
932e273
b1decbe
7cecffc
d511c44
 
d066a0e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import streamlit as st
import pandas as pd
import matplotlib.pyplot as plt
import time
from neuralforecast.core import NeuralForecast
from neuralforecast.models import NHITS, TimesNet, LSTM, TFT
from neuralforecast.losses.pytorch import HuberMQLoss
from neuralforecast.utils import AirPassengersDF
import plotly.graph_objects as go

st.set_page_config(layout='wide')

def generate_forecast(model, df, tag=False):
    if tag == 'retrain':
        return model.predict()
    return model.predict(df=df)

def determine_frequency(df):
    df['ds'] = pd.to_datetime(df['ds'])
    df = df.drop_duplicates(subset='ds').set_index('ds')
    freq = pd.infer_freq(df.index)
    if not freq:
        st.warning('Defaulting to Daily frequency due to date inconsistencies. Please check your data.', icon="⚠️")
        freq = 'D'
    return freq

def plot_forecasts(forecast_df, train_df, title):
    plot_df = pd.concat([train_df, forecast_df]).set_index('ds')
    historical_col = 'y'
    forecast_col = next((col for col in plot_df.columns if 'median' in col), None)
    lo_col = next((col for col in plot_df.columns if 'lo-90' in col), None)
    hi_col = next((col for col in plot_df.columns if 'hi-90' in col), None)

    if forecast_col is None:
        raise KeyError("No forecast column found in the data.")

    fig = go.Figure()
    fig.add_trace(go.Scatter(x=plot_df.index, y=plot_df[historical_col], mode='lines', name='Historical'))
    fig.add_trace(go.Scatter(x=plot_df.index, y=plot_df[forecast_col], mode='lines', name='Forecast'))

    if lo_col and hi_col:
        fig.add_trace(go.Scatter(x=plot_df.index, y=plot_df[hi_col], mode='lines', line=dict(color='rgba(0,100,80,0.2)'), showlegend=False))
        fig.add_trace(go.Scatter(x=plot_df.index, y=plot_df[lo_col], mode='lines', line=dict(color='rgba(0,100,80,0.2)'), fill='tonexty', fillcolor='rgba(0,100,80,0.2)', name='90% Confidence Interval'))

    fig.update_layout(title=title, xaxis_title='Timestamp [t]', yaxis_title='Value', template='plotly_white')
    st.plotly_chart(fig)

def select_model(horizon, model_type, max_steps=50):
    if model_type == 'NHITS':
        return NHITS(input_size=5 * horizon, h=horizon, max_steps=max_steps, stack_types=3*['identity'], n_blocks=3*[1], mlp_units=[[256, 256] for _ in range(3)], batch_size=32, scaler_type='standard', loss=HuberMQLoss(level=[90]))
    elif model_type == 'TimesNet':
        return TimesNet(h=horizon, input_size=horizon * 5, hidden_size=32, conv_hidden_size=64, loss=HuberMQLoss(level=[90]), scaler_type='standard', learning_rate=1e-3, max_steps=max_steps)
    elif model_type == 'LSTM':
        return LSTM(h=horizon, input_size=horizon * 5, loss=HuberMQLoss(level=[90]), scaler_type='standard', encoder_n_layers=3, encoder_hidden_size=256, context_size=10, decoder_hidden_size=256, decoder_layers=3, max_steps=max_steps)
    elif model_type == 'TFT':
        return TFT(h=horizon, input_size=horizon*5, hidden_size=96, loss=HuberMQLoss(level=[90]), learning_rate=0.005, scaler_type='standard', windows_batch_size=128, max_steps=max_steps)
    else:
        raise ValueError(f"Unsupported model type: {model_type}")

def model_train(df, model, freq):
    nf = NeuralForecast(models=[model], freq=freq)
    df['ds'] = pd.to_datetime(df['ds'])
    nf.fit(df)
    return nf

def forecast_time_series(df, model_type, horizon, max_steps, y_col):
    start_time = time.time()
    freq = determine_frequency(df)
    st.sidebar.write(f"Data frequency: {freq}")
    
    selected_model = select_model(horizon, model_type, max_steps)
    model = model_train(df, selected_model, freq)
    
    forecast_results = {model_type: generate_forecast(model, df, tag='retrain')}
    st.session_state.forecast_results = forecast_results

    for model_name, forecast_df in forecast_results.items():
        plot_forecasts(forecast_df, df, f'{model_name} Forecast for {y_col}')

    time_taken = time.time() - start_time
    st.success(f"Time taken for {model_type} forecast: {time_taken:.2f} seconds")
    
    if 'forecast_results' in st.session_state:
        st.markdown('Download Input and Forecast Data below')
        tab_insample, tab_forecast = st.tabs(["Input data", "Forecast"])
        
        with tab_insample:
            df_grid = df.drop(columns="unique_id")
            st.write(df_grid)
        
        with tab_forecast:
            if model_type in forecast_results:
                df_grid = forecast_results[model_type]
                st.write(df_grid)

@st.cache_data
def load_default():
    return AirPassengersDF.copy()

def personalized_forecasting():
    st.title("Personalized Neural Forecasting")
    st.markdown("Train a time series forecasting model from scratch using various deep neural network models.")

    with st.sidebar.expander("Upload and Configure Dataset", expanded=True):
        uploaded_file = st.file_uploader("Upload your time series data (CSV)", type=["csv"])
        df = pd.read_csv(uploaded_file) if uploaded_file else load_default()

        columns = df.columns.tolist()
        ds_col = st.selectbox("Select Date/Time column", options=columns, index=columns.index('ds') if 'ds' in columns else 0)
        target_columns = [col for col in columns if col != ds_col]
        y_col = st.selectbox("Select Target column", options=target_columns, index=0)

        df = df.rename(columns={ds_col: 'ds', y_col: 'y'}).assign(unique_id=1)[['unique_id', 'ds', 'y']]

    st.sidebar.subheader("Dynamic Model Selection and Forecasting")
    dynamic_model_choice = st.sidebar.selectbox("Select model", ["NHITS", "TimesNet", "LSTM", "TFT"], key="dynamic_model_choice")
    dynamic_horizon = st.sidebar.number_input("Forecast horizon", value=12)
    dynamic_max_steps = st.sidebar.number_input('Max steps', value=20)

    if st.sidebar.button("Submit"):
        with st.spinner('Training model...'):
            forecast_time_series(df, dynamic_model_choice, dynamic_horizon, dynamic_max_steps, y_col)

pg = st.navigation({
    "Neuralforecast": [
        st.Page(personalized_forecasting, title="Personalized Forecasting", icon=":material/query_stats:")
    ],
})

pg.run()