Predicción a corto plazo con aprendizaje automático

ML
DL
CNN
Predicción meteorológica
UNet
Predicción a corto plazo con aprendizaje automático cover image

Hoy en día, los meteorólogos estiman que el 90% de las predicciones meteorológicas son correctas en un lapso de 5 días. Las predicciones que se hacen generalmente se basan en dos métodos separados:

  1. Enfoques basados en la física: estos enfoques utilizan modelos que integran cantidades medibles como la presión, el movimiento de las nubes, las condiciones del cielo... Estos modelos son buenos para predecir el clima para los próximos días o semanas.

  2. Enfoques sin física (basados en datos): estos enfoques utilizan datos históricos para crear modelos que pueden hacer predicciones. Dichos modelos muestran buenos resultados en la predicción del clima hasta por 6 horas o lo que se conoce como predicción meteorológica inmediata.

En este artículo, discutiremos la segunda categoría de enfoques. Hablaremos sobre los diferentes formatos de datos meteorológicos, cómo se puede aprovechar el aprendizaje automático para hacer predicciones meteorológicas inmediatas y cómo las arquitecturas desarrolladas pueden ser beneficiosas para resolver problemas similares.

Datos para la Predicción del Clima

Dado que los enfoques libres de física utilizan datos históricos, comencemos examinando los datos disponibles.

Utilizaremos dos fuentes principales de datos:

  1. Datos de imagen: estos datos toman la forma de imágenes de radar o satélite de un área geográfica específica. Se utiliza para predecir la precipitación, el movimiento del viento o la humedad.

Imagen satelital

  1. Datos tabulares: estos datos toman la forma de registros de cantidades medibles como la temperatura, la humedad o la velocidad del viento.

Datos tabulares

Si bien ambas fuentes de datos son importantes para construir modelos potentes, nos centraremos en la primera (datos de imágenes recopilados de radares o satélites) por razones de simplicidad. Los modelos más utilizados con datos de imágenes son Redes neuronales convolucionales (CNN).

Siguiendo este trabajo, vamos a utilizar una arquitectura U-Net para crear nuestro propio modelo de predicción inmediata.

Arquitectura

Partir de una arquitectura existente es útil por muchas razones, entre las cuales:

Las arquitecturas sirven como pautas para crear nuevos modelos. Las personas que crean nuevas arquitecturas adoptan un enfoque de prueba y error para llegar a sus resultados finales. Al reutilizar sus resultados finales, podemos ahorrar mucho tiempo.

Los modelos preentrenados suelen estar disponibles para su uso inmediato. Cuando los investigadores publican sus nuevas arquitecturas, generalmente también publican los parámetros entrenados, para que los usuarios no tengan que pasar por la molestia de entrenar/optimizar desde cero. Esto es especialmente útil para modelos muy grandes y sedientos de recursos.

Ejemplos de arquitecturas de visión famosas incluyen:

  • LeNet (60k parámetros)

LeNet

  • AlexNet (parámetros de 60m)

AlexNet

  • VGG-16 (parámetros de 138m)

VGG-16

Red en U

U-Net es una arquitectura basada en una red totalmente convolucional, lo que significa que no tiene capas totalmente conectadas. Se introdujo por primera vez para una tarea de segmentación de imágenes médicas. Estos resultados inspiraron a los investigadores a extenderlo a otras tareas en la visión artificial.

En 2019, Google utilizó una arquitectura basada en U-Net para crear un modelo de pronóstico de precipitaciones.

El nombre “U-Net” proviene de la forma de “U” de su arquitectura.

Arquitectura U-net

Identificamos tres componentes principales:

  1. Contratación/Codificador: Sucesión de capas de convolución/agrupación que comprimen la imagen de entrada en una representación de menor tamaño.

  2. Puente / Cuello de botella: La parte inferior de la “U” conecta el codificador con el decodificador. Está formado por una serie de operaciones de convolución.

  3. Descontracturación / Decodificador: Sucesión de upconvolutions y capas de convolución, que “descomprimen” la salida del cuello de botella.

La arquitectura de una U-Net parece un codificador automático hasta ahora. Sin embargo, la diferencia reside en la información que pasa entre el codificador y el decodificador. Esta información se pasa concatenando los resultados de las convoluciones del codificador con los de la convolución ascendente del decodificador. Esta modificación mejora la resolución y permite que el modelo genere una salida espacialmente más precisa (la ubicación de los píxeles en la salida será más precisa, resolviendo un problema que ocurría en modelos que no incluían concatenación). La concatenación se realiza de forma simétrica.

