Um breve tutorial sobre Séries Temporais Nebulosas

Petrônio Silva
Ensina.AI
Published in
13 min readJul 25, 2018

Introdução

As séries temporais encontram aplicações nos mais diversos segmentos, sendo indispensáveis em áreas como a engenharia, medicina, economia, meteorologia, etc.

Existem inúmeras ferramentas de análise e previsão de séries temporais, desde as consagradas técnicas estatísticas (ARMA, ARIMA, etc), passando pelas técnicas de inteligência computacional (Redes neurais recorrentes, LSTM, GRU, etc). Não há modelo perfeito, nem o que vamos apresentar aqui. Mas alguns pontos distinguem a técnica das Séries Temporais Nebulosas das demais:

  • Analisabilidade
  • Atualizabilidade
  • Manutenabilidade
  • Simplicidade
  • Escalabilidade

Vou aqui presumir que o leitor é um não iniciado nas áreas de inteligência computacional e séries temporais e introduzir os principais conceitos dessas áreas. Logo em seguida falamos especificamente sobre séries temporais nebulosas e sobre a biblioteca pyFTS. E então? Partiu?

O que são Conjuntos Nebulosos?

Fonte

Se você já conhece sobre Lógica Fuzzy e Conjuntos Nebulosos, pode seguir para a próxima seção, essa daqui é bem introdutória!

A teoria lógica/matemática clássica define os conjuntos como uma dicotomia: qualquer elemento está OU não está dentro do conjunto. Não há meio termo! A pertinência de um elemento em relação a um conjunto é um valor booleano, ou seja: um valor no conjunto {0 , 1}, impondo a cada conjunto um limite muito rígido.

Essa forma dicotômica de se pensar é incômoda para o ser humano pois inúmeras realidades não são assim. Teremos dificuldades quando tentarmos classificar as pessoas em categorias com limites rígidos, por exemplo: peso = {magro, esbelto, gordo}, idade = {criança, adolescente, jovem, adulto, idoso}, altura = {baixo, mediano, alto}. Se formos descrever alguém usando esses conceitos em pelo menos um deles acharemos que a pessoa poderia estar numa categoria intermediária entre esses dois valores. Mas os conjuntos clássicos/rígidos não nos dão essa flexibilidade.

A Lógica Fuzzy, proposta por Zadeh(1965), propõe uma dualidade ao invés dessa dicotomia: um determinado elemento pode pertencer E simultaneamente não pertencer a um mesmo conjunto em determinados níveis, de tal forma que a pertinência seja um valor no intervalo [0,1]. Os conjuntos nebulosos não tem limites rígidos e geralmente são sobrepostos, dessa forma — usando o exemplo anterior — eu posso ser meio alto e meio mediano, ou 90% mediano e 10% alto por exemplo.

Dado que eu tenho uma Variável Numérica X, tal que X ∈ ℝ — altura por exemplo — o seu Universo de Discurso U é a amplitudade de valores que essa variável pode assumir, de forma que U = [ min(X), max(X) ].

Uma Variável Linguística à é a transformação dos valores da variável numérica X em um conjunto de palavras/termos linguísticos (o que chamamos de fuzzificação). Cada palavra/termo linguístico é um conjunto nebuloso ã ∈ Ã, e cada conjunto nebuloso ã está associado à uma função μ (letra grega mu), tal que μã: X →[0,1] (isso quer dizer que μã recebe um valor de X e retorna um valor no intervalo [0,1]).

Voltemos à variável numéria altura, medida em centímetros. Definimos U = [20,220]. Vamos definir nossa variável linguística à como:

à = {“muito pequeno”, “pequeno”, “baixo”, “mediano”, “alto”, “muito alto” }

Percebe o que estamos querendo fazer? Queremos parar de trabalhar com os dados numéricos de X e começar a trabalhar com o vocabulário expresso por Ã, e para tal precisamos mapear os valores de X para cada conjunto ã ∈ Ã.

Faremos isso divindo U em 6 intervalos sobrepostos, um para cada conjunto ã ∈ Ã. Para cada intervalo nós associaremos uma função de pertiência μã(X). Existem vários tipos de funções de pertinência, mas para simplificar aqui vamos usar a função de pertinência triangular. A função de pertinência triangular pode ser definida como:

