Data Science How-To's

Detecção de anomalias em séries temporais

Read this post in other languages:

Como identificar padrões incomuns em dados, que podem revelar problemas críticos ou oportunidades ocultas? A detecção de anomalias ajuda a identificar dados que divergem significativamente do normal. Dados de séries temporais, ou seja, coletados ao longo do tempo, costumam incluir tendências e padrões sazonais. Ocorrem anomalias nesses dados quando esses padrões são rompidos, o que torna a detecção de anomalias uma ferramenta valiosa em setores como vendas, finanças, indústria e saúde.

Como dados de séries temporais têm características únicas, como sazonalidade e tendências, são necessários métodos especializados para detectar anomalias de forma eficaz. Nesta postagem de blog, vamos explorar alguns métodos populares de detecção de anomalias em séries temporais, incluindo a decomposição de STL e a previsão com LSTM, com exemplos detalhados de código para ajudar você a começar a usá-la.

Detecção de anomalias em séries temporais em empresas

Dados de séries temporais são essenciais em muitos ramos de atividade. Muitas empresas registram dados com marcações temporais, permitindo que mudanças sejam analisadas e que os dados sejam comparados ao longo do tempo. Séries temporais são úteis ao comparar uma certa quantidade ao longo de um certo período — por exemplo, comparações de um ano para o outro quando os dados têm características sazonais.

Monitoramento de vendas

Dados de vendas são um dos exemplos mais comuns de dados de séries temporais com diferenças sazonais. Como muitas vendas são afetadas pelos feriados anuais e pela época do ano, é difícil tirar conclusões sobre dados de vendas sem considerar as variações sazonais. Por isso, um método comum de analisar e encontrar anomalias em dados de vendas é a decomposição de STL, que abordaremos em detalhes mais adiante nesta postagem.

Finanças

Dados financeiros, como transações e preços de ações, são exemplos típicos de dados de séries temporais. No setor financeiro, é prática comum analisar e detectar anomalias nesses dados. Por exemplo, pode-se usar modelos de previsão de séries temporais em transações automáticas. Usaremos uma previsão de série temporal para identificar anomalias em dados de ações mais adiante nesta postagem.

Indústria

Outro caso de uso da detecção de anomalias em séries temporais é o monitoramento de falhas em linhas de produção. Máquinas costumam ser monitoradas, disponibilizando dados de séries temporais. É essencial poder notificar a administração sobre falhas em potencial e a detecção de anomalias desempenha um papel-chave nisso.

Medicina e saúde

Em Medicina e saúde, sinais vitais humanos são monitorados e pode-se detectar anomalias. Isso já é muito importante na pesquisa médica, mas é crítico no diagnóstico. Se um paciente hospitalizado tiver sinais vitais anômalos e não for tratado imediatamente, os resultados podem ser fatais.

Por que é importante usar métodos especiais na detecção de anomalias em séries temporais?

Dados de séries temporais são especiais, no sentido de que às vezes não podem ser tratados como outros tipos de dados. Por exemplo, se aplicarmos uma divisão de teste de treinamento a dados de séries temporais, a natureza sequencial desses dados significa que não podemos embaralhá-los. Isso também se aplica ao usarmos dados de séries temporais em modelos de aprendizado profundo. É comum usar uma rede neural recorrente (RNN) para levar em conta essa relação sequencial e os dados de treinamento são introduzidos como janelas de tempo, o que preserva a sequência de eventos dentro delas.

Dados de séries temporais também são especiais porque costumam ser sazonais e ter tendências que não podemos ignorar. Essa sazonalidade pode se manifestar em ciclos de 24 horas, 7 dias ou 12 meses, para citar apenas algumas possibilidades. Só podem ser determinadas anomalias depois de levar em conta a sazonalidade e as tendências, como você verá no nosso exemplo abaixo

Métodos usados para a detecção de anomalias em séries temporais

