Creando nuestra red neuronal usando numpy y matemáticas

Clase 14 de 29Curso de Fundamentos de Redes Neuronales con Python y Keras

Contenido del curso

Fundamentos en la arquitectura de redes neuronales

Manejo de redes neuronales con Keras

Resumen

Construir una red neuronal sin depender de frameworks de alto nivel es una de las formas más efectivas de comprender cómo funcionan internamente las capas, los pesos y las funciones de activación. Aquí se recorre paso a paso la implementación en Python con NumPy, desde la generación de datos hasta la inicialización de parámetros, dejando todo listo para el entrenamiento.

¿Cómo generar un dataset de clasificación con make_gaussian_quantiles?

Antes de construir la red, se necesita un conjunto de datos sobre el cual trabajar. En lugar de cargar un dataset externo, se genera uno sintético con la función make_gaussian_quantiles de scikit-learn [01:22].

La configuración utilizada es:

  • n = 1000 ejemplos (samples).
  • 2 features: cada punto vive en un plano bidimensional.
  • 2 clases: el modelo debe distinguir entre dos categorías.
  • cov = 0.1: controla la dispersión de la distribución gaussiana.
  • shuffle = True: mezcla los datos aleatoriamente.

python from sklearn.datasets import make_gaussian_quantiles

n = 1000 X, Y = make_gaussian_quantiles(cov=0.1, n_samples=n, n_features=2, n_classes=2, shuffle=True, random_state=None)

Al graficar con plt.scatter, se observa un patrón de círculos concéntricos: una clase se concentra en el centro y la otra en los bordes [03:15]. Este es el problema de clasificación que la red deberá resolver.

Un detalle importante: a Y se le agrega una dimensión extra con np.newaxis para que su forma sea (1000, 1), compatible con la salida de la red [02:50].

¿Qué funciones de activación y de pérdida se necesitan?

Se reutilizan las implementaciones de clases anteriores, cada una con su respectiva derivada, indispensable para el proceso de backpropagation [04:00].

¿Cómo funcionan sigmoide y ReLU con sus derivadas?

La función sigmoide comprime cualquier valor al rango (0, 1). Cuando se activa el parámetro deriv=True, devuelve la derivada calculada con Wolfram Alpha [04:10].

python def sigmoid(x, deriv=False): if deriv: return np.exp(-x) / ((1 + np.exp(-x)) ** 2) return 1 / (1 + np.exp(-x))

La función ReLU devuelve cero si el valor es negativo y deja pasar el valor original si es positivo. Se implementa con np.maximum(0, x). Su derivada es 0 cuando x ≤ 0 y 1 cuando x > 0 [04:35].

python def relu(x, deriv=False): if deriv: return np.where(x <= 0, 0, 1) return np.maximum(0, x)

Como función de pérdida se emplea el mean squared error (error cuadrático medio), que mide la distancia promedio entre las predicciones y los valores reales [05:15].

¿Cómo inicializar los pesos y bias de cada capa?

La función initialize_parameters_deep recibe una lista que describe la topología de la red: cantidad de neuronas en cada capa [05:40].

python layer_dims = [2, 4, 8, 1]

Esto significa:

  • Capa de entrada: 2 neuronas (coincide con las 2 features).
  • Primera capa oculta: 4 neuronas.
  • Segunda capa oculta: 8 neuronas.
  • Capa de salida: 1 neurona (clasificación binaria).

Dentro de la función se itera sobre cada capa y se generan los pesos (W) con np.random.rand, multiplicados por 2 y restando 1 para obtener valores en el rango [-1, 1] en lugar de [0, 1] [07:20]. El bias (b) sigue la misma lógica pero siempre tiene dimensión 1 en uno de sus ejes [08:05].

python def initialize_parameters_deep(layer_dims): parameters = {} L = len(layer_dims) for l in range(0, L - 1): parameters['W' + str(l + 1)] = np.random.rand(layer_dims[l], layer_dims[l + 1]) * 2 - 1 parameters['b' + str(l + 1)] = np.random.rand(1, layer_dims[l + 1]) * 2 - 1 return parameters

Al inspeccionar los resultados, W1 tiene forma (2, 4), W2 tiene forma (4, 8) y W3 tiene forma (8, 1) [09:10]. Cada matriz conecta la capa anterior con la siguiente mediante un producto punto, y cada capa cuenta con su propio vector de bias.

Con los datos generados, las funciones de activación definidas, la función de pérdida lista y los parámetros inicializados aleatoriamente, todos los componentes están preparados para ejecutar el forward propagation, el cálculo de la pérdida y el ajuste de pesos con gradient descent. ¿Qué arquitectura de capas probarías tú para resolver este problema de clasificación?