Primeira vez aqui? Seja bem vindo e cheque o FAQ!
x

Explorando o paper “Machine Learning Advances for Time Series Forecasting”.

0 votos
10 visitas
perguntada Nov 5 em Programação Computacional por Fabio Fujita (46 pontos)  

Trabalho final da disciplina de Métodos Computacionais do Programa de Pós-Graduação em Economia da UnB, semestre 2021/1.

São reproduzidas algumas das técnicas discutidas no paper "Machine Learning Advances for Time Series Forecasting", de Ricardo Masini, Marcelo Medeiros e Eduardo Mendes. São avaliados os erros de projeção out of sample dos modelos LASSO, Ridge e Elastic Net, comparados aos benchmarks Arima e Random Walk.

Compartilhe

1 Resposta

0 votos
respondida Nov 5 por Fabio Fujita (46 pontos)  
editado Nov 5 por Fabio Fujita

No artigo em referência, os autores fazem uma revisão dos principais métodos de machine learning aplicados a previsão de time series. Com foco no aprendizado supervisionado, são abordados os fundamentos tanto de métodos lineares (como LASSO, Ridge Regression, adaLASSO, Elastic Net e Complete Subset Regression) quanto de métodos não lineares (como Redes Neurais e Regression Trees). Ao final do artigo, é apresentado rapidamente um exercício empírico da aplicação de algumas técnicas na previsão da volatilidade do índice Ibovespa.

Um dos autores (Marcelo Medeiros) é co-autor de outros artigos relacionados à aplicação de técnicas de machine learning na previsão da inflação brasileira. No artigo “Forecasting Brazilian Inflation with High Dimensional Models” (2016), são testados os modelos LASSO e adaLASSO na previsão do IPCA e do IGP-M, com os resultados comparados com Factor Models e modelos AR. Entre outras conclusões, constata-se que:

• O adaLASSO apresenta erros menores para horizontes de até 4 meses;
• Modelos AR apresentam erros menores para horizontes mais longos;
• Pelo teste de Giacomini e White, os modelos não são estatisticamente diferentes.

Já no artigo “Real-time inflation forecasting with high dimensional models: The case of Brazil” (2017), são feitas projeções para o IPCA utilizando modelos random walk, AR, Factor Models, LASSO, Flex adaLASSO, Post-OLS, random forest e complete subset regression. Os resultados são comparados ainda com expectativas de mercado coletadas do Relatório Focus. Para os horizontes t+1 e t+2, os modelos LASSO e Flex adaLASSO apresentam os menores erros. Para horizontes entre t+3 e t+12, o modelo CSR apresenta os melhores resultados.

O EXERCÍCIO

A literatura conta com exemplos de previsão do IPCA agregado utilizando técnicas de machine learning. Há também trabalhos em que se argumenta a favor dos benefícios de fazer projeções com variáveis desagregadas, realizando a agregação posteriormente. No Brasil, o IPCA é tradicionalmente dividido nos segmentos de alimentos, serviços, monitorados e bens industriais, sendo que cada segmento tem uma dinâmica distinta.

A imagem será apresentada aqui.

A idéia inicial desse exercício empírico era a de utilizar variáveis independentes relacionadas aos custos de produção de bens industriais para tentar prever o componente da inflação de bens industriais no IPCA. No entanto, ao longo do exercício provou-se necessária a inclusão de outras variáveis macroeconômicas e expectativas de mercado para obtenção de um melhor resultado.

BASE DE DADOS E METODOLOGIA

Foram utilizados dados no período entre janeiro de 2007 e agosto de 2021. As variáveis independentes fornecidas aos modelos são apresentadas na tabela a seguir:

A imagem será apresentada aqui.

Optou-se nesse momento por não incluir variáveis como dados de emprego, confiança e outras que poderiam trazer ganhos para a previsão.

Os modelos testados foram os modelos regularizados LASSO, Ridge e Elastic Net, sendo feita a comparação dos erros de previsão com os benchmarks Arima e Random Walk. Os hiperparâmetros dos modelos foram escolhidos por meio de cross-validation a cada passo da janela móvel, com os coeficientes dos modelos sendo estimados em uma janela móvel de 60 períodos. Foram consideradas 12 defasagens das variáveis independentes entre os regressores, em linha com o que é feito na literatura análoga e com os fundamentos econômicos, como o repasse ao consumidor final de custos de produção, variação cambial e outros.