Como dados de séries temporais são especiais, há métodos específicos para detectar anomalias neles. Dependendo do tipo dos dados, alguns dos métodos e algoritmos que mencionamos na nossa postagem anterior sobre detecção de anomalias podem ser usados em dados de séries temporais. Porém, com esses métodos, a detecção de anomalias pode não ser tão robusta quanto ao usar métodos projetados especificamente para dados de séries temporais. Em alguns casos, pode ser usada uma combinação de métodos de detecção para reconfirmar os resultados da detecção e evitar falsos positivos ou negativos.

Decomposição de STL

Uma das maneiras mais populares de usar dados sazonais de séries temporais é a decomposição de STL — sigla em inglês para “decomposição de tendências sazonais usando LOESS (suavização estimada localmente de gráficos de dispersão)”. Nesse método, decompõe-se uma série temporal usando uma estimativa da sazonalidade (com o período fornecido ou determinado através de um algoritmo), uma tendência (estimada) e um resíduo (o ruído nos dados). A biblioteca de Python statsmodels tem ferramentas para decomposição de STL.

Decomposição de STL

Detecta-se uma anomalia quando o resíduo ultrapassa um determinado limiar. 

Usando a decomposição de STL em dados de colmeias de abelhas

Em uma postagem anterior no blog, exploramos a detecção de anomalias em colmeias de abelhas usando os métodos OneClassSVM e IsolationForest

Neste tutorial, vamos analisar dados de colmeias como uma série temporal, usando a classe STL, fornecida pela biblioteca statsmodels. Para começar, configure o seu ambiente usando o arquivo requirements.txt

1. Instale a biblioteca

Já que vimos usando apenas o modelo fornecido pelo Scikit-learn, precisaremos instalar a biblioteca statsmodels a partir do PyPI. Isso é fácil de fazer no PyCharm.

Vá até a janela Python Packages (clique no ícone na parte inferior esquerda do IDE) e digite “statsmodels” na caixa de pesquisa.

Statsmodels no PyCharm

Você pode ver todas as informações sobre o pacote no lado direito. Para instalá-lo, basta clicar em Install package.

2. Criação de um notebook do Jupyter

Para investigar o conjunto de dados mais a fundo, vamos criar um notebook do Jupyter para aproveitarmos as ferramentas do ambiente do PyCharm para esses notebooks.

Criação de um notebook do Jupyter no PyCharm

Vamos importar o pandas e carregar o arquivo em .csv.

import pandas as pd

df = pd.read_csv('../data/Hive17.csv', sep=";")
df = df.dropna()
df
Importação do pandas no PyCharm

3. Inspeção dos dados como gráficos

Agora podemos inspecionar os dados como gráficos. Aqui, gostaríamos de ver a temperatura da colmeia 17 ao longo do tempo. Clique em Chart view no inspetor de dataframes e selecione T17 como o eixo Y nas configurações da série.

Inspeção dos dados como gráficos no PyCharm

Quando expressa na forma de uma série temporal, a temperatura tem muitos altos e baixos. Isso indica um comportamento periódico, provavelmente devido ao ciclo de dias e noites. Portanto, pode-se presumir com confiança que há um período de 24 horas para a temperatura. 

Depois, há uma tendência de queda da temperatura ao longo do tempo. Se você inspecionar a coluna DateTime, verá que as datas estão no período de agosto a novembro. Como a página de Kaggle do conjunto de dados indica que os dados foram coletados na Turquia, a transição do verão para o outono explica nossa observação de que a temperatura está caindo com o tempo.

4. Decomposição da série temporal

Para compreendermos a série temporal e detectarmos anomalias, faremos uma decomposição de STL, importando a classe STL da statsmodels e alimentando-a com nossos dados de temperatura.

from statsmodels.tsa.seasonal import STL

stl = STL(df["T17"], period=24, robust=True) 
result = stl.fit()

Para a decomposição funcionar, teremos que informar um período. Como já mencionado, podemos presumir um ciclo de 24 horas.

