Chismes del Lince 41: La neurona del Lince
Atardecía en Sierra Bermeja y las peridotitas, esas rocas rojizas que dan nombre a la sierra, parecían brasas entre los pinsapos.
El Lince de la Noche llevaba una hora sentado frente a su tableta, con dos montones de puntos: unos rojos y otros azules. Murmuraba cosas como «capa oculta número catorce» y «necesito más GPU».
—¿Qué estás construyendo? —preguntó la Gata Montesa, que había subido de un salto a una roca sin hacer ruido.
—Una red neuronal gigante —dijo el Lince, muy serio—. Diecisiete capas, miles de neuronas. Para decidir si un punto es rojo o azul.
La Gata miró la tableta. Luego cogió una ramita y dibujó una raya en el polvo entre los dos montones de puntos.
—¿Y si bastara con una raya?
—Eso no es inteligencia artificial —protestó el Lince—. Es una raya.
—Es una neurona —dijo ella—. Una sola. Hace una cuenta, decide, y si se equivoca, corrige. Pero antes de que me lo discutas, dime una cosa: cuando se equivoca, ¿cómo se entera?
El Lince abrió la boca, la cerró y miró la tableta. Diecisiete capas y ni una respuesta para eso.
—Empecemos por la raya —admitió.
Introducción
En este chisme construimos una neurona artificial y la vemos aprender. Una sola, sin diecisiete capas.
La página tiene un gráfico donde pones puntos rojos y azules. La neurona empieza con una recta al azar, se equivoca, se corrige y mueve la recta hasta separar los dos colores.
Al lado hay un dibujo de la neurona con sus pesos y una flecha naranja: el error que vuelve para corregirlos. Es la parte que casi nunca se ve y la más importante.
Este chisme es solo para el ordenador y no necesita placa. Está pensado para entender. En el chisme 42 aplicaremos lo aprendido con el móvil y la Heltec.
Por qué este chisme
En el chisme 40 el móvil reconocía gestos con k-NN. Dijimos entonces que eso no era una red neuronal: era un examen con los apuntes abiertos, porque guardaba todos los ejemplos y los consultaba cada vez.
Una neurona hace justo lo contrario: un examen de memoria. Mira los ejemplos, ajusta unos pocos números y se olvida de los ejemplos. Lo aprendido son solo esos números.
Las redes que reconocen caras o escriben textos funcionan con la misma idea, solo que con millones de números en lugar de tres. Si entiendes una neurona, entiendes lo esencial de todas.
Por eso este chisme va antes de aplicarlo a gestos. Primero se ve, con puntos que tú pones y una neurona que se corrige delante de ti. Luego se usa con datos reales.
La página funciona entera en el navegador: los puntos no salen de tu ordenador y no hay servidor ni cuenta.
Cómo funciona una neurona artificial
Una neurona artificial es una cuenta: recibe números, los multiplica, los suma y decide. No hay nada misterioso. Se llama neurona porque se inventó imitando, muy simplificada, a las neuronas reales, que suman las señales que reciben y «se disparan» si pasan de cierto nivel.
Las entradas y los pesos
Cada punto del gráfico tiene dos números entre 0 y 1: x1 (su posición horizontal) y x2 (la vertical). Son las entradas.
A cada entrada la neurona le asigna un peso: un número que dice cuánto le importa esa entrada y en qué sentido. Un peso positivo empuja hacia el azul, uno negativo hacia el rojo y uno cercano a 0 hace que la neurona casi ignore esa entrada.
Además hay un número que se suma siempre, el sesgo (b). Sirve para mover la recta sin girarla.
La cuenta y la decisión
suma = w_1 x_1 + w_2 x_2 + b
Si la suma es mayor que 0, la neurona contesta azul (1). Si no, contesta rojo (0).
La recta que ves en el gráfico es el sitio donde la suma vale exactamente 0. Los pesos la giran y el sesgo la desplaza. A un lado la suma es positiva y al otro negativa.
¿Cómo se entera de que se ha equivocado?
Es la pregunta de la Gata. La neurona no lo sabe por sí sola: se lo decimos nosotros al poner cada punto rojo o azul. Ese es el color correcto. La neurona compara su respuesta con el correcto:
error = correcto - salida
| Debía ser | Contestó | Error |
|---|---|---|
| Azul (1) | Rojo (0) | +1 |
| Rojo (0) | Azul (1) | −1 |
| Lo mismo | Lo mismo | 0 |
La fórmula con la que aprende
Por cada punto que falla, se corrige cada peso:
w_1 \leftarrow w_1 + tasa \cdot error \cdot x_1
w_2 \leftarrow w_2 + tasa \cdot error \cdot x_2
b \leftarrow b + tasa \cdot error
Si acierta, el error es 0 y no cambia nada. Si debía ser azul y dijo rojo, los pesos suben y la suma sube la próxima vez. Si debía ser rojo y dijo azul, bajan.
Se multiplica por x porque la entrada que más valía es la que más «culpa» tuvo. La tasa de aprendizaje es el tamaño del paso: pequeña, aprende despacio; grande, da saltos y puede pasarse.
Repasar todos los puntos una vez es una vuelta. Se repiten vueltas hasta que no queda ningún error.
Un ejemplo con números
Tasa 0,1; pesos w1 = 0,30, w2 = −0,20, b = 0,10. Un punto con x1 = 0,8 y x2 = 0,5 que debe ser rojo.
suma = 0,30×0,8 + (−0,20)×0,5 + 0,10 = 0,24 → contesta azul
error = 0 − 1 = −1
w1 = 0,30 + 0,1×(−1)×0,8 = 0,22
w2 = −0,20 + 0,1×(−1)×0,5 = −0,25
b = 0,10 + 0,1×(−1) = 0,00
suma nueva = 0,22×0,8 + (−0,25)×0,5 + 0 = 0,051
Sigue siendo mayor que 0, así que todavía dice azul. Una corrección no basta, pero la suma ha bajado de 0,24 a 0,05: se ha acercado. Por eso hacen falta muchas vueltas.
¿Vuelve la señal hacia atrás?
Sí, pero no la salida: vuelve el error. La señal va hacia delante (entradas, suma, respuesta). Luego se compara con el color correcto y ese error viaja de vuelta para corregir cada peso. En la página es la flecha naranja.
En las redes grandes, con muchas capas, ocurre lo mismo: el error se reparte hacia atrás capa por capa. Eso se llama retropropagación y es como se entrenan las redes modernas. Con una sola neurona es la versión más simple posible.
Qué necesitamos
Solo un ordenador con un navegador moderno (Chrome, Edge o Firefox) y un editor de texto para guardar la página. No hay placa, cables ni librerías: es un único archivo HTML.
La página está pensada para pantalla grande, con el gráfico a la izquierda y el dibujo de la neurona a la derecha. En un móvil se ve, pero apretada.
Cómo usar la página
- Copia el código de la sección siguiente en un archivo de texto y guárdalo como
neurona.html. Ábrelo con doble clic. - Pulsa Ejemplo fácil: aparecen dos grupos de puntos, rojos y azules, y la neurona con pesos al azar.
- Pulsa Un punto varias veces. En cada pulsación la neurona mira un punto, contesta y, si falla, corrige sus pesos. Mira la flecha naranja y los números «cambia +0,04» que aparecen en cada peso.
- Pulsa Una vuelta para repasar todos los puntos de golpe y Entrenar hasta que acierte para que repita vueltas solo. El gráfico de barras de abajo cuenta los errores de cada vuelta.
- Pulsa Preguntar, toca un sitio vacío del gráfico y mira qué contesta la neurona y la cuenta que ha hecho.
- Prueba con tus propios puntos: elige un color, toca el gráfico y vuelve a entrenar.
- Pulsa Ejemplo imposible y entrena. Es el que no se puede resolver con una recta: la neurona se rinde tras 200 vueltas y lo avisa.

