Volver a la página principal
martes 11 febrero 2025
5

Implementación de una Red Neuronal MLP en JavaScript

Las redes neuronales multicapa (MLP, por sus siglas en inglés) son una de las arquitecturas más básicas pero poderosas en el mundo del aprendizaje profundo. En este artículo, te mostraré cómo construir una red neuronal MLP en JavaScript desde cero. 🚀

Vamos a programar una red capaz de aprender la función XOR, que es un clásico ejemplo de problemas no lineales que requieren una capa oculta para resolverse correctamente.

📌 ¿Qué es una Red Neuronal MLP?

Una MLP (Multilayer Perceptron) es un tipo de red neuronal artificial que consta de:

  • Una capa de entrada, que recibe los datos de entrada.
  • Una o más capas ocultas, que procesan la información con pesos y funciones de activación.
  • Una capa de salida, que genera la predicción del modelo.

A diferencia de un perceptrón simple, que solo puede aprender funciones lineales, la MLP utiliza una función de activación (como la sigmoide) en sus neuronas ocultas para modelar relaciones más complejas.

🚀 Implementación en JavaScript

A continuación, vamos a ver la implementación completa de una red neuronal MLP en JavaScript que puede aprender la función XOR.

🔧 Creando la Clase NeuralNetwork

Vamos a definir una clase llamada NeuralNetwork, que representará nuestra red neuronal con una capa de entrada, una capa oculta y una capa de salida.

class NeuralNetwork {

    constructor(inputSize, hiddenSize, outputSize) {
        this.inputSize = inputSize;
        this.hiddenSize = hiddenSize;
        this.outputSize = outputSize;

        // Inicializar las neuronas y sus pesos
        this.inputNeurons = Array.from({ length: this.inputSize }, () => ({
            value: 0,
            weights: Array.from({ length: this.hiddenSize }, () => Math.random() * 2 - 1)
        }));

        this.hiddenPerceptrons = Array.from({ length: this.hiddenSize }, () => ({
            value: 0,
            bias: Math.random() * 2 - 1,
            weights: Array.from({ length: this.outputSize }, () => Math.random() * 2 - 1)
        }));

        this.outputNeurons = Array.from({ length: this.outputSize }, () => ({
            value: 0,
            bias: Math.random() * 2 - 1
        }));

        this.learningRate = 0.1;
    }

En este constructor:

  • Creamos la capa de entrada, con valores iniciales en 0 y pesos aleatorios hacia la capa oculta.
  • Creamos la capa oculta, con valores iniciales en 0, un sesgo (bias) y pesos aleatorios hacia la capa de salida.
  • Creamos la capa de salida, con valores en 0 y un sesgo (bias).
  • Establecemos una tasa de aprendizaje de 0.1.

📌 Funciones de Activación: Sigmoide

La función sigmoide es una de las más utilizadas en redes neuronales porque permite normalizar los valores entre 0 y 1, lo que facilita la interpretación de los resultados.

sigmoid(value) {
        return 1 / (1 + Math.exp(-value));
    }

    sigmoidDerivative(value) {
        return value * (1 - value);
    }
  • sigmoid(value): Calcula el valor de la función sigmoide.
  • sigmoidDerivative(value): Calcula la derivada de la sigmoide, necesaria para el algoritmo de backpropagation.

🔮 Forward Propagation (Predicción)

El proceso de forward propagation consiste en tomar los valores de entrada y pasarlos a través de la red para obtener una predicción.

Predict(inputs) {
        // Capa de entrada
        for (let i = 0; i < this.inputSize; i++) {
            this.inputNeurons[i].value = inputs[i];
        }

        // Capa oculta
        for (let h = 0; h < this.hiddenSize; h++) {
            let sum = this.hiddenPerceptrons[h].bias;
            for (let i = 0; i < this.inputSize; i++) {
                sum += this.inputNeurons[i].value * this.inputNeurons[i].weights[h];
            }
            this.hiddenPerceptrons[h].value = this.sigmoid(sum);
        }

        // Capa de salida
        for (let o = 0; o < this.outputSize; o++) {
            let sum = this.outputNeurons[o].bias;
            for (let h = 0; h < this.hiddenSize; h++) {
                sum += this.hiddenPerceptrons[h].value * this.hiddenPerceptrons[h].weights[o];
            }
            this.outputNeurons[o].value = this.sigmoid(sum);
        }

        return this.outputNeurons.map(neuron => neuron.value);
    }

Aquí, pasamos los valores de entrada por la capa oculta y finalmente obtenemos una salida.

🔄 Backpropagation (Entrenamiento)

Para que la red aprenda, usamos el algoritmo de backpropagation, que ajusta los pesos minimizando el error.

Train(data, epochs) {
        const m = data.input.length; // Número de muestras

        for (let epoch = 0; epoch < epochs; epoch++) {
            let totalError = 0;

            for (let i = 0; i < m; i++) {
                // Paso 1: Forward propagation
                const prediction = this.Predict(data.input[i]);

                // Paso 2: Calcular errores en la salida
                const outputErrors = prediction.map((pred, o) => {
                    const error = data.output[i][o] - pred;
                    totalError += Math.pow(error, 2);
                    return error * this.sigmoidDerivative(pred);
                });

                // Paso 3: Backpropagation para la capa oculta
                const hiddenErrors = Array.from({ length: this.hiddenSize }, (_, h) =>
                    outputErrors.reduce((sum, outputError, o) =>
                        sum + outputError * this.hiddenPerceptrons[h].weights[o], 0)
                    * this.sigmoidDerivative(this.hiddenPerceptrons[h].value)
                );

                // Actualizar pesos y biases entre capa oculta y salida
                for (let o = 0; o < this.outputSize; o++) {
                    for (let h = 0; h < this.hiddenSize; h++) {
                        this.hiddenPerceptrons[h].weights[o] +=
                            this.learningRate * outputErrors[o] * this.hiddenPerceptrons[h].value;
                    }
                    this.outputNeurons[o].bias += this.learningRate * outputErrors[o];
                }

                // Actualizar pesos y biases entre entrada y capa oculta
                for (let h = 0; h < this.hiddenSize; h++) {
                    for (let i = 0; i < this.inputSize; i++) {
                        this.inputNeurons[i].weights[h] +=
                            this.learningRate * hiddenErrors[h] * this.inputNeurons[i].value;
                    }
                    this.hiddenPerceptrons[h].bias += this.learningRate * hiddenErrors[h];
                }
            }

            // Mostrar el error total al final de cada época
            if (epoch % 1000 === 0) {
                console.log(`Epoch ${epoch}, Error total: ${totalError / m}`);
            }
        }
    }

Aquí:

1. Calculamos los errores de salida.

2. Propagamos esos errores hacia atrás y ajustamos los pesos de la red.

🏋️‍♂️ Entrenando la Red Neuronal

Ahora entrenamos la red con la función XOR:

const nn = new NeuralNetwork(2, 4, 1);

const trainingData = {
    input: [
        [0, 0],
        [0, 1],
        [1, 0],
        [1, 1]
    ],
    output: [
        [0],
        [1],
        [1],
        [0]
    ]
};

nn.Train(trainingData, 10000);

console.log(nn.Predict([0, 0])); // Cerca de 0
console.log(nn.Predict([1, 1])); // Cerca de 0
console.log(nn.Predict([0, 1])); // Cerca de 1
console.log(nn.Predict([1, 0])); // Cerca de 1

🎉 ¡Listo! Nuestra red ha aprendido la función XOR. ¡Increíble! 🚀

Etiquetas:
javascript
Compartir:
Creado por:
Author photo

Jorge García

Fullstack developer