Segundo a documentação, a classe STL decompõe uma série temporal em três componentes: de tendência, sazonal e residual. Para termos uma visão mais clara do resultado decomposto, podemos usar o método incorporado plot:

result.plot()
Decomposição da série temporal

Você pode ver que os gráficos Trend e Season parecem se alinhar com nossas premissas acima. Porém, estamos interessados no gráfico residual, embaixo, que é a série original sem as alterações sazonais e de tendência. Qualquer valor residual extremamente alto ou baixo indica uma anomalia.

5. Limiar de anomalia

Agora, gostaríamos de determinar quais valores residuais consideraremos anormais. Para isso, podemos olhar o histograma dos valores residuais.

result.resid.plot.hist()
Limiar de anomalia no PyCharm

Esta pode ser considerada uma distribuição normal em torno de 0, com uma cauda longa acima de 5 e abaixo de −5. Então, vamos determinar um limiar de 5.

Para mostrar as anomalias na série temporal original, podemos colorir todas elas em vermelho no gráfico, assim:

import matplotlib.pyplot as plt

threshold = 5
anomalies_filter = result.resid.apply(lambda x: True if abs(x) > threshold else False)
anomalies = df["T17"][anomalies_filter]

plt.figure(figsize=(14, 8))
plt.scatter(x=anomalies.index, y=anomalies, color="red", label="anomalies")
plt.plot(df.index, df['T17'], color='blue')
plt.title('Temperatures in Hive 17')
plt.xlabel('Hours')
plt.ylabel('Temperature')
plt.legend()
plt.show()
Anomalias na série temporal original no PyCharm

Sem a decomposição de STL, é muito difícil identificar essas anomalias em uma série temporal que consista de períodos e tendências.

Previsão com LSTM

Uma outra maneira de detectar anomalias em dados de séries temporais é fazer uma previsão usando métodos de aprendizado profundo para estimar o desfecho de pontos de dados. Se uma estimativa for muito diferente do ponto real de dados correspondente, isso pode ser um sinal de dados anômalos.

Um algoritmo popular de aprendizado profundo para fazer previsões em dados sequenciais é o modelo “long short-term memory” (LSTM), um tipo de rede neural recorrente (RNN). Esse modelo tem portões de entrada, esquecimento e saída, que são matrizes numéricas. Isso garante que as informações importantes sejam passadas para a próxima iteração dos dados.

Célula de LSTM

Como os dados de uma série temporal são sequenciais, o que significa que a ordem dos pontos de dados é sequencial e não deve ser embaralhada, o modelo LSTM de aprendizado profundo é eficaz para prever um desfecho em um determinado momento. Essa previsão pode ser comparada com os dados reais e pode-se determinar um limiar para decidir se os dados reais são anômalos.

Usando a previsão com LSTM em preços de ações

Agora, vamos criar um novo projeto do Jupyter para detectar quaisquer anomalias no preço das ações da Apple nos últimos 5 anos. O conjunto de dados de preços de ações contém os dados mais atualizados. Se você também quiser fazer isso para acompanhar esta postagem, pode baixar o conjunto de dados que estamos usando.

1. Criação de um projeto do Jupyter

Ao iniciar um novo projeto, você pode optar por criar um projeto do Jupyter, que é otimizado para ciência de dados. Na janela New Project, você pode criar um repositório de Git e determinar qual instalação do conda deve ser usada para gerenciar o seu ambiente.

Criação de um projeto do Jupyter no PyCharm

Depois de criar o projeto, você verá um notebook de exemplo. Vá em frente e crie um novo notebook do Jupyter para este exercício.

Um notebook de exemplo no PyCharm

Depois disso, vamos configurar o requirements.txt. Precisaremos do pandas, do matplotlib e do PyTorch, que é chamado de “torch” no PyPI. Como o PyTorch não está incluído no ambiente do conda, o PyCharm nos avisará que esse pacote está faltando. Para instalá-lo, clique no ícone da lâmpada e selecione Install all missing packages.

