Ir al contenido
wannacode

La neurona

En resumen: Una neurona es la suma de sus entradas multiplicadas por pesos, más un sesgo, pasada por una activación como la sigmoide o ReLU.

Problema

La red detuvo el pago. ¿Por qué?

La tienda reemplaza su vieja regla antifraude por una «red neuronal» de una empresa externa. El primer día un cliente paga 230 € desde una cuenta nueva con una tarjeta extranjera, y el pago se detiene para una revisión manual. El cliente escribe a soporte: «¿Por qué?»

Nadie lo sabe. Para el equipo la red es una caja negra: entran las características del pago y sale un número. Y mientras no sepas qué pasa por dentro, no puedes explicar la decisión, corregir un error ni siquiera decir si el número tiene sentido.

La caja se abre. Dentro no hay nada que no hayas visto: multiplicación, suma y una función.

La idea

Una neurona: una suma con pesos

Una neurona artificial recibe unos cuantos números (sus entradas). Cada entrada tiene su propio peso: cuánto cuenta y en qué dirección. La neurona multiplica cada entrada por su peso, suma todo y suma un número más, el sesgo. El resultado se llama z.

Luego z pasa por una función que decide qué sale. Se llama función de activación. Por ahora es la sigmoide: comprime cualquier z hasta un número entre 0 y 1.

¿Te suena? Es la regresión logística: una suma ponderada, una sigmoide, una probabilidad y un umbral. Una neurona con sigmoide es exactamente eso. Una red neuronal son muchas neuronas así, conectadas una tras otra.

Míralo

La neurona en código

Las tres funciones: la suma ponderada, la sigmoide y la neurona que las une.

static double WeightedSum(double[] x, double[] w, double b)
{
var z = b; // el sesgo
for (var i = 0; i < x.Length; i++)
{
z += x[i] * w[i]; // entrada × peso
}
return z;
}
static double Sigmoid(double z) => 1 / (1 + Math.Exp(-z));
static double Neuron(double[] x, double[] w, double b) =>
Sigmoid(WeightedSum(x, w, b));

Los pagos tienen tres características: el monto en cientos de euros, una cuenta nueva (1 o 0) y una tarjeta extranjera (1 o 0). Los pesos están puestos a mano para que puedas hacer las cuentas de cabeza. Cómo los encuentra una red por sí sola lo explica la lección sobre retropropagación.

// monto (cientos de €), cuenta nueva, tarjeta extranjera
double[] w = [0.9, 1.4, 1.8];
var b = -4.0;
double[][] payments =
[
[0.40, 0, 0],
[1.20, 1, 0],
[8.90, 1, 1],
[0.60, 0, 1],
[6.40, 0, 0],
[2.30, 1, 1],
];
Console.WriteLine(" monto € nueva extr. z p");
foreach (var x in payments)
{
var z = WeightedSum(x, w, b);
var p = Sigmoid(z);
var verdict = p >= 0.5 ? "revisar" : "ok";
Console.WriteLine($"{x[0] * 100,8:F0} {x[1],5:F0} {x[2],7:F0} {z,6:F2} {p,6:F3} {verdict}");
}
Salida
monto € nueva extr. z p
40 0 0 -3.64 0.026 ok
120 1 0 -1.52 0.179 ok
890 1 1 7.21 0.999 revisar
60 0 1 -1.66 0.160 ok
640 0 0 1.76 0.853 revisar
230 1 1 1.27 0.781 revisar

Ya puedes responder al cliente. El pago de 230 € es la última fila: z = −4 + 2,3 × 0,9 + 1 × 1,4 + 1 × 1,8 = −4 + 2,07 + 1,4 + 1,8 = 1,27. La sigmoide da 0,781, por encima del umbral de 0,5. La cuenta nueva y la tarjeta extranjera sumaron 3,2. Sin ellas, z sería −1,93, y la probabilidad, alrededor de 0,13.

Míralo

La señal pasa por la neurona

static double WeightedSum(double[] x, double[] w, double b)
{
var z = b; // el sesgo
for (var i = 0; i < x.Length; i++)
{
z += x[i] * w[i]; // entrada × peso
}
return z;
}
static double Sigmoid(double z) => 1 / (1 + Math.Exp(-z));
static double Neuron(double[] x, double[] w, double b) =>
Sigmoid(WeightedSum(x, w, b));
Paso a paso, en palabras
  1. Un pago de 230 € desde una cuenta nueva con una tarjeta extranjera. Las entradas son 2,30, 1 y 1.
  2. z empieza con el sesgo: −4,00.
  3. Cada entrada por su peso: 2,30 × 0,9 = 2,07, 1 × 1,4 = 1,40, 1 × 1,8 = 1,80.
  4. La suma: z = −4 + 2,07 + 1,40 + 1,80 = 1,27.
  5. La sigmoide comprime z entre 0 y 1: σ(1,27) = 0,781.
  6. 0,781 está por encima del umbral de 0,5: el pago pasa a revisión.
  7. 40 € de una cuenta antigua con una tarjeta búlgara: z = −4 + 0,36 = −3,64, p = 0,026. Ok.
  8. Una neurona: una suma con pesos, un sesgo y una sigmoide. Eso es la regresión logística.