def triangular(x, a, b, c):
return max( min( (x-a)/(b-a), (c-x)/(c-b) ), 0 )

Em que a, b e c formam um triângulo onde a é a base esquerda, b o ponto mais alto e c a base direita. Quando um valor x é igual a b dizemos que ele está 100% dentro do conjunto nebuloso( ou seja: a pertinência é igual a 1). Do contrário, a pertinência vai subindo de 0 até 1 no intervalo entre a e b, e diminuindo de 1 até 0 no intervalo entre b e c. Se x for menor que a ou maior do que c então dizemos que x está completamente fora do conjunto nebuloso.

Como ficariam então nossos conjuntos para a variável linguística Ã?

Fonte

Vamos supor que uma pessoa tenha 163cm de altura. Para a variável à ela seria .27 mediano e .73 alto, ou seja:

μ_mediano(163) = triangular(163, 90, 130, 175) = 0.2666666…

μ_alto(163) = triangular(163, 130, 175,220) = 0.7333…

Confira esses códigos aqui!

Mas isso é só o começo de uma enorme área de conhecimento. Se você quiser um pouco mais de detalhes, recentemente eu apresentei um minicurso sobre Lógica Fuzzy no IFNMG e a apresentação está aqui e o código aqui.

Mas estamos aqui para falar de séries temporais, certo? Então vamos falar sobre o tempo!

O que são Séries Temporais?

Se você já conhece sobre séries temporais, pode seguir para a próxima seção, essa daqui é bem introdutória!

As séries temporais são conjuntos de dados que representam o comportamento de uma (ou mais) variável aleatória ao longo do tempo, e sua principal característica é que os registros sucessivos dessa variável não são independentes entre si e a sua análise deve levar em conta a ordem em que foram coletadas.

Conforme Ehlers (2009), “as observações vizinhas são dependentes e estamos interessados em analisar e modelar esta dependência”. O quê isso significa? Que para predizer valores futuros da série temporal eu uso valores passados/defasados da mesma série, o que é chamado de lagged values, lagged variables ou simplesmente lags.

Um exemplo simples é o modelo regressivo AR(p). Dada uma série temporal X(t), em que t indica um momento no tempo, se eu for utilizar apenas um lag na previsão eu tenho um AR(1), que seria algo como X(t) = α·X(t-1) + ε, onde X(t-1) é o valor defasado, α é um coeficiente ajustado por métodos estatísticos e ε é o ruído/erro aleatório, que indica a incerteza associada a essa previsão, ε ~𝓝(0,1). Se eu quiser utilizar os dois últimos lags eu terei um AR(2), que seria algo como X(t) = α0·X(t-1) +α1·X(t-2) +ε.

Qual a quantidade da lags e quais os lags que devem ser usados? Para isso é preciso estudar os componentes da série temporal e e analisar suas propriedades, utilizando gráficos como ACF e PACF. Usualmente séries temporais são modeladas (usando um modelo aditivo) em alto nível como:

X(t) =C(t) + T(t) + S(t) + R(t)

Fonte

Onde:

  • t é o índice de tempo;
  • X(t) é uma estimativa pontual da série no tempo t;
  • C(t) é o componente cíclico, as flutuações previsíveis de curto/curtíssimo prazo;
  • T(t) é o componente de tendência, que indica o comportamento de longo prazo da série. Geralmente a tendência é de crescimento/subida ou redução/descida, do contrário diz-se que a série não possui tendência;
  • S(t) é o componente sazonal, que são flutuações periódicas de médio e longo prazo. Um bom exemplo de componente sazonal são… adivinha? As estações do ano! São intervalos de 4 meses que têm uma característica muito própria e se repetem anualmente — o que torna qualquer variável com essa sazonalidade muito mais previsível.
  • R(t) é o componente de ruído, um valor aleatório com média e variância constantes. Esse ruído aleatório não é previsível!

Nem todas as séries temporais possuem tendência ou sazonalidade. Algumas séries são ditas estacionárias, significando que sua média são (mais ou menos) constantes. Já nas séries não estacionárias a média muda ao longo do tempo. Já nas séries homocedásticas a variância é constante e nas séries heterocedásticas a variância muda ao longo do tempo. Para resumir: séries temporais estacionárias e homocedásticas são “bem comportadas” e mais simples de se prever e séries não-estacionárias e com heterocedasticidade são bem mais complexas. Nesse último caso podemos aplicar transformações matemáticas (como diferenciação, Box-Cox, etc) para tornar a série estacionária e homocedástica.

