Hola! Bienvenida al libro 🌸
Voy a empezar por la frase que más me costó entender, y que cuando la entiendes se te acomoda todo lo demás:
En programación normal tú escribes las reglas. En machine learning das los ejemplos y la máquina escribe las reglas.
Piénsalo con algo que ya sabes hacer. Si quisieras marcar los pedidos sospechosos con Python, escribirías algo así:
if monto > 5000 and cliente_nuevo and hora > 23:
marcar_como_sospechoso()
Y funciona hasta que el fraude cambia de forma, que es la semana que viene 🙃
Entonces alguien añade otra condición, y otra, y a los seis meses hay
cuatrocientas líneas de if que nadie se atreve a tocar.
Machine learning le da la vuelta: le das mil pedidos etiquetados como buenos o sospechosos, y el modelo saca las reglas solo. Cuando el fraude cambie, le das ejemplos nuevos y vuelve a aprender.
Los tres tipos, en una frase cada uno
- 🎯 Supervisado. Tienes ejemplos con la respuesta correcta y quieres predecir esa respuesta en casos nuevos. "Estos 3.000 clientes compraron o no compraron; dime qué va a hacer el 3.001". Es el 90% de lo que se usa en empresa, y es lo que hace este libro.
- 🧩 No supervisado. No tienes respuesta y quieres que salgan grupos. "Aquí están mis clientes, dime en cuántos grupos se parecen entre ellos". A eso se le llama clustering o análisis de conglomerados, y su algoritmo más conocido es k-means. No sale en este libro, que va entero de supervisado, y te lo digo aquí para que no lo busques en vano durante quince capítulos.
- 🎮 Por refuerzo. Un sistema que prueba, se equivoca y recibe premio o castigo. Es lo de los robots y los juegos. No sale en este libro porque en empresa peruana casi no aparece, y prefiero decírtelo que hacerte perder el tiempo.
Dentro del supervisado hay dos sabores, y la diferencia es solo qué tipo de respuesta quieres:
- 🔢 Regresión cuando la respuesta es un número: cuántos soles va a facturar este cliente.
- 🏷️ Clasificación cuando la respuesta es una etiqueta: va a comprar o no va a comprar.
Este libro va casi todo sobre clasificación, porque es lo que más te van a pedir y porque ahí están los errores más interesantes 💛
Cuándo NO usar machine learning
Esta sección va antes que el código a propósito, porque es la que más plata ahorra.
- 📏 Si la regla se puede escribir. "Avisar cuando el stock
baje de 10" es un
if. Meterle un modelo es cobrar de más y entregar algo peor. - 🔍 Si necesitas explicar cada decisión con certeza. Un modelo da probabilidades, no razones. Si el área legal necesita el porqué exacto de cada rechazo, hay que pensarlo distinto desde el principio.
- 📉 Si tienes cien filas. Con cien filas no se aprende nada que no se vea mirando el Excel un rato.
- 🤷♀️ Si nadie va a hacer nada con la predicción. El caso más común y el más caro: un modelo precioso que nadie usa porque no había una decisión esperándolo. Antes de modelar, pregunta qué se va a hacer distinto con el resultado.
De hecho, ese último punto es el capítulo 2 entero 🌟
Los datos con los que vamos a trabajar
Todo este libro corre sobre el mismo archivo, y viene sucio a propósito.
import pandas as pd URL = 'https://missyera.com/static/datasets/ventas-miss-yera.csv' ventas = pd.read_csv(URL) print(ventas.shape) print(ventas['compro'].value_counts())
(3037, 14) compro 1 1753 0 1284 Name: count, dtype: int64
3.037 ventas y 14 columnas. Y la columna que vamos a predecir es
compro: 1 si la venta se cerró, 0 si no.
print(ventas[['ciudad', 'segmento', 'canal', 'unidades', 'satisfaccion', 'compro']].head(5).to_string(index=False))
ciudad segmento canal unidades satisfaccion compro
Trujillo Horeca Marketplace 10 4.0 1
lima Minimarket Web 10 3.0 1
Cusco Minimarket Web 13 1.0 1
Arequipa Mayorista Marketplace 3 4.0 1
Cusco Minimarket Marketplace 1 1.0 0
Cada fila es una venta: dónde, a qué tipo de cliente, por qué canal, cuántas unidades, qué satisfacción tenía ese cliente y si compró o no.
Si te fijas en la segunda fila ya vas a ver el primer problema: la ciudad dice
lima en minúscula. Hay más, muchos más, y son el capítulo 4 🧽
El número que hay que saber antes que ningún otro
print(round(ventas['compro'].mean(), 4))
0.5772
El 57,7% de las ventas se cerraron.
Guárdate ese número, porque es el listón. Si yo digo "todas las ventas se cierran" sin mirar nada, acierto el 57,7% de las veces. Cualquier modelo que no le gane a eso no vale nada, por muy elegante que sea 📏
La cantidad de proyectos que se presentan con un "77% de exactitud" sin decir que el listón estaba en 75... muchos. Vamos a volver sobre esto en el capítulo 14, que va entero de eso.
Tu primer modelo, en seis líneas
Vamos a hacer el equivalente del "hola mundo":
- Un modelo de verdad
- Con dos columnas
- Sin limpiar nada
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
d = ventas[['unidades', 'satisfaccion', 'compro']].dropna()
X, y = d[['unidades', 'satisfaccion']], d['compro']
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.25,
random_state=42, stratify=y)
arbol = DecisionTreeClassifier(max_depth=2, random_state=42).fit(X_tr, y_tr)
print(round(arbol.score(X_te, y_te), 4))
0.582
0,582. O sea que acierta el 58,2% de las veces 🎉
Y ahora la ducha fría, que es la parte importante de este capítulo.
print(round(y_te.mean(), 4))
0.5735
El listón en ese mismo trozo de datos era 0,5735. Nuestro modelo saca 0,582. Le ganamos por menos de un punto porcentual.
Eso no es un fracaso, es información. Y se puede averiguar por qué en una línea:
print(ventas.groupby('compro')['unidades'].mean().round(2))
compro 0 11.47 1 11.69 Name: unidades, dtype: float64
11,47 unidades en las ventas que no se cerraron y 11,69 en las que sí. Prácticamente lo mismo. Esa columna no sabe nada sobre el resultado, así que el modelo estaba trabajando con una sola señal de verdad, la satisfacción, y con un ruido al lado.
Yo elegí esas dos columnas porque eran las dos numéricas que tenía a mano, y esa es exactamente la trampa: las columnas que sí separan en este archivo son de texto, y scikit-learn no come texto. Lo vas a ver en los ejercicios y es lo que justifica los quince capítulos que vienen 🐣
Qué aprendió, exactamente
Un árbol tiene una gracia enorme y es que se puede leer.
from sklearn.tree import export_text print(export_text(arbol, feature_names=['unidades', 'satisfaccion']))
|--- satisfaccion <= 3.50 | |--- satisfaccion <= 1.50 | | |--- class: 0 | |--- satisfaccion > 1.50 | | |--- class: 1 |--- satisfaccion > 3.50 | |--- satisfaccion <= 4.50 | | |--- class: 1 | |--- satisfaccion > 4.50 | | |--- class: 1
Ahí está: aprendió que si la satisfacción es 1 (o sea, malísima) la venta no se cierra, y en todo lo demás dice que sí. Eso son cuatro reglas que nadie escribió, salidas solas de mirar 2.100 ejemplos.
Fíjate también que las ramas de abajo a la derecha dicen las dos
class: 1, o sea que ese corte no separaba nada. Un árbol se parte
aunque la partición no sirva, y eso es exactamente el tipo de cosa que hay que
saber mirar 🔍
Y una cosa que voy a repetir todo el libro: ese
random_state=42 no es decoración. Sin él, cada vez que
corras esto te sale un número distinto y no puedes comparar nada.
Cómo se ve un proyecto entero, para que tengas el mapa
Casi todos los cursos te enseñan el paso 6 y te dejan sola con los otros siete. Estos son los ocho, y este libro los recorre en orden:
- 1️⃣ La pregunta. Qué se va a decidir distinto con la predicción. Sin esto no se empieza. (Capítulo 2)
- 2️⃣ Mirar los datos. Cuántas filas, cuántos huecos, qué está sucio. (Capítulo 4)
- 3️⃣ Construir las columnas. Convertir lo que hay en algo que un modelo pueda usar. Es donde más se gana. (Capítulos 9 y 11)
- 4️⃣ El listón. Lo tonto que hay que ganar. (Capítulo 14)
- 5️⃣ Comprobar que no hay trampa. Que ninguna columna esté contando el futuro. (Capítulo 12)
- 6️⃣ Los modelos. Probar varios y quedarse con uno. (Capítulos 13 y 17)
- 7️⃣ Medir bien. Elegir la métrica que corresponde al problema, no la que se ve mejor. (Capítulos 14 a 16)
- 8️⃣ Explicarlo y vigilarlo. Porque un modelo que nadie entiende no se usa, y todos se pudren con el tiempo. (Capítulos 19 a 23)
Y fíjate dónde está el paso de los modelos: en el sexto de ocho. Cuando alguien dice que el 80% del trabajo es limpiar y preparar datos no está exagerando ni quejándose, está describiendo el oficio 🧹
El vocabulario, para que no te agarre desprevenida
| Palabra | Qué es en cristiano |
|---|---|
| Feature o variable | Una columna que usas para predecir. unidades, ciudad |
| Target u objetivo | La columna que quieres predecir. Aquí, compro |
| Entrenar | Mostrarle los ejemplos al modelo para que saque sus reglas |
| Train y test | Los datos partidos en dos: con unos aprende y con los otros se le toma examen |
| Predecir | Pedirle la respuesta para filas que no vio |
| Hiperparámetro | Una decisión tuya sobre el modelo, no algo que aprenda. El max_depth=2 de arriba |
| Baseline o listón | Lo tonto que hay que ganarle. Aquí, decir siempre que sí |
Ejercicios
Siete sobre el mismo archivo. Intenta antes de abrir 💛
1. Cuánto se cierra por canal
Qué porcentaje de ventas se cierra en cada canal.
print(ventas.groupby('canal')['compro'].mean().round(4).sort_values(ascending=False))
canal WhatsApp 0.6524 Tienda 0.6190 Web 0.5830 Marketplace 0.4613 Name: compro, dtype: float64
Diecinueve puntos entre WhatsApp (65%) y Marketplace (46%). Eso es muchísimo: el canal solo ya separa mejor que nuestro modelo entero, que ganaba menos de un punto 😳
Y es una columna de texto, o sea de las que scikit-learn no come. Ahí está el resumen de por qué este libro tiene quince capítulos.
2. Y por segmento de cliente
Lo mismo con segmento, y de paso cuántas filas
tiene cada uno.
print(ventas.groupby('segmento')['compro'].agg(['count', 'mean']).round(4))
count mean segmento Bodega 719 0.3755 Horeca 750 0.6320 Mayorista 758 0.7230 Minimarket 810 0.5691
Y esta es todavía más fuerte: Bodega cierra el 37,6% y Mayorista el 72,3%. Treinta y cinco puntos de diferencia.
Si tuvieras que apostar con un solo dato, apostarías con este y le ganarías a nuestro árbol sin despeinarte. Lo que hace un modelo, cuando está bien hecho, es combinar estas señales en vez de elegir una 🔧
3. La satisfacción sí separa
Tasa de cierre por nivel de satisfacción.
print(ventas.groupby('satisfaccion')['compro'].agg(['count', 'mean']).round(4))
count mean satisfaccion 1.0 568 0.4472 2.0 542 0.5351 3.0 557 0.5404 4.0 567 0.6279 5.0 569 0.7135
De 44,7% con satisfacción 1 a 71,4% con satisfacción 5: veintisiete puntos, y
además ordenaditos de menos a más. Por eso el árbol de arriba eligió justo esta
columna para su primer corte, y no unidades 🌟
Fíjate también en la columna count: los cinco niveles tienen
entre 542 y 569 filas. Cuando un nivel tiene veinte filas, su tasa no significa
nada por mucho que se vea distinta, y eso hay que mirarlo siempre.
4. Un árbol un poco más profundo
El mismo modelo con max_depth=4. ¿Mejora?
arbol4 = DecisionTreeClassifier(max_depth=4, random_state=42).fit(X_tr, y_tr) print(round(arbol4.score(X_te, y_te), 4))
0.5892
0,5892. Subió un punto respecto al de profundidad 2. Poquísimo, y con dos columnas flojas es lo que hay.
Lo interesante viene en el ejercicio siguiente, porque ese "mejoró" hay que mirarlo con lupa.
5. Cuánto acierta en los datos que ya vio
Compara la nota del árbol de profundidad 4 en train y en test.
print('en train:', round(arbol4.score(X_tr, y_tr), 4))
print('en test :', round(arbol4.score(X_te, y_te), 4))
en train: 0.6047 en test : 0.5892
0,6047 en los datos que ya vio contra 0,5892 en los que no. Kilómetro y medio de diferencia, todavía poco. Pero mira qué pasa si le damos más profundidad todavía:
for prof in (2, 4, 6, 10):
a = DecisionTreeClassifier(max_depth=prof, random_state=42).fit(X_tr, y_tr)
print(prof, round(a.score(X_tr, y_tr), 4), round(a.score(X_te, y_te), 4))
2 0.599 0.582 4 0.6047 0.5892 6 0.6132 0.582 10 0.6318 0.582
Ahí está clarísimo: la columna del medio (train) sube y sube, de 0,599 a 0,6318. La de la derecha (test) se queda clavada. El modelo está memorizando, y eso se llama sobreajuste, o overfitting, que es como te lo vas a encontrar escrito casi siempre.
Es de los problemas más traicioneros del oficio, porque el número que ves mientras trabajas sube mientras el que importa no se mueve. La nota de entrenamiento no se reporta nunca: sirve solo para compararla con la otra y cazar esto. Capítulo 16 entero 🫠
6. El error que vas a cometer el primer día
Entrena con una columna de texto sin convertirla.
X_malo = ventas[['ciudad', 'unidades']] DecisionTreeClassifier().fit(X_malo, ventas['compro'])
ValueError: could not convert string to float: 'Trujillo'
could not convert string to float. Y es de los mensajes más honestos que hay: scikit-learn solo entiende números. Ni una letra.
Convertir ciudad en algo que el modelo entienda tiene nombre,
OneHotEncoder, y está en el capítulo 11. Por ahora quédate con el
mensaje, porque lo vas a ver muchas veces 💛
7. El listón, escrito como se debe
Calcula el baseline con la herramienta que trae scikit-learn para eso, en vez de a mano.
from sklearn.dummy import DummyClassifier tonto = DummyClassifier(strategy='most_frequent').fit(X_tr, y_tr) print(round(tonto.score(X_te, y_te), 4))
0.5735
0,5735, el mismo número que sacamos a mano. DummyClassifier
existe justo para esto y merece estar en tu primer cuaderno de cada proyecto:
te da el listón sin que tengas que pensarlo, y sin listón no hay forma de saber
si tu modelo sirve 📏
Comprueba que lo tienes
El 57,7% de las ventas se cierra y tu modelo acierta el 58%. ¿Qué tienes?
- Nada todavía: eso es lo mismo que decir que sí a todo
- Un modelo que acierta más de la mitad, que es un buen empiece
- Un modelo que hay que ajustar un poco
- Un modelo mal entrenado
Lo que te llevas
- 🔄 En programación tú escribes las reglas; en ML das ejemplos y salen las reglas.
- 🎯 Supervisado es predecir con ejemplos etiquetados, y es el 90% de lo que se usa. Dentro, regresión para números y clasificación para etiquetas.
- 🚫 No uses ML si la regla se puede escribir, si tienes cien filas, o si nadie va a hacer nada con la predicción.
- 📏 El listón primero. Aquí es 57,7% y todo se compara contra eso.
- 🌳 Tu primer modelo sacó 0,582 contra un listón de 0,5735. Menos de un punto, y eso también es un resultado.
- 🎲
random_stateen todo lo que reparta o baraje, o no puedes comparar nada. - 🔢 scikit-learn solo entiende números.
En el capítulo 2 vamos a lo que va antes del código y que casi nadie enseña: el contrato de datos, o sea qué se acuerda con el cliente antes de tocar una sola fila.
Que tengas lindo día! 🌸