IA Intermedio

El Perceptrón desde Cero: escribe la matemática de una red neuronal simple en Vanilla JS, sin librerías

Construye un perceptrón desde cero en Vanilla JS y entiende cómo una neurona artificial calcula, se equivoca y ajusta sus pesos sin librerías.

Publicado: 29/03/2026 Por: Juan Felipe Orozco Cortés Duración: 55 min Nivel: Intermedio
Contenido del tutorial

🧠 IA desde fundamentos · Código fuente antes que magia

El perceptrón no es magia: es una suma, una decisión y un ajuste.

Vamos a abrir una neurona artificial mínima y leerla desde dentro: cómo calcula, cómo se equivoca y cómo corrige sus pesos usando JavaScript puro.

🟧 HTML 🎨 CSS 🟨 Vanilla JS 🧮 Arrays 📉 Error ⚙️ Pesos y bias

La tesis exacta de esta pieza es simple y fuerte: antes de usar librerías de IA, conviene construir la neurona más pequeña que todavía puede aprender. No porque el perceptrón resuelva todo, sino porque deja a la vista la mecánica que después queda enterrada dentro de sistemas mucho más grandes: entradas, pesos, bias, predicción, error y ajuste.

Muchísima gente entra al mundo de la inteligencia artificial cuando la matemática ya está encapsulada en frameworks, tensores y utilidades de entrenamiento. El problema de ese camino es que uno aprende a consumir modelos antes de aprender a leer cómo nace una decisión. Entonces la IA empieza a sentirse como algo que se invoca, no como algo que se entiende.

Aquí vamos a corregir exactamente eso. Este tutorial demuestra que una neurona artificial mínima cabe en JavaScript básico, arrays normales y unas pocas funciones bien escritas. No habrá cajas negras ni promesas vacías. Habrá una suma ponderada, una activación, una medida de error y una regla de aprendizaje supervisado lo bastante pequeña como para seguirla línea por línea.

Lo que esta pieza quiere romper

La idea de que la inteligencia artificial empieza cuando importas una librería enorme y dejas de mirar lo que ocurre por dentro.

Lo que esta pieza quiere instalar

Que una neurona artificial mínima ya puede escribirse completa, leerse completa y entenderse como un sistema matemático ejecutable.

La promesa real del tutorial

Salir viendo al perceptrón no como una palabra famosa, sino como una máquina concreta que calcula, se equivoca y corrige sus pesos.

Antes de hablar de redes profundas, conviene escribir la neurona más simple posible y ver cómo una máquina convierte error en dirección de corrección.

Ese es el corazón del tutorial. No vamos a describir el perceptrón desde fuera como si fuera teoría vieja de manual. Vamos a abrir su pequeño laboratorio visual, leer sus archivos y seguir la matemática paso a paso hasta que el lector pueda decir algo mucho más valioso que “ya usé una librería”: ya entendí qué hace una neurona artificial por dentro.

  • La IA no empieza en modelos gigantes: empieza en una unidad matemática pequeña.
  • Ese sistema puede escribirse completo con JavaScript puro y arrays normales.
  • La pieza no promete una demo bonita sin fondo: promete comprensión real del mecanismo.
  • El lector no va a consumir una neurona: va a leer su código fuente como sistema.
Alt: Diagrama del perceptrón con entradas, pesos, bias, predicción y corrección por error.
Figura 1. Anatomía mínima del perceptrón: las entradas se combinan con pesos y bias, producen una predicción, y el error guía la corrección de los parámetros.

Estructura de archivos: el laboratorio mínimo del perceptrón

Antes de entrar en funciones, fórmulas y ajustes de pesos, conviene mirar el proyecto como lo miraría alguien que acaba de abrir una carpeta de código por primera vez. Este tutorial no necesita una arquitectura gigantesca porque la idea que quiere enseñar también es mínima: una neurona artificial pequeña, visible y completamente recorrible. Por eso el proyecto cabe en muy pocos archivos y cada uno cumple un papel muy preciso dentro del laboratorio.

Estructura base del proyecto 📁 Proyecto pequeño · idea grande
│   index.html
│   README.md
│
└───src
        script.js
        styles.css