Primero va el sesgo, y luego cada entrada aporta su parte. La sigmoide no cambia el orden: un z mayor da una p mayor. Solo la comprime entre 0 y 1, para que se lea como una probabilidad.

Pruébalo

Encuentra el error

Un colega escribe la suma ponderada por su cuenta. Con un sesgo de −4, todos los pagos salen con un z mucho menor del que deberían, y ninguno llega a revisión. ¿En qué línea está el error?

Elige la línea con el error.

Problema

Tres neuronas que valen una

Un colega quiere que la red capte reglas más complejas. Añade dos neuronas que leen el pago y una tercera que lee sus salidas. No hay ninguna activación: cada neurona devuelve la suma z tal cual.

El programa calcula las tres neuronas y, por separado, una neurona cuyos pesos se calculan a partir de los suyos:

// dos neuronas leen el pago, sin activación
double[] w1 = [0.5, 1.0, 1.5];
var b1 = -1.0;
double[] w2 = [1.0, -0.5, 0.5];
var b2 = 0.5;
// una tercera neurona lee sus salidas
double[] v = [1.5, -0.5];
var c = 0.5;
// lo mismo que una neurona con estos pesos
var w3 = new double[3];
for (var i = 0; i < 3; i++)
{
w3[i] = v[0] * w1[i] + v[1] * w2[i];
}
var b3 = v[0] * b1 + v[1] * b2 + c;
Console.WriteLine($"w = [{w3[0]:F2}, {w3[1]:F2}, {w3[2]:F2}], b = {b3:F2}");
double[][] payments = [[0.40, 0, 0], [1.20, 1, 0], [8.90, 1, 1]];
Console.WriteLine(" monto € tres neuronas una neurona");
foreach (var x in payments)
{
var a1 = WeightedSum(x, w1, b1);
var a2 = WeightedSum(x, w2, b2);
var output = WeightedSum([a1, a2], v, c);
var one = WeightedSum(x, w3, b3);
Console.WriteLine($"{x[0] * 100,8:F0} {output,13:F4} {one,12:F4}");
}
Salida
w = [0.25, 1.75, 2.00], b = -1.25
monto € tres neuronas una neurona
40 -1.1500 -1.1500
120 0.8000 0.8000
890 4.7250 4.7250

Las columnas coinciden hasta el último dígito. Las tres neuronas valen una. La razón es álgebra de escuela: una suma de sumas sigue siendo una suma. Pongas lo que pongas una tras otra, sin activación el resultado es una línea recta (un plano, para ser exactos) con otros pesos.

La idea

La activación dobla la línea

Algo nuevo solo aparece cuando entre las neuronas hay una función que no es recta. Las dos más comunes:

  • Sigmoide: comprime z entre 0 y 1. Es cómoda para la salida, cuando quieres una probabilidad.
  • ReLU (rectified linear unit): max(0, z). Lo positivo pasa, lo negativo se vuelve 0. Es la activación más común en las neuronas ocultas de las redes de hoy.
static double Relu(double z) => Math.Max(0, z);
double[] zs = [-3, -1, 0, 1, 3];
Console.WriteLine(" z sigmoid ReLU");
foreach (var z in zs)
{
Console.WriteLine($"{z,6:F2} {Sigmoid(z),8:F4} {Relu(z),5:F2}");
}
Salida
z sigmoid ReLU
-3.00 0.0474 0.00
-1.00 0.2689 0.00
0.00 0.5000 0.00
1.00 0.7311 1.00
3.00 0.9526 3.00

Las dos están dobladas. La sigmoide es una «S», y ReLU es una recta con un quiebre en cero. Una suma de líneas dobladas ya no es recta, así que las neuronas con activación una tras otra pueden describir una regla que una sola neurona no puede. Esa regla llega en la próxima lección.

Pruébalo

Predice la salida

Una neurona con ReLU estima cuántos días de retraso tiene una entrega. Las entradas son la distancia en cientos de kilómetros y si el paquete es pesado. Un retraso no puede ser negativo, de ahí ReLU. ¿Qué imprimirá?

double[] w = [0.5, 1.0]; // cientos de km, paquete pesado (1 o 0)
var b = -1.0;
var near = Relu(WeightedSum([1.5, 0], w, b));
var far = Relu(WeightedSum([4.4, 0], w, b));
var heavy = Relu(WeightedSum([3.0, 1], w, b));
Console.WriteLine($"{near:F1} {far:F1} {heavy:F1}");
Respuesta

Pruébalo

Completa

Termina ReLU.

static double Relu(double z) => (0, z);

