1aera0ngq0z56sb2thznfya.jpeg

Cómo las redes neuronales son herramientas sólidas para resolver ecuaciones diferenciales sin el uso de datos de entrenamiento

Foto por Linus Mimietz en desempaquetar

Las ecuaciones diferenciales son una de las protagonistas de las ciencias físicas, con amplias aplicaciones en ingeniería, biología, economía e incluso ciencias sociales. En términos generales, nos dicen cómo varía una cantidad en el tiempo (o algún otro parámetro, pero normalmente nos interesan las variaciones en el tiempo). Podemos entender cómo una población, o el precio de una acción, o incluso cómo la opinión de alguna sociedad hacia determinados temas cambia con el tiempo.

Normalmente, los métodos utilizados para resolver los ED no son analíticos (es decir, no existe una «fórmula cerrada» para la solución) y tenemos que recurrir a métodos numéricos. Sin embargo, los métodos numéricos pueden resultar costosos desde el punto de vista computacional y, peor aún, el error acumulado puede ser significativamente grande.

Este artículo mostrará cómo una red neuronal puede ser un aliado valioso para resolver una ecuación diferencial y cómo podemos tomar prestados conceptos de redes neuronales basadas en la física para abordar la pregunta: ¿podemos utilizar un enfoque de aprendizaje automático para resolver una DE?

En esta sección, hablaré muy brevemente sobre las redes neuronales basadas en la física. Supongo que conoces la parte de la «red neuronal», pero ¿qué hace que estén informadas por la física? Bueno, no están exactamente informados por la física, sino más bien por una ecuación (diferencial).

Por lo general, las redes neuronales se entrenan para encontrar patrones y descubrir qué sucede con un conjunto de datos de entrenamiento. Sin embargo, cuando entrena una red neuronal para que obedezca el comportamiento de sus datos de entrenamiento y, con suerte, se ajuste a datos invisibles, su modelo depende en gran medida de los datos en sí y no de la naturaleza subyacente de su sistema. Suena casi como una cuestión filosófica, pero es más práctico que eso: si los datos provienen de mediciones de corrientes oceánicas, estas corrientes tienen que obedecer las ecuaciones físicas que describen las corrientes oceánicas. Tenga en cuenta, sin embargo, que su red neuronal es completamente agnóstica acerca de estas ecuaciones y solo intenta ajustar puntos de datos.

Aquí es donde entra en juego la física informada. Si, además de aprender a ajustar sus datos, su modelo también aprende a ajustar las ecuaciones que gobiernan ese sistema, las predicciones de su red neuronal serán mucho más precisas y se generalizarán mucho mejor, solo citando algunas ventajas de los modelos basados ​​en la física. .

Tenga en cuenta que las ecuaciones que rigen su sistema no tienen por qué involucrar la física en absoluto, lo «informado por la física» es solo la nomenclatura (y de todos modos, la técnica es la más utilizada por los físicos). Si su sistema es el tráfico de una ciudad y tiene un buen modelo matemático que desea que obedezcan las predicciones de su red neuronal, entonces las redes neuronales basadas en la física son una buena opción para usted.

¿Cómo informamos a estos modelos?

Con suerte, te he convencido de que vale la pena hacer que el modelo sea consciente de las ecuaciones subyacentes que gobiernan nuestro sistema. Sin embargo, ¿cómo podemos hacer esto? Hay varios enfoques para esto, pero el principal es adaptar la función de pérdida para que tenga un término que tenga en cuenta las ecuaciones rectoras, además de la parte habitual relacionada con los datos. Es decir, la función de pérdida. l estará compuesto por la suma

Aquí, la pérdida de datos es la habitual: una diferencia cuadrática media o alguna otra forma adecuada de función de pérdida; pero la parte de la ecuación es la encantadora. Imagine que su sistema se rige por la siguiente ecuación diferencial:

¿Cómo podemos encajar esto en la función de pérdida? Bueno, como nuestra tarea al entrenar una red neuronal es minimizar la función de pérdida, lo que queremos es minimizar la siguiente expresión:

Entonces nuestra función de pérdida relacionada con la ecuación resulta ser

es decir, es la diferencia de medias al cuadrado de nuestra DE. Si logramos minimizar esto (es decir, hacer que este término sea lo más cercano a cero posible), automáticamente satisfacemos la ecuación gobernante del sistema. Bastante inteligente, ¿verdad?