A primera vista parece poca cosa, y justamente esa es una de las virtudes del tutorial. Aquí no queremos esconder la lógica detrás de una arquitectura aparatosa. Queremos que el lector pueda mirar el proyecto y sentir que lo entiende completo. Cada archivo funciona como una pieza del experimento: uno monta el laboratorio, otro lo viste para que pensar resulte más fácil, otro contiene la matemática viva y otro deja documentado cómo correrlo o reutilizarlo después.

🟧 index.html

Es la mesa del laboratorio. Aquí viven los controles, las métricas, la tabla del dataset, la bitácora y el bloque donde se resume el código central. No está para decorar: está para volver visible el estado interno del perceptrón mientras aprende.

🎨 src/styles.css

Es la capa de claridad visual. Su trabajo no es “hacer bonito” el proyecto por capricho, sino organizar la atención del lector: separar paneles, dar jerarquía a las métricas, volver legible la tabla y hacer que la matemática no se vea como ruido plano.

🟨 src/script.js

Aquí vive el cerebro matemático del tutorial. Define los datasets, guarda el estado del modelo, calcula la suma ponderada, aplica la activación, mide el error, ajusta los pesos y renderiza el resultado en pantalla. Es el archivo donde el perceptrón deja de ser concepto y se vuelve comportamiento.

📝 README.md

Es la puerta de entrada para quien descargue el proyecto. Resume qué contiene, cómo ejecutarlo y para qué sirve. En una pieza didáctica como esta, el README no es relleno: es el puente entre leer el tutorial y ponerse a experimentar por cuenta propia.

Un buen proyecto didáctico no impresiona por la cantidad de archivos, sino por lo claramente que cada archivo deja ver una parte del sistema.

La idea importante aquí es arquitectónica: este proyecto está diseñado para que puedas recorrerlo sin perderte. index.html muestra, styles.css ordena, script.js piensa y README.md abre la puerta a seguir explorando. Con ese mapa ya claro, el siguiente paso natural es entrar al primero de esos archivos y entender por qué el HTML de este tutorial funciona como un auténtico panel de control del experimento.

El HTML: construyendo el laboratorio visual

Ahora sí podemos entrar al primer archivo real del proyecto. Y conviene hacerlo con una idea clara: este HTML no está aquí para decorar una demo. Está aquí para convertir una neurona artificial en algo visible, manipulable y entendible. En vez de esconder la matemática detrás de una interfaz opaca, el documento organiza el experimento para que el lector pueda tocar parámetros, observar estados y seguir el aprendizaje paso a paso.

Dicho de otra forma: index.html funciona como la mesa del laboratorio. Cada bloque del archivo expone una parte del sistema. El encabezado instala la promesa del tutorial, el panel de configuración deja modificar el experimento, las métricas muestran el estado interno del modelo, la tabla deja ver la propagación muestra por muestra y la bitácora conserva la historia matemática de cada ajuste.

La clave arquitectónica aquí es simple: el HTML no “sabe” entrenar al perceptrón, pero sí sabe organizar el espacio donde ese entrenamiento se vuelve legible. Por eso este archivo importa tanto: convierte una idea matemática en una interfaz que enseña.

Hero inicial

Instala la tesis del tutorial y le dice al lector qué va a construir: una neurona mínima escrita en JavaScript puro, sin librerías ni cajas negras.

Panel de configuración

Permite cambiar dataset, tasa de aprendizaje, cantidad de épocas por lote y semilla inicial. Es la zona donde el lector deja de ser espectador y empieza a experimentar.

Estado del modelo

Muestra bias, pesos, época y errores. Es la ventana más directa al interior del perceptrón mientras aprende.

Tabla y bitácora

La tabla deja ver la propagación por muestra y la bitácora convierte cada época en un relato matemático legible. Juntas hacen visible el aprendizaje.

Fíjate además en algo importante: casi todos los elementos clave tienen un id. Eso no es casualidad. Es la forma en que script.js va a encontrar exactamente qué control leer, qué valor actualizar y qué bloque reescribir cuando el modelo cambie. El HTML, entonces, no solo estructura la interfaz: también define los puntos de contacto entre la presentación y la lógica del sistema.

Un buen HTML didáctico no solo muestra contenido: organiza el experimento para que el lector pueda ver cómo piensa el sistema.

Veamos ahora el archivo completo y después lo desarmamos bloque por bloque.

<!doctype html>
<html lang="es">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Perceptrón desde Cero — Vanilla JS</title>
  <meta name="description" content="Perceptrón desde cero en Vanilla JS: propagación, error y ajuste de pesos usando arrays, sin librerías." />
  <link rel="stylesheet" href="src/styles.css" />
