
- Publicado el
Grafos computacionales en Python: potenciando el aprendizaje automático
Introducción a los grafos computacionales
Los grafos computacionales son herramientas esenciales en machine learning, particularmente en deep learning, ya que permiten representar y optimizar operaciones matemáticas complejas. Estos grafos sirven como la base de los frameworks modernos de deep learning, habilitando la computación eficiente y la diferenciación automática, que es crucial para el proceso de entrenamiento de modelos. En pocas palabras, permiten descomponer cálculos complejos en operaciones más simples y manejables.
En este artículo, exploraremos en detalle qué son los grafos computacionales, su importancia en el aprendizaje automático y cómo implementarlos de manera práctica en Python. Si quieres entender mejor cómo las redes neuronales artificiales (ANNs) se relacionan con los grafos dirigidos acíclicos (DAGs), visita mi artículo: Redes neuronales representadas como DAGs de programas computacionales parametrizados.
import tensorflow as tf
# Define constants
a = tf.constant(3.0)
b = tf.constant(4.0)
# Define an operation
c = a * b
# Print the result
print(c)
## Output
# tf.Tensor(12.0, shape=(), dtype=float32)
En el código anterior, hemos definido dos constantes a
y b
y una operación c
que multiplica ambas constantes. Al imprimir el resultado, obtenemos un tensor de TensorFlow con el valor 12.0
. Este es un ejemplo simple se puede representar un grafo computacional, donde las operaciones se representan como nodos y las constantes como entradas.
¿Qué es un grafo computacional?
Un grafo computacional es una estructura matemática que representa una secuencia de operaciones matemáticas. En un grafo computacional, los nodos representan las operaciones y las aristas representan los datos que fluyen entre ellas. Cada nodo en el grafo computacional toma uno o más tensores como entrada y produce uno o más tensores como salida.
Los grafos computacionales son dirigidos acíclicos, lo que significa que las aristas tienen una dirección y no hay ciclos en la estructura. Esto permite que las operaciones se ejecuten en un orden específico, lo que es esencial para el cálculo eficiente de gradientes en el aprendizaje automático.
import tensorflow as tf
# Create nodes (operations and variables)
x = tf.Variable(2.0)
y = tf.Variable(3.0)
z = x * y + tf.square(x)
# Compute the result
result = z.numpy()
print(f"Result: {result}")
# Output
# Result: 10.0
En el código anterior, hemos definido tres nodos en el grafo computacional: +
, *
y $\square^2$
. En este caso las primeras dos operaciones son multiplicación y el cuadrado de x
, respectivamente, que toman por entrada las variables x
e y
. Luego se ejecuta la operación de suma entre los resultados de las dos operaciones anteriores. Finalmente, se imprime el resultado, que es 10.0
. Este es un ejemplo simple de cómo se ejecutan las operaciones en grafo computacional.
Forward propagation en grafos computacionales
Forward propagation (o forward pass) se refiere al proceso de calcular y almacenar los variables intermedias (incluyendo las salidas) de un grafo computacional. En el forward propagation, los datos fluyen desde las entradas a las salidas a través de las operaciones definidas en el grafo.
import tensorflow as tf
def forward_pass(x):
w1 = tf.Variable(3.0)
b1 = tf.Variable(4.0)
w2 = tf.Variable(1.0)
b2 = tf.Variable(0.5)
# First layer
z1 = w1 * x + b1
a1 = tf.nn.relu(z1)
# Second layer
z2 = w2 * a1 + b2
a2 = tf.nn.sigmoid(z2)
return a2
# Compute the result
x = tf.constant(2.0)
result = forward_pass(x).numpy()
print(f"Result: {result}")
# Output
# Result: 0.9999724626541138
Si ahora vamos paso a paso en el código anterior, primero definimos las variables w1
, b1
, w2
y b2
, que son los pesos y sesgos de las dos capas de la red neuronal. Luego, calculamos las salidas de la primera capa (z1
y a1
) y de la segunda capa (z2
y a2
) utilizando las operaciones de multiplicación y suma. Finalmente, aplicamos las funciones de activación ReLU
y sigmoid
a las salidas de las capas respectivas y devolvemos el resultado final.
Diferenciación automática en grafos computacionales
Una aplicación importante de los grafos computacionales es la diferenciación automática (AD por sus siglas en inglés). En general, hay tres tipos de AD, que son: reverse-mode, forward-mode y mixed-mode. En el aprendizaje automático.
Básicamente, el forward-mode y el reverse-mode DA ambos usan la regla de la cadena para calcular los gradiente. Evaluan los gradientes de las funciones «pequeñas» analíticamente (simbólica) y encadena todos los gradientes numéricos calculados mediante la regla de la cadena
En el forward-mode se evalúa la derivada de la función externa primero, mientras que en el reverse-mode se evalúa la derivada de la función interna primero.
Forward-mode AD
En el forward-mode AD, los gradientes se calculan en la dirección de las entradas, es decir, se calcula primero y luego se multiplica por . La idea es la misma en un grafo computacional, salvo que primero tenemos que agregar todos los gradientes de los nodos ascendentes y luego reenviarlos a los nodos descendentes. Veamos un ejemplo de cómo se calculan los gradientes en el forward-mode AD. Supongamos que tenemos la siguiente función: