Introducción

Las redes neuronales son el núcleo del Deep Learning. Inspiradas en el funcionamiento del cerebro humano, estas estructuras computacionales están diseñadas para reconocer patrones y aprender de los datos. En esta sección, exploraremos los conceptos fundamentales de las redes neuronales, sus componentes y cómo funcionan.

  1. ¿Qué es una red neuronal?

Una red neuronal es un modelo computacional compuesto por capas de nodos (neuronas) que están interconectados. Cada nodo procesa una entrada y transmite una salida a los nodos de la siguiente capa. Las redes neuronales pueden aprender a realizar tareas específicas ajustando los pesos de estas conexiones durante el entrenamiento.

Componentes de una red neuronal

  1. Neuronas (Nodos): Unidades básicas que reciben entradas, las procesan y generan una salida.
  2. Pesos (Weights): Valores que multiplican las entradas y determinan la importancia de cada una.
  3. Sesgo (Bias): Un valor adicional que se suma a la entrada ponderada para ajustar la salida.
  4. Función de activación: Una función que transforma la salida de una neurona antes de pasarla a la siguiente capa.

  1. Arquitectura de una red neuronal

Las redes neuronales están organizadas en capas:

  1. Capa de entrada: Recibe los datos de entrada.
  2. Capas ocultas: Procesan las entradas a través de múltiples neuronas y funciones de activación.
  3. Capa de salida: Genera la salida final del modelo.

Ejemplo de una red neuronal simple

Imaginemos una red neuronal con una capa de entrada, una capa oculta y una capa de salida:

Entrada -> Capa Oculta -> Salida

Cada capa está compuesta por varias neuronas interconectadas. A continuación, se muestra un ejemplo de una red neuronal con 3 neuronas en la capa de entrada, 4 en la capa oculta y 1 en la capa de salida:

Entrada: [x1, x2, x3]
Capa Oculta: [h1, h2, h3, h4]
Salida: [y]

  1. Funcionamiento de una red neuronal

Propagación hacia adelante (Forward Propagation)

La propagación hacia adelante es el proceso mediante el cual las entradas se transforman en salidas a través de la red neuronal. Este proceso incluye:

  1. Cálculo de la entrada ponderada: Multiplicar las entradas por los pesos y sumar el sesgo.
  2. Aplicación de la función de activación: Transformar la entrada ponderada para obtener la salida de la neurona.

Ejemplo de cálculo

Supongamos una neurona con dos entradas \( x_1 \) y \( x_2 \), pesos \( w_1 \) y \( w_2 \), y un sesgo \( b \). La salida \( y \) se calcula como:

\[ z = w_1 \cdot x_1 + w_2 \cdot x_2 + b \] \[ y = \text{función de activación}(z) \]

Función de activación

Las funciones de activación introducen no linealidades en la red, permitiendo que la red neuronal aprenda patrones complejos. Algunas funciones de activación comunes son:

  • Sigmoide: \( \sigma(z) = \frac{1}{1 + e^{-z}} \)
  • ReLU (Rectified Linear Unit): \( \text{ReLU}(z) = \max(0, z) \)
  • Tanh: \( \text{tanh}(z) = \frac{e^z - e^{-z}}{e^z + e^{-z}} \)

Ejemplo en código

A continuación, se muestra un ejemplo en Python utilizando NumPy para calcular la salida de una neurona con la función de activación ReLU:

import numpy as np

# Definir entradas, pesos y sesgo
x = np.array([0.5, -0.2])
w = np.array([0.8, -0.5])
b = 0.1

# Calcular la entrada ponderada
z = np.dot(w, x) + b

# Aplicar la función de activación ReLU
y = np.maximum(0, z)

print(f"Salida de la neurona: {y}")

  1. Entrenamiento de una red neuronal

El entrenamiento de una red neuronal implica ajustar los pesos y sesgos para minimizar el error en las predicciones. Este proceso se realiza mediante:

  1. Función de pérdida: Mide la diferencia entre la predicción de la red y el valor real.
  2. Optimización: Ajusta los pesos y sesgos para minimizar la función de pérdida.

Retropropagación (Backpropagation)

La retropropagación es un algoritmo utilizado para calcular el gradiente de la función de pérdida con respecto a los pesos y sesgos. Este gradiente se utiliza para actualizar los pesos mediante un optimizador, como el descenso de gradiente.

Ejemplo en código

A continuación, se muestra un ejemplo simplificado de retropropagación en Python:

import numpy as np

# Definir entradas, pesos, sesgo y valor real
x = np.array([0.5, -0.2])
w = np.array([0.8, -0.5])
b = 0.1
y_real = 0.6

# Propagación hacia adelante
z = np.dot(w, x) + b
y_pred = np.maximum(0, z)

# Calcular la función de pérdida (error cuadrático medio)
loss = (y_pred - y_real) ** 2

# Retropropagación (cálculo del gradiente)
dL_dy_pred = 2 * (y_pred - y_real)
dy_pred_dz = 1 if z > 0 else 0
dz_dw = x
dz_db = 1

# Gradientes
dL_dw = dL_dy_pred * dy_pred_dz * dz_dw
dL_db = dL_dy_pred * dy_pred_dz * dz_db

# Actualizar pesos y sesgo (descenso de gradiente)
learning_rate = 0.01
w -= learning_rate * dL_dw
b -= learning_rate * dL_db

print(f"Nuevos pesos: {w}")
print(f"Nuevo sesgo: {b}")

Ejercicio Práctico

Ejercicio

Implementa una red neuronal simple con una capa de entrada, una capa oculta y una capa de salida utilizando NumPy. La red debe ser capaz de realizar una tarea de clasificación binaria.

Requisitos

  1. La red debe tener 2 neuronas en la capa de entrada, 3 en la capa oculta y 1 en la capa de salida.
  2. Utiliza la función de activación ReLU en la capa oculta y la función sigmoide en la capa de salida.
  3. Implementa la propagación hacia adelante y la retropropagación para entrenar la red.

Solución

import numpy as np

# Funciones de activación
def relu(x):
    return np.maximum(0, x)

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

# Derivadas de las funciones de activación
def relu_derivative(x):
    return np.where(x > 0, 1, 0)

def sigmoid_derivative(x):
    return sigmoid(x) * (1 - sigmoid(x))

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

# Inicializar pesos y sesgos
np.random.seed(42)
w1 = np.random.randn(2, 3)
b1 = np.zeros((1, 3))
w2 = np.random.randn(3, 1)
b2 = np.zeros((1, 1))

# Parámetros de entrenamiento
learning_rate = 0.1
epochs = 10000

# Entrenamiento
for epoch in range(epochs):
    # Propagación hacia adelante
    z1 = np.dot(X, w1) + b1
    a1 = relu(z1)
    z2 = np.dot(a1, w2) + b2
    a2 = sigmoid(z2)
    
    # Calcular la función de pérdida (error cuadrático medio)
    loss = np.mean((a2 - y) ** 2)
    
    # Retropropagación
    dL_da2 = 2 * (a2 - y) / y.size
    da2_dz2 = sigmoid_derivative(z2)
    dz2_dw2 = a1
    dz2_da1 = w2
    da1_dz1 = relu_derivative(z1)
    dz1_dw1 = X
    
    dL_dz2 = dL_da2 * da2_dz2
    dL_dw2 = np.dot(dz2_dw2.T, dL_dz2)
    dL_db2 = np.sum(dL_dz2, axis=0, keepdims=True)
    
    dL_da1 = np.dot(dL_dz2, dz2_da1.T)
    dL_dz1 = dL_da1 * da1_dz1
    dL_dw1 = np.dot(dz1_dw1.T, dL_dz1)
    dL_db1 = np.sum(dL_dz1, axis=0, keepdims=True)
    
    # Actualizar pesos y sesgos
    w1 -= learning_rate * dL_dw1
    b1 -= learning_rate * dL_db1
    w2 -= learning_rate * dL_dw2
    b2 -= learning_rate * dL_db2
    
    if epoch % 1000 == 0:
        print(f"Epoch {epoch}, Pérdida: {loss}")

# Predicción
z1 = np.dot(X, w1) + b1
a1 = relu(z1)
z2 = np.dot(a1, w2) + b2
a2 = sigmoid(z2)

print("Predicciones finales:")
print(a2)

Conclusión

En esta sección, hemos cubierto los conceptos básicos de las redes neuronales, incluyendo su estructura, funcionamiento y entrenamiento. Hemos aprendido sobre la propagación hacia adelante, las funciones de activación y la retropropagación. Además, hemos implementado una red neuronal simple en Python para resolver un problema de clasificación binaria. Con estos fundamentos, estamos preparados para profundizar en arquitecturas más complejas y técnicas avanzadas en los siguientes módulos.

© Copyright 2024. Todos los derechos reservados