</head>
<body>
  <header class="hero">
    <div class="wrap">
      <p class="kicker">TuCodigoCotidiano · Inteligencia artificial desde fundamentos</p>
      <h1>El Perceptrón desde Cero</h1>
      <p class="lead">Escribe la matemática de una red neuronal simple usando <strong>arrays en Vanilla JS</strong>, sin librerías. Verás la propagación, el error y el ajuste de pesos paso a paso.</p>
    </div>
  </header>

  <main class="wrap layout">
    <section class="card">
      <h2>Configuración</h2>

      <div class="grid-2">
        <label>
          Dataset
          <select id="dataset">
            <option value="and">AND</option>
            <option value="or">OR</option>
          </select>
        </label>

        <label>
          Tasa de aprendizaje
          <input id="learningRate" type="number" min="0.001" step="0.01" value="0.1" />
        </label>

        <label>
          Épocas por lote
          <input id="epochsPerRun" type="number" min="1" step="1" value="10" />
        </label>

        <label>
          Semilla inicial
          <input id="seed" type="number" min="1" step="1" value="42" />
        </label>
      </div>

      <div class="actions">
        <button id="initBtn">Inicializar perceptrón</button>
        <button id="stepBtn">Entrenar 1 época</button>
        <button id="runBtn">Entrenar lote</button>
        <button id="resetBtn" class="secondary">Reiniciar</button>
      </div>

      <p class="hint">Usamos activación escalón: si la suma ponderada es mayor o igual que 0, la salida es 1; si no, 0.</p>
    </section>

    <section class="card">
      <h2>Estado del modelo</h2>
      <div class="metrics">
        <div class="metric"><span class="label">Bias</span><span id="biasValue" class="value">—</span></div>
        <div class="metric"><span class="label">Peso w1</span><span id="w1Value" class="value">—</span></div>
        <div class="metric"><span class="label">Peso w2</span><span id="w2Value" class="value">—</span></div>
        <div class="metric"><span class="label">Época</span><span id="epochValue" class="value">0</span></div>
        <div class="metric"><span class="label">Errores en última época</span><span id="errorsValue" class="value">0</span></div>
      </div>
    </section>

    <section class="card wide">
      <h2>Dataset y propagación</h2>
      <table>
        <thead>
          <tr>
            <th>x1</th>
            <th>x2</th>
            <th>objetivo</th>
            <th>suma = w1·x1 + w2·x2 + b</th>
            <th>predicción</th>
            <th>error = objetivo - predicción</th>
          </tr>
        </thead>
        <tbody id="datasetBody"></tbody>
      </table>
    </section>

    <section class="card wide">
      <h2>Bitácora matemática</h2>
      <pre id="log"></pre>
    </section>

    <section class="card wide">
      <h2>Código central del tutorial</h2>
      <pre><code id="codeSample"></code></pre>
    </section>
  </main>

  <script src="src/script.js"></script>
</body>
</html>

Ya vimos el archivo completo. Ahora conviene hacer algo más útil que simplemente volver a leerlo: desarmarlo por capas. La mejor forma de entender un HTML didáctico como este no es memorizar etiquetas, sino preguntarse qué papel cumple cada bloque dentro del laboratorio del perceptrón.

Lo importante aquí no es solo “qué elementos hay”, sino por qué están organizados así. Este documento fue escrito para enseñar una máquina matemática pequeña. Eso significa que cada sección del HTML está pensada para volver visible una parte del sistema: la promesa, los controles, el estado interno, el flujo de datos y el registro del aprendizaje.

1 El <head>: preparar el documento antes del experimento

El tutorial empieza con una decisión madura: antes de mostrar nada, el documento se prepara bien. <meta charset="utf-8"> asegura compatibilidad con caracteres en español, viewport hace que la interfaz responda en móvil, el <title> le da identidad a la pestaña del navegador y la meta descripción resume de inmediato el propósito del proyecto.

Después aparece <link rel="stylesheet" href="src/styles.css">. Esto ya instala una separación arquitectónica importante: el HTML define la estructura del laboratorio, pero no se encarga de su presentación. El estilo vive fuera porque el proyecto quiere enseñar también una buena división de responsabilidades.

El archivo empieza bien porque deja claro, desde la primera línea útil, que esto no es una demo improvisada: es un sistema pequeño, pero ordenado.
2 El bloque <header class="hero">: instalar la tesis del tutorial

El encabezado cumple una función editorial, no solo visual. La clase hero marca la zona donde el tutorial presenta su identidad, su tema y su promesa. El kicker posiciona la pieza dentro del universo de TuCodigoCotidiano, el <h1> deja claro qué se construirá y el párrafo lead resume la experiencia: escribir la matemática de una neurona simple sin depender de librerías.

Esta parte importa porque el lector todavía no está entrenando nada, pero ya está entrando en el marco mental correcto. El HTML aquí no muestra datos: prepara la lectura. Le dice al usuario que no viene a consumir magia, sino a recorrer una neurona artificial desde dentro.

3 El <main class="wrap layout">: la mesa donde vive todo el laboratorio

Cuando empieza el <main>, el proyecto deja el terreno de la promesa y entra al terreno del experimento. Las clases wrap y layout no son casuales: una controla el ancho legible del contenido y la otra prepara la distribución espacial del laboratorio. Es decir, aquí el HTML ya está pensando en cómo se va a recorrer visualmente el sistema.

Dentro de ese contenedor principal aparecen varias <section class="card">. Esa repetición enseña otra buena práctica: cada parte importante del proyecto vive dentro de una unidad reconocible. En lugar de una masa plana de elementos, el documento construye zonas separadas: configuración, estado, tabla, bitácora y resumen de código.

4 La sección “Configuración”: donde el lector empieza a intervenir

Esta sección convierte el tutorial en un experimento interactivo. El select con id dataset permite alternar entre AND y OR, mientras que los input controlan la tasa de aprendizaje, la cantidad de épocas por lote y la semilla inicial. El HTML, por tanto, no solo muestra información: abre puntos de entrada para modificar el comportamiento del modelo.

Los botones refuerzan eso todavía más. initBtn enciende el experimento, stepBtn permite observar una sola época, runBtn deja ver el aprendizaje acumulado y resetBtn reinicia el laboratorio. Esa secuencia no es arbitraria: está pensada para enseñar la dinámica del perceptrón a distintas velocidades.

5 La sección “Estado del modelo”: hacer visible la memoria interna

Una de las decisiones más fuertes del HTML es reservar un bloque entero para el estado del sistema. Bias, peso w1, peso w2, época y errores no se quedan ocultos dentro de JavaScript: el documento les da sitio visible. Eso cambia por completo la experiencia del lector, porque ahora el aprendizaje deja de ser una caja negra y pasa a ser algo observable.

Los id como biasValue, w1Value o errorsValue son fundamentales. Esos marcadores convierten cada métrica en un punto de actualización directa para script.js. El HTML aquí actúa como interfaz de salida del cerebro matemático.

6 La tabla del dataset: convertir la propagación en observación concreta

La sección Dataset y propagación es donde la matemática empieza a dejar huella visual. La tabla no está puesta para “rellenar” la interfaz, sino para mostrar exactamente qué ocurre con cada muestra: entradas, objetivo, suma ponderada, predicción y error. Eso vuelve visible el recorrido que hace la neurona antes de decidir.

El detalle importante es el <tbody id="datasetBody">. El HTML no escribe manualmente las filas porque esas filas dependen del estado actual del modelo. JavaScript las regenerará después de cada inicialización o entrenamiento. Dicho simple: la tabla es una superficie donde el estado matemático se imprime en tiempo real.

7 Bitácora y bloque de código: dos formas distintas de enseñar lo mismo

Los dos bloques finales son especialmente inteligentes desde el punto de vista didáctico. <pre id="log"> guarda la historia del aprendizaje: inicialización, época, sumas, errores y ajustes. En cambio, <code id="codeSample"> resume la lógica central del tutorial en una forma más conceptual.

Uno muestra el proceso vivo; el otro muestra la estructura. Uno deja seguir la narración matemática; el otro fija la idea esencial del algoritmo. Juntos convierten la interfaz en algo más que una demo: la convierten en un entorno de aprendizaje.

La bitácora cuenta lo que el perceptrón hizo. El bloque de código recuerda por qué pudo hacerlo.
8 La idea de fondo: este HTML no decora, instrumenta

Si uno mira el archivo completo con calma, aparece la idea central de esta sección: el HTML no sabe entrenar al perceptrón, pero sí sabe construir el escenario donde ese entrenamiento se vuelve inteligible. Y eso es muchísimo más importante de lo que parece.

Este documento organiza un experimento. Define entradas, salidas, puntos de control y zonas de observación. Gracias a eso, cuando lleguemos a script.js, no veremos solo funciones sueltas: veremos una neurona artificial dialogando con una interfaz diseñada para dejarla pensar a la vista del lector.

Ahora sí, con el HTML ya diseccionado por capas, tiene sentido pasar al archivo que le da forma visual a todo esto. Porque una cosa es que el laboratorio exista, y otra muy distinta es que esté presentado de una manera que ayude a pensar. Ahí es exactamente donde entra src/styles.css.

El JavaScript: donde el perceptrón deja de ser interfaz y se vuelve sistema

Hasta aquí hemos construido el laboratorio y le hemos dado una forma visual legible. Pero el perceptrón todavía no existe de verdad mientras todo eso siga siendo solo HTML y CSS. La neurona aparece en el momento en que entra src/script.js, porque ahí ya no estamos describiendo el experimento: lo estamos poniendo a funcionar.

Este archivo cumple varios trabajos al mismo tiempo. Define los datasets con los que el modelo va a aprender, guarda el estado interno del sistema, conecta la interfaz con el DOM, inicializa pesos reproducibles, calcula la propagación hacia adelante, mide el error, ajusta parámetros, entrena por épocas y finalmente vuelve a pintar el resultado en pantalla.

Dicho de otra forma: si index.html era la mesa del laboratorio y styles.css organizaba la atención del lector, script.js es el archivo que piensa. Aquí el perceptrón deja de ser promesa editorial y pasa a ser una máquina mínima que recibe entradas, calcula una salida, compara contra un objetivo y corrige sus pesos cuando se equivoca.

En este punto del tutorial ya no vamos a mirar una interfaz. Vamos a mirar una neurona artificial escrita en JavaScript puro.

Conviene además leer este archivo con una idea clara en la cabeza: no fue escrito como una lista arbitraria de funciones, sino como una secuencia lógica de responsabilidades. Primero aparecen los datos, luego la memoria del sistema, después los enlaces con el DOM, luego la inicialización, después la matemática de la predicción, luego el entrenamiento y al final la traducción de todo eso a la interfaz.

  • DATASETS define el problema que el perceptrón intentará aprender.
  • state guarda la memoria viva del modelo mientras cambia.
  • els conecta la lógica con los elementos del documento.
  • initializePerceptron() prepara el experimento con pesos iniciales.
  • weightedSum(), activation() y forward() convierten entradas en predicción.
  • trainOneEpoch() y trainBatch() hacen visible el aprendizaje.
  • render() convierte el estado matemático en algo que se puede observar.

Veamos entonces el archivo completo. Después lo vamos a desmontar por capas, porque la mejor forma de entender esta neurona no es memorizar funciones aisladas, sino seguir el orden con el que el sistema pasa de datos binarios a aprendizaje real.

const DATASETS = {
  and: [
    { x: [0, 0], y: 0 },
    { x: [0, 1], y: 0 },
    { x: [1, 0], y: 0 },
    { x: [1, 1], y: 1 },
  ],
  or: [
    { x: [0, 0], y: 0 },
    { x: [0, 1], y: 1 },
    { x: [1, 0], y: 1 },
    { x: [1, 1], y: 1 },
  ],
};

const state = {
  weights: [0, 0],
  bias: 0,
  epoch: 0,
  lastErrors: 0,
  datasetName: "and",
  learningRate: 0.1,
};

const els = {
  dataset: document.getElementById("dataset"),
  learningRate: document.getElementById("learningRate"),
  epochsPerRun: document.getElementById("epochsPerRun"),
  seed: document.getElementById("seed"),
  initBtn: document.getElementById("initBtn"),
  stepBtn: document.getElementById("stepBtn"),
  runBtn: document.getElementById("runBtn"),
  resetBtn: document.getElementById("resetBtn"),
  biasValue: document.getElementById("biasValue"),
  w1Value: document.getElementById("w1Value"),
  w2Value: document.getElementById("w2Value"),
  epochValue: document.getElementById("epochValue"),
  errorsValue: document.getElementById("errorsValue"),
  datasetBody: document.getElementById("datasetBody"),
  log: document.getElementById("log"),
  codeSample: document.getElementById("codeSample"),
};

function makeSeededRandom(seed) {
  let s = Number(seed) || 1;
  return function () {
    s = (s * 1664525 + 1013904223) % 4294967296;
    return s / 4294967296;
  };
}

function round(value, digits = 4) {
  return Number(value.toFixed(digits));
}

function initializePerceptron() {
  const random = makeSeededRandom(els.seed.value);
  state.weights = [
    round(random() * 2 - 1),
    round(random() * 2 - 1),
  ];
  state.bias = round(random() * 2 - 1);
  state.epoch = 0;
  state.lastErrors = 0;
  state.datasetName = els.dataset.value;
  state.learningRate = Number(els.learningRate.value);
  appendLog(
    `Inicialización\n` +
    `w1 = ${state.weights[0]}, w2 = ${state.weights[1]}, b = ${state.bias}\n` +
    `dataset = ${state.datasetName.toUpperCase()}, tasa = ${state.learningRate}\n`
  );
  render();
}

function weightedSum(x) {
  return state.weights[0] * x[0] + state.weights[1] * x[1] + state.bias;
}

function activation(sum) {
  return sum >= 0 ? 1 : 0;
}

function forward(sample) {
  const sum = weightedSum(sample.x);
  const prediction = activation(sum);
  const error = sample.y - prediction;
  return { sum, prediction, error };
}

function trainOneEpoch() {
  const dataset = DATASETS[state.datasetName];
  let errors = 0;
  let chunk = [`Época ${state.epoch + 1}`];

  dataset.forEach((sample, index) => {
    const { sum, prediction, error } = forward(sample);

    chunk.push(
      `Muestra ${index + 1}: x=[${sample.x.join(", ")}], y=${sample.y}\n` +
      `  suma = (${round(state.weights[0])}·${sample.x[0]}) + (${round(state.weights[1])}·${sample.x[1]}) + ${round(state.bias)} = ${round(sum)}\n` +
      `  predicción = ${prediction}\n` +
      `  error = ${sample.y} - ${prediction} = ${error}`
    );

    if (error !== 0) {
      errors += 1;
      const oldW1 = state.weights[0];
      const oldW2 = state.weights[1];
      const oldB = state.bias;

      state.weights[0] = round(state.weights[0] + state.learningRate * error * sample.x[0]);
      state.weights[1] = round(state.weights[1] + state.learningRate * error * sample.x[1]);
      state.bias = round(state.bias + state.learningRate * error);

      chunk.push(
        `  ajuste:\n` +
        `    w1 = ${oldW1} + (${state.learningRate}·${error}·${sample.x[0]}) = ${state.weights[0]}\n` +
        `    w2 = ${oldW2} + (${state.learningRate}·${error}·${sample.x[1]}) = ${state.weights[1]}\n` +
        `    b  = ${oldB} + (${state.learningRate}·${error}) = ${state.bias}`
      );
    } else {
      chunk.push(`  ajuste: no cambia nada porque error = 0`);
    }
  });

  state.epoch += 1;
  state.lastErrors = errors;
  appendLog(chunk.join("\n") + "\n");
  render();
}

function trainBatch() {
  const epochs = Number(els.epochsPerRun.value) || 1;
  for (let i = 0; i < epochs; i += 1) {
    trainOneEpoch();
    if (state.lastErrors === 0) {
      appendLog(`Convergencia alcanzada en la época ${state.epoch}.\n`);
      break;
    }
  }
}

function appendLog(text) {
  els.log.textContent = `${text}\n${els.log.textContent}`.trim();
}

function renderTable() {
  const dataset = DATASETS[state.datasetName];
  els.datasetBody.innerHTML = dataset.map(sample => {
    const { sum, prediction, error } = forward(sample);
    const rowClass = error === 0 ? "good" : "bad";
    return `
      <tr class="${rowClass}">
        <td>${sample.x[0]}</td>
        <td>${sample.x[1]}</td>
        <td>${sample.y}</td>
        <td>${round(sum)}</td>
        <td>${prediction}</td>
        <td>${error}</td>
      </tr>
    `;
  }).join("");
}

function renderMetrics() {
  els.biasValue.textContent = round(state.bias);
  els.w1Value.textContent = round(state.weights[0]);
  els.w2Value.textContent = round(state.weights[1]);
  els.epochValue.textContent = state.epoch;
  els.errorsValue.textContent = state.lastErrors;
}