Ahora, el término extra L_IC Es necesario abordar la función de pérdida: tiene en cuenta las condiciones iniciales del sistema. Si no se proporcionan las condiciones iniciales de un sistema, hay infinitas soluciones para una ecuación diferencial. Por ejemplo, una pelota lanzada desde el nivel del suelo tiene su trayectoria regida por la misma ecuación diferencial que la de una pelota lanzada desde el décimo piso; sin embargo, sabemos con certeza que los caminos que trazarán estas bolas no serán los mismos. Lo que cambia aquí son las condiciones iniciales del sistema. ¿Cómo sabe nuestro modelo de qué condiciones iniciales estamos hablando? ¡Es natural en este punto que lo apliquemos usando un término de función de pérdida! Para nuestro DE, impongamos eso cuando t = 0, y = 1. Por lo tanto, queremos minimizar una función de pérdida de condición inicial que diga:

Si minimizamos este término, automáticamente satisfacemos las condiciones iniciales de nuestro sistema. Ahora lo que queda por entender es cómo utilizar esto para resolver una ecuación diferencial.

Si una red neuronal se puede entrenar con el término relacionado con los datos de la función de pérdida (esto es lo que generalmente se hace en las arquitecturas clásicas), y también se puede entrenar tanto con los datos como con el término relacionado con la ecuación (esto es física- redes neuronales informadas que acabo de mencionar), debe ser cierto que se puede entrenar para minimizar solo el término relacionado con la ecuación. ¡Esto es exactamente lo que vamos a hacer! La única función de pérdida utilizada aquí será la L_ecuación. Con suerte, este diagrama a continuación ilustra lo que acabo de decir: hoy apuntamos al tipo de modelo de la parte inferior derecha, nuestro solucionador DE NN.

Figura 1: diagrama que muestra los tipos de redes neuronales con respecto a sus funciones de pérdida. En este artículo, apuntamos al de abajo a la derecha. Imagen del autor.

Implementación de código

Para mostrar los aprendizajes teóricos que acabamos de obtener, implementaré la solución propuesta en código Python, utilizando la biblioteca PyTorch para aprendizaje automático.

Lo primero que debe hacer es crear una arquitectura de red neuronal:

import torch
import torch.nn as nn

class NeuralNet(nn.Module):
def __init__(self, hidden_size, output_size=1,input_size=1):
super(NeuralNet, self).__init__()
self.l1 = nn.Linear(input_size, hidden_size)
self.relu1 = nn.LeakyReLU()
self.l2 = nn.Linear(hidden_size, hidden_size)
self.relu2 = nn.LeakyReLU()
self.l3 = nn.Linear(hidden_size, hidden_size)
self.relu3 = nn.LeakyReLU()
self.l4 = nn.Linear(hidden_size, output_size)

def forward(self, x):
out = self.l1(x)
out = self.relu1(out)
out = self.l2(out)
out = self.relu2(out)
out = self.l3(out)
out = self.relu3(out)
out = self.l4(out)
return out

Este es solo un MLP simple con funciones de activación LeakyReLU. Luego, definiré las funciones de pérdida para calcularlas más adelante durante el ciclo de entrenamiento:

# Create the criterion that will be used for the DE part of the loss
criterion = nn.MSELoss()

# Define the loss function for the initial condition
def initial_condition_loss(y, target_value):
return nn.MSELoss()(y, target_value)

Ahora, crearemos una matriz de tiempo que se usará como datos del tren, crearemos una instancia del modelo y también elegiremos un algoritmo de optimización:

# Time vector that will be used as input of our NN
t_numpy = np.arange(0, 5+0.01, 0.01, dtype=np.float32)
t = torch.from_numpy(t_numpy).reshape(len(t_numpy), 1)
t.requires_grad_(True)

# Constant for the model
k = 1

# Instantiate one model with 50 neurons on the hidden layers
model = NeuralNet(hidden_size=50)

# Loss and optimizer
learning_rate = 8e-3
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)

# Number of epochs
num_epochs = int(1e4)

Finalmente, comencemos nuestro ciclo de entrenamiento:

for epoch in range(num_epochs):

# Randomly perturbing the training points to have a wider range of times
epsilon = torch.normal(0,0.1, size=(len