Capítulo 4 de 21 10 secciones 7 min

Compartir

La operación que hace todo: el producto punto

Qué es el producto punto, por qué se llama así, y por qué es literalmente la línea que hay dentro de cualquier modelo lineal.

El producto punto de dos vectores multiplica cada componente con la que le toca en el otro y suma todo, así que de dos listas sale un solo número. En machine learning es la operación central: la predicción de una regresión lineal, de una logística antes de la sigmoide y de una neurona sola son el mismo producto punto entre los pesos y tu fila.

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".

𝐚·𝐛=a1b1+a2b2++adbd=i=1daibi

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ímboloQué es
a · bel producto punto de los dos
aila componente número i del primero
bila 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:

y^=𝐰·𝐱+b

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ímboloQué es
ŷla predicción, que se lee "y sombrero"
wlos pesos, uno por columna
xuna fila de tus datos
bel 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.

¿Le sirve a alguien que conoces?

Pásale el libro. Es gratis, está entero y no pide registro 🐣

Instagram y TikTok no dejan compartir enlaces desde la web: esos dos copian la URL para que la pegues en tu historia.

¿Tienes alguna duda o consulta?