function renderCodeSample() {
  els.codeSample.textContent = `function weightedSum(x, weights, bias) {
  return x[0] * weights[0] + x[1] * weights[1] + bias;
}

function activation(sum) {
  return sum >= 0 ? 1 : 0;
}

function forward(sample, weights, bias) {
  const sum = weightedSum(sample.x, weights, bias);
  const prediction = activation(sum);
  const error = sample.y - prediction;
  return { sum, prediction, error };
}

function update(weights, bias, sample, learningRate) {
  const { error } = forward(sample, weights, bias);

  weights[0] = weights[0] + learningRate * error * sample.x[0];
  weights[1] = weights[1] + learningRate * error * sample.x[1];
  bias = bias + learningRate * error;

  return { weights, bias };
}`;
}

function render() {
  renderMetrics();
  renderTable();
  renderCodeSample();
}

els.initBtn.addEventListener("click", initializePerceptron);
els.stepBtn.addEventListener("click", trainOneEpoch);
els.runBtn.addEventListener("click", trainBatch);
els.resetBtn.addEventListener("click", () => {
  els.log.textContent = "";
  initializePerceptron();
});
els.dataset.addEventListener("change", initializePerceptron);

initializePerceptron();

Cierre: escribiste una neurona, no solo la usaste

Llegados a este punto, el recorrido completo ya se ve con mucha claridad. Empezamos desmontando una idea que suele aparecer envuelta en demasiada abstracción: la idea de que una red neuronal solo puede entenderse desde herramientas enormes o desde conceptos lejanos. Y terminamos en un lugar mucho más interesante: una neurona artificial mínima, legible, ejecutable y escrita con herramientas que cualquier desarrollador puede seguir.

Eso es lo que vuelve valioso este tutorial. No se limitó a mostrar una demo llamativa. Construyó algo mejor para aprender: una máquina pequeña que deja ver todas sus costuras. Entradas, pesos, bias, predicción, error, ajuste, épocas, convergencia e interfaz. Todo quedó visible.

Lo que ya entendiste

Que una neurona artificial mínima cabe en muy poco código y que su lógica puede seguirse línea por línea sin cajas negras.

Lo que ya viste funcionar

Cómo una suma ponderada se convierte en decisión, cómo el error se convierte en corrección y cómo esa corrección termina cambiando el modelo.

Lo que también quedó claro

Que esta neurona no puede aprender cualquier cosa y que su límite abre la puerta natural hacia arquitecturas más profundas.

Hoy no consumiste una red neuronal como caja cerrada. Hoy abriste una neurona artificial, la leíste desde dentro y entendiste cómo aprende.

Esa diferencia importa muchísimo. Porque cuando alguien entiende esta escala mínima, deja de mirar los modelos más grandes como estructuras inaccesibles. Empieza a verlos como sistemas compuestos por capas, reglas y mecanismos que también pueden desarmarse. Y esa mirada cambia por completo la forma de estudiar este tema.

Además, el tutorial no termina en una conclusión cerrada, sino en una frontera fértil. Ya sabes qué puede hacer una sola neurona y ya sabes dónde falla. Ese borde conceptual es exactamente el punto desde el que conviene seguir. Porque la siguiente pregunta no es decorativa: qué necesitas agregar para que una máquina pueda aprender un problema que una sola línea no puede separar.

  • Ya entendiste el perceptrón como sistema, no como palabra de manual.
  • Ya viste cómo el error guía el ajuste de pesos.
  • Ya viste por qué AND y OR sí funcionan.
  • Ya viste por qué XOR obliga a pensar en una arquitectura más rica.
La continuación natural de este tutorial es una pieza sobre XOR, perceptrón multicapa y backpropagation desde cero. Porque el lector ya no necesita más presentación: ahora necesita el siguiente mecanismo.

Y ahí está el mayor logro de esta pieza. No solo explicó un código fuente. Preparó la mente del lector para el siguiente salto. Ya no estás en el punto de “quiero probar algo”, sino en uno mucho más potente: quiero entender qué arquitectura hace falta cuando una sola neurona deja de alcanzar.

Estás viendo solo el 60% del contenido. Hazte Premium para acceder al tutorial completo.

Comunidad

Comentarios y valoraciones

No hay comentarios aún. ¡Sé el primero en opinar!