"""Red 1 de 4: el perceptrón de 1958 con AND y XOR, y una red con capa oculta (retropropagación) que sí resuelve XOR. Es la demo del vídeo «Cómo funcionan las redes neuronales por dentro: del perceptrón a GPT» (Frogames). Uso: uv run --with numpy python perceptron.py """ import numpy as np X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]], float) TABLAS = {"AND": np.array([0, 0, 0, 1]), "XOR": np.array([0, 1, 1, 0])} def perceptron(y, vueltas=20): """Regla de Rosenblatt: si falla, mueve los pesos hacia la respuesta correcta.""" w, b, aciertos = np.zeros(2), 0.0, [] for _ in range(vueltas): for x, t in zip(X, y): err = t - int(w @ x + b > 0) # 0 si acierta; +1 o -1 si falla w, b = w + err * x, b + err aciertos.append(int(sum(int(w @ x + b > 0) == t for x, t in zip(X, y)))) return w, b, aciertos def red_oculta(y, ocultas=4, pasos=5000, lr=1.0): """Dos capas con sigmoide, entrenadas con retropropagación (1986).""" r = np.random.default_rng(0) W1, b1 = r.normal(0, 1, (2, ocultas)), np.zeros(ocultas) W2, b2 = r.normal(0, 1, ocultas), 0.0 sig = lambda z: 1 / (1 + np.exp(-z)) for _ in range(pasos): h = sig(X @ W1 + b1) # capa oculta p = sig(h @ W2 + b2) # salida d2 = (p - y) / len(y) # error en la salida... d1 = np.outer(d2, W2) * h * (1 - h) # ...repartido hacia atrás: la retropropagación W2 -= lr * h.T @ d2; b2 -= lr * d2.sum() W1 -= lr * X.T @ d1; b1 -= lr * d1.sum(0) return p, W1.size + b1.size + W2.size + 1 for nombre, y in TABLAS.items(): w, b, aciertos = perceptron(y) print(f"Perceptrón con {nombre}: aciertos por vuelta {aciertos} (pesos {w}, sesgo {b})") p, n = red_oculta(TABLAS["XOR"]) print(f"Capa oculta con XOR ({n} pesos): salidas {p.round(3)} -> aciertos {int(((p > 0.5) == TABLAS['XOR']).sum())}/4")