Dito isso, onde os conjuntos nebulosos entram nessa história?

O que são Séries Temporais Nebulosas?

Fonte

A utilização de conjuntos nebulosos para modelagem e predição de séries temporais surge quase intuitivamente, primeiro baseando-se na capacidade dos modelos nebulosos de aproximar funções, mas também na legibilidade das regras utilizando variáveis linguísticas que as tornam mais acessíveis à analise de especialistas.

Os trabalhos pioneiros em séries temporais nebulosas são de Song e Chisson (1993) mas aqui apresentamos a evolução de Chen (1996). A idéia é de dividir o Universo de Discurso da série temporal em áreas (os conjuntos nebulosos), e aprender como cada área se comporta (extraindo regras através dos padrões temporais da série). As regras desses modelo dizem como as áreas se relacionam ao longo do tempo, como os valores pulam de um lugar para o outro. Em outras palavras: vamos criar uma variável linguística para representar a série temporal numérica, e essas áreas serão as termos linguísticos da nossa variável.

Quando criamos uma variável linguística para representar o universo de discurso, criamos um “vocabulário”, e então a série fuzzificada é composta de palavras desse vocabulário. A sequência dessas palavras — as sentenças ou frases — são os padrões que precisam serem aprendidos.

Para facilitar, vou dividir a metodologia de séries temporais nebulosas em dois procedimentos: treinamento e inferência.

Procedimento de Treinamento do Modelo

Utilizaremos para esse tutorial uma série temporal muito conhecida dos primeiros trabalhos de séries temporais nebulosas: as matrículas da Universidade de Alabama (Enrollments):

1. Definição do Universo de Discurso U
Primeiro é necessário conhecer o universo de discuro U dos dados de treinamento U = [min(X), max(X)]. Normalmente extrapolamos um pouco esses limites (para baixo e para cima) em 20%.

2. Criação da variável linguística
Precisamos agora particionar U em vários intervalos sobrepostos e para cada intervalo será criado um conjunto nebuloso. O número de intervalos é um dos parâmetros mais importantes das Séries Temporais Nebulosas e ele impactará diretamente na precisão do modelo.

Além do número de conjuntos nebulosos a forma de particionar U também tem grande impacto na acurácia. Aqui adotaremos um particionamento em grade (Grid Partitioning) em que todas as partições têm o mesmo tamanho e formato. Para conhecer outros tipos de particionamento veja aqui. Para a nossa série temporal de exemplo vamos usar 10 conjuntos nebulosos de forma que a variável linguística seja à = { A0, A1, …, A9 }.

3. Fuzzificação
Agora os valores numéricos da série temporal X(t) serão convertidos em valores nebulosos da variável linguística Ã, dando origem à série temporal nebulosa F(t). É bom lembrar que os conjuntos nebulosos de à são sobrepostos, ou seja, para cada x ∈ X(t) é provável que haja mais de um conjunto nebuloso Ai ∈ Ã. No modelo específico de Chen ele simplifica as coisas escolhendo apenas o conjunto que tiver a maior pertinência.

Para os nossos dados de exemplo a série temporal nebulosa será F(t) = { A1, A2, A2, A3, A4, A4, A4, A4, A5, A5, A5, A4, A4, A4, A4, A4, A5, A7, A8, A8, A8, A7 }.

4. Criando os padrões temporais
Um padrão temporal indica dois conjuntos nebulosos que apareceram em sequência na série temporal nebulosa F(t) e têm o formato Precedente →Consequente, onde o precedente indica um conjunto no tempo t e o consequente o cojunto que apareceu no tempo t+1.

Para os dados fuzzificados no exemplo anterior os padrões serão:
A1→A2, A2→A2, A2→A3, A3→A4, A4→A4, …, A8→A8, A8→A7

