Introducción

La propagación hacia adelante y hacia atrás son dos procesos fundamentales en el entrenamiento de redes neuronales. La propagación hacia adelante se refiere al cálculo de la salida de la red dado un conjunto de entradas, mientras que la propagación hacia atrás se utiliza para ajustar los pesos de la red minimizando el error de predicción.

Propagación hacia adelante (Forward Propagation)

Conceptos Clave

  1. Entrada (Input): Los datos que se alimentan a la red neuronal.
  2. Pesos (Weights): Parámetros ajustables que determinan la importancia de cada entrada.
  3. Sesgo (Bias): Un término adicional que permite ajustar la salida de la red.
  4. Función de activación (Activation Function): Una función no lineal que introduce no linealidades en la red.

Proceso

  1. Cálculo de la entrada ponderada: \[ z = W \cdot X + b \] Donde \( W \) son los pesos, \( X \) es la entrada y \( b \) es el sesgo.

  2. Aplicación de la función de activación: \[ a = \sigma(z) \] Donde \( \sigma \) es la función de activación.

  3. Repetición del proceso para cada capa: El resultado \( a \) se convierte en la entrada para la siguiente capa.

Ejemplo

Supongamos una red neuronal con una sola capa oculta:

import numpy as np

# Definición de la función de activación (sigmoide)
def sigmoid(z):
    return 1 / (1 + np.exp(-z))

# Entradas
X = np.array([0.5, 0.1])

# Pesos y sesgos
W1 = np.array([[0.2, 0.8], [0.5, 0.3]])
b1 = np.array([0.1, 0.2])

W2 = np.array([0.6, 0.9])
b2 = 0.3

# Propagación hacia adelante
z1 = np.dot(W1, X) + b1
a1 = sigmoid(z1)

z2 = np.dot(W2, a1) + b2
a2 = sigmoid(z2)

print("Salida de la red:", a2)

Explicación del Código

  1. Definición de la función de activación: En este caso, se utiliza la función sigmoide.
  2. Entradas, pesos y sesgos: Se definen las entradas \( X \), los pesos \( W1 \) y \( W2 \), y los sesgos \( b1 \) y \( b2 \).
  3. Cálculo de la entrada ponderada y aplicación de la función de activación: Se realiza para cada capa de la red.
  4. Salida de la red: La salida final \( a2 \) es el resultado de la propagación hacia adelante.

Propagación hacia atrás (Backpropagation)

Conceptos Clave

  1. Función de pérdida (Loss Function): Mide el error entre la salida predicha y la salida real.
  2. Gradiente (Gradient): La derivada de la función de pérdida con respecto a los pesos.
  3. Actualización de pesos: Ajuste de los pesos utilizando el gradiente para minimizar el error.

Proceso

  1. Cálculo del error: \[ \text{Error} = \text{Salida predicha} - \text{Salida real} \]

  2. Cálculo del gradiente: \[ \frac{\partial L}{\partial W} = \frac{\partial L}{\partial a} \cdot \frac{\partial a}{\partial z} \cdot \frac{\partial z}{\partial W} \] Donde \( L \) es la función de pérdida, \( a \) es la activación y \( z \) es la entrada ponderada.

  3. Actualización de pesos: \[ W = W - \eta \cdot \frac{\partial L}{\partial W} \] Donde \( \eta \) es la tasa de aprendizaje.

Ejemplo

Continuando con el ejemplo anterior, supongamos que la salida real es \( y = 0.7 \):

# Salida real
y = 0.7

# Función de pérdida (error cuadrático medio)
def loss(a2, y):
    return 0.5 * (a2 - y) ** 2

# Derivada de la función de pérdida con respecto a la salida
dL_da2 = a2 - y

# Derivada de la función de activación sigmoide
def sigmoid_derivative(a):
    return a * (1 - a)

# Gradiente de la capa de salida
dL_dz2 = dL_da2 * sigmoid_derivative(a2)
dL_dW2 = dL_dz2 * a1