La idea

Cuándo basta una neurona y cuándo perjudica

  • La regla es casi recta. Si las características dicen la respuesta por sí solas (un monto mayor, más riesgo), una neurona, es decir, la regresión logística, rinde igual que una red grande. Aprende más rápido y necesita menos datos.
  • Hay pocos datos. Una red con cientos de pesos y 200 ejemplos se los aprende de memoria. Eso es sobreajuste.
  • Tienes que explicar la decisión. Los pesos de una neurona se pueden leer: «la cuenta nueva sumó 1,4». En una red con muchas capas la respuesta al «por qué» es mucho más difícil, y para un pago detenido o un crédito rechazado se exige.
  • La regla es conocida y firme. «Por encima de 5000 € siempre revisión manual» es un if, no un modelo.
  • Las entradas tienen escalas distintas. Un monto en euros (40, 890) junto a características de 0 y 1 da pesos difíciles de aprender. Por eso aquí el monto está en cientos de euros. En un modelo real las entradas se normalizan.
Más precisamente, para curiosos

Las fórmulas. Para una neurona con entradas x₁, x₂, x₃:

z = w₁·x₁ + w₂·x₂ + w₃·x₃ + b

p = σ(z) = 1 / (1 + e^(−z))

En palabras: cada entrada por su peso, más el sesgo, y luego la sigmoide. e es el número de Euler (aproximadamente 2,718), y Math.exp(-z) es e elevado a −z.

Por qué tres neuronas sin activación son una. Si a₁ = w₁·x + b₁ y a₂ = w₂·x + b₂, entonces v₁·a₁ + v₂·a₂ + c = (v₁·w₁ + v₂·w₂)·x + (v₁·b₁ + v₂·b₂ + c). Eso es una neurona con pesos v₁·w₁ + v₂·w₂. Es exactamente lo que calcula el programa de las tres neuronas.

Otras activaciones: tanh (como la sigmoide, pero entre −1 y 1), leaky ReLU (lo negativo no es 0 sino pequeño, por ejemplo 0,01·z), softmax (en la salida, cuando hay más de dos clases: convierte varios números en probabilidades que suman 1). Hay muchas más, pero ReLU y la sigmoide bastan para entender todo lo de este módulo.

Con qué se construyen las redes reales. Aquí todo son ciclos y arreglos, para que veas cada número. Las redes reales tienen millones de pesos, y las matemáticas se hacen con matrices en una tarjeta gráfica. La práctica del aprendizaje automático es sobre todo en Python: NumPy para arreglos, scikit-learn para modelos clásicos, PyTorch para redes neuronales. Otros lenguajes también tienen herramientas: ML.NET para C#, DJL y Tribuo para Java, TensorFlow.js y ONNX Runtime para JavaScript. A menudo un modelo se entrena en Python y se usa desde un programa en otro lenguaje (por ejemplo, mediante ONNX).

El nombre. «Neurona» viene de la biología: una célula que reúne señales y «dispara» cuando su suma pasa un umbral. El parecido termina ahí. Una neurona artificial es una función de números.

Ejercicio

Una neurona para reseñas

Resuélvelo en tu editor.

La tienda quiere clasificar las reseñas en buenas y malas. De cada reseña ya están contadas las palabras buenas («rápido», «calidad») y las palabras malas («roto», «tarde»). Escribe una neurona con una sigmoide, con un peso de 1,1 para las palabras buenas, −1,7 para las malas y un sesgo de 0,2. Ejecútala con las cinco reseñas (buenas, malas): (3, 0), (1, 2), (0, 0), (2, 1), (0, 3). Una reseña es buena si p es al menos 0,5.

Salida
palabras buenas 3, palabras malas 0: p = 0.971 → buena
palabras buenas 1, palabras malas 2: p = 0.109 → mala
palabras buenas 0, palabras malas 0: p = 0.550 → buena
palabras buenas 2, palabras malas 1: p = 0.668 → buena
palabras buenas 0, palabras malas 3: p = 0.007 → mala

La reseña sin ninguna palabra sale buena con 0,550: eso es solo el sesgo. Sin nada más en que apoyarse, el modelo cree levemente que la reseña es buena.

Más tareas

  • Una tercera característica. Añade los signos de exclamación como característica con un peso de −0,3 y mira qué reseñas cambian su decisión.
  • El umbral. Usa un umbral de 0,7 en lugar de 0,5. ¿Qué reseñas pasan a ser malas?

Test

Ponte a prueba

1. ¿Qué calcula una neurona?
2. Una neurona con una sigmoide y un umbral de 0,5 sobre las características de un pago. ¿Qué es?
3. Un colega pone tres neuronas una tras otra, sin activación, para captar reglas más complejas. ¿Qué consigue?
4. ¿Por qué ReLU devuelve 0 para un z negativo?
5. ¿Cuándo es una neurona (o la regresión logística) mejor opción que una red grande?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·