5. Geração das Regras
As regras do nosso modelo também tem o formato Precedente →Consequente. Dados os padrões temporais da etapa anterior, eles são agrupados pelo precedente. Cada regra do nosso modelo vai agrupar todos os padrões que possuam o mesmo precedente, unindo todos os consequentes.
A1 →A2
A2 →A2,A3
A3 →A4
A4 →A4,A5
A5 →A4,A5,A7
A7 →A8
A8 →A7,A8

Visualização das regras

Essas regras são o nosso modelo de Séries Temporais Nebulosas. Elas descrevem como a nossa série temporal se comporta e, se ela for suficientemente estacionária (bem comportada) podemos usar esse modelo para prever os próximos passos da série.

Um modelo simples e legível como esse tem outras vantagens:

a) é muito fácil de se paralelizar e/ou distribuir, o que o torna muito atrativo para grandes volumes de dados (leia: Big Data!);

b) é muito fácil de se atualizar, o que o torna atrativo para dados que mudam frequentemente.

E como se usa essas regras para prever os próximos valores?

Procedimento de Inferência

Dado que conhecemos o valor numérico para o instante t, x(t) ∈ X(t), queremos agora prever o próximo instante, x(t+1).

1. Fuzzificar os valores de entrada
O valor de entrada x(t) será convertidos em valores nebulosos da variável linguística Ã, gerando o valor f(t). Como no processo de treinamento apenas o conjunto de maior pertinência é escolhido.

Para os dados de exemplo, para t = 1992 o valor é x(t) = 18876. Fuzzificando x(t) o conjunto de maior pertinência é A7, logo f(t) = A7.

2. Encontrar as regras compatíveis
Localiza-se a regra cujo precedente seja igual a f(t). O consequente da regra será a previsão nebulosa para t+1, ou seja, f(t+1).

Para f(t) = A7 temos a regra A7→A8. Logo f(t+1) = A8.

3. Defuzzificação
Agora é necessário converter f(t+1) para um valor numérico. Para tal usamos o método do centro de massa, onde o valor numérico é igual à média dos centros dos conjuntos nebulosos de f(t+1), ou seja x(t+1) = n⁻¹∑ Ai, para i = 0..n-1 e n igual ao número de conjuntos em f(t+1).

Como f(t+1) só tem um conjunto, então x(t+1) = 19366.46.

Esse modelo apresentado é muito simples, e devo dizer, arcaico (é de 1996!!!). Mas ele é um bom guia para entender como as séries temporais nebulosas funcionam. Atualmente os métodos mais precisos usam mais lags (esse só usou t-1), usam pesos nos consequentes das regras (a partir do trabalho de Yu (2005)), otimizadores para encontrar o melhor número de coonjuntos, formato dos conjuntos, lags, etc. Nos próximos tutoriais vamos nos aprofundar em modelos mais avançados.

Todos os procedimentos explicados aí acima podem ser reproduzidos nesta notebook no Google Colab.

Agora é hora de colocar a mão na massa e brincar um pouco!!!! :-)

A biblioteca pyFTS

A biblioteca pyFTS: Fuzzy Time Series for Python foi desenvolvida no MINDS — Machine Intelligence and Data Science da UFMG e é voltada para estudantes, pesquisadores, cientistas de dados e outros profissionais que desejem explorar os métodos de Séries Temporais Nebulosas. A pyFTS é um projeto em contínuo desenvolvimento e todas as contribuições são bem vindas!

Neles , pré-processamento de dados, visualização, etc.

1. Dados de teste

No pacote pyFTS.data há diversos datasets de séries temporais comuns como TAIEX, NASDAQ, S&P 500, passengers, etc. Cada dataset desse tem suas próprias características — uns são monovariados, outros multivariados, etc — mas todos eles têm basicamente duas funções:

  • get_data(): retorna a série monovariada
  • get_dataset(): retorna a série multivariada
from pyFTS.data import Enrollmentstrain = Enrollments.get_data()

2. Transformações de dados

No pacote pyFTS.common.Transformations Diversas transformações de dados podem ser utilizadas para o pré-processamento e/ou pós-processamento de dados, o que impacta diretamente no particionamento do universo de discurso.

from pyFTS.common import Transformations
tdiff = Transformations.Differential(1)

3. Particionadores