Para a projeção, foram considerados os dados de fechamento do mês t para previsão dos horizontes t+1, t+3, t+6, t+9 e t+12. Os erros out of sample foram então calculados utilizando as métricas do Erro Médio Quadrático (MSE) e Erro Absoluto Médio (MAE). Os resultados são apresentados a seguir.

RESULTADOS

A imagem será apresentada aqui.

Nota-se que para o horizonte t+1, o modelo AR(3) apresentou os menores erros, diferentemente da literatura, onde os modelos regularizados apresentam performance melhor para horizontes curtos. No entanto, a diferença da data de corte dos dados é fundamental na análise. O paper de 2017 utiliza dados disponíveis até 5 dias úteis antes da publicação do IPCA de t+1, enquanto que esse exercício apresentado considera apenas os dados de fechamento do mês t para previsão de t+1. Dessa forma, os modelos do paper de 2017 contam com praticamente 25 dias a mais de dados de inflação, além de expectativas de mercado mais refinadas para projetar o mesmo horizonte.

Para os horizontes t+3 e t+6, o modelo Ridge apresenta os menores erros, sendo substituído pela Elastic Net para t+9.

O random walk apresentou os menores erros para t+12. Uma possível explicação é fato de que, apesar da maior parte da literatura considerar as taxas de inflação no Brasil estacionárias, sabemos que o IPCA apresenta sazonalidade anual. Dessa forma, o random walk tende a apresentar melhores resultados para períodos múltiplos de 12 meses (um modelo random walk com drift possivelmente apresentaria resultados ainda melhores).

Entre as variáveis selecionadas pelo LASSO, destacam-se a presença sistemática das expectativas de mercado e o fato de que a escolha das variáveis independentes para horizontes mais longos torna-se mais dispersa, indicando uma possível exaustão do seu conteúdo informacional para a previsão.

A imagem será apresentada aqui.

CONSIDERAÇÕES FINAIS

Como considerações finais, temos:

• Nessa avaliação inicial, não foi avaliado se os modelos testados são diferentes estatisticamente;
• Métodos de ML podem ser beneficiados ao considerar expectativas de mercado;
• As variáveis mais frequentemente selecionadas pelo LASSO foram relacionadas a expectativas de mercado, dados de exportação, defasagens do IPCA e componentes autoregressivos do segmento de bens industriais. A variação do câmbio médio apresentou menos influência direta do que o esperado (influência indireta por meio de outras variáveis);
• No geral, para os dados avaliados, os modelos Ridge apresentaram erros menores do que o LASSO. Observando a alternância das variáveis independentes selecionadas pelo LASSO, uma hipótese é a de que as variáveis mais influentes se alternam ao longo do tempo, beneficiando os modelos Ridge.

Os códigos referentes à programação dos modelos Lasso, Ridge e Elastic Net são apresentados a seguir.

LASSO/RIDGE

import pandas as pd
import numpy as np
from sklearn import linear_model
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error


class Tools_LassoRidgeEN:

    def make_lagged_df(df_to_lag, lags_list):
    #Generates dataframe with lagged data   
        columns_names = df_to_lag.columns
        df_lagged = pd.DataFrame(index=df_to_lag.index) 
        for lag in lags_list:
            df = df_to_lag.shift(lag)
            df.columns = [item + f'_t-{lag}' for item in columns_names]
            df_lagged = df_lagged.join(df)
        df_lagged = df_lagged.dropna()        
        return df_lagged

    def make_advanced_df(df_to_advance, horizons_list):
    #Generates dataframe with advanced data   
        columns_names = df_to_advance.columns
        df_advanced = pd.DataFrame(index=df_to_advance.index)  
        for horizon in horizons_list:
            df = df_to_advance.shift(-horizon)
            df.columns = [item + f'_t+{horizon}' for item in columns_names]
            df_advanced = df_advanced.join(df)
        df_advanced=df_advanced.dropna()
        return df_advanced

    def lasso_ridge_cv(cv_data, alphas, window_size, horizon, independent_variable_list, dependent_variable, modelname):
        alpha_scores=[]
        for a in alphas:
            cv_pred=[]
            cv_true=[]
            for i in range(len(cv_data)-window_size):
                train_data = cv_data[i:window_size+i]
                forecast_data = data[window_size+i:window_size+i+1]
                model = modelname(alpha=a, fit_intercept=True, max_iter=1e6)
                my_model = MySklearningModel(model,train_data,independent_variable_list,
                                         dependent_variable, forecast_data)
                my_model.run_sklearn_regression()
                cv_pred.append(my_model.forecast())
                cv_true.append(forecast_data[dependent_variable])                            
            alpha_scores.append(mean_absolute_error(y_true=cv_true, y_pred=cv_pred))
        best_alpha = alphas[alpha_scores.index(min(alpha_scores))]
        return best_alpha

class MySklearningModel:
    def __init__(self,model,df,independent_variable_list,dependent_variable, df_forecast):
        self.model=model
        self.independent_variable_list=independent_variable_list
        self.dependent_variable=dependent_variable
        self.X=df[self.independent_variable_list].values
        self.y=np.squeeze(df[[self.dependent_variable]].values)
        self.X_forecast=df_forecast[self.independent_variable_list].values       
    def run_sklearn_regression(self):
        scaler = StandardScaler()
        scaler.fit(self.X)
        self.model.fit(scaler.transform(self.X), self.y)
    def get_parameters(self):    
        return self.model.coef_
    def forecast(self):
        return self.model.predict(self.X_forecast)



if __name__ == '__main__': 
    data = pd.read_excel('BaseDados-2007.xlsx', header=0, index_col=0, squeeze=True)
    expect = pd.read_excel('Expectativas.xlsx', header=0, index_col=0, squeeze=True)
    forecast_variable = 'IPCA_Industriais'        
    lags_independent_variables = [1,2,3,4,5,6,7,8,9,10,11]
    horizon = 12
    n_alphas = 100
    alphas = np.logspace(-4, 5, n_alphas)
    window_size=60
    validation_points = 12  
    modelname = linear_model.Lasso    

# Prepare the data - Lags, Horizons and NA removal    
    data_lagged = Tools_LassoRidgeEN.make_lagged_df(data, lags_independent_variables)        
    data = data.join(data_lagged)

    data=data.join(expect)    

    independent_variable_list = list(data.columns)
    data_advanced = Tools_LassoRidgeEN.make_advanced_df(data[[forecast_variable]], [horizon])
    dependent_variable = np.squeeze(data_advanced.columns)
    data = data.join(data_advanced)
    data = data.dropna()

    true_value=[]
    final_forecast=[]
    selected_alphas=[]
    coefs = []
    coefs.append(independent_variable_list)

    for i in range(len(data) - window_size - validation_points):
        cv_data = data[i:window_size+validation_points+i]
        test_data = data[window_size+validation_points+i:window_size+validation_points+i+1]
        true_value.append(test_data[[dependent_variable]].values)

# Call Cross Validation
        best_alpha = Tools_LassoRidgeEN.lasso_ridge_cv(cv_data, alphas, window_size, horizon, 
                                                   independent_variable_list, dependent_variable, modelname)
        selected_alphas.append(best_alpha)


# Out of sample model test

        train_data = data[validation_points+i:validation_points+i+window_size]
        model = modelname(alpha=best_alpha, fit_intercept=True, max_iter=1e6)
        my_model = MySklearningModel(model,train_data,independent_variable_list,
                                         dependent_variable, test_data)
        my_model.run_sklearn_regression()
        pred = my_model.forecast()
        final_forecast.append(pred)
        coefs.append(my_model.get_parameters())

    true_value=np.squeeze(true_value)

    error_MAE = mean_absolute_error(y_true=true_value, y_pred=final_forecast)
    error_MSE = mean_squared_error(y_true=true_value, y_pred=final_forecast)

    coefs = pd.DataFrame(coefs)
    coefs = coefs.transpose()
    coefs.rename( columns={0 :'independent_variables'}, inplace=True )

Elastic Net

import pandas as pd
import numpy as np
from sklearn import linear_model
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error


class Tools_LassoRidgeEN:

    def make_lagged_df(df_to_lag, lags_list):
    #Generates dataframe with lagged data   
        columns_names = df_to_lag.columns
        df_lagged = pd.DataFrame(index=df_to_lag.index) 
        for lag in lags_list:
            df = df_to_lag.shift(lag)
            df.columns = [item + f'_t-{lag}' for item in columns_names]
            df_lagged = df_lagged.join(df)
        df_lagged = df_lagged.dropna()        
        return df_lagged

    def make_advanced_df(df_to_advance, horizons_list):
    #Generates dataframe with advanced data   
        columns_names = df_to_advance.columns
        df_advanced = pd.DataFrame(index=df_to_advance.index)  
        for horizon in horizons_list:
            df = df_to_advance.shift(-horizon)
            df.columns = [item + f'_t+{horizon}' for item in columns_names]
            df_advanced = df_advanced.join(df)
        df_advanced=df_advanced.dropna()
        return df_advanced

    def elasticnet_cv(cv_data, alphas, l1Ratios, window_size, horizon, independent_variable_list, dependent_variable, modelname):
        alpha_l1_scores=[]
        alpha_l1=[]

        for l1 in l1Ratios:
            for a in alphas:
                cv_pred=[]
                cv_true=[]
                for i in range(len(cv_data)-window_size):
                    train_data = cv_data[i:window_size+i]
                    forecast_data = data[window_size+i:window_size+i+1]
                    model = modelname(alpha=a, l1_ratio=l1, max_iter=1e6)
                    my_model = MySklearningModel(model,train_data,independent_variable_list,
                                         dependent_variable, forecast_data)
                    my_model.run_sklearn_regression()
                    cv_pred.append(my_model.forecast())
                    cv_true.append(forecast_data[dependent_variable])                            

                alpha_l1_scores.append(mean_squared_error(y_true=cv_true, y_pred=cv_pred))
                alpha_l1.append([a,l1])

        best_parameters = alpha_l1[alpha_l1_scores.index(min(alpha_l1_scores))]
        best_alpha = best_parameters[0]
        best_l1 = best_parameters[1]
        return best_alpha, best_l1                

class MySklearningModel:
    def __init__(self,model,df,independent_variable_list,dependent_variable, df_forecast):
        self.model=model
        self.independent_variable_list=independent_variable_list
        self.dependent_variable=dependent_variable
        self.X=df[self.independent_variable_list].values
        self.y=np.squeeze(df[[self.dependent_variable]].values)
        self.X_forecast=df_forecast[self.independent_variable_list].values       
    def run_sklearn_regression(self):
        scaler = StandardScaler()
        scaler.fit(self.X)
        self.model.fit(scaler.transform(self.X), self.y)
    def get_parameters(self):    
        return self.model.coef_
    def forecast(self):
        return self.model.predict(self.X_forecast)



if __name__ == '__main__': 
    data = pd.read_excel('BaseDados-2007.xlsx', header=0, index_col=0, squeeze=True)
    expect = pd.read_excel('Expectativas.xlsx', header=0, index_col=0, squeeze=True)
    forecast_variable = 'IPCA_Industriais'    
    lags_independent_variables = [1,2,3,4,5,6,7,8,9,10,11]
    horizon = 9
    n_alphas = 100
    alphas = np.logspace(-4, 5, n_alphas)
    l1Ratios = np.arange(0.02, 1, 0.05)
    window_size=60
    validation_points = 12  
    modelname = linear_model.ElasticNet    

# Prepare the data - Lags, Horizons and NA removal    
    data_lagged = Tools_LassoRidgeEN.make_lagged_df(data, lags_independent_variables)        
    data = data.join(data_lagged)

    data=data.join(expect)    

    independent_variable_list = list(data.columns)
    data_advanced = Tools_LassoRidgeEN.make_advanced_df(data[[forecast_variable]], [horizon])
    dependent_variable = np.squeeze(data_advanced.columns)
    data = data.join(data_advanced)
    data = data.dropna()

    true_value=[]
    final_forecast=[]
    selected_alphas=[]
    selected_l1=[]

    for i in range(len(data) - window_size - validation_points):
        cv_data = data[i:window_size+validation_points+i]
        test_data = data[window_size+validation_points+i:window_size+validation_points+i+1]
        true_value.append(test_data[[dependent_variable]].values)

# Call Cross Validation
        best_alpha, best_l1 = Tools_LassoRidgeEN.elasticnet_cv(cv_data, alphas, l1Ratios, window_size, horizon, 
                                                   independent_variable_list, dependent_variable, modelname)
        selected_alphas.append(best_alpha)
        selected_l1.append(best_l1)


# Out of sample model test

        train_data = data[validation_points+i:validation_points+i+window_size]
        model = modelname(alpha=best_alpha, l1_ratio=best_l1, max_iter=1e6)
        my_model = MySklearningModel(model,train_data,independent_variable_list,
                                         dependent_variable, test_data)
        my_model.run_sklearn_regression()
        pred = my_model.forecast()
        final_forecast.append(pred)
        print('iteration ' f'{i}' ' of ' f'{len(data) - window_size - validation_points}')

    true_value=np.squeeze(true_value)

    error_MAE = mean_absolute_error(y_true=true_value, y_pred=final_forecast)
    error_MSE = mean_squared_error(y_true=true_value, y_pred=final_forecast)
...