Série: Deep Learning With Python — Parte 6 (3)

Original article was published by Preta Tech — (Carol Silva) on Deep Learning on Medium


Série: Deep Learning With Python — Parte 6 (3)

Vamos para a segunda parte do capítulo 6 do livro Deep Learning with Python do François Chollet.

Porém, antes de tudo:

1: Você pode adquirir o livro aqui!

2: Não esquece de olhar os hiperlinks e as referências ao final do capítulo. Tem muita coisa legal para complementar o artigo.

3: Já estamos na parte 6! Perdeu alguma parte? Ta aqui ó:

PARTE 1 | PARTE 2 | PARTE 3 | PARTE 4 | PARTE 5 (1) | PARTE 5 (2) | PARTE 6 (1) | PARTE 6 (2)

Na última semana nós vimos sobre as redes neurais recorrentes, o foco hoje é ver aplicações e ir além na utilização dessas redes.

Então bora lá!

Uso avançado das redes neurais recorrentes

Nessa seção do livro, nós vamos algumas técnicas para melhorar a performance e generalizar o modelo da rede neural recorrente. Isso tudo aplicado à um exemplo prático!

Estrale os dedos e bora lá!

Um problema de previsão de temperatura

Até esse ponto nós trabalhamos apenas com sequência de dados em texto, como no caso das reviews da IMDB e as notícias Reuters. Porém a sequência de dados pode ser encontrada em diferentes problemas além do processamento de texto. No exemplo utilizado aqui, lidaremos com um conjunto de dados de séries temporais meteorológicas registrados na Estação Meteorológica do Instituto Max Planck de Biogeoquímica em Jena, na Alemanha.

Nesse dataset são registrados dados como temperatura do ar, pressão atmosférica, umidade, direção do vendo entre outros, totalizando 14 medidas apuradas. Essas medidas são gravadas de 10 em 10 minutos. O primeiro dado registrado é datado de 2003, porém os dados analisados aqui serão de 2009 a 2016. Esse dataset é ótimo para trabalhar com séries temporais numéricas. E o nosso objetivo será utilizar os dados registrados para prever a temperatura nas próximas 24 horas.

Uma imagem tirada direto do site: https://www.bgc-jena.mpg.de/wetter/

O download do dados pode ser realizado por aqui: https://s3.amazonaws.com/keras-datasets/jena_climate_2009_2016.csv.zip

Vamos já começar fazendo o importe dos dados.

Tem-se um total de 420.551 linhas de dados, cada linha é um registro das 14 variáveis medidas.

Abaixo podemos observar quais são essas variáveis medidas.

Para visualizar melhor os dados, vamos convertê-los em um Numpy array.

Agora sim podemos plotar um gráfico para analisar a evolução das variáveis medidas. No caso vamos observar a temperatura. Porém você é livre para plotar qual variável quiser.

São muitas medições, então o gráfico pode ter ficado complicado de entender. Então vamos observar os dados dos primeiros 10 dias. Como os dados são gravados de 10 en 10 minutos, temos um total de 144 pontos por dia. Então para os 10 dias teremos no gráfico os 1440 primeiros pontos.

Observando a coluna vertical, podemos perceber que os dados de temperatura são bem baixos, o que significa que era um período de inverno rigoroso. Concordam?

Se você estivesse tentando prever a temperatura média para o próximo mês, considerando alguns meses de dados anteriores, o problema seria fácil, devido à periodicidade confiável dos dados em escala anual. Mas olhando os dados em uma escala de dias, a temperatura parece muito mais caótica. Será que essa série do tempo é previsível em uma escala diária?

E é esse o nosso desafio.

Preparando os dados

A formulação exata do problema será a seguinte: será que tendo os dados que vão até passos de tempo de lookback (um passo de tempo é 10 minutos) e amostras de cada step (passo) de tempo, você pode prever a temperatura em passos de tempo em delay (atraso)?