No pacote pyFTS.partitioners se encontram os particionadores. Cada particionador tem a função de representar a variável linguística, criando as partições do Universo de Discurso e seus conjuntos nebulosos. Em todos eles no mínimo dois parâmetros são necessários:

  • data: os dados de treino da série temporal;
  • npart: o número mínimo de partições/conjuntos nebulosos;
  • mf: a função de pertinência que será usada, que por padrão é a triangular (trimf). As diversas funções de pertinência se encontram em pyFTS.common.Membership;
  • transformation: se alguma transformação for utilizada na série, ela deve ser informada aqui.
from pyFTS.partitioners import Grid, Entropy, Util as pUtilfs = Grid.GridPartitioner(data=train, npart=20)print(fs)

Podemos explorar diversas alternativas de particionadores de Universo de Discurso, cada um deles com seus próprios algoritmos.

4. Modelos de Séries Temporais Nebulosas

Os diversos métodos se encontram no pacote pyFTS.models. Na biblioteca pyFTS todos os modelos de Séries Temporais Nebulosas herdam da classe common.fts.FTS. Para o usuário final há dois métodos principais

  • FTS.fit(data, partitioner=fs) : Treina o modelo a partir dos dados de treinamento data e da variável linguística já construída pelo particionador fs. É aconselhável estudar o help dessa função pois aqui já é possível fazer o treinamento distribuído utilizando clusters dispy. Em breve uma versão com pySpark também será disponibilizada.
  • FTS.predict(data, type=’point’, steps_ahead=1): Utiliza o modelo já treinado para fazer predições a partir dos lags contidos em data. Há três tipos de previsão possíveis, indicados pelo parâmetro ‘type’: ‘point’ (padrão), ‘interval’ e ‘distribution’. Deve se ter atenção ao escolher o método pois nem todos eles trabalham com todos esses tipos. Por fim o parâmetro ‘steps_ahead’ indica o horizonte de predição, ou quantos passos à frente deseja-se prever.
from pyFTS.models import chenmodelo = chen.ConventionalFTS(partitioner=fs)
modelo.fit(train)
print(modelo)predicoes = modelo.predict(test)

Há muitos outros modelos para serem explorados, dê uma olhada nos códigos de exemplo.

Concluindo…

Agora que você já sabe mais ou menos como o bicho é, podemos seguir adiante. Na segunda parte deste tutorial exploramos os modelos de séries temporais nebulosas ponderadas, modelos de alta ordem, sazonais, multivariados e então aplicamos essas técnicas na predição de energia solar. Não deixe de ler!

Nos vemos nas próximas!

REFERÊNCIAS

Chen Shyi-Ming. Forecasting enrollments based on fuzzy time series. Fuzzy sets and systems, vol. 81, number 3, pp. 311–319, 1996. URL: https://doi.org/10.1016/0165–0114(95)00220–0. . Acesso em 25/07/2018.

Ehlers, R.S. Análise de séries temporais. Departamento de Estatística, Universidade Federal do Paraná. URL: http://conteudo.icmc.usp.br/pessoas/ehlers/stemp/stemp.pdf. Acesso em 25/07/2018.

Morettin, P.A. e Toloi, C.M.C. Análise de Séries Temporais. Blucher, 2004

Silva, P. C. L. pyFTS: Fuzzy Time Series for Python, v4.0. URL: https://doi.org/10.5281/zenodo.1194859. Acesso em 25/07/2018.

Song, Qiang; Chissom, Brad S. Fuzzy time series and its models. Fuzzy sets and systems, vol. 54, number 3, pp. 269–277, 1993. URL: https://doi.org/10.1016/0165-0114(93)90372-O. Acesso em 25/07/2018.

Yu, Hui-Kuang, Weighted fuzzy time series models for TAIEX forecasting. Physica A: Statistical Mechanics and its Applications, vol. 349, number 3, pp. 609–624, 2005. URL: https://doi.org/10.1016/j.physa.2004.11.006. Acesso em 25/07/2018.

Zadeh, L. A. Fuzzy sets. Information and Control 8 (3) 338–353, 1965. URL: https://doi.org/10.1016/S0019-9958(65)90241-X. Acesso em 25/07/2018.

--

--

Petrônio Silva
Ensina.AI

Ph.D. in Computacional Intelligence, Professor at IFNMG, data science and machine intelligence enthusiast at MINDS and {ci∂ic}