Si alguna vez usaste una librería de machine learning y te preguntaste qué pasa debajo del .fit(), el perceptrón es un buen punto de partida. No porque sea el modelo más útil hoy para producción, sino porque te obliga a mirar la matemática mínima que hace posible una clasificación binaria: pesos, sesgo, suma ponderada y una función de activación.
En este artículo vamos a construir un perceptrón en Python desde cero, sin TensorFlow, sin PyTorch y sin atajos raros. La idea no es que memorices fórmulas, sino que entiendas por qué un perceptrón aprende, cuándo falla y cómo se conecta con redes neuronales más grandes.
Qué es un perceptrón y por qué te conviene entenderlo
Un perceptrón es un clasificador lineal. Toma varias entradas numéricas, las multiplica por pesos, suma todo y decide una salida binaria. En su forma más simple, responde con 0 o 1. Eso ya te dice mucho: solo puede separar datos que sean linealmente separables.
Si lo aterrizas a ejemplos reales, piensa en un filtro básico de aprobación de crédito con dos variables: ingreso mensual y nivel de deuda. Un perceptrón podría aprender una frontera que diga “aprueba” o “rechaza” si los datos siguen una separación más o menos lineal. Si la relación es más compleja, como detectar fraude con patrones no lineales, el perceptrón solo no alcanza.
Lo valioso aquí es la base. Entender un perceptrón te ayuda a leer mejor conceptos como gradient descent, backpropagation y capas densas. También te da contexto para no tratar las redes neuronales como cajas mágicas. La documentación de PyTorch sobre nn.Linear y torch.nn parte justamente de esa idea de combinación lineal más activación: https://pytorch.org/docs/stable/nn.html
La intuición matemática
La salida del perceptrón se calcula así:
y = f(w1*x1 + w2*x2 + ... + wn*xn + b)
Donde:
x1...xnson tus entradasw1...wnson los pesosbes el sesgo o biasfes una función de activación
Si la suma ponderada supera cierto umbral, el modelo devuelve 1. Si no, devuelve 0. En la práctica, ese umbral suele estar embebido en el sesgo y la función de activación. Matemáticamente, la frontera de decisión es una recta en 2D, un plano en 3D o un hiperplano en más dimensiones.
Eso explica por qué el perceptrón funciona bien para problemas lineales y mal para problemas como XOR. XOR no se puede separar con una sola línea recta. Ese caso clásico es el motivo por el que las redes multicapa importan tanto.
La matemática mínima que necesitas
La parte más útil del perceptrón no es la teoría abstracta, sino las operaciones concretas que ejecuta. Si tienes dos entradas, la fórmula se vuelve fácil de visualizar. Supón x = [2, 3], w = [0.4, -0.2] y b = 0.1. La suma ponderada sería:
(2 * 0.4) + (3 * -0.2) + 0.1 = 0.8 - 0.6 + 0.1 = 0.3
Si usas una función escalón con umbral en 0, la salida sería 1. Si el resultado fuera negativo, devolvería 0. Esa simplicidad es útil porque te deja ver exactamente qué está haciendo el modelo.
La función de activación más clásica es la step function. No es diferenciable, así que no sirve para entrenar redes profundas con backpropagation, pero sí sirve para entender la idea de decisión binaria. En perceptrones históricos, el aprendizaje se hacía ajustando pesos cuando el modelo se equivocaba, no con gradientes sofisticados.
Pesos, sesgo y frontera de decisión
Los pesos controlan cuánto aporta cada entrada. Si un peso es grande y positivo, esa variable empuja la salida hacia 1. Si es negativo, la empuja hacia 0. El sesgo desplaza la frontera de decisión. Sin bias, la recta siempre pasaría por el origen, lo cual limita bastante al modelo.
Un ejemplo concreto: si quieres clasificar correos como spam usando solo dos señales, como número de enlaces y número de palabras sospechosas, los pesos te dirían cuánto importa cada señal. El sesgo te permite mover la frontera para que no dependa de que ambas variables sean exactamente cero, algo que en datos reales casi nunca ocurre.
Regla de aprendizaje del perceptrón
La regla clásica de actualización es simple:
w = w + learning_rate * (y_true - y_pred) * x
b = b + learning_rate * (y_true - y_pred)
Si el modelo acertó, el error es 0 y no cambias nada. Si se equivocó, ajustas en la dirección correcta. Esa idea es la base de un montón de algoritmos posteriores: medir error y corregir parámetros.
La tasa de aprendizaje, o learning_rate, controla el tamaño del paso. Si es muy alta, puedes oscilar y no converger. Si es muy baja, aprendes lento. En perceptrones simples, valores como 0.1 suelen ser un buen punto de partida para experimentar.
Construcción del perceptrón en Python
Ahora sí, vamos al código. No necesitamos frameworks para implementar un perceptrón básico. Con Python estándar y math o numpy alcanza. Para este ejemplo, usaremos listas y funciones simples para que veas cada paso.
Primero, definimos el modelo y la función de activación. Mantenerlo explícito ayuda a entender qué entra y qué sale.
from typing import List
class Perceptron:
def __init__(self, n_inputs: int, learning_rate: float = 0.1):
self.weights = [0.0 for _ in range(n_inputs)]
self.bias = 0.0
self.learning_rate = learning_rate
def activation(self, z: float) -> int:
return 1 if z >= 0 else 0
def predict(self, inputs: List[float]) -> int:
z = sum(w * x for w, x in zip(self.weights, inputs)) + self.bias
return self.activation(z)
Este código hace tres cosas. Inicializa pesos en cero, calcula la suma ponderada y aplica la función escalón. La clase todavía no aprende, solo predice. Eso es deliberado: separar predicción y entrenamiento te ayuda a leer mejor el flujo.
Entrenamiento paso a paso
El entrenamiento itera sobre ejemplos etiquetados. Cada ejemplo tiene una entrada x y una etiqueta real y_true. Si la predicción falla, actualizas pesos y sesgo. Si acierta, no haces nada.
from typing import List, Tuple
class Perceptron:
def __init__(self, n_inputs: int, learning_rate: float = 0.1):
self.weights = [0.0 for _ in range(n_inputs)]
self.bias = 0.0
self.learning_rate = learning_rate
def activation(self, z: float) -> int:
return 1 if z >= 0 else 0
def predict(self, inputs: List[float]) -> int:
z = sum(w * x for w, x in zip(self.weights, inputs)) + self.bias
return self.activation(z)
def fit(self, X: List[List[float]], y: List[int], epochs: int = 10):
for _ in range(epochs):
for inputs, target in zip(X, y):
prediction = self.predict(inputs)
error = target - prediction
for i in range(len(self.weights)):
self.weights[i] += self.learning_rate * error * inputs[i]
self.bias += self.learning_rate * error
Con esto ya tienes un perceptrón funcional. No hay magia. Cada error mueve la frontera de decisión un poco. Si el conjunto de datos es separable, eventualmente debería encontrar una solución.
Para probarlo, usemos una tabla de verdad simple con la compuerta AND. Es un caso clásico porque sí es linealmente separable.
| x1 | x2 | y |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
Con estos datos, el perceptrón puede aprender una frontera que solo active la salida cuando ambas entradas sean 1. En cambio, si pruebas XOR, verás que no converge a una solución correcta porque el problema no es linealmente separable.
Un ejemplo realista con AND y XOR
Vamos a entrenar el modelo con AND y luego contrastarlo con XOR. Ese contraste vale más que una explicación larga porque te muestra el límite del algoritmo en números.
X_and = [
[0, 0],
[0, 1],
[1, 0],
[1, 1],
]
y_and = [0, 0, 0, 1]
p = Perceptron(n_inputs=2, learning_rate=0.1)
p.fit(X_and, y_and, epochs=20)
for x in X_and:
print(x, p.predict(x))
Si el entrenamiento converge, deberías ver predicciones coherentes con la tabla AND. Los pesos exactos pueden variar según el orden de los datos y el número de épocas, pero la lógica general se mantiene: el modelo aprende una frontera lineal que separa el caso positivo del negativo.
Ahora el caso XOR:
X_xor = [
[0, 0],
[0, 1],
[1, 0],
[1, 1],
]
y_xor = [0, 1, 1, 0]
p = Perceptron(n_inputs=2, learning_rate=0.1)
p.fit(X_xor, y_xor, epochs=20)
for x in X_xor:
print(x, p.predict(x))
Aquí el modelo no puede representar la solución correcta de forma estable porque XOR requiere una frontera no lineal. Esa limitación no es un bug, es la definición del modelo.
Qué pasa si normalizas los datos
Aunque el perceptrón es simple, la escala de los datos sigue importando. Si una variable está en miles y otra entre 0 y 1, los pesos van a tener que compensar esa diferencia. En problemas reales, normalizar o estandarizar ayuda a que el entrenamiento sea más estable.
Por ejemplo, si clasificas clientes usando ingresos mensuales en dólares y número de compras, conviene llevar ambas variables a rangos comparables. No porque el perceptrón lo exija como un framework moderno, sino porque la suma ponderada se vuelve más manejable.
Limitaciones y cuándo dejar de usarlo
El perceptrón sirve para aprender la lógica de clasificación lineal, pero no para todo. Su mayor límite es claro: no resuelve problemas no lineales sin ayuda extra. Si necesitas modelar relaciones más complejas, vas a tener que pasar a redes multicapa o a otro tipo de algoritmo.
También depende mucho de que los datos estén bien preparados. Si tienes ruido, clases superpuestas o etiquetas inconsistentes, el perceptrón puede oscilar o quedarse con errores. En datasets pequeños y limpios funciona como ejercicio didáctico; en escenarios más complejos, se queda corto rápido.
Otro punto: la función escalón no es útil para aprendizaje por gradiente en redes profundas. Por eso hoy casi nunca construyes sistemas modernos con un perceptrón aislado. Aun así, entenderlo te da una ventaja práctica: cuando leas sobre capas densas, activaciones y backpropagation, vas a reconocer la estructura básica.
Qué aprender después
Si ya entendiste el perceptrón, el siguiente paso natural es una neurona con activación sigmoide o ReLU, y luego una red multicapa. También te conviene revisar cómo se calcula el gradiente y por qué el error se propaga hacia atrás. La documentación oficial de NumPy puede ayudarte si quieres vectorizar el código y evitar bucles manuales: https://numpy.org/doc/stable/
Un camino razonable sería este:
- Implementar el perceptrón con listas de Python.
- Reescribirlo con NumPy para trabajar con vectores.
- Probar AND, OR y XOR.
- Cambiar la función escalón por una activación diferenciable.
- Comparar tu implementación con una capa densa de PyTorch.
Ese orden te deja ver la evolución natural del modelo sin saltarte fundamentos. Si quieres practicar con datos reales, usa un dataset pequeño y binario, por ejemplo detección de spam simple o aprobación de un préstamo con pocas variables.
Tabla resumen
| Pregunta corta | Respuesta corta |
|---|---|
| ¿Qué hace un perceptrón? | Clasifica entradas con una frontera lineal. |
| ¿Qué calcula internamente? | Una suma ponderada más un sesgo. |
| ¿Sirve para XOR? | No, porque XOR no es linealmente separable. |
| ¿Necesita framework? | No, lo puedes escribir en Python puro. |
| ¿Qué valor de learning rate probar? | 0.1 es un buen punto de partida. |
| ¿Qué aprendes al construirlo? | La base matemática de las redes neuronales. |
Si te quedas con una sola idea, que sea esta: el perceptrón no es una reliquia decorativa, es el modelo mínimo que te muestra cómo una máquina puede aprender una regla de decisión a partir de datos. No te resuelve todo, pero te enseña el mecanismo central que luego ves en modelos mucho más grandes.
Preguntas frecuentes
¿Un perceptrón es una red neuronal?
¿Por qué el perceptrón no resuelve XOR?
¿Puedo implementarlo sin NumPy?
¿Qué función de activación usa el perceptrón clásico?
¿Cómo sé si mis datos sirven para un perceptrón?
¿Qué pasa si inicializo todos los pesos en cero?
¿Vale la pena aprender esto si ya uso PyTorch?
Azirgo
¿Listo para construir tu Producto Digital?
Sitios web, apps móviles, software a medida y soluciones blockchain. Cuéntanos qué tienes en mente y armamos un plan claro contigo.
- Cotización clara en 48 horas
- Equipo en Ecuador, atención en español
- Desde un MVP hasta un producto en producción