Vamos utilizar os seguinte valores de parâmetros:

lookback = 720 — Os dados serão dos ultimos 5 dias (144 medidas por dias)

steps = 6 — as amostra serão de um ponto por hora

delay = 144 — as previsões serão de 24 horas no futuro

Precisamos fazer duas coisas antes:

  • Pré-processamento dos dados: Até aqui já sabemos que os dados precisam ser trabalhados para o formato que as redes neurais possam formular. Como nesse caso os dados analisados já são numéricos, vamos precisar apenas normalizar eles, já que a variação desses dados não estão em uma mesma escala.
  • Escrever um gerador Python que pegue a matriz atual de dados flutuantes e produza lotes de dados do passado recente, junto com uma temperatura desejada no futuro. Como as amostras no conjunto de dados são altamente redundantes (a amostra N e a amostra N + 1 terão a maioria de seus passos de tempo em comum), seria um desperdício alocar explicitamente cada amostra. Em vez disso, vamos gerar as amostras em tempo real usando os dados originais.

Então vamos começar normalizando os dados, ou seja, colocando todos os dados em uma mesma escala. Para isso vamos subtrair a média de cada série temporal e dividir pelo desvio padrão. Vamos utilizar os primeiros 200.000 steps de tempo como os dados de treinamento, por isso abaixo podemos observar o código de calculo de média e desvio padrão sobre essa quantidade de dados.

Agora tem-se a função generator de dados que vamos utilizar nesse desenvolvimento. Ele produz uma tupla (samples, targets), onde samples é um lote de dados de entrada e targets é a matriz correspondente das temperaturas.

Essa função leva como parâmetros:

data — o array original dos dados normalizado anteriormente

lookback — quantos passos de tempo passados os dados de entrada terão

delay — quantos passos de tempo no futuro os targets terão

min_index e max_index — Índices na matriz de dados que delimitam de quais etapas de tempo desenhar. Isso é útil para manter um segmento dos dados para validação e outro para teste.

shuflle — Se deve ‘embaralhar’ as amostras ou desenhá-las em ordem cronológica.

batch_size — o número de amostrar por lote

step — o período, em passos de tempo, que terá a coleta dos dados. Que já definimos que terá o valor 6 nesse exemplo.

Agora, vamos usar a função do gerador abstrato para instanciar três geradores: um para treinamento, um para validação e um para teste. Cada um vai olhar para diferentes segmentos temporais dos dados originais: o gerador de treinamento olha para os primeiros 200.000 passos de tempo, o gerador de validação olha para os seguintes 100.000, e o gerador de teste olha para o resto.

Senso comum, uma baseline que não utiliza machine-learning

Antes de ir direto utilizando a rede neural recorrente, vamos passar por algumas abordagens, como Frnaçois chama de senso-comum. Isso servirá pra checar a validade, e estabelecer uma baseline que teremos que ultrapassar para demonstrar que as técnicas avançadas que vamos utilizar alcançam melhores resultados, uma forma de justificar a utilização de outras técnicas. A ideia de ter uma baseline é importante principalmente quando temos algum problema e nenhuma solução conhecida ainda.

Um exemplo clássico é o de tarefas de classificação desequilibrada, onde algumas classes são muito mais comuns do que outras. Se o seu conjunto de dados contém 90% de instâncias de classe A e 10% de instâncias de classe B, então uma abordagem de bom senso para a tarefa de classificação é sempre prever “A” quando apresentado com uma nova amostra. Tal classificador é 90% preciso em geral, e qualquer abordagem baseada no aprendizado deve, portanto, superar essa pontuação de 90% para demonstrar utilidade. Às vezes, essas linhas de base elementares podem se provar surpreendentemente difíceis de superar.

Nesse caso, a série temporal de temperatura pode ser assumida com segurança como contínua (as temperaturas de amanhã provavelmente serão próximas às temperaturas de hoje), bem como periódica com um período diário. Portanto, uma abordagem de bom senso é sempre prever que a temperatura em 24 horas a partir de agora será igual à temperatura agora.