# Gradiente de la capa oculta
dL_da1 = dL_dz2 * W2
dL_dz1 = dL_da1 * sigmoid_derivative(a1)
dL_dW1 = np.outer(dL_dz1, X)

# Actualización de pesos
eta = 0.1
W2 -= eta * dL_dW2
b2 -= eta * dL_dz2

W1 -= eta * dL_dW1
b1 -= eta * dL_dz1

print("Pesos actualizados W1:", W1)
print("Pesos actualizados W2:", W2)

Explicación del Código

  1. Cálculo del error: Se calcula la diferencia entre la salida predicha \( a2 \) y la salida real \( y \).
  2. Cálculo del gradiente: Se calcula el gradiente de la función de pérdida con respecto a los pesos de cada capa.
  3. Actualización de pesos: Se ajustan los pesos utilizando el gradiente y una tasa de aprendizaje \( \eta \).

Ejercicio Práctico

Ejercicio

Implementa una red neuronal con una capa oculta que realice la clasificación binaria de un conjunto de datos simple. Utiliza la propagación hacia adelante y hacia atrás para entrenar la red.

Solución

import numpy as np

# Función de activación (sigmoide)
def sigmoid(z):
    return 1 / (1 + np.exp(-z))

# Derivada de la función de activación sigmoide
def sigmoid_derivative(a):
    return a * (1 - a)

# Función de pérdida (error cuadrático medio)
def loss(a, y):
    return 0.5 * (a - y) ** 2

# Datos de entrenamiento
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y = np.array([0, 1, 1, 0])  # XOR

# Inicialización de pesos y sesgos
np.random.seed(42)
W1 = np.random.rand(2, 2)
b1 = np.random.rand(2)
W2 = np.random.rand(2)
b2 = np.random.rand(1)

# Tasa de aprendizaje
eta = 0.1

# Entrenamiento
for epoch in range(10000):
    for i in range(len(X)):
        # Propagación hacia adelante
        z1 = np.dot(W1, X[i]) + b1
        a1 = sigmoid(z1)
        
        z2 = np.dot(W2, a1) + b2
        a2 = sigmoid(z2)
        
        # Cálculo del error
        error = loss(a2, y[i])
        
        # Propagación hacia atrás
        dL_da2 = a2 - y[i]
        dL_dz2 = dL_da2 * sigmoid_derivative(a2)
        dL_dW2 = dL_dz2 * a1
        
        dL_da1 = dL_dz2 * W2
        dL_dz1 = dL_da1 * sigmoid_derivative(a1)
        dL_dW1 = np.outer(dL_dz1, X[i])
        
        # Actualización de pesos
        W2 -= eta * dL_dW2
        b2 -= eta * dL_dz2
        
        W1 -= eta * dL_dW1
        b1 -= eta * dL_dz1

# Prueba de la red entrenada
for i in range(len(X)):
    z1 = np.dot(W1, X[i]) + b1
    a1 = sigmoid(z1)
    
    z2 = np.dot(W2, a1) + b2
    a2 = sigmoid(z2)
    
    print(f"Entrada: {X[i]}, Salida predicha: {a2}, Salida real: {y[i]}")

Explicación del Código

  1. Inicialización de pesos y sesgos: Se inicializan aleatoriamente los pesos y sesgos.
  2. Entrenamiento: Se realiza la propagación hacia adelante y hacia atrás para cada muestra de entrenamiento, ajustando los pesos en cada iteración.
  3. Prueba: Se prueba la red entrenada con los datos de entrada para verificar la precisión de las predicciones.

Conclusión

En esta sección, hemos cubierto los conceptos y procesos de la propagación hacia adelante y hacia atrás en redes neuronales. Estos procesos son fundamentales para el entrenamiento de redes neuronales, permitiendo ajustar los pesos para minimizar el error de predicción. Con la práctica y la implementación de estos conceptos, podrás desarrollar y entrenar redes neuronales para una variedad de aplicaciones en Deep Learning.

© Copyright 2024. Todos los derechos reservados