Arriba hay un desplegable con toda la teoría de la sección anterior, por si quieres tenerla a mano mientras pruebas.
El botón Descargar CSV guarda tus puntos en un archivo en tu ordenador. No se envía a ningún sitio.
El código de la página
Es un solo archivo HTML. Si copias y pegas sin leer, lee al menos esto: solo la PARTE 1 es la neurona. Son unas 45 líneas y no hay magia. La PARTE 2 son los datos y la PARTE 3 es el dibujo y los botones, que no son necesarios para entender cómo aprende.
Guárdalo como neurona.html y ábrelo con doble clic.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Chisme 41 · La neurona del Lince (ordenador)</title>
<style>
/* Los colores, en un solo sitio: cámbialos y cambia toda la página */
:root { --fondo:#eef2ef; --texto:#10201c; --suave:#4d625b; --verde:#1f5b4a; --linea:#7b3fa0;
--aviso:#ffd23f; --corrige:#c2410c; --tarjeta:#ffffff; --borde:#c5d2cc; --rojo:#d9480f; --azul:#1971c2; }
@media (prefers-color-scheme: dark) {
:root { --fondo:#0f1a17; --texto:#e8f1ed; --suave:#9db3aa; --verde:#5fc4a2; --linea:#c79bff;
--tarjeta:#16251f; --borde:#2d443b; --corrige:#ffb86b; --rojo:#ff8c5a; --azul:#5aa9ff; }
}
body { margin:0; padding:1.5rem 1rem; background:var(--fondo); color:var(--texto);
font:18px/1.45 system-ui, sans-serif; text-align:center; }
h1 { font-size:1.5rem; margin:0 0 .3rem; }
h2 { font-size:1.15rem; margin:1.6rem 0 .4rem; }
p { color:var(--suave); margin:.3rem auto; max-width:36rem; }
button { font:inherit; font-weight:700; padding:.65rem 1rem; margin:.25rem;
border:2px solid var(--verde); background:var(--verde); color:var(--fondo);
border-radius:.9rem; cursor:pointer; touch-action:manipulation; }
button.claro { background:transparent; color:var(--verde); }
button.marcado { outline:3px solid var(--linea); outline-offset:2px; }
button:disabled { opacity:.45; cursor:not-allowed; }
select { font:inherit; padding:.3rem; border-radius:.5rem; }
canvas#plano { width:100%; max-width:36rem; aspect-ratio:1; display:block; margin:.6rem auto;
background:var(--tarjeta); border:2px solid var(--borde); border-radius:.8rem;
touch-action:manipulation; cursor:crosshair; }
canvas#neurona { width:100%; max-width:36rem; aspect-ratio:6/5; display:block; margin:.6rem auto;
background:var(--tarjeta); border:2px solid var(--borde); border-radius:.8rem; }
canvas#errores { width:100%; max-width:36rem; height:4rem; display:block; margin:.3rem auto;
border-bottom:2px solid var(--suave); }
.columnas { max-width:80rem; margin:0 auto; display:grid; grid-template-columns:1fr; gap:1rem; }
@media (min-width: 62rem) { .columnas { grid-template-columns:1fr 1fr; align-items:start; } }
.columnas h2 { margin-top:.4rem; }
details#explicacion { max-width:46rem; margin:1rem auto; text-align:left; background:var(--tarjeta);
border:2px solid var(--borde); border-radius:.9rem; padding:.2rem 1rem; }
details#explicacion summary { cursor:pointer; font-weight:700; color:var(--verde); padding:.6rem 0; }
details#explicacion h3 { font-size:1.05rem; margin:1.2rem 0 .2rem; color:var(--texto); }
details#explicacion p, details#explicacion li { color:var(--texto); max-width:none; margin:.35rem 0; font-size:1rem; }
details#explicacion pre { background:var(--fondo); border:1px solid var(--borde); border-radius:.6rem;
padding:.6rem .8rem; overflow-x:auto; font-size:.95rem; color:var(--texto); }
details#explicacion .naranja { color:var(--corrige); font-weight:700; }
#mensaje { font-weight:700; min-height:1.6rem; color:var(--texto); }
#respuesta { font-size:2.2rem; font-weight:700; color:var(--linea); min-height:3rem; }
#formula { font-family:ui-monospace, monospace; font-size:.95rem; color:var(--texto); }
#estadoNeurona { font-weight:700; color:var(--texto); }
.fila { max-width:36rem; margin:.5rem auto; display:flex; align-items:center; gap:.5rem;
justify-content:center; flex-wrap:wrap; }
.fila label { color:var(--suave); }
.oculto { display:none; }
.rojo { color:var(--rojo); font-weight:700; } .azul { color:var(--azul); font-weight:700; }
</style>
</head>
<body>
<h1>La neurona del Lince</h1>
<p>Una sola neurona artificial aprende a separar dos grupos de puntos. Tú pones los datos; ella mueve una recta hasta acertar. Pensado para la pantalla grande de un ordenador.</p>
<details id="explicacion">
<summary>¿Qué es una neurona artificial? Pesos, sesgo y la fórmula con la que aprende</summary>
<h3>1 · Una neurona artificial es una cuenta</h3>
<p>No es un trozo de cerebro ni nada misterioso: recibe números, los multiplica, los suma y decide. Se llama «neurona» porque se inventó imitando, de forma muy simplificada, a una neurona real, que suma las señales que le llegan y «se dispara» si pasan de cierto nivel.</p>
<h3>2 · Las entradas</h3>
<p>Son los números con los que trabaja. Aquí, cada punto del gráfico tiene dos: <b>x1</b> (su posición horizontal) y <b>x2</b> (la vertical), los dos entre 0 y 1. Al lado de cada punto hay también un color: el que <i>debería</i> contestar la neurona (rojo = 0, azul = 1).</p>
<h3>3 · Los pesos</h3>
<p>Un <b>peso</b> es un número que dice cuánto le importa a la neurona una entrada, y en qué sentido. Si el peso de x1 es grande y positivo, cuanto más a la derecha esté el punto, más empuja hacia el azul. Si es negativo, empuja hacia el rojo. Si está cerca de 0, la neurona casi ignora esa entrada. En el dibujo: <b>grosor</b> = cuánto manda; <b>color</b> = verde suma, morado resta.</p>
<h3>4 · El sesgo</h3>
<p>El <b>sesgo</b> (b) es un número que se suma siempre, pase lo que pase. Es como una entrada que vale siempre 1 y tiene su propio peso. Sirve para mover la recta sin girarla; sin él, la recta tendría que pasar siempre por la esquina (0, 0).</p>
<h3>5 · La cuenta y la decisión</h3>
<pre>suma = w1·x1 + w2·x2 + b
salida = 1 (azul) si suma > 0
0 (rojo) si no</pre>
<p>La recta del gráfico es el sitio donde la suma vale exactamente 0. Los pesos la <b>giran</b> y el sesgo la <b>desplaza</b>. A un lado de la recta la suma es positiva (azul); al otro, negativa (rojo).</p>
<h3>6 · Aprender es corregirse cuando se equivoca</h3>
<p>Quien dice cuál es la respuesta correcta eres tú, al poner cada punto rojo o azul. La neurona contesta y se compara con el color correcto:</p>
<pre>error = correcto − salida
debía ser azul (1) y contestó rojo (0) → error = +1
debía ser rojo (0) y contestó azul (1) → error = −1
acertó → error = 0</pre>
<h3>7 · La fórmula de actualización (la regla del perceptrón)</h3>
<pre>w1 nuevo = w1 + tasa × error × x1
w2 nuevo = w2 + tasa × error × x2
b nuevo = b + tasa × error</pre>
<ul>
<li>Si <b>acierta</b>, el error es 0 y no cambia nada. Solo se corrige lo que falla.</li>
<li>Si debía ser azul y dijo rojo (error +1), los pesos <b>suben</b>, y la suma sube la próxima vez. Si debía ser rojo y dijo azul (error −1), <b>bajan</b>.</li>
<li>Se multiplica por x porque la entrada que más valía fue la que más «tuvo la culpa»: se corrige más su peso.</li>
<li>La <b>tasa de aprendizaje</b> es el tamaño del paso. Pequeña: aprende despacio pero con calma. Grande: da saltos y puede pasarse de largo.</li>
<li>Una <b>vuelta</b> es repasar todos los puntos una vez, corrigiendo en cada fallo. Se repiten vueltas hasta que no hay errores.</li>
</ul>
<h3>8 · Un ejemplo con números</h3>
<p>Tasa = 0,1. Pesos: w1 = 0,30; w2 = −0,20; b = 0,10. Punto: x1 = 0,8; x2 = 0,5, que debe ser <b>rojo</b> (0).</p>
<pre>suma = 0,30×0,8 + (−0,20)×0,5 + 0,10 = 0,24
salida = 1 (azul) → se equivoca
error = 0 − 1 = −1
w1 = 0,30 + 0,1×(−1)×0,8 = 0,22
w2 = −0,20 + 0,1×(−1)×0,5 = −0,25
b = 0,10 + 0,1×(−1) = 0,00
suma nueva = 0,22×0,8 + (−0,25)×0,5 + 0 = 0,051</pre>
<p>Sigue siendo mayor que 0, así que aún dice azul: una corrección no basta, pero la suma ha bajado de 0,24 a 0,05, o sea, se ha acercado. Por eso se dan muchas vueltas. Pulsa «Un punto» y mira cómo cambian los números.</p>
<h3>9 · ¿Vuelve la señal hacia atrás?</h3>
<p>Sí, pero no la salida: lo que vuelve es el <span class="naranja">error</span>. La señal va hacia delante (entradas → suma → respuesta). Luego se compara con el color correcto y ese error viaja de vuelta para corregir cada peso: es la <span class="naranja">flecha naranja</span> del dibujo. En las redes grandes, con muchas capas de neuronas, ocurre lo mismo pero el error se reparte capa por capa hacia atrás. Eso se llama <b>retropropagación</b> y es como se entrenan las redes neuronales modernas. Aquí, con una sola neurona, es la versión más sencilla posible.</p>
<h3>10 · Lo que una neurona NO puede hacer</h3>
<p>Una neurona dibuja <b>una recta</b>. Si existe una recta que separa los dos colores, se demuestra que esta regla la encuentra en un número finito de vueltas. Si no existe (el «Ejemplo imposible»: rojos en dos esquinas y azules en las otras dos), seguirá corrigiéndose para siempre sin acertar. Para eso se necesitan varias neuronas colocadas en capas.</p>
</details>
<div class="columnas">
<!-- IZQUIERDA: los datos -->
<div>
<h2>1 · Los datos</h2>
<p>Elige un color y toca el gráfico para poner puntos. «Preguntar» sirve para ver qué contesta la neurona en cualquier sitio.</p>
<canvas id="plano" width="720" height="720" aria-label="Gráfico de puntos de dos colores y la recta que separa la neurona"></canvas>
<div class="fila">
<button id="tinta0" class="marcado" data-tinta="0">Punto <span class="rojo">rojo</span></button>
<button id="tinta1" class="claro" data-tinta="1">Punto <span class="azul">azul</span></button>
<button id="tinta2" class="claro" data-tinta="2">Preguntar</button>
</div>
<div class="fila">
<button id="ejemploSeparable" class="claro">Ejemplo fácil</button>
<button id="ejemploMezclado" class="claro">Ejemplo imposible</button>
<button id="quitarUltimo" class="claro">Quitar último</button>
<button id="borrarPuntos" class="claro">Borrar puntos</button>
</div>
</div>
<!-- DERECHA: la neurona -->
<div>
<h2>2 · La neurona</h2>
<p>Cada línea lleva un peso. Verde: suma. Morado: resta. Cuanto más gruesa, más manda. La flecha naranja es el error que vuelve a corregir los pesos.</p>
<canvas id="neurona" width="720" height="600" aria-label="Dibujo de la neurona: dos entradas, un sesgo, sus pesos y la salida"></canvas>
<p id="mensaje"></p>
<div id="respuesta" aria-live="polite"></div>
<div id="formula"></div>
<p id="estadoNeurona">Sin entrenar.</p>
<div class="fila">
<button id="unPunto" class="claro">Un punto</button>
<button id="unaVuelta">Una vuelta</button>
<button id="entrenar">Entrenar hasta que acierte</button>
<button id="nuevaNeurona" class="claro">Neurona nueva</button>
</div>
<div class="fila">
<label for="tasa">Tasa de aprendizaje</label>
<select id="tasa"><option value="0.02">0,02 (despacio)</option><option value="0.1" selected>0,1</option><option value="0.5">0,5 (a lo bruto)</option></select>
<button id="botonCSV" class="claro" disabled>Descargar CSV</button>
</div>
<p>Errores en cada vuelta (cuando llega a 0, ya sabe separar los dos grupos):</p>
<canvas id="errores" width="720" height="80" aria-label="Barras con los errores de cada vuelta de entrenamiento"></canvas>
</div>
</div>
<script>
/* ============================================================
PARTE 1 · LA NEURONA (léela: son unas 45 líneas, no hay magia)
Una neurona artificial hace tres cosas:
1. MULTIPLICA cada entrada por su peso: w1·x1 y w2·x2
2. SUMA todo, y suma también el sesgo (b): suma = w1·x1 + w2·x2 + b
3. DECIDE: si la suma es mayor que 0, contesta 1 (azul); si no, contesta 0 (rojo).
Aprender es corregir los pesos cuando se equivoca (la regla del perceptrón):
error = lo que debía contestar − lo que contestó (vale −1, 0 o +1)
cada peso cambia en: tasa · error · su entrada
La recta que ves es el sitio donde la suma vale exactamente 0.
============================================================ */
let tasa = 0.1; // cuánto corrige cada vez (tasa de aprendizaje)
const MAX_VUELTAS = 200; // si en 200 vueltas no acierta, nos rendimos
const azar = () => Math.round((Math.random() * 2 - 1) * 100) / 100;
function crearNeurona() { return { w1: azar(), w2: azar(), b: azar(), vuelta: 0, historial: [], cursor: 0, ultima: null }; }
// Una neurona nueva con pesos al azar. Si por casualidad ya acertara todo, le damos la vuelta a los pesos
// para que tenga algo que aprender (si no, no veríais cómo se corrige).
function neuronaConFallos(lista) {
const n = crearNeurona();
if (lista.length > 0 && contarErrores(n, lista) === 0) { n.w1 = -n.w1; n.w2 = -n.w2; n.b = -n.b; }
return n;
}
function suma(n, x1, x2) { return n.w1 * x1 + n.w2 * x2 + n.b; }
function contestar(n, x1, x2) { return suma(n, x1, x2) > 0 ? 1 : 0; }
function contarErrores(n, puntos) { return puntos.filter(p => contestar(n, p.x1, p.x2) !== p.c).length; }
// Corregir con UN punto: contesta, mira el error y cambia los pesos (la fórmula de actualización)
function corregir(n, p) {
const sumaAntes = suma(n, p.x1, p.x2);
const salida = sumaAntes > 0 ? 1 : 0;
const error = p.c - salida; // -1, 0 o +1
const d1 = tasa * error * p.x1, d2 = tasa * error * p.x2, db = tasa * error;
n.w1 += d1; n.w2 += d2; n.b += db;
return { p: p, sumaAntes: sumaAntes, salida: salida, error: error, d1: d1, d2: d2, db: db };
}
function unaVueltaDeEntrenamiento(n, puntos) { // repasa todos los puntos una vez
let ultima = null;
for (const p of puntos) {
const r = corregir(n, p);
if (r.error !== 0 || ultima === null) ultima = r; // nos quedamos con la última corrección real
}
n.ultima = ultima; n.cursor = 0;
n.vuelta++;
n.historial.push(contarErrores(n, puntos));
}
function unPuntoDeEntrenamiento(n, puntos) { // solo el siguiente punto; al llegar al final, cuenta una vuelta
if (n.cursor >= puntos.length) n.cursor = 0;
n.ultima = corregir(n, puntos[n.cursor]);
n.cursor++;
const finDeVuelta = n.cursor >= puntos.length;
if (finDeVuelta) { n.cursor = 0; n.vuelta++; n.historial.push(contarErrores(n, puntos)); }
return finDeVuelta;
}
/* ============================================================
PARTE 2 · LOS DATOS
Cada punto es { x1, x2, c }: dos números entre 0 y 1 y su color (0 rojo, 1 azul).
Los pones tú con el ratón (o el dedo). x1 es el eje horizontal y x2 el vertical.
(En el chisme 42 estos dos números saldrán de gestos del móvil.)
============================================================ */
const NOMBRES = ["Rojo", "Azul"];
let puntos = []; // todos los puntos
let neurona = crearNeurona();
let pregunta = null; // el último punto preguntado: { x1, x2, c }
let tinta = 0; // 0 rojo, 1 azul, 2 preguntar
const entre01 = v => Math.min(1, Math.max(0, v));
/* ============================================================
PARTE 3 · EL DIBUJO Y LOS BOTONES (NO ES NECESARIA para que la neurona funcione)
============================================================ */
const decir = t => document.getElementById("mensaje").textContent = t;
const coma = (v, d = 2) => v.toFixed(d).replace(".", ",");
function dibujar() {
const lienzo = document.getElementById("plano"), c = lienzo.getContext("2d");
const W = lienzo.width, H = lienzo.height, estilo = getComputedStyle(document.body);
const rojo = estilo.getPropertyValue("--rojo"), azul = estilo.getPropertyValue("--azul");
const texto = estilo.getPropertyValue("--texto"), suave = estilo.getPropertyValue("--suave");
const n = neurona, lista = puntos;
const X = x1 => x1 * W, Y = x2 => (1 - x2) * H;
c.clearRect(0, 0, W, H);
// Fondo: en cada cuadradito, el color que contestaría la neurona
const celda = 24;
c.globalAlpha = 0.14;
for (let i = 0; i < W / celda; i++) for (let j = 0; j < H / celda; j++) {
c.fillStyle = contestar(n, (i + 0.5) * celda / W, 1 - (j + 0.5) * celda / H) ? azul : rojo;
c.fillRect(i * celda, j * celda, celda, celda);
}
c.globalAlpha = 1;
// La recta donde la suma vale 0: w1·x1 + w2·x2 + b = 0
c.save();
c.beginPath(); c.rect(0, 0, W, H); c.clip();
if (Math.abs(n.w1) > 1e-6 || Math.abs(n.w2) > 1e-6) {
c.beginPath(); c.strokeStyle = texto; c.lineWidth = 3;
if (Math.abs(n.w2) > Math.abs(n.w1)) {
c.moveTo(X(-1), Y(-(n.b + n.w1 * -1) / n.w2)); c.lineTo(X(2), Y(-(n.b + n.w1 * 2) / n.w2));
} else {
c.moveTo(X(-(n.b + n.w2 * -1) / n.w1), Y(-1)); c.lineTo(X(-(n.b + n.w2 * 2) / n.w1), Y(2));
}
c.stroke();
}
c.restore();
// Los puntos; con aro negro los que la neurona falla
lista.forEach(function (p) {
c.beginPath(); c.arc(X(p.x1), Y(p.x2), 11, 0, 6.2832);
c.fillStyle = p.c ? azul : rojo; c.fill();
if (contestar(n, p.x1, p.x2) !== p.c) { c.lineWidth = 3; c.strokeStyle = texto; c.stroke(); }
});
// El último punto que ha corregido a la neurona, con aro naranja
if (!pregunta && n.ultima && lista.indexOf(n.ultima.p) >= 0) {
c.beginPath(); c.arc(X(n.ultima.p.x1), Y(n.ultima.p.x2), 19, 0, 6.2832);
c.lineWidth = 4; c.strokeStyle = estilo.getPropertyValue("--corrige"); c.stroke();
}
// El punto de la pregunta: hueco, con el borde del color que contesta la neurona
if (pregunta) {
c.beginPath(); c.arc(X(pregunta.x1), Y(pregunta.x2), 14, 0, 6.2832);
c.fillStyle = estilo.getPropertyValue("--tarjeta"); c.fill();
c.lineWidth = 4; c.strokeStyle = pregunta.c ? azul : rojo; c.stroke();
}
c.fillStyle = suave; c.font = "24px system-ui";
c.textAlign = "right"; c.fillText("x1 →", W - 8, H - 8);
c.textAlign = "left"; c.fillText("↑ x2", 8, 28);
dibujarNeurona();
dibujarErrores();
escribirEstado();
}
// El dibujo de la neurona: entradas a la izquierda, pesos en las líneas, salida a la derecha
// y, en naranja, el error que VUELVE para corregir los pesos.
function flecha(c, x0, y0, x1, y1, color) {
const ang = Math.atan2(y1 - y0, x1 - x0);
c.beginPath(); c.moveTo(x1, y1);
c.lineTo(x1 - 16 * Math.cos(ang - 0.45), y1 - 16 * Math.sin(ang - 0.45));
c.lineTo(x1 - 16 * Math.cos(ang + 0.45), y1 - 16 * Math.sin(ang + 0.45));
c.closePath(); c.fillStyle = color; c.fill();
}
const con = v => (v > 0 ? "+" : v < 0 ? "−" : "") + coma(Math.abs(v));
function dibujarNeurona() {
const lienzo = document.getElementById("neurona"), c = lienzo.getContext("2d");
const W = lienzo.width, H = lienzo.height, estilo = getComputedStyle(document.body);
const verde = estilo.getPropertyValue("--verde"), morado = estilo.getPropertyValue("--linea");
const texto = estilo.getPropertyValue("--texto"), suave = estilo.getPropertyValue("--suave");
const tarjeta = estilo.getPropertyValue("--tarjeta"), rojo = estilo.getPropertyValue("--rojo"), azul = estilo.getPropertyValue("--azul");
const naranja = estilo.getPropertyValue("--corrige");
const n = neurona, u = pregunta ? null : n.ultima; // u = la última corrección (si no estamos preguntando)
const alto = 450; // la parte de arriba es la neurona; debajo, el camino de vuelta
c.clearRect(0, 0, W, H);
const fuente = pregunta || (u ? u.p : null); // el punto que se está mostrando
const ent = [{ y: alto * 0.2, nombre: "x1", valor: fuente ? fuente.x1 : null, w: n.w1, d: u ? u.d1 : null },
{ y: alto * 0.5, nombre: "x2", valor: fuente ? fuente.x2 : null, w: n.w2, d: u ? u.d2 : null },
{ y: alto * 0.8, nombre: "1", valor: 1, w: n.b, d: u ? u.db : null, esSesgo: true }];
const xe = 80, xs = W - 120, ys = alto * 0.5, r = 36, rs = 60;
const px = xe + r + (xs - rs - xe - r) * 0.42; // dónde se escribe cada peso
// las líneas, con grosor según el peso
ent.forEach(function (e) {
c.beginPath(); c.moveTo(xe + r, e.y); c.lineTo(xs - rs, ys);
c.lineWidth = 1 + Math.min(14, Math.abs(e.w) * 8); c.strokeStyle = e.w >= 0 ? verde : morado;
c.globalAlpha = 0.85; c.stroke(); c.globalAlpha = 1;
});
// los pesos, sobre las líneas (y lo que han cambiado, en naranja)
ent.forEach(function (e) {
const py = e.y + (ys - e.y) * 0.42;
const t = (e.esSesgo ? "b=" : "w=") + coma(e.w);
c.font = "bold 24px ui-monospace, monospace"; c.textAlign = "center"; c.textBaseline = "middle";
const ancho = Math.max(c.measureText(t).width, 120) + 10, dosLineas = e.d !== null;
c.fillStyle = tarjeta; c.fillRect(px - ancho / 2, py - (dosLineas ? 30 : 17), ancho, dosLineas ? 60 : 34);
c.fillStyle = e.w >= 0 ? verde : morado; c.fillText(t, px, py - (dosLineas ? 12 : 0));
if (dosLineas) {
c.font = "bold 22px ui-monospace, monospace";
c.fillStyle = Math.abs(e.d) < 1e-9 ? suave : naranja;
c.fillText(Math.abs(e.d) < 1e-9 ? "sin cambio" : "cambia " + con(e.d), px, py + 14);
}
});
// los círculos de entrada
ent.forEach(function (e) {
c.beginPath(); c.arc(xe, e.y, r, 0, 6.2832); c.fillStyle = tarjeta; c.fill();
c.lineWidth = 3; c.strokeStyle = texto; c.stroke();
c.fillStyle = texto; c.font = "bold 24px system-ui"; c.textAlign = "center"; c.textBaseline = "middle";
c.fillText(e.valor === null ? e.nombre : (e.esSesgo ? "1" : coma(e.valor)), xe, e.y);
c.fillStyle = suave; c.font = "20px system-ui"; c.textBaseline = "alphabetic";
c.fillText(e.esSesgo ? "sesgo" : e.nombre, xe, e.y - r - 8);
});
// la neurona
const s = pregunta ? suma(n, pregunta.x1, pregunta.x2) : (u ? u.sumaAntes : null);
const salida = pregunta ? pregunta.c : (u ? u.salida : null);
c.beginPath(); c.arc(xs, ys, rs, 0, 6.2832);
c.fillStyle = salida === null ? tarjeta : (salida ? azul : rojo); c.globalAlpha = salida === null ? 1 : 0.9; c.fill(); c.globalAlpha = 1;
c.lineWidth = 4; c.strokeStyle = texto; c.stroke();
c.textAlign = "center"; c.textBaseline = "middle";
c.fillStyle = salida === null ? texto : "#ffffff"; c.font = "bold 22px system-ui";
c.fillText("suma", xs, ys - 14);
c.font = "bold 26px ui-monospace, monospace";
c.fillText(s === null ? "?" : coma(s), xs, ys + 16);
c.fillStyle = suave; c.font = "20px system-ui"; c.textBaseline = "alphabetic";
c.fillText(salida === null ? "¿mayor que 0? → azul" : "contesta " + NOMBRES[salida], xs, ys - rs - 12);
// ---- EL CAMINO DE VUELTA (naranja): el error vuelve a los pesos
const yb = alto + 40, activo = u !== null;
const col = activo && u.error !== 0 ? naranja : suave;
c.setLineDash([10, 8]); c.lineWidth = 4; c.strokeStyle = col; c.globalAlpha = activo ? 1 : 0.45;
c.beginPath(); c.moveTo(xs, ys + rs + 4); c.lineTo(xs, yb); c.lineTo(px, yb); c.lineTo(px, alto - 70); c.stroke();
c.setLineDash([]);
flecha(c, px, yb, px, alto - 70, col);
c.globalAlpha = 1;
c.textAlign = "center"; c.fillStyle = col;
if (activo) {
c.font = "bold 22px system-ui";
c.fillText("Debía ser " + NOMBRES[u.p.c] + " (" + u.p.c + ") y contestó " + NOMBRES[u.salida] + " (" + u.salida + ")", W / 2, yb + 38);
c.fillText(u.error === 0 ? "error = 0 → acertó: los pesos no cambian" : "error = " + u.p.c + " − " + u.salida + " = " + (u.error > 0 ? "+1" : "−1") + " → vuelve a corregir los pesos", W / 2, yb + 68);
} else {
c.font = "20px system-ui";
c.fillText("Aquí vuelve el error (correcto − respuesta) para corregir los pesos.", W / 2, yb + 38);
c.fillText("Pulsa «Un punto» o «Una vuelta» para verlo.", W / 2, yb + 66);
}
}
function dibujarErrores() {
const lienzo = document.getElementById("errores"), c = lienzo.getContext("2d");
const estilo = getComputedStyle(document.body);
c.clearRect(0, 0, lienzo.width, lienzo.height);
const h = neurona.historial.slice(-60);
if (!h.length) return;
const max = Math.max(1, ...h), ancho = lienzo.width / 60;
c.fillStyle = estilo.getPropertyValue("--linea");
h.forEach((e, i) => { const alto = e / max * (lienzo.height - 4); c.fillRect(i * ancho + 1, lienzo.height - alto, ancho - 2, alto); });
}
function escribirEstado() {
const n = neurona, lista = puntos;
const errores = contarErrores(n, lista);
document.getElementById("estadoNeurona").textContent =
"Vuelta " + n.vuelta + (n.cursor > 0 ? " (punto " + n.cursor + " de " + lista.length + ")" : "") + " · errores: " + errores + " de " + lista.length +
" · w1 = " + coma(n.w1) + " · w2 = " + coma(n.w2) + " · sesgo = " + coma(n.b);
document.getElementById("botonCSV").disabled = lista.length === 0;
}
function explicar(p) { // enseña la cuenta que hizo la neurona
const n = neurona, s = suma(n, p.x1, p.x2);
document.getElementById("formula").textContent =
coma(n.w1) + "×" + coma(p.x1) + " + " + coma(n.w2) + "×" + coma(p.x2) + " + " + coma(n.b) + " = " + coma(s) +
(s > 0 ? " → mayor que 0 → " : " → no mayor que 0 → ") + NOMBRES[p.c];
}
function preguntarA(x1, x2) {
const n = neurona;
pregunta = { x1: x1, x2: x2, c: contestar(n, x1, x2) };
const nombre = NOMBRES[pregunta.c];
document.getElementById("respuesta").textContent = "La neurona dice: " + nombre;
explicar(pregunta);
dibujar();
}
function avisoDePuntos() {
const l = puntos;
if (l.filter(p => p.c === 0).length === 0 || l.filter(p => p.c === 1).length === 0) {
decir("Necesito puntos de los dos colores para poder entrenar.");
return false;
}
return true;
}
// Toca el gráfico
document.getElementById("plano").addEventListener("click", function (e) {
const r = this.getBoundingClientRect();
const x1 = entre01((e.clientX - r.left) / r.width), x2 = entre01(1 - (e.clientY - r.top) / r.height);
if (tinta === 2) { preguntarA(x1, x2); return; }
puntos.push({ x1: x1, x2: x2, c: tinta });
pregunta = null; document.getElementById("respuesta").textContent = ""; document.getElementById("formula").textContent = "";
decir("");
dibujar();
});
document.querySelectorAll("[data-tinta]").forEach(function (b) {
b.onclick = function () {
tinta = parseInt(b.dataset.tinta, 10);
document.querySelectorAll("[data-tinta]").forEach(o => { o.classList.toggle("marcado", o === b); o.classList.toggle("claro", o !== b); });
};
});
function ejemplo(separable) { // dos nubes de puntos
const l = puntos; l.length = 0;
const nube = (cx, cy, clase, cuantos, dispersion) => {
for (let i = 0; i < cuantos; i++) l.push({ x1: entre01(cx + (Math.random() - 0.5) * dispersion), x2: entre01(cy + (Math.random() - 0.5) * dispersion), c: clase });
};
if (separable) { nube(0.25, 0.7, 0, 8, 0.3); nube(0.75, 0.3, 1, 8, 0.3); }
else { nube(0.3, 0.3, 0, 5, 0.25); nube(0.7, 0.7, 0, 5, 0.25); nube(0.3, 0.7, 1, 5, 0.25); nube(0.7, 0.3, 1, 5, 0.25); } // una X: ninguna recta lo separa
pregunta = null; neurona = neuronaConFallos(l); document.getElementById("respuesta").textContent = ""; document.getElementById("formula").textContent = "";
decir(separable ? "Dos grupos separados: una recta basta." : "Rojos en dos esquinas y azules en las otras dos: ¿hay alguna recta que lo separe?");
dibujar();
}
document.getElementById("ejemploSeparable").onclick = () => ejemplo(true);
document.getElementById("ejemploMezclado").onclick = () => ejemplo(false);
function quitarUltimo() { puntos.pop(); pregunta = null; neurona.ultima = null; neurona.cursor = 0; dibujar(); }
function borrarPuntos() { puntos.length = 0; pregunta = null; neurona.ultima = null; neurona.cursor = 0; decir(""); dibujar(); }
document.getElementById("quitarUltimo").onclick = quitarUltimo;
document.getElementById("borrarPuntos").onclick = borrarPuntos;
// Entrenar
let reloj = null;
function pararEntrenamiento() { if (reloj) { clearInterval(reloj); reloj = null; } document.getElementById("entrenar").textContent = "Entrenar hasta que acierte"; }
function unPaso() { // una vuelta + dibujar
const n = neurona;
pregunta = null;
unaVueltaDeEntrenamiento(n, puntos);
const errores = n.historial[n.historial.length - 1];
dibujar();
if (errores === 0) { pararEntrenamiento(); decir("¡Aprendido! Ha necesitado " + n.vuelta + " vueltas."); return true; }
if (n.vuelta >= MAX_VUELTAS) { pararEntrenamiento(); decir("Sigue fallando después de " + MAX_VUELTAS + " vueltas: no hay ninguna recta que separe estos puntos."); return true; }
return false;
}
document.getElementById("unaVuelta").onclick = function () { pararEntrenamiento(); if (avisoDePuntos()) { decir(""); neurona.cursor = 0; pregunta = null; unPaso(); } };
document.getElementById("unPunto").onclick = function () { // paso a paso: un solo punto cada vez
pararEntrenamiento();
if (!avisoDePuntos()) return;
pregunta = null; document.getElementById("respuesta").textContent = ""; document.getElementById("formula").textContent = "";
const n = neurona, finDeVuelta = unPuntoDeEntrenamiento(n, puntos), r = n.ultima;
decir("Punto " + (finDeVuelta ? puntos.length : n.cursor) + " de " + puntos.length + ": contestó " + NOMBRES[r.salida] + ", debía ser " + NOMBRES[r.p.c] +
(r.error === 0 ? " → acertó, no cambia nada." : " → error " + (r.error > 0 ? "+1" : "−1") + ", corrige los pesos.") +
(finDeVuelta ? " Fin de la vuelta " + n.vuelta + ": errores " + n.historial[n.historial.length - 1] + "." : ""));
dibujar();
};
document.getElementById("entrenar").onclick = function () {
if (reloj) { pararEntrenamiento(); return; }
if (!avisoDePuntos()) return;
decir("Entrenando…");
this.textContent = "Parar";
reloj = setInterval(unPaso, 250);
};
document.getElementById("nuevaNeurona").onclick = function () {
pararEntrenamiento(); neurona = neuronaConFallos(puntos); pregunta = null;
document.getElementById("respuesta").textContent = ""; document.getElementById("formula").textContent = "";
decir("Neurona nueva, con pesos al azar."); dibujar();
};
document.getElementById("tasa").onchange = function () { tasa = parseFloat(this.value); };
document.getElementById("botonCSV").onclick = function () { // se descarga EN TU ordenador; no sale a ningún servidor
const texto = ["x1,x2,color"].concat(puntos.map(p => p.x1.toFixed(3) + "," + p.x2.toFixed(3) + "," + NOMBRES[p.c])).join("\n");
const enlace = document.createElement("a");
enlace.href = URL.createObjectURL(new Blob([texto], { type: "text/csv" }));
enlace.download = "neurona.csv";
enlace.click();
};
dibujar();
</script>
</body>
</html>
Cómo probarlo
Antes de pulsar cada botón, escribe tu predicción en el cuaderno. Después pulsa y comprueba. Si fallas, mejor: ahora ya sabes algo que no sabías.
Lee y predice
- Pulsa Ejemplo fácil y Un punto. La neurona corrige un peso. ¿Crees que ese peso va a subir o a bajar? Mira el signo del error en la flecha naranja antes de contestar.
- Pulsa Un punto otra vez hasta que salga uno que acierte. ¿Qué pone en cada peso? ¿Por qué?
- Pulsa Borrar puntos, pon solo puntos rojos y pulsa Una vuelta. ¿Qué crees que dirá la página y por qué no puede entrenar?
- Elige Ejemplo fácil y añade un punto rojo en medio del grupo azul. ¿Crees que la neurona conseguirá acertar todos? Pulsa Entrenar y comprueba cuántas vueltas aguanta.
- Elige Preguntar y toca justo encima de la recta negra. ¿Qué vale la suma? ¿Y un poco a cada lado?
Cambios guiados
Abre el archivo en un editor de texto, haz un solo cambio cada vez, guarda y recarga la página.
- En la PARTE 1, cambia
const MAX_VUELTAS = 200;por20. Pulsa Ejemplo imposible y Entrenar. ¿Cuánto tarda ahora en rendirse? - Cambia
let tasa = 0.1;por1. Entrena el ejemplo fácil cinco veces. ¿Necesita más o menos vueltas que con 0,1? Apunta los números. - Cambia el color de la flecha: en el
:rootdel principio, busca--corrige:#c2410c;y pon otro color. ¿Qué partes del dibujo cambian? - Dentro de
corregir, quita* p.x1de la línea que calculad1. Esa neurona ya no usa el valor de la entrada para corregir x1. ¿Sigue aprendiendo el ejemplo fácil? ¿En todos los intentos?
Qué estamos midiendo realmente
Aquí no medimos nada del mundo: los puntos los pones tú. Lo que sí medimos es cuánto se equivoca la neurona, y conviene saber qué significa ese número.
Qué es el error de la página
El «Errores: 3 de 16» cuenta cuántos de los puntos que le has dado contesta mal. Es un error de entrenamiento: mide si la neurona ha aprendido los ejemplos que ha visto, no si sabrá contestar bien con puntos nuevos.
Por eso existe el botón Preguntar: pone a prueba a la neurona con un sitio donde no había ningún punto. Si tus datos son pocos o están muy pegados, la recta que separa los ejemplos puede fallar en cuanto aparece uno nuevo.
Aprender no es lo mismo que acertar
Con el ejemplo fácil, la neurona llega a 0 errores y se detiene. Pero hay muchas rectas que separan bien los mismos puntos, y a ella le vale la primera que encuentra, aunque quede casi pegada a un punto.
Empieza dos veces con pesos distintos y verás rectas distintas con el mismo resultado: 0 errores. Que dos neuronas distintas acierten igual con los mismos datos no quiere decir que contesten igual en cualquier sitio.
Lo que una neurona no puede aprender
La neurona solo sabe dibujar una recta. Si los puntos no se pueden separar así, como en el ejemplo imposible, no hay pesos que lo arreglen. La página lo detecta de una forma muy sencilla: tras 200 vueltas sigue fallando y se rinde. Que no haya conseguido 0 errores no prueba que sea imposible; solo que 200 vueltas no han bastado. Aquí lo sabemos porque diseñamos nosotros ese ejemplo.
La solución, en las redes reales, es poner varias neuronas en capas. No lo hacemos aquí, pero ya sabes por qué existen.
Los datos mandan
La neurona aprende lo que le enseñas y nada más. Si pones los puntos rojos siempre arriba y los azules siempre abajo, aprenderá que «arriba es rojo», aunque eso no sea cierto fuera de tu dibujo. Una IA que parece muy segura puede estar repitiendo los defectos de sus datos.
Medir no significa medir bien: un 0 de errores solo dice que la neurona ha memorizado tus ejemplos con una recta.
Para el aula
Pensado para una sesión de 50 minutos con 1º de Bachillerato, en parejas y con un ordenador por pareja.
Actividad 1: la neurona a mano (15 minutos)
Cada pareja coge los pesos y el punto del ejemplo con números y repite la cuenta con calculadora: suma, error y los tres pesos nuevos. Después pulsan Un punto y comprueban que la página da el mismo resultado. Si no coincide, hay que averiguar quién se ha equivocado, ellos o la página.
Actividad 2: el reto de los datos (15 minutos)
Cada pareja dibuja con la página un conjunto de unos 12 puntos de dos colores, sin decir si se puede separar con una recta o no, y deja el ordenador sin entrenar. Las parejas cambian de sitio. La que llega mira el gráfico y predice por escrito: ¿se podrá separar? ¿En cuántas vueltas? Después pulsa Entrenar y comprueba. Gana quien se acerque más.
Si quieren analizar los datos con más calma, el botón Descargar CSV guarda los puntos en un archivo que se abre en una hoja de cálculo.
Actividad 3: investigación con la tasa (15 minutos)
Pregunta: ¿depende el número de vueltas de la tasa de aprendizaje?
- Hipótesis por escrito antes de empezar.
- Con el ejemplo fácil, entrenar 5 veces con 0,02, 5 con 0,1 y 5 con 0,5, y apuntar las vueltas en una tabla.
- Calcular la media de cada tasa y comparar.
- Contestar: ¿qué conclusiones podemos sacar con 15 pruebas? ¿Cuáles no?
Cada intento empieza con pesos al azar, así que dos intentos con la misma tasa no dan lo mismo. Esa variación es parte de la lección: con pocas pruebas, la diferencia entre dos tasas puede ser solo azar.
Preguntas para discutir
- ¿Qué variable mide el contador de errores y qué unidad tiene?
- ¿Por qué las dos neuronas de dos parejas pueden acertar igual con los mismos puntos y tener pesos distintos?
- ¿Qué datos tendrías que añadir para saber si la neurona sabe contestar bien con puntos nuevos?
- ¿Qué pasaría si pones por error un punto con el color equivocado en medio de un grupo?
- ¿Por qué no basta una neurona para el ejemplo imposible? ¿Cuántas harían falta?
Conexión con AIDARAC
Este chisme es el primer paso del recorrido sensor → dato → conjunto de datos → análisis → conclusión. Aquí los datos los ponen los alumnos; en el chisme 42 los pondrá el acelerómetro del móvil.
Retos
Reto 1. Haz que funcione. Pon tus propios puntos, rojos a un lado y azules al otro, y entrena hasta llegar a 0 errores.
Reto 2. Modifica algo. Cambia la tasa de aprendizaje, el número máximo de vueltas o los colores de la página, y anota qué ha cambiado.
Reto 3. Añade un ejemplo. En la función ejemplo, añade un tercer tipo de conjunto, por ejemplo dos nubes en diagonal, con su propio botón. ¿Se puede separar con una recta?
Reto 4. Guarda los datos. Haz tres conjuntos distintos, descárgalos con Descargar CSV y ábrelos en una hoja de cálculo. ¿Cuántos puntos de cada color tiene cada uno?
Reto 5. Representa los datos. Modifica unPaso para guardar w1, w2 y b al final de cada vuelta, expórtalos en un CSV y dibuja cómo cambian los tres pesos con las vueltas. ¿Hacia dónde se estabilizan?
Reto 6. Automatiza el proceso. Escribe una función que entrene 20 neuronas nuevas seguidas con la misma tasa y devuelva la media de vueltas. Repite con las tres tasas de la página.
Reto 7. Haz que otro dispositivo participe. Sigue en el chisme 42: los dos números de cada punto ya no los pondrás con el ratón, sino con gestos del móvil, y la placa Heltec enseñará lo que decide la neurona.
Reto 8. Conviértelo en una investigación. Pregunta: ¿cuántos puntos de entrenamiento hacen falta para que la neurona acierte con puntos nuevos? Entrena con 4, 8, 16 y 32 puntos de un mismo patrón, prueba cada neurona con 20 puntos nuevos usando Preguntar, apunta los aciertos y dibuja la gráfica. Formula una hipótesis antes de empezar.
Qué hemos aprendido
Al atardecer, el Lince había borrado las diecisiete capas y miraba la raya de la Gata en el polvo, con una neurona de verdad corrigiéndose en la tableta.
—Entonces la neurona no sabe nada —dijo—. Solo tiene tres números y los va cambiando cuando se equivoca.
—Eso es todo —dijo la Gata—. Lo demás es hacerlo muchas veces y con muchos más números.
—¿Y mañana?
—Mañana le damos algo mejor que puntos: gestos de verdad. Y que la placa nos cuente lo que decide.
Lo que nos llevamos:
- Una neurona artificial es una cuenta: multiplica cada entrada por un peso, suma y decide con un umbral.
- Aprender es corregir los pesos con el error de cada punto: error = correcto − salida.
- Lo que vuelve a corregir no es la salida, sino el error: eso es lo esencial de la retropropagación.
- Una neurona solo dibuja una recta; lo que no se puede separar así necesita más neuronas.
- 0 errores de entrenamiento no es lo mismo que saber contestar bien con datos nuevos.
En el chisme 42 usaremos la misma neurona con los gestos del móvil y la Heltec.
Para seguir investigando
Otros chismes de la serie que enlazan con este:
- Chismes del Lince 18, parte 1: entrenar una IA con Teachable Machine, otra forma de que una máquina aprenda de ejemplos:
- Chismes del Lince 22: Simón dice con gestos, donde el movimiento se convierte en datos:
- Todos los chismes de la serie:
Fuente externa: la entrada «Perceptrón» de Wikipedia describe la regla de aprendizaje de esta neurona y su limitación con datos no separables por una recta:
Para seguir con muchas más neuronas, capas y parámetros, hay un simulador abierto y gratuito que se ejecuta en el navegador: el Neural Network Playground de TensorFlow, creado por Daniel Smilkov y Shan Carter. Permite elegir el conjunto de datos, la tasa de aprendizaje y añadir capas ocultas con sus neuronas:
Una prueba para hacer allí: elige el conjunto de datos de tipo XOR, que es el mismo patrón de nuestro «Ejemplo imposible» (rojos en dos esquinas y azules en las otras dos), y comprueba qué ocurre con una sola neurona y qué cambia al añadir una capa oculta.
Este chisme es un prototipo educativo para entender una idea, no una herramienta para tomar decisiones.