Instalação de todos os pacotes faltando no PyCharm

2. Carregamento e inspeção dos dados

Em seguida, vamos colocar nosso conjunto de dados apple_stock_5y.csv na pasta de dados e carregá-lo como uma dataframe do pandas para inspecioná-lo.

import pandas as pd
 
df = pd.read_csv('data/apple_stock_5y.csv')
df

Com a tabela interativa, podemos ver facilmente se está faltando algum dado.

Não há dados faltando, mas temos outro problema: gostaríamos de usar o preço Close/Last, mas seu tipo de dados não é numérico. Vamos fazer uma conversão e inspecionar nossos dados de novo:

df["Close/Last"] = df["Close/Last"].apply(lambda x: float(x[1:]))
df

Agora podemos inspecionar o preço com a tabela interativa. Clique no ícone de gráfico à esquerda e será criado um gráfico. Como padrão, esse gráfico usará Date como o eixo X e Volume como eixo Y. Como gostaríamos de inspecionar o preço Close/Last, acesse as configurações clicando no ícone da engrenagem à direita e selecione Close/Last como o eixo Y.

3. Preparação dos dados de treinamento para LSTM

Agora, temos que preparar os dados de treinamento a serem usados no modelo de LSTM. Precisamos preparar uma sequência de vetores (a partir do eixo X), cada um representando uma janela de tempo, para prevermos o próximo preço. Este formará outra sequência (alvo no eixo Y). Aqui, podemos determinar a duração dessa janela de tempo através da variável lookback. O código a seguir cria as sequências de X e Y, que serão então convertidas em tensores do PyTorch:

import torch

lookback = 5
timeseries = df[["Close/Last"]].values.astype('float32')

X, y = [], []
for i in range(len(timeseries)-lookback):
    feature = timeseries[i:i+lookback]
    target = timeseries[i+1:i+lookback+1]
    X.append(feature)
    y.append(target)
    
X = torch.tensor(X)
y = torch.tensor(y)

print(X.shape, y.shape)

Em linhas gerais, quanto mais longa for a janela, maior será o nosso modelo, pois o vetor de entrada será maior. Porém, com uma janela mais longa, a sequência de entradas será menor. Portanto, a determinação dessa janela de “lookback” é um balanceamento. Vamos começar com 5, mas fique à vontade para experimentar outros valores e ver as diferenças.

4. Criação e treinamento do modelo

Podemos criar o modelo antes de treiná-lo usando o método nn module no PyTorch para criar uma classe. Esse método fornece blocos de construção, tais como diferentes camadas de redes neurais. Neste exercício, criaremos uma camada simples de LSTM, seguida de uma camada linear:

import torch.nn as nn

class StockModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.lstm = nn.LSTM(input_size=1, hidden_size=50, num_layers=1, batch_first=True)
        self.linear = nn.Linear(50, 1)
    def forward(self, x):
        x, _ = self.lstm(x)
        x = self.linear(x)
        return x

Em seguida, treinaremos nosso modelo, mas antes disso, precisaremos criar um otimizador, uma função de perda usada para calcular a perda entre o valor real e o previsto de Y, e um carregador de dados para alimentar o modelo com nossos dados de treinamento:

import numpy as np
import torch.optim as optim
import torch.utils.data as data

model = StockModel()
optimizer = optim.Adam(model.parameters())
loss_fn = nn.MSELoss()
loader = data.DataLoader(data.TensorDataset(X, y), shuffle=True, batch_size=8)

O carregador de dados pode embaralhar a entrada, pois já criamos as janelas de tempo. Isso preserva a relação sequencial em cada janela.

O treinamento é feito através de um loop for iterado para cada período. A cada 100 períodos, imprimiremos a perda e observaremos enquanto o modelo converge:

