Hoje em dia, os meteorologistas estimam que 90% das previsões meteorológicas estão corretas num período de 5 dias. As previsões feitas geralmente são baseadas em dois métodos separados:
-
Abordagens baseadas na física: Estas abordagens utilizam modelos que integram quantidades mensuráveis, como pressão, movimento das nuvens, condições do céu… Tais modelos são bons para prever o tempo para os próximos dias ou semanas.
-
Abordagens livres de física (baseadas em dados): Essas abordagens usam dados históricos para criar modelos que podem fazer previsões. Esses modelos apresentam bons resultados na previsão do tempo por até 6 horas ou no que é conhecido como previsão do tempo.
Neste artigo, discutiremos a segunda categoria de abordagens. Discutiremos os diferentes formatos de dados meteorológicos, como o aprendizado de máquina pode ser aproveitado para fazer previsões meteorológicas e como as arquiteturas desenvolvidas podem ser benéficas para resolver problemas semelhantes.
Dados para previsão climática
Como as abordagens livres de física usam dados históricos, vamos começar examinando os dados disponíveis.
Estaremos usando duas fontes principais de dados:
- Dados de imagem: Esses dados assumem a forma de imagens de radar ou satélite de uma área geográfica específica. É usado para prever precipitação, movimento do vento ou umidade.
- Dados tabulares: Esses dados assumem a forma de registros de grandezas mensuráveis, como temperatura, umidade ou velocidade do vento.
Embora ambas as fontes de dados sejam importantes para construir modelos poderosos, focaremos nas primeiras (dados de imagens coletados de radares ou satélites) por razões de simplicidade. Os modelos mais comumente usados com dados de imagem são Redes Neurais Convolucionais (CNNs ).
Seguindo este trabalho, usaremos uma arquitetura U-Net para construir nosso próprio modelo de previsão a curto prazo.
Arquitetura
Começar a partir de uma arquitetura existente é útil por vários motivos, entre os quais:
As arquiteturas servem como diretrizes para a criação de novos modelos. As pessoas que criam novas arquiteturas adotam uma abordagem de tentativa e erro para chegar aos resultados finais. Ao reutilizar os resultados finais, podemos economizar muito tempo.
Modelos pré-treinados geralmente estão disponíveis para uso imediato. Quando os pesquisadores publicam suas novas arquiteturas, eles geralmente publicam também os parâmetros treinados, para que os usuários não tenham que se preocupar em treinar/otimizar do zero. Isso é especialmente útil para modelos muito grandes e que exigem recursos.
Exemplos de arquiteturas de visão famosas incluem:
- LeNet (parâmetros de 60k)
- AlexNet (parâmetros de 60m)
- VGG-16 (parâmetros de 138m)
U-Net
U-Net é uma arquitetura baseada em uma rede totalmente convolucional, o que significa que não possui camadas totalmente conectadas. Foi introduzido pela primeira vez para uma tarefa de segmentação de imagens médicas. Esses resultados inspiraram os pesquisadores a estendê-lo a outras tarefas em visão computacional.
Em 2019, o Google usou uma arquitetura baseada em U-Net para criar um modelo de previsão de precipitação.
O nome “U-Net” vem do formato em “U” de sua arquitetura.
Identificamos três componentes principais:
-
Contratação/Codificador: Sucessão de camadas de convolução/pooling que comprimem a imagem de entrada em uma representação de tamanho menor.
-
Ponte / Gargalo: A parte inferior do “U” conecta o codificador ao decodificador. É formado por uma série de operações de convolução.
-
Descontratação/Decodificador: Sucessão de upconvolutions e camadas de convolução, que “descomprimem” a saída do gargalo.
A arquitetura de uma U-Net parece até agora um codificador automático. Porém, a diferença reside na informação que passa entre o codificador e o decodificador. Esta informação é passada concatenando os resultados das convoluções do codificador com os da convolução ascendente do decodificador. Esta modificação melhora a resolução e permite que o modelo produza uma saída mais espacialmente precisa (a localização dos pixels na saída será mais precisa, resolvendo um problema que ocorria em modelos que não incluíam concatenação). A concatenação é feita simetricamente.
Construindo um modelo U-Net para previsão de precipitação
Nesta seção, construiremos um modelo U-Net para prever o clima a partir de imagens de satélite. Usaremos um modelo pré-treinado chamado Rain-Net.
O código abaixo está disponível neste colab.
Primeiro instalamos wradlib, uma biblioteca de código aberto para processamento de dados de radar meteorológico
!pip install wradlib
import wradlib as wrl
Em seguida, escrevemos duas funções utilitárias para baixar dados de satélite do servidor de dados abertos do DWD
import urllib.request
import io
import numpy as np
import datetime
def download_and_read_RY(RY_timestamp):
url = f"https://opendata.dwd.de/weather/radar/radolan/ry/raa01-ry_10000-{RY_timestamp}-dwd---bin"
data_binary = urllib.request.urlopen(url).read()
data, attr = wrl.io.read_radolan_composite(io.BytesIO(data_binary), missing = 0)
data = data.astype("float32")
return data,attr
def download_data():
latest_scan, latest_attr = download_and_read_RY("latest")
latest_datetime = latest_attr["datetime"]
list_for_downloading = [ts.strftime("%y%m%d%H%M") for ts in
[latest_datetime - datetime.timedelta(minutes=t) for t in [15, 10, 5]]]
previous_scans = np.array([download_and_read_RY(ts)[0] for ts in list_for_downloading])
print(list_for_downloading)
scans = np.concatenate([previous_scans, latest_scan[np.newaxis, ::, ::]], axis = 0)
return scans, latest_datetime
Esses utilitários nos permitem baixar as últimas 4 imagens de satélite, que é o número de imagens de satélite que precisamos para fazer previsões com nosso modelo pré-treinado.
Podemos então usar as funções criadas para obter as 4 imagens mais recentes
RY_latest, RY_latest_timestep = download_data()
Depois de obter as imagens, usamos o método vis.plot_ppi do wradlib para plotar os dados
for i in range(RY_latest.shape[0]):
wrl.vis.plot_ppi(RY_latest[i])
Agora carregamos nossos dados. Vamos carregar o modelo a seguir.
Começamos importando as classes relevantes. Usaremos o TensorFlow neste artigo.
de tensorflow.keras.layers importar entrada, Conv2D, ativação, concatenar, Conv2DTranspose, MaxPool2D
do modelo de importação tensorflow.keras.models
Vamos construir 3 blocos de construção primitivos. Esses "blocos de construção" serão usados para criar toda a arquitetura, de acordo com esta implementação.
O primeiro bloco corresponde à sucessão de camadas convolucionais, chamamos de "conv_block"
def conv_block(input, num_filters):
x = Conv2D(num_filters, 3, padding ="same", kernel_initializer="he_normal"(input))
x = Activation("relu")(x)
x = Conv2D(num_filters, 3, padding ="same", kernel_initializer="he_normal"(x))
x = Activation("relu")(x)
return x
O segundo bloco é usado para construir a partir da parte do codificador (bloco convolucional + pooling máximo). Chamamos isso de “encoder_block”
def encoder_block(input, num_filters):
x = conv_block(input, num_filters)
p = MaxPool2D((2,2))(x)
return x,p
O terceiro e último bloco é um “decoder_block” (upconvolution + concatenação + convolução).
def decoder_block(input, skip_features, num_filters):
x = Conv2DTranspose(num_filters, (2,2), strides=2, padding="same", kernel_initializer="he_normal")
x = Concatenate()([x, skip_features])
x = conv_block(x, num_filters)
return x
Combinamos esses blocos de construção para construir o modelo U-Net
def build_unet(input_shape = (928, 928, 4)):
inputs = Input(input_shape)
e1, p1 = encoder_bock(inputs, 64)
e2, p2 = encoder_bock(p1, 128)
e3, p3 = encoder_bock(p2, 256)
e4, p4 = encoder_bock(p3, 512)
b1 = conv_block(p4, 1024)
d1 = decoder_block(b1, e4, 512)
d2 = decoder_block(d1, e3, 256)
d3 = decoder_block(d2, e2, 128)
d4 = decoder_block(d3, e1, 64)
outputs = Conv2D(1, 1, padding="same", activation="linear")(d4)
unet = Model(inputs, outputs, name="U-Net")
return unet
Sinta-se à vontade para brincar com a implementação, melhorá-la ou adaptá-la às suas necessidades se quiser usá-la para outra coisa.
Treinar esse modelo pode levar muito tempo. Felizmente, existe um modelo chamado Rain-Net, que foi criado com base em uma arquitetura U-Net e é especializado em previsão de precipitação.
Vamos clonar seu repositório GitHub
! git clone https://github.com/hydrogo/rainnet.git
Em seguida, baixamos os pesos pré-treinados para este modelo
!wget -O /content/rainnet/rainnet_weights.h5 https://zenodo.org/record/3630429/files/rainnet_weights.h5
O próximo passo é criar um modelo baseado na arquitetura encontrada no repositório e então carregar os pesos baixados para este modelo
import sys
from rainnet import rainnet
model = rainnet.rainnet()
model.load_weights('/content/rainnet/rainnet_weights.h5')
model.summary()
As imagens que baixamos têm tamanho de 900*900 pixels. Vamos remodelar essas imagens para corresponder à entrada esperada do Rain-Net
def Scaler(array):
return np.log(array+0.01)
def invScaler(array):
return np.exp(array) - 0.01
def pad_to_shape(array, from_shape=900, to_shape=928, how="mirror"):
# calculate how much to pad in respect with native resolution
padding = int( (to_shape - from_shape) / 2)
# for input shape as (batch, W, H, channels)
if how == "zero":
array_padded = np.pad(array, ((0,0),(padding,padding),(padding,padding),(0,0)), mode="constant", constant_values=0)
elif how == "mirror":
array_padded = np.pad(array, ((0,0),(padding,padding),(padding,padding),(0,0)), mode="reflect")
return array_padded
def pred_to_rad(pred, from_shape=928, to_shape=900):
# pred shape 12,928,928
padding = int( (from_shape - to_shape) / 2)
return pred[::, padding:padding+to_shape, padding:padding+to_shape].copy()
'''
the spatial extent of input data has to be a multiple of 2n+1
where n is the number of max pooling layers
'''
def data_preprocessing(X):
# 0. Right shape for batch
X = np.moveaxis(X, 0, -1)
X = X[np.newaxis, ::, ::, ::]
# 1. To log scale
'''
We use a log scale to respond to skewness towards large values
'''
X = Scaler(X)
# 2. from 900x900 to 928x928
X = pad_to_shape(X)
return X
def data_postprocessing(nwcst):
# 0. Squeeze empty dimensions
nwcst = np.squeeze(np.array(nwcst))
# 1. Convert back to rainfall depth
nwcst = invScaler(nwcst)
# 2. Convert from 928x928 back to 900x900
nwcst = pred_to_rad(nwcst)
# 3. Return only positive values
nwcst = np.where(nwcst>0, nwcst, 0)
return nwcst
Em seguida, criamos uma função que faz as previsões.
def prediction(model_instance, input_data, lead_time=24):
input_data = data_preprocessing(input_data)
nwcst = []
for _ in range(lead_time):
pred = model_instance.predict(input_data)
nwcst.append(pred)
input_data = np.concatenate([input_data[::, ::, ::, 1:], pred], axis = -1)
nwcst = data_postprocessing(nwcst)
return nwcst
Em seguida, chamamos esta função nos dados que baixamos anteriormente
Y_pred = prediction(model, RY_latest)
Podemos traçar as previsões e salvá-las para usar os resultados salvos para criar uma imagem gif que nos permite visualizar as previsões
import matplotlib.pyplot as plt
names = []
for i in range(0, 19):
im = wrl.vis.plot_ppi(Y_pred[i])
name = '/content/image'+str(i)+'.png'
names.append(name)
plt.savefig(name)
import imageio
from google.colab import files
imgs = []
for name in names:
imgs.append(imageio.imread(name))
imageio.mimsave('/content/pred.gif', imgs, fps=4)
files.download('/content/pred.gif')
Parabéns por chegar até aqui! Agora você pode usar um Rain-Net para fazer previsões e visualizá-las.
Conclusão
Neste artigo, usamos um modelo de aprendizado de máquina (Rain-Net) para fazer previsão de precipitação. Usamos uma arquitetura U-Net, que construímos usando TensorFlow. Carregamos um modelo pré-treinado para prever imagens de satélite.
Esta implementação pode ser melhorada de várias maneiras. Por exemplo:
-
Ajuste o modelo em seu conjunto de dados
-
Use um módulo baseado em atenção, como CBAM (Convolutional Block Attention Module) na arquitetura.
Referências
Venha para um de nossos workshops gratuitos!
Comece sua carreira como cientista de dados com nossos workshops gratuitos, baseados em um currículo adaptável e orientados por especialistas do setor.