Vamos avaliar esta abordagem, usando a métrica do erro absoluto médio (MAE):

np.mean(np.abs(preds — targets))

Então atingimos um MAE de 0.28. Porque os dados de temperatura foram normalizados para serem centralizado em 0 e tem um desvio padrão de 1, este número não é imediatamente interpretável. Isso se traduz em um erro absoluto médio de 0,28 × temperatura_std graus.

Concluindo então que o valor de temperatura é 2,479ºC.

Esse erro é bem considerável. Vamos para as abordagens de machine learning.

Uma abordagem básica de machine-learning

Vamos usar aqui uma técnica básica de machine learning antes de irmos para as recorrentes. Mais uma forma de justificar o uso de técnicas mais avançadas.

O modelo da rede é mostrada a seguir e podemos notar que é composto por camadas de flatenning e duas camadas densas. Vamos avaliar o modelo utilizando o MAE também, assim fica mais fácil de comparar os resultados gerados.

Bora lá.

Após modelo treinado, vamos observar a curva de perda.

Podemos notar que algumas das perdas de validação estão próximas da linha de base sem aprendizagem, mas não são confiáveis. Por isso é interessante ter o modelo de baseline.

Uma primeira baseline recorrente

O último exemplo utilizando machine learning não teve uma boa performance, mas isso não significa que as técnicas de machine-learning serão ruins. A primeira abordagem não teve bons resultados pois não soube trabalhar a informação de tempo dos dados e é por isso que vamos aplicar as redes neurais recorrentes.

Ao invés de utilizar uma LSTM, vamos usar uma camada de GRU, desenvolvida por Chung et al. em 2014. Gated recurrent unit (GRU) trabalha com o mesmo principio que as LSTM, mas eles são simplificadas o que reduz o custo computacional para ser utilizado. Então vamos lá.

A nova validação MAE de ~ 0,265 (antes de começar a ajustar significativamente) se traduz em um erro absoluto médio de 2,35˚C após a desnormalização.

Usando uma camada recorrente de dropout para combater o overfitting

É bem claro de perceber que o modelo está ‘overfitando’. Porém, como podemos fazer para combater isso? Já conhecemos aqui o conceito de dropout, porém sua aplicação em redes recorrentes são um pouco delicadas. Vou deixar algumas referências ao final que explicam a teoria por detrás disso, assim podemos partir para a aplicação. Beleza?

Depois de já iniciarmos a RNN e adicionar a camada de dropout, vamos visualizar os resultados. E olha só que sucesso hein!!

Empilhando de camadas recorrentes

Bom, já alçamos resultados muito interessantes, inclusive já ultrapassamos a baseline.

Então é hora de parar por aqui?

Não mesmo. Podemos incrementar a nossa rede e melhorar a capacidade do modelo. Uma dessas formas é empilhar as layers, ou seja, formar um sequencial de camadas recorrentes.

No caso das RNNs esse sequenciamento é realizado um pouco diferente. Para empilhar camadas recorrentes umas sobre as outras no Keras, todas as camadas intermediárias devem retornar sua sequência completa de saídas (um tensor 3D) em vez de sua saída no último passo de tempo. Isso é feito especificando return_sequences=True.

Através do resultado podemos perceber que o empilhamento das camadas melhora um pouco o resultado, com isso duas conclusões são possíveis:

  1. Como você ainda não está super dimensionando demais, pode aumentar com segurança o tamanho de suas camadas em uma busca pela melhoria da perda de validação. No entanto, isso tem um custo computacional não desprezível.
  2. Adicionar uma camada não ajudou em um fator significativo, então você pode estar vendo retornos decrescentes com o aumento da capacidade da rede neste ponto.

Então é isso, vimos aqui aplicação das redes neurais recorrentes.