n_epochs = 1000
for epoch in range(n_epochs):
    model.train()
    for X_batch, y_batch in loader:
        y_pred = model(X_batch)
        loss = loss_fn(y_pred, y_batch)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
    if epoch % 100 != 0:
        continue
    model.eval()
    with torch.no_grad():
        y_pred = model(X)
        rmse = np.sqrt(loss_fn(y_pred, y))
    print(f"Epoch {epoch}: RMSE {rmse:.4f}")

Começamos com 1000 períodos, mas o modelo converge bem rapidamente. Fique à vontade para experimentar outros números de períodos no treinamento, para obter o melhor resultado.

Períodos para o treinamento

No PyCharm, uma célula que demore algum tempo para ser executada mandará uma notificação de quanto tempo falta e dará um atalho para a célula. Isso é muito útil ao treinar modelos de aprendizado de máquina, especialmente de aprendizado profundo, em notebooks do Jupyter.

5. Criação de um gráfico da previsão e localização de erros

Depois, vamos criar a previsão e fazer um gráfico dela, juntamente com a série temporal real. Observe que teremos que criar uma série 2D np para coincidir com a série temporal real. Esta aparecerá em azul, enquanto a série prevista aparecerá em vermelho.

import matplotlib.pyplot as plt

with torch.no_grad():
    pred_series = np.ones_like(timeseries) * np.nan
    pred_series[lookback:] = model(X)[:, -1, :]

plt.plot(timeseries, c='b')
plt.plot(pred_series, c='r')
plt.show()
Criação de um gráfico da previsão e localização de erros

Se você observar com atenção, verá que a previsão e os valores reais não se alinham perfeitamente. Porém, a maioria das previsões é razoável.

Para inspecionar os erros mais de perto, podemos criar uma série dos erros e usar a tabela interativa para observá-los. Desta vez, vamos usar o erro absoluto.

error = abs(timeseries-pred_series)
error

Use as configurações para criar um histograma com o valor do erro absoluto como o eixo X e a contagem do valor como o eixo Y.

6. Determinação do limiar de anomalia e visualização

A maioria dos pontos terá um erro absoluto de menos de 6. Portanto, podemos determinar esse valor como o limiar de anomalia. De forma semelhante ao que fizemos com as anomalias na colmeia, podemos traçar os pontos de dados anômalos no gráfico.

threshold = 6
error_series = pd.Series(error.flatten())
price_series = pd.Series(timeseries.flatten())

anomalies_filter = error_series.apply(lambda x: True if x > threshold else False)
anomalies = price_series[anomalies_filter]

plt.figure(figsize=(14, 8))
plt.scatter(x=anomalies.index, y=anomalies, color="red", label="anomalies")
plt.plot(df.index, timeseries, color='blue')
plt.title('Closing price')
plt.xlabel('Days')
plt.ylabel('Price')
plt.legend()
plt.show()
Traçando os pontos de dados anômalos no gráfico

Resumo

É comum usar dados de séries temporais em muitas aplicações, inclusive de negócios e de pesquisa científica. Devido à natureza sequencial desses dados, usam-se métodos e algoritmos especiais para ajudar a detectar anomalias neles. Nesta postagem de blog, demonstramos como identificar anomalias usando a decomposição de STL para eliminar a sazonalidade e as tendências. Também demonstramos como usar o aprendizado profundo e o modelo LSTM para comparar a estimativa prevista com os dados reais, com a finalidade de detectar anomalias.

Detecte anomalias com o PyCharm

Com um projeto do Jupyter no PyCharm Professional, você pode facilmente organizar a sua detecção de anomalias, usando diversos arquivos de dados e notebooks. Podem ser gerados gráficos de saída, muito acessíveis no PyCharm, para inspecionar anomalias. Outros recursos, como sugestões de complementação automática, tornam muito fácil a navegação pelos modelos do Scikit-learn e configurações de gráficos da Matplotlib.

Incremente os seus projetos de ciência de dados usando o PyCharm e confira os recursos que ele oferece para otimizar o seu fluxo de trabalho em ciência de dados.

Artigo original em inglês por:

Cheuk Ting Ho

Cheuk Ting Ho

image description

Discover more