Ya lo hiciste en el capítulo anterior 🌸
En el capítulo 3 multiplicamos cada característica de una venta por su peso y sumamos todo. Salió 448.13.
Esa operación tiene nombre propio, y es el nombre que vas a ver en todos los papers: producto punto. También le dicen producto escalar, y en inglés dot product. Las tres son lo mismo.
Este capítulo es corto y es el más importante del bloque, porque casi todo lo que viene después es esta operación repetida muchas veces.
Volvemos a abrir el archivo
Cada capítulo arranca de cero, así que estas líneas se repiten. Son las mismas del capítulo 1 y las vas a ver en todos.
import pandas as pd import numpy as np df = pd.read_csv('ventas-miss-yera.csv') for c in ('unidades', 'monto', 'satisfaccion'): df[c] = pd.to_numeric(df[c], errors='coerce') X = df[['unidades', 'monto', 'satisfaccion']].dropna().to_numpy() print('X.shape:', X.shape)
X.shape: (2238, 3)
Tres formas de escribir lo mismo
a = X[0] pesos = np.array([12.0, 0.35, 40.0]) print('uno a uno :', np.round(a * pesos, 2)) print('sumado :', round(float((a * pesos).sum()), 2)) print('np.dot :', round(float(np.dot(a, pesos)), 2)) print('a @ pesos :', round(float(a @ pesos), 2))
uno a uno : [120. 168.13 160. ] sumado : 448.13 np.dot : 448.13 a @ pesos : 448.13
Las tres últimas dan lo mismo porque son lo mismo. El símbolo
@ es el que vas a ver en código moderno, y se lee "por".
el producto punto multiplica cada componente con la que le toca en el otro vector y suma todo, así que de dos listas sale un solo número
| Símbolo | Qué es |
|---|---|
| a · b | el producto punto de los dos |
| ai | la componente número i del primero |
| bi | la del mismo sitio en el segundo |
| Σ | la sigma, que solo quiere decir "suma todo esto" |
Si la sigma te asustaba, mírala otra vez ahora que sabes qué hay debajo: es un bucle que suma. Nada más 🙂
Dos cosas que hay que tener claras
Uno: de dos listas sale un número. Entran tres componentes y tres pesos, y sale 448.13. Esto es lo que hace que el producto punto sirva para predecir: colapsa una fila entera en un solo valor.
Dos: el orden da igual.
b = X[1] print('a . b :', round(float(a @ b), 2)) print('b . a :', round(float(b @ a), 2))
a . b : 252258.21 b . a : 252258.21
Guárdate esto, porque con matrices deja de ser cierto y es uno de los errores más comunes. Lo vemos en el capítulo 5.
Y aquí está el modelo
Esta es la fórmula que hay dentro de una regresión lineal:
la predicción de un modelo lineal es el producto punto de los pesos con la fila de datos, más un número suelto que ajusta la altura
| Símbolo | Qué es |
|---|---|
| ŷ | la predicción, que se lee "y sombrero" |
| w | los pesos, uno por columna |
| x | una fila de tus datos |
| b | el sesgo o intercepto, que sube o baja todo por igual |
Eso es todo. Un producto punto y una suma. Cuando alguien te diga que un modelo lineal es simple, esto es lo que quiere decir: cabe en una línea.
La b es lo único nuevo. Es un número suelto que se suma al final, y sirve para cuando la predicción tiene que arrancar de un sitio que no es cero. Si todas tus ventas facturan al menos 100 soles, ese 100 lo pone la b y no los pesos.
Todas las filas de golpe
pred = X @ pesos print('pred.shape :', pred.shape) print('primeras 5 :', np.round(pred[:5], 2))
pred.shape : (2238,) primeras 5 : [ 448.13 423.72 250.19 1064.29 184.79]
Una sola línea y 2.238 predicciones. Fíjate en las formas: entra
(2238, 3) con (3,) y sale (2238,).
El 3 desapareció, y desapareció porque es el que se gasta en la suma. Esa es la regla que tienes que aprender a leer de un vistazo, y es el capítulo que viene.
El error que te va a salir
a @ X
ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 2238 is different from 3)
El mensaje es feo pero dice exactamente lo que pasa: "2238 es distinto de 3". El vector tiene 3 componentes y la matriz tiene 2.238 filas, así que no hay manera de emparejarlas.
Y date cuenta de que X @ a sí funciona. El orden importa 🙃
Lo que te llevas
- El producto punto multiplica componente con componente y suma. De dos listas sale un número.
- Se escribe
a @ b,np.dot(a, b)o a · b, y las tres son lo mismo. - La sigma es un bucle que suma. Ya está.
- ŷ = w · x + b es una regresión lineal entera.
- Entre vectores el orden da igual. Con matrices no, y eso viene ahora.
Comprueba que se entendió
Comprueba que lo tienes
Tienes v con forma (3,) y una matriz X con forma (2238, 3). ¿Qué te devuelve X @ v?
- Un vector de 2.238 números: un producto punto por cada fila
- Un solo número, porque el producto punto siempre da un número
- Una matriz de 2238 por 3, del mismo tamaño que X
- Un error, porque las formas no coinciden
Ejercicios
1. Hazlo a mano con un bucle
Para que veas que no hay magia debajo.
a = X[0] pesos = np.array([12.0, 0.35, 40.0]) total = 0.0 for i in range(len(a)): total = total + a[i] * pesos[i] print('con bucle :', round(total, 2)) print('con @ :', round(float(a @ pesos), 2))
con bucle : 448.13 con @ : 448.13
Eso es literalmente lo que hace NumPy, solo que en C y sobre todas las filas
a la vez. El bucle está para que lo veas una vez; en tu código de trabajo usa
siempre @.
2. La forma antes de multiplicar
Mira las formas y predice el resultado antes de correrlo.
a = X[0] pesos = np.array([12.0, 0.35, 40.0]) print('a.shape', a.shape, 'X.shape', X.shape) print('X @ pesos ->', (X @ pesos).shape)
a.shape (3,) X.shape (2238, 3) X @ pesos -> (2238,)
El hábito que quiero que te lleves de este libro: antes de multiplicar, mira las dos formas y di en voz alta qué forma va a salir. Si no lo sabes, todavía no estás listo para escribir esa línea.
3. Un peso en cero borra la columna
Comprueba que el producto punto ignora lo que pesa cero.
a = X[0] con = np.array([12.0, 0.35, 40.0]) sin = np.array([12.0, 0.00, 40.0]) print('con monto :', round(float(a @ con), 2)) print('sin monto :', round(float(a @ sin), 2)) print('diferencia:', round(float(a @ con - a @ sin), 2))
con monto : 448.13 sin monto : 280.0 diferencia: 168.13
Esos 168.13 son exactamente lo que aportaba el monto. Así es como se lee la importancia de una característica en un modelo lineal, con la trampa de que si las columnas están en escalas distintas los pesos no se pueden comparar entre sí.
Practica este capítulo 📓
Todo el código de arriba en un cuaderno que corre de principio a fin, y los ejercicios con una celda vacía para que los hagas tú. Se abre en Google Colab de un clic y no hay que instalar nada. Donde veas %%revisa, escribe tu respuesta y el cuaderno te dice si te salió.
¿Prefieres trabajar en tu máquina? Bájate el cuaderno de práctica o el de soluciones. Todos están también en github.com/soymissyera/MissYeraEjercicios.