Una persona escribe código de Python en una libreta junto a una calculadora y un diagrama sencillo de neurona dibujado a mano sobre una mesa.
Volver al blog

Un perceptrón en Python desde cero

Aprende qué es un perceptrón en Python y cómo construirlo desde cero, con matemática básica, código claro y ejemplos pensados para lectores técnicos de Latinoamérica que quieren entender IA sin depender de frameworks.

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...xn son tus entradas
  • w1...wn son los pesos
  • b es el sesgo o bias
  • f es 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.

x1x2y
000
010
100
111

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:

  1. Implementar el perceptrón con listas de Python.
  2. Reescribirlo con NumPy para trabajar con vectores.
  3. Probar AND, OR y XOR.
  4. Cambiar la función escalón por una activación diferenciable.
  5. 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 cortaRespuesta 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?
Sí, pero en su forma más simple. Es una neurona artificial con entradas, pesos, sesgo y una función de activación. No tiene capas ocultas ni puede resolver problemas complejos por sí solo.
¿Por qué el perceptrón no resuelve XOR?
Porque XOR no se puede separar con una sola línea recta. El perceptrón solo aprende fronteras lineales, así que necesita más capas o una transformación de características para ese tipo de problema.
¿Puedo implementarlo sin NumPy?
Sí. De hecho, hacerlo con listas de Python ayuda a entender cada paso del cálculo. Luego puedes migrarlo a NumPy para trabajar con vectores y matrices de forma más eficiente.
¿Qué función de activación usa el perceptrón clásico?
La función escalón o step function. Devuelve 1 si la suma ponderada supera el umbral y 0 en caso contrario. Es útil para entender el concepto, aunque hoy no se usa mucho en redes profundas.
¿Cómo sé si mis datos sirven para un perceptrón?
Si puedes separar las clases con una línea recta en 2D o un hiperplano en más dimensiones, el perceptrón puede funcionar. Si las clases se mezclan de forma no lineal, vas a necesitar otro modelo.
¿Qué pasa si inicializo todos los pesos en cero?
En un perceptrón simple suele funcionar para aprender, porque la actualización depende del error y de las entradas. En redes más grandes, la inicialización simétrica sí puede ser un problema, pero aquí el modelo es demasiado pequeño para ese bloqueo.
¿Vale la pena aprender esto si ya uso PyTorch?
Sí, porque te ayuda a entender qué hace una capa lineal y por qué una red aprende. Cuando depuras un modelo o ajustas hiperparámetros, esa base matemática te ahorra tiempo y confusión.

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