Construcción de un modelo U-Net para predicción inmediata de precipitaciones

En esta sección, construiremos un modelo U-Net para predecir el clima a partir de imágenes satelitales. Usaremos un modelo previamente entrenado llamado Rain-Net.

El siguiente código está disponible en este colab.

Primero instalamos wradlib, una biblioteca de código abierto para el procesamiento de datos de radares meteorológicos

!pip install wradlib
import wradlib as wrl

Luego escribimos dos funciones de utilidad para descargar datos satelitales del servidor de datos abiertos de 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

Estas utilidades nos permiten descargar las últimas 4 imágenes de satélite, que es el número de imágenes de satélite que necesitamos para hacer predicciones con nuestro modelo preentrenado.

Luego podemos usar las funciones creadas para obtener las últimas 4 imágenes


RY_latest, RY_latest_timestep = download_data()

Después de obtener las imágenes, usamos el método vis.plot_ppi de wradlib para trazar los datos.

for i in range(RY_latest.shape[0]):
   wrl.vis.plot_ppi(RY_latest[i])

Radar VIS

Ya hemos cargado nuestros datos. Carguemos el modelo a continuación.

Comenzamos importando las clases relevantes. Usaremos TensorFlow en este artículo.

desde tensorflow.keras.layers importar Entrada, Conv2D, Activación, Concatenar, Conv2DTranspose, MaxPool2D

de tensorflow.keras.models modelo de importación

Construyamos 3 bloques de construcción primitivos. Estos "bloques de construcción" se utilizarán para crear toda la arquitectura, según esta implementación.

El primer bloque corresponde a la sucesión de capas convolucionales, lo llamamos "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

El segundo bloque se usa para construir la parte del codificador (bloque convolucional + agrupación máxima). Lo llamamos un "encoder_block"

def encoder_block(input, num_filters):
   x = conv_block(input, num_filters)
   p = MaxPool2D((2,2))(x)
   return x,p

El tercer y último bloque es un "decoder_block" (convolución ascendente + concatenación + convolución).

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 estos componentes básicos para construir el 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

Siéntete libre de jugar con la implementación, mejorarla o adaptarla a tus necesidades si quieres usarla para otra cosa.

Entrenar este modelo puede llevar mucho tiempo. Por suerte, existe un modelo llamado Rain-Net, que se ha creado en base a una arquitectura U-Net y está especializado en el pronóstico inmediato de precipitaciones.

Clonemos su repositorio de GitHub

! git clone https://github.com/hydrogo/rainnet.git

Luego descargamos los pesos preentrenados para este modelo.

!wget -O /content/rainnet/rainnet_weights.h5 https://zenodo.org/record/3630429/files/rainnet_weights.h5

El siguiente paso es crear un modelo basado en la arquitectura que se encuentra en el repositorio y luego cargar los pesos descargados en este modelo.

import sys
from rainnet import rainnet
model = rainnet.rainnet()
model.load_weights('/content/rainnet/rainnet_weights.h5')
model.summary()

Las imágenes que descargamos tienen un tamaño de 900*900 píxeles. Vamos a remodelar estas imágenes para que coincidan con la entrada esperada de 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

Luego creamos una función que hace las predicciones.

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

Luego llamamos a esta función en los datos que descargamos anteriormente

Y_pred = prediction(model, RY_latest)

Podemos trazar las predicciones y guardarlas para usar los resultados guardados para crear una imagen gif que nos permita visualizar las predicciones.

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')

Resultados

¡Felicitaciones por llegar tan lejos! Ahora puede usar Rain-Net para hacer predicciones y visualizarlas.

Conclusión

En este artículo, hemos utilizado un modelo de aprendizaje automático (Rain-Net) para realizar pronósticos inmediatos de precipitaciones. Usamos una arquitectura U-Net, que construimos usando TensorFlow. Cargamos un modelo preentrenado para predecir imágenes de satélite.

Esta implementación se puede mejorar de muchas maneras. Por ejemplo:

  1. Ajuste el modelo en su conjunto de datos

  2. Utilice un módulo basado en la atención, como CBAM (Módulo de atención de bloque convolucional) en la arquitectura.

Referencias

¡Ven a uno de nuestros talleres gratuitos!

Comience su carrera como científico de datos con nuestros talleres gratuitos, que se basan en un plan de estudios adaptable y están guiados por expertos de la industria.


Career Services background pattern

Servicios profesionales

Contact Section background image

Mantengámonos en contacto

Code Labs Academy © 2024 Todos los derechos reservados.