Prevendo valores de ações com LSTM
Note: an english version of this article is available at Predicting stock prices with LSTM
Este artigo tem por objetivo mostrar a utilização de Redes Neurais, mais especificamente uma rede LSTM, para prever o comportamento de uma série temporal. Vamos adotar o clássico problema de prever o valor de uma ação do mercado de valores. Os dados e o código completo e detalhado estão disponibilizados neste repositório do GitHub.
Embora este problema não seja novo, ele continua sem uma solução definitiva. Isto porque o valor de uma ação é determinada por diversos fatores, muitos além de seu histórico de preços, o que torna muito difícil prever seu comportamento, a não ser que você seja um macaco jogando dardos.
Resumo
Primeiro começarei com uma breve visualização dos dados. Depois, vou abordar brevemente a dificuldade de predizer o comportamento de uma ação mostrando a limitação de algoritmos como o da média móvel. Em seguida, introduzirei o conceito de uma Rede Recorrente e uma LSTM. Em sequência, utilizarei uma rede LSTM para a previsão do valor de uma única ação. Por último, utilizarei uma LSTM para fazer a previsão de 4 ações diferentes ao mesmo tempo para verificar se a previsão melhora de forma geral.
Visualizando os dados
O dataset foi baixado do Yahoo Finance em CSV. Ele é composto pelo histórico do valor das ações de quatro empresas durante o período de 08/01/2010 até 07/01/2019. Vamos chamá-las de empresa A, B, C e D.
O primeiro passo é abrir os CSVs com o Pandas. Em uma primeira visão dos dados, temos:
df_A = pd.read_csv(‘data/company_A.csv’)
df_A[‘Date’] = pd.to_datetime(df_A[‘Date’])
df_A.tail()
plt.figure(figsize = (15,10))plt.plot(df_A['Date'], df_A['Close'], label='Company A')
plt.plot(df_B['Date'], df_B['Close'], label='Company B')
plt.plot(df_C['Date'], df_C['Close'], label='Company C')
plt.plot(df_D['Date'], df_D['Close'], label='Company D')plt.legend(loc='best')
plt.show()
Média Móvel
Um algoritmo clássico para este tipo de problema é o da Média Móvel. Ele consiste em utilizar a média de m
dias observados para prever o próximo dia. Vamos utilizar esta técnica na empresa A para um m
de 10 e de 20 dias.
df['MA_window_10'] = df['Close'].rolling(10).mean().shift() #shift so the day we want to predict won't be used
df['MA_window_20'] = df['Close'].rolling(20).mean().shift()
Quando tentamos prever o comportamento do preço de fechamento de 10 em 10 dias com a média móvel, o resultado é:
Note que cada reta vermelha no gráfico representa uma previsão de 10 dias, baseado nos 10 dias anteriores. Por isso elas são descontínuas.
Quando utilizamos um algoritmo um pouco mais elaborado, a Média Móvel Exponencial (EMA), obtemos o seguinte resultado:
Comparando os dois métodos, temos:
Este tipo de abordagem é muito simplista. Isto porque o verdadeiro objetivo é prever n
dias a frente para ver qual será o comportamento da ação. E ambos algoritmos falham nessa função.
Rede Neural Recorrente (RNN)
Antes de avançar para LSTM, primeiro vamos introduzir o conceito de Redes Recorrentes. Elas são redes utilizadas para reconhecer padrões quando os resultados do passado influenciam no resultado atual. Um exemplo disso são as séries temporais, em que a ordem dos dados é muito importante.
Nesta arquitetura, um neurônio tem como entrada seu estado anterior, além das entradas da camada anterior. A imagem abaixo ilustra esta nova modelagem.
Observe que H
representa o estado. Assim, no estado H_1
, o neurônio recebe como parâmetro de entrada X_1
e, além disso, seu estado anterior H_0
. O principal problema desta arquitetura é que os estados mais antigos são esquecidos muito rapidamente. Ou seja, para sequências em que precisamos lembrar além de um passado imediato, as redes RNNs são limitadas.
Rede LSTM
Uma rede LSTM tem origem em uma RNN (Rede Neural Recorrente). Mas ela resolve o problema de memória mudando sua arquitetura.
Nesta nova arquitetura, cada neurônio possui 3 gates, cada um com uma função diferente. São eles:
- Input Gate
- Output Gate
- Forget Gate
Agora, um neurônio LSTM recebe entradas de seu estado anterior, assim como ocorria na Rede Recorrente:
LSTM para uma empresa
Vamos agora utilizar uma Rede LSTM para prever o comportamento da empresa A sozinha.
Primeiro, definimos alguns parâmetros. Queremos prever até n
dias a frente (foward_days
) observando m
dias passados (look_back
). Ou seja, para uma entrada de m
dias passados, a saída da rede será os próximos n
dias. Vamos testar com k
períodos (num_periods
), onde cada período é um conjunto de n
dias previstos.
look_back = 40
forward_days = 10
num_periods = 20
Depois, abrimos o CSV com o Pandas e mantemos apenas as colunas que utilizaremos, que são a data e o preço de fechamento. O gráfico inicial do valor de fechamento da ação A é:
plt.figure(figsize = (15,10))
plt.plot(df)
plt.show()
Em seguida temos que normalizar as entradas, separar os dados em treino/validação e teste e preparar os dados no formato certo para o modelo. Todo este processo está detalhado no meu GitHub.
Agora construímos e treinamos o modelo.
NUM_NEURONS_FirstLayer = 128
NUM_NEURONS_SecondLayer = 64
EPOCHS = 220#Build the model
model = Sequential()
model.add(LSTM(NUM_NEURONS_FirstLayer,input_shape=(look_back,1), return_sequences=True))
model.add(LSTM(NUM_NEURONS_SecondLayer,input_shape=(NUM_NEURONS_FirstLayer,1)))
model.add(Dense(foward_days))
model.compile(loss='mean_squared_error', optimizer='adam')history = model.fit(X_train,y_train,epochs=EPOCHS,validation_data=(X_validate,y_validate),shuffle=True,batch_size=2, verbose=2)
Obtemos o seguinte resultado:
Observando a parte de teste mais de perto:
Repare que cada linha vermelha no gráfico representa uma previsão de 10 dias (forward_days
), baseado nos 40 dias anteriores (look_back
), e há 20 linhas vermelhas, pois escolhemos prever 20 períodos (num_periods
). Por isso elas são descontínuas.
Repetindo o mesmo processo para todas as empresas, o melhor resultado foi o da empresa C:
E mesmo assim, o resultado ainda deixa muito a desejar. Os motivos que podem ter causado este resultado são vários. Dentre eles, temos:
- Apenas o histórico de preços não é suficiente para prever o comportamento de uma ação
- O modelo poderia ser melhorado
LSTM para quatro empresa
Vamos agora utilizar uma Rede LSTM para prever o comportamento das empresas A, B, C e D e comparar com os resultados de cada LSTM das empresas individualmente. A idéia é verificar se ter os dados de um conjunto de empresas melhora a previsão individual de cada uma.
Um ponto importante é verificar se as datas de fechamento dos CSVs das empresas batem. Foi feito um pré-tratamento para garantir que todas as empresas possuíssem as mesmas datas.
Inicialmente, temos os seguintes dados:
Depois, após normalizar os dados e deixá-los na formatação certa de entrada e saída para a rede, treinei o modelo:
NUM_NEURONS_FirstLayer = 100
NUM_NEURONS_SecondLayer = 50
EPOCHS = 200#Build the model
model = Sequential()model.add(LSTM(NUM_NEURONS_FirstLayer,input_shape=(look_back,num_companies), return_sequences=True))
model.add(LSTM(NUM_NEURONS_SecondLayer,input_shape=(NUM_NEURONS_FirstLayer,1)))
model.add(Dense(foward_days * num_companies))
model.compile(loss='mean_squared_error', optimizer='adam')history = model.fit(X_train,y_train,epochs=EPOCHS,validation_data=(X_validate,y_validate),shuffle=True,batch_size=1, verbose=2)
Os resultados obtidos foram:
Olhando apenas para o teste, temos:
Vamos comparar individualmente os resultados previstos dos dados de teste da LSTM de cada empresa com o modelo de LSTM para todas as 4. Com as fotos da esquerda sendo da LSTM individual, e da direita da LSTM das 4 empresas, temos:
Empresa A
Empresa B
Empresa C
Empresa D
Conclusão
Usar apenas uma LSTM não é suficiente para prever o comportamento de uma ação no mercado. Quando usamos apenas o histórico de preço de uma empresa, a previsão fica longe da realidade. Quando usamos o preço de diversas empresas simultaneamente, a previsão se torna mais imprecisa ainda.
Referências
https://www.datacamp.com/community/tutorials/lstm-python-stock-market
http://colah.github.io/posts/2015-08-Understanding-LSTMs/
https://towardsdatascience.com/train-validation-and-test-sets-72cb40cba9e7
https://machinelearningmastery.com/diagnose-overfitting-underfitting-lstm-models/
https://machinelearningmastery.com/reshape-input-data-long-short-term-memory-networks-keras/