Misión 7. Enseña a predecir

▶ Abrir en Colab ▶ Ejecutar en Binder

Colab abre en segundos. Binder, la primera vez, construye el entorno en la nube (10-20 min) y luego queda en caché.

Misión 7. Encargo: «que el ordenador adivine el género de una canción por su sonido».
Victoria: un acierto que puedes comprobar.
Insignia en juego: domador de modelos.

El encargo

Hasta aquí la máquina ha mirado el pasado. Le pediste que contara canciones, que limpiara datos sucios, que dibujara el mapa sonoro de un catálogo —y lo hizo, obediente, con números que ya existían—. Pero hoy el encargo cambia de tarea. Hoy vas a pedirle algo que ninguna casilla del fichero contiene: que adivine el género de una canción escuchando solo su sonido. Que le des lo bailable que es, cuánta energía tiene, su tono alegre o triste, su tempo y su duración, y te conteste con una etiqueta —«esto es reggaetón», «esto es música clásica»— sin haber leído nunca el título ni el nombre del artista. Un juicio. Una etiqueta sobre algo que la máquina no tiene delante.

Suena a oído mágico, y por eso conviene decir enseguida dónde está el truco —porque no lo hay—. La máquina no «oye» nada; lo que hace es mucho más terrenal y mucho más útil: aprende de cientos de canciones ya etiquetadas qué combinación de rasgos suele acompañar a cada género, y cuando le das una canción nueva, responde con aquello que aprendió. Es lo mismo que haces tú cuando, al oír una guitarra distorsionada, una voz que grita y un ritmo a mil, dices «esto es heavy metal» sin ser crítico musical: no adivinas, reconoces un patrón que has oído muchas veces. Enseñar a la máquina a hacer eso —a reconocer patrones y usarlos para clasificar— se llama aprendizaje automático, y es de aquello que va esta misión.

Y no es un juguete de laboratorio: es, probablemente, la tecnología que más está cambiando el mundo mientras lees esto. La misma idea que vas a usar con la música —aprender un patrón de cientos de ejemplos y aplicarlo a un caso nuevo— es la que hay detrás de la app que te recomienda canciones, del corrector que adivina la palabra que ibas a escribir, del sistema que ayuda a un médico a leer una radiografía o del coche que se conduce solo. Cambia el dato (canciones, palabras, radiografías, carreteras) y cambia el modelo, pero el motor es este, el que vas a montar tú hoy con un puñado de géneros. Aprenderlo con cinco rasgos y unas canciones es aprenderlo para todo lo demás.

La trampa que hay que evitar.

Un juicio es fácil de soltar y difícil de defender. Cualquiera puede decir «esto es jazz»; lo honesto —y aquello que separa a un analista de un charlatán— es poder contestar a la pregunta siguiente: ¿y cómo sé que tu modelo acierta de verdad?. Toda esta misión gira alrededor de esa pregunta. No te vas a conformar con que el ordenador clasifique: le vas a medir el acierto, con honestidad, sobre canciones que no ha oído nunca. Tu victoria no será una etiqueta bonita; será un acierto que puedes comprobar, con su porcentaje puesto encima de la mesa. Cuando lo logres, te habrás ganado la insignia domador de modelos: porque un modelo que no sabes medir es un caballo salvaje, y tú vas a aprender a domarlo.

El mapa de la misión.

Como siempre, no saltamos a la victoria: la construimos pieza a pieza, y cada pieza es una micro-victoria que puedes enseñar. Este es el recorrido:

  1. Entender qué es aprender de ejemplos: la idea entera del aprendizaje automático, sin una sola fórmula.

  2. Partir los datos en entrenamiento y prueba: la regla de oro para no hacerte trampas mirándote el examen.

  3. Entrenar tu primer modelo con scikit-learn: las dos palabras mágicas, fit y predict.

  4. Medir el acierto de las clasificaciones: qué porcentaje de canciones colocas en su género.

  5. Descubrir el sobreajuste: el modelo que empolla sin entender y se estrella en cuanto le cambias las canciones.

  6. Comparar dos modelos y quedarte, con pruebas, con el que más acierta.

  7. La victoria: adivinar el género de canciones que el modelo no oyó jamás, y comprobar cuánto acierta.

Cada paso desbloquea el siguiente. Y esta misión tiene su propia regla de oro, hermana de la que ya conoces (predice antes de ejecutar): el examen no se toca hasta el final. Ya verás por qué es sagrada. Vamos allá.

La herramienta

Aprender de ejemplos

Para de leer un segundo y contesta: ¿cómo distingues un perro de un gato? No hay una regla que te sepas de memoria —nadie te dio una lista de «si las orejas son así y el hocico asá»—. Lo aprendiste de otra manera: viste muchísimos perros y muchísimos gatos, cada uno con la etiqueta puesta («eso es un perro», «eso es un gato»), y tu cerebro fue sacando solo el patrón. Ahora reconoces un perro que no has visto nunca, de una raza rara, en una foto borrosa. Aprendiste de ejemplos, no de reglas.

El aprendizaje automático es exactamente eso, hecho por una máquina. Le das muchos ejemplos, cada uno con dos partes:

  • Las pistas (los profesionales las llaman características o, en inglés, features): aquello que sabes de cada caso. En nuestra música serán cinco rasgos de audio —lo bailable (danceability), la energy, el tono alegre (valence), el tempo y la duración (duration_ms)—.

  • La respuesta (el objetivo): la etiqueta que quieres adivinar. Aquí, el track_genre, el género de esa canción.

La máquina mira cientos de ejemplos con las dos partes puestas —rasgos y género, rasgos y género— y busca sola la relación que las une: qué sonido suele acompañar a cada género. A esa relación aprendida la llamamos modelo. Una vez que la tiene, le puedes dar unos rasgos sin la etiqueta —una canción nueva— y el modelo la completa: clasifica. Eso es todo. No hay magia, hay ejemplos.

NotaPara saber más: ni magia ni robots con sentimientos

Cuando oigas «inteligencia artificial», baja las expectativas y sube la curiosidad. Aquello que vas a hacer en esta misión —y aquello que hace, por dentro, buena parte de la IA que usas cada día— no es una mente que piensa: es una función que aprende de ejemplos a completar el hueco que falta. El modelo que te recomienda canciones aprendió de millones de personas qué escuchaste después de qué; el que traduce, de millones de frases en dos idiomas; el tuyo aprenderá de un puñado de géneros. No entienden nada de música, de idiomas ni de gustos: encuentran patrones en ejemplos y los repiten en casos nuevos. Saber esto no le quita magia —sigue siendo asombroso que funcione—, pero te convierte de espectador en alguien que sabe cómo está hecho el truco.

NotaPara saber más: aprender con la respuesta delante

Cuando los ejemplos vienen con la respuesta puesta —y el modelo aprende copiando esa pareja rasgos/respuesta— se llama aprendizaje supervisado (supervised learning): es como estudiar con las soluciones al lado. Dentro de él hay dos sabores según qué predigas. Si la respuesta es una etiqueta —el género: «clásica», «reggaetón», «jazz»— es clasificación, aquello que haremos aquí. Si la respuesta es un número —el tempo exacto, los segundos que dura— es regresión. Comparten casi todo; solo cambia si al final quieres una casilla o una cifra. Hay un tercer sabor, el no supervisado, donde los ejemplos vienen sin respuesta y la máquina busca grupos por su cuenta; pero eso es otra misión.

Los datos: canciones con rasgos

Necesitamos ejemplos, y con la etiqueta puesta. Vamos a usar musica.csv, el catálogo de Spotify que ya conoces: cada fila es una canción real, con sus rasgos de audio medidos y su género anotado. Para que la lección sea clara, empezamos con un puñado de géneros bien distintos y reconocibles —música clásica, reggaetón, heavy metal, hip-hop y jazz—. Cárgalo con pandas, que ya conoces, y quédate solo con esos cinco:

import pandas as pd

musica = pd.read_csv("musica.csv")
generos = ["classical", "reggaeton", "heavy-metal", "hip-hop", "jazz"]
canciones = musica[musica["track_genre"].isin(generos)].copy()

print("Canciones:", len(canciones))
print(canciones["track_genre"].value_counts())
Canciones: 148
track_genre
classical      30
heavy-metal    30
reggaeton      30
hip-hop        29
jazz           29

Ciento cuarenta y ocho canciones, unas treinta de cada género: un reparto equilibrado, que nos vendrá muy bien para no engañarnos al medir. Échale un vistazo a los rasgos con los que trabajará el modelo —mirar los datos antes de modelarlos no es opcional, es la primera regla del oficio—:

pistas = ["danceability", "energy", "valence", "tempo", "duration_ms"]
print(canciones[["track_genre"] + pistas].head(4))
    track_genre  danceability  energy  valence    tempo  duration_ms
469   classical         0.447  0.4490   0.0360   92.468       315426
470   classical         0.191  0.0582   0.0454  132.614       357706
471   classical         0.315  0.2200   0.1160  145.195       188786
472   classical         0.468  0.0127   0.3560   72.776       205866

Cada fila es una canción, y en ella tienes las cinco pistas —danceability, energy, valence, tempo, duration_ms— y la respuesta que la máquina deberá aprender a adivinar, el track_genre. Fíjate en esas cuatro piezas de clásica: poco bailables (todas por debajo de 0,5) y con energía muy baja en varias (0,06, 0,01) —piano y cuerdas, nada de percusión—. Cientos de filas como esas son el material de estudio de tu modelo.

Un apunte honesto, porque en esta edición no escondemos nada: estos datos son reales, medidos por Spotify sobre canciones de verdad. Los rasgos como danceability o energy no los inventamos nosotros; los calcula un sistema a partir del audio. Eso los hace más interesantes —son música que existe— y también más difíciles: el mundo real es ruidoso, y ya verás que ni el mejor modelo acierta siempre. La técnica que vas a aprender es idéntica para estos datos y para cualquier otro.

Mira el patrón con tus propios ojos

Antes de pedirle a ninguna máquina que aprenda un patrón, comprueba tú mismo que el patrón existe. Sería absurdo entrenar un modelo para adivinar algo que no depende de nada. Así que hagamos lo que ya sabes hacer desde la Misión 4: agrupar con pandas y mirar medias. Cada género tiene una firma acústica; vamos a verla en dos rasgos, la energía y lo bailable.

firma = canciones.groupby("track_genre")[pistas].mean()
print(firma[["energy", "danceability"]].round(2))
             energy  danceability
track_genre
classical      0.14          0.32
heavy-metal    0.89          0.48
hip-hop        0.66          0.75
jazz           0.45          0.56
reggaeton      0.68          0.77

¡Ahí están las firmas, clarísimas! La clásica es un mundo aparte: energía 0,14, la más baja con diferencia, y poco baile —piano y cuerdas, nada de percusión—. El heavy metal está en el otro extremo de energía (0,89, casi el tope), pero no es bailable (0,48): pura potencia. Y el reggaetón y el hip-hop comparten techo de danceability (0,77 y 0,75): están hechos para moverse. Los rasgos contienen información sobre el género. No son adorno; llevan dentro la señal que tu modelo va a exprimir.

Fíjate también en una pista de por dónde vendrán los problemas: reggaetón y hip-hop se parecen mucho (ambos bailables, energías cercanas). Si el modelo confunde algún género con otro, apuesta a que serán esos dos. Con el patrón confirmado a ojo, ya tiene sentido pedirle a la máquina que lo aprenda entero, combinando los cinco rasgos a la vez —algo que, con tantas canciones, a ojo sería imposible—.

El truco de todo: no te mires el examen

Aquí viene la idea más importante de la misión, y no es de programación sino de honestidad. Presta atención.

Imagina que tu profesor te da veinte problemas para practicar y te avisa: «el examen serán estos mismos veinte». Estudias, te los aprendes de memoria, y sacas un diez. ¿Ese diez demuestra que entiendes la materia? No. Demuestra que tienes buena memoria. En cuanto te pongan un problema distinto, se verá la verdad. Para saber si de verdad aprendiste, el examen tiene que traer preguntas que no has visto antes.

Con un modelo pasa exactamente igual. Si lo entrenas con unas canciones y luego le mides el acierto con esas mismas canciones, no estás midiendo si aprendió el patrón: estás midiendo su memoria. Y un modelo con buena memoria te engaña —y te engañas tú— con una nota inflada que se derrumba con canciones nuevas. Por eso, desde el primer minuto, los datos se parten en dos montones:

  • El entrenamiento (train): el material de estudio. Es lo único que el modelo llega a ver; con esto aprende.

  • La prueba o examen (test): se aparta en un cajón y no se toca. Hace de «canciones del futuro»: temas que el modelo no verá al aprender, y solo sirven para juzgarlo al final, una vez.

Partir es una línea. scikit-learn —la caja de herramientas del aprendizaje automático en Python— trae una función, train_test_split, que baraja los ejemplos y los reparte:

from sklearn.model_selection import train_test_split

X = canciones[pistas].to_numpy(float)   # los 5 rasgos de cada cancion
y = canciones["track_genre"].to_numpy() # la etiqueta a adivinar

X_tr, X_te, y_tr, y_te = train_test_split(
    X, y, test_size=0.2, random_state=2026, stratify=y)
print("Canciones para estudiar:", len(X_tr))
print("Canciones para el examen:", len(X_te))
Canciones para estudiar: 118
Canciones para el examen: 30

Guardamos el 20 % para el examen —30 canciones— y dejamos las 118 restantes para estudiar. La X mayúscula es la tabla de rasgos (una fila por canción, una columna por rasgo); la y minúscula es la columna de géneros. Es la convención universal en aprendizaje automático: X aquello que sabes, y aquello que quieres adivinar.

¿Y ese stratify=y? Le pide al reparto que mantenga los géneros equilibrados en los dos montones —más o menos las mismas clásicas en el estudio que en el examen—, para que ninguno se quede fuera por mala suerte del barajado. El random_state=2026 es la semilla, y cumple la misma promesa de reproducibilidad de la Misión 1: fija el azar para que el reparto salga idéntico cada vez que ejecutes: hoy, mañana y en la máquina de tu compañero. Un resultado que no puedes repetir no es un resultado.

NotaPara saber más: mirar el examen es «hacer trampa»

Mirar el test mientras entrenas —elegir un modelo, un ajuste o un truco porque mejora justo esa nota— es tan común que tiene nombre: fuga de datos (data leakage). Es traicionera porque no salta ningún error rojo; el programa corre feliz y te devuelve una nota estupenda… que es mentira. La regla que te blinda es tajante y fácil de recordar: el examen no se abre hasta que ya no queda nada que decidir. Todo lo pruebas, lo ajustas y lo eliges con el entrenamiento; el test entra en juego una sola vez, al final, para dar la nota definitiva. Si te acostumbras a esto desde hoy, te habrás ahorrado el error que más caro sale en ciencia de datos.

NotaPara saber más: a veces son tres montones

Cuando quieras ir más en serio, verás que los datos se parten a menudo en tres montones, no dos. Al de estudio y al de examen se le añade uno intermedio, la validación, y cada uno tiene un trabajo distinto: con el de estudio el modelo aprende; con el de validación tú eliges entre modelos y ajustes (qué profundidad, cuántos árboles); y el de examen se queda en el cajón, intacto, para la nota final —esa que ya no cambia nada—. ¿Por qué tanto cuidado? Porque en cuanto usas un montón para decidir, deja de ser un juez imparcial: se ha contaminado de tus decisiones. Con dos montones y una sola decisión, como aquí, nos apañamos de sobra; recuerda el tercero para cuando empieces a probar muchas cosas.

Una regla boba para empezar: adivinar el género de moda

Antes de sacar la artillería, un truco de analista listo: fija una vara de medir. ¿Cuál es la clasificación más tonta posible, la que haría alguien sin ninguna idea? Contestar siempre lo mismo: el género más común. Como en nuestro estudio hay parecidas cantidades de cada uno, da igual cuál elijas: siempre acertarás más o menos una de cada cinco. Vamos a medir cuánto acierta esa regla boba, porque será el listón que cualquier modelo de verdad tiene que superar.

import numpy as np
from sklearn.metrics import accuracy_score

comun = pd.Series(y_tr).value_counts().index[0]  # el genero mas comun
pred_boba = np.full(len(y_te), comun)            # el mismo para todos
print("Adivino siempre:", comun)
print("Aciertos:", round(accuracy_score(y_te, pred_boba) * 100, 1), "%")
Adivino siempre: classical
Aciertos: 20.0 %

La regla boba acierta el 20 % de las canciones del examen: exactamente una de cada cinco, como cabía esperar con cinco géneros a partes iguales. Guárdate ese número: es el listón. A partir de ahora, si un modelo con toda su maquinaria no sube del 20 %, es que no ha aprendido nada que no supiéramos ya sin él. En un rato tu primer modelo lo va a batir; luego, un segundo modelo lo dejará muy atrás. Pero sin este listón no sabrías si tu 50 o tu 60 son buenos o malos —un número solo cobra sentido comparado con otro—.

Tu primer modelo: fit y predict

Ha llegado el momento. Vas a entrenar un modelo de verdad, y te va a sorprender lo corto que es. Toda la biblioteca scikit-learn está montada alrededor de dos palabras, dos verbos que se repiten en todos sus modelos:

  • fit(X, y) —«ajústate»—: aquí el modelo aprende. Le pasas los rasgos X y las etiquetas y del entrenamiento, y él busca el patrón que las une.

  • predict(X) —«clasifica»—: aquí el modelo responde. Le pasas rasgos nuevos y te devuelve su etiqueta para cada uno.

Empezamos por un modelo con una intuición preciosa para clasificar: el árbol de decisión (DecisionTree Classifier). Un árbol no es más que un juego de preguntas encadenadas —«¿la energía es menor que 0,30? Entonces mira si es bailable…»— que va acorralando el género, como el «¿quién es quién?» de toda la vida. Le ponemos un tope de tres preguntas seguidas (max_depth=3) para que empiece sencillo. Mira qué poco código:

from sklearn.tree import DecisionTreeClassifier

arbol = DecisionTreeClassifier(max_depth=3, random_state=2026)
arbol.fit(X_tr, y_tr)          # aprende SOLO con el estudio
pred = arbol.predict(X_te)     # clasifica el examen

for i in range(6):
    marca = "acierta" if pred[i] == y_te[i] else "falla"
    print(f"predice {pred[i]:12s} -> real {y_te[i]:12s}  {marca}")
predice reggaeton    -> real hip-hop       falla
predice classical    -> real classical     acierta
predice reggaeton    -> real reggaeton     acierta
predice reggaeton    -> real jazz          falla
predice jazz         -> real heavy-metal   falla
predice classical    -> real classical     acierta

Ya está: has entrenado tu primer clasificador en tres líneas. Y ha puesto etiqueta a seis canciones del examen —canciones que no oyó al estudiar— acertando tres de las seis. No las clava todas, pero fíjate en cómo falla: llamó reggaetón a un hip-hop (los dos hermanos bailables que ya predijimos) y tropezó con el jazz y el metal. No se equivoca al azar; confunde vecinos acústicos. La pregunta que sigue es la de todo analista: ¿acertar la mitad es suficiente?. Para contestarla hay que medir.

NotaPara saber más: el mismo mango para todos los modelos

Que fit y predict valgan para todos los modelos de scikit-learn no es casualidad: es una decisión de diseño preciosa. Cambiar de modelo —del árbol a un bosque, a lo que sea— es cambiar una sola línea, la de crearlo; el fit y el predict de después no se tocan. Es la misma idea de las funciones de la Misión 1 llevada al extremo: un mango estándar para máquinas muy distintas, de modo que puedas probar diez modelos con el mismo código. Lo aprovecharás dentro de un rato para comparar dos.

Lo que aprendió: las preguntas del árbol

Antes de medir, asómate a lo que el modelo lleva dentro, porque el árbol de decisión tiene una virtud rara: es transparente. Puedes leer sus preguntas tal cual, como un diagrama de flujo. Aquí están las de un arbolito de dos niveles, para que quepan en la página:

from sklearn.tree import export_text

mini = DecisionTreeClassifier(max_depth=2, random_state=2026)
mini.fit(X_tr, y_tr)
print(export_text(mini, feature_names=pistas))
|--- energy <= 0.30
|   |--- danceability <= 0.39
|   |   |--- class: classical
|   |--- danceability >  0.39
|   |   |--- class: classical
|--- energy >  0.30
|   |--- danceability <= 0.59
|   |   |--- class: heavy-metal
|   |--- danceability >  0.59
|   |   |--- class: reggaeton

Y lo bonito es que estas preguntas tienen sentido musical, no son un galimatías. La primera de todas es «¿la energía es menor que 0,30?»: si sí, ya sentencia clásica —da igual el resto, las dos ramas de abajo dicen lo mismo—, porque ningún otro género de los cinco tiene tan poca energía. Si no, se fija en lo bailable: poco bailable y con energía es heavy metal; muy bailable es reggaetón. El modelo, sin que nadie le explicara nada de música, ha redescubierto las firmas acústicas que tú viste a ojo, solo mirando ejemplos. Que sus preguntas cuadren con la realidad es, en sí mismo, una señal de que ha aprendido algo verdadero y no ruido. También puedes preguntarle en qué rasgo se apoya más:

for pista, peso in zip(pistas, arbol.feature_importances_):
    print(f"{pista:14s} {peso:.2f}")
danceability   0.34
energy         0.56
valence        0.06
tempo          0.00
duration_ms    0.04

Más de la mitad del mérito (0,56) se lo lleva la energy, y casi todo el resto lo bailable (0,34). El tempo y la duración apenas cuentan para este árbol: la energía y el baile ya separan casi todo.

NotaPara saber más: la clasificación, a mano

Con las preguntas del árbol puedes clasificar una canción tú mismo, sin la máquina: solo tienes que seguir el diagrama. Coge una pieza clásica del examen con energía 0,01. Primera pregunta: ¿energy \(\le 0{,}30\)? Sí (\(0{,}01 < 0{,}30\)). Fin: el árbol dice classical… y era clásica. Predecir, para un árbol, es literalmente bajar por sus ramas contestando sí o no hasta llegar a una hoja. Entender esto te vacuna contra el miedo a las «cajas negras»: por dentro, muchas son un «¿quién es quién?» con nombre elegante.

Medir el acierto: el porcentaje de aciertos

Medir en clasificación es facilísimo de decir con palabras: de todas las canciones del examen, ¿en qué fracción acertó el género? Eso es la exactitud (accuracy), el porcentaje de aciertos. Ya lo usaste con la regla boba. Ahora míralo en tu árbol, y —esto es clave— mídelo en los dos montones, el de estudio y el de examen:

acc_est = accuracy_score(y_tr, arbol.predict(X_tr))
acc_exa = accuracy_score(y_te, arbol.predict(X_te))
print("Aciertos en el estudio:", round(acc_est * 100, 1), "%")
print("Aciertos en el examen: ", round(acc_exa * 100, 1), "%")
Aciertos en el estudio: 73.7 %
Aciertos en el examen:  50.0 %

Buena noticia primero: 50 % en el examen es mucho mejor que el 20 % de la regla boba. Tu árbol ha aprendido algo real sobre el sonido —acierta dos veces y media más que adivinar a lo tonto—. Pero ahora fíjate en algo que huele raro: acierta el 73,7 % de lo que estudió y solo el 50 % del examen. Esa distancia —estudia mejor de lo que examina— es una pista. ¿Estará empezando a memorizar en vez de entender? Guarda esa pregunta: es, ni más ni menos, el corazón de la misión, y la respondemos ahora mismo.

NotaPara saber más: cuidado, el porcentaje de aciertos a veces miente

El porcentaje de aciertos es comodísimo, pero tiene una trampa que hay que conocer. Imagina que en vez de cinco géneros a partes iguales tuvieras 95 canciones de pop y 5 de jazz. Un modelo tramposo que dijera «pop» siempre acertaría el 95 % sin saber nada —simplemente porque casi todo es pop—. El número suena espectacular y es basura. Por eso, con clases desequilibradas, la exactitud engaña, y hay que mirar otras medidas (cuántos jazz de verdad pilla, por ejemplo). Nosotros nos libramos de la trampa a propósito: elegimos cinco géneros equilibrados, así que aquí el 20 % de la regla boba es un listón honrado y superarlo significa algo. Recuérdalo el día que tus clases no estén tan repartidas.

El sobreajuste: empollar sin entender

Toca conocer al enemigo número uno de todo modelo, el fallo del que hay que protegerse siempre. Tiene nombre técnico —sobreajuste (overfitting)— pero tú ya sabes lo que es, porque lo has visto en clase: es el empollón que memoriza sin entender. Ese que se sabe los veinte problemas de memoria, palabra por palabra, y saca un diez si el examen es igual… y un cero en cuanto le cambian un número. Memorizó el ruido, no aprendió el patrón.

Vamos a verlo aparecer con nuestros propios ojos, que es la única forma de creérselo. Nuestro árbol tiene un «dial de listeza» perfecto para el experimento: max_depth, la profundidad, o sea cuántas preguntas seguidas se permite. Poca profundidad, un árbol simplón; mucha, un árbol que puede llegar a aprenderse cada canción de memoria. Vamos a subir el dial y a vigilar los dos aciertos —estudio y examen— a la vez:

for prof in [1, 2, 3, 5, 10, None]:
    a = DecisionTreeClassifier(max_depth=prof, random_state=2026)
    a.fit(X_tr, y_tr)
    est = accuracy_score(y_tr, a.predict(X_tr))
    exa = accuracy_score(y_te, a.predict(X_te))
    print(f"profundidad {str(prof):>5}:  estudio {est*100:5.1f}"
          f"   examen {exa*100:5.1f}")
profundidad     1:  estudio  39.0   examen  40.0
profundidad     2:  estudio  59.3   examen  56.7
profundidad     3:  estudio  73.7   examen  50.0
profundidad     5:  estudio  85.6   examen  46.7
profundidad    10:  estudio 100.0   examen  50.0
profundidad  None:  estudio 100.0   examen  50.0

Lee esta tabla de arriba abajo, porque cuenta una historia perfecta. Con profundidad 1 el árbol es tan simple que acierta poco en ambos montones (39 y 40): ni siquiera aprende lo que hay —eso se llama subajuste, lo contrario del sobreajuste: un modelo demasiado tonto—. Con profundidad 2 llega a su mejor examen (56,7), y estudio y examen van pegados: aprendizaje del bueno.

Pero mira de la 3 hacia abajo, y prepárate: el acierto de estudio sube y sube —73,7… 85,6… hasta 100, ¡el árbol se ha aprendido las 118 canciones de estudio de memoria!— mientras el acierto de examen se queda clavado o baja (50, 46,7, 50). Ahí lo tienes, el empollón en estado puro: espectacular en aquello que memorizó, torpe con lo nuevo. Y aquí está la lección que vale oro: si te hubieras fijado solo en el acierto de estudio, habrías elegido el árbol más profundo (¡100 %, el mejor de todos!) y te habrías llevado a casa uno de los peores. Por eso el examen es sagrado. La distancia que se abre entre 100 y 50 —estudio genial, examen mediocre— es la huella dactilar del sobreajuste. Se llama la brecha.

La figura 7.1 dibuja esa brecha en toda su gloria, subiendo el dial poco a poco. La curva azul (acierto de estudio) sube y sube sin parar: cuanto más listo dejas al árbol, mejor se sabe aquello que ya vio —siempre—. La curva bermellón (acierto de examen), en cambio, toca techo pronto, en un punto dulce, y luego se queda plana o cae. Ese punto es donde el árbol es justo lo bastante listo, y ni una pregunta más. A su izquierda, subajuste; a su derecha, sobreajuste. Elegir bien un modelo es, ni más ni menos, encontrar ese punto. Y ahora una confesión incómoda, de las que hacen falta: para enseñarte la brecha acabamos de mirar la columna del examen seis veces, que es justo la trampa contra la que te avisé. Lo hemos hecho para ver el fenómeno, no para elegir: fíjate en que el árbol que seguimos usando es el de profundidad 3, no el de profundidad 2 que sacó la mejor nota. En un proyecto de verdad, esa elección se hace con el montón de validación del que hablaba la caja de los tres montones, y el examen no se abre hasta el final. Mirar para aprender, sí; mirar para decidir, nunca.

Figura 7.1. La brecha del sobreajuste. Al subir la profundidad del árbol, el acierto en el estudio (azul) sube siempre hasta el 100 %: memoriza cada vez más. El acierto en el examen (bermellón) toca techo en un punto dulce y luego se estanca o cae, cuando el árbol empieza a empollar ruido en vez de aprender el patrón. La distancia que se abre entre las dos curvas es la brecha, y la señal de que hay sobreajuste. Datos: cinco géneros de música.
NotaPara saber más: el equilibrio del que nadie escapa

Detrás de esa brecha hay una tensión que los mayores llaman compromiso sesgo-varianza. Un modelo demasiado simple tiene mucho sesgo: sus ideas son tan rígidas que ni ve el patrón (subajusta). Uno demasiado flexible tiene mucha varianza: se deja llevar por el ruido de los ejemplos concretos que le tocaron (sobreajusta). No puedes bajar los dos a cero a la vez —bajar uno sube el otro—, y todo el arte consiste en encontrar el equilibrio. ¿El truco casero más poderoso contra el sobreajuste, además de no pasarte de listo? Darle más ejemplos: cuantas más canciones distintas ve el modelo, más difícil le resulta memorizar el ruido de unas pocas, y más se ve obligado a aprender el patrón que se repite.

Comparar dos modelos y quedarte con el mejor

Ya sabes entrenar, medir y detectar trampas. Con eso puedes hacer aquello que hace un analista de verdad todos los días: probar varios modelos y quedarse, con pruebas, con el que más acierta en el examen. Tienes el primero, el árbol (50 %). Vamos a por un segundo, más astuto: el bosque aleatorio (RandomForestClassifier).

La idea del bosque es preciosa y muy humana. Un solo árbol se equivoca y tiene manías. Pero ¿y si en lugar de fiarlo todo a un árbol plantas cien árboles, cada uno entrenado con una porción distinta de las canciones y mirando rasgos distintos, y luego les haces votar el género y te quedas con el más votado? Los errores de unos se cancelan con los de otros, las manías se diluyen, y el voto del bosque acierta mucho más que cualquier árbol suelto. Es la sabiduría de la multitud, aplicada a máquinas. Y lo mejor: gracias al mango estándar de scikit-learn, cambiar de modelo es cambiar una línea.

from sklearn.ensemble import RandomForestClassifier

bosque = RandomForestClassifier(
    n_estimators=100, random_state=2026, n_jobs=-1)
bosque.fit(X_tr, y_tr)
pred_bosque = bosque.predict(X_te)
print("Aciertos del bosque en el examen:",
      round(accuracy_score(y_te, pred_bosque) * 100, 1), "%")
Aciertos del bosque en el examen: 60.0 %

n_estimators=100 son los cien árboles; random_state=2026 fija el azar para que el bosque salga idéntico cada vez; n_jobs=-1 le dice «usa todos los núcleos de la máquina» para ir más rápido. Y el resultado: 60 % de aciertos. Ahora ponlos cara a cara —la comparación honesta, los tres medidos en el mismo examen que ninguno vio al entrenar—:

print("Regla boba:", round(accuracy_score(y_te, pred_boba) * 100, 1), "%")
print("Arbol:     ", round(accuracy_score(y_te, pred) * 100, 1), "%")
print("Bosque:    ", round(accuracy_score(y_te, pred_bosque) * 100, 1), "%")
Regla boba: 20.0 %
Arbol:      50.0 %
Bosque:     60.0 %

Veredicto con pruebas: el bosque gana. Sube el acierto del 50 al 60 % —diez puntos sobre el árbol solo, y el triple que la regla boba—. La figura 7.2 lo pone en barras, que entran por los ojos: cuanto más alta la barra, mejor el modelo. La regla boba (20) es un enano; el árbol (50) la dobla; el bosque (60) es la barra más alta de las tres. Esto es comparar modelos: no opinar sobre cuál «suena mejor», sino medirlos a los tres en el examen y dejar que los números decidan.

Figura 7.2. Tres modelos, un examen. Porcentaje de aciertos en el examen de las tres estrategias: adivinar siempre el género más común (la regla boba, 20 %), un árbol de decisión (50 %) y el bosque aleatorio (60 %). Más acierto es mejor. El bosque gana, pero solo lo sabemos porque los tres se midieron sobre las mismas canciones que ninguno oyó al entrenar.

El bosque por dentro: de un árbol a cien

¿De dónde sale la ventaja del bosque? De la multitud. Subamos el número de árboles poco a poco —de uno solo a trescientos— y miremos su examen:

for n in [1, 100, 300]:
    b = RandomForestClassifier(n_estimators=n, random_state=2026, n_jobs=-1)
    b.fit(X_tr, y_tr)
    acc = accuracy_score(y_te, b.predict(X_te))
    print(f"{n:4d} arboles -> examen {acc*100:.1f} %")
   1 arboles -> examen 53.3 %
 100 arboles -> examen 60.0 %
 300 arboles -> examen 63.3 %

Aquí se ve la magia en directo. Un solo árbol del bosque acierta 53,3: es caprichoso, se deja llevar por sus manías. Cien árboles votando suben a 60; trescientos, a 63,3. La sabiduría de la multitud en estado puro: cada árbol se equivoca a su manera, pero al votar muchos, los errores tiran unos para un lado y otros para el otro y se cancelan, y queda aquello que todos aciertan. Un apunte honesto: con solo 30 canciones en el examen, estos porcentajes tiemblan un poco (cada canción vale más de tres puntos), así que no te obsesiones con el último decimal. Lo robusto es la idea: muchos árboles baten a uno solo. Por eso 100 es el número de partida de casi todo el mundo.

NotaPara saber más: un bosque no necesita casi ajustes

Parte de la fama del bosque aleatorio es que funciona sorprendentemente bien «recién sacado de la caja», sin que tengas que tocar apenas nada —por eso es el modelo que mucha gente prueba de primeras—. ¿Recuerdas el drama de la profundidad del árbol, que había que ajustar con cuidado para no sobreajustar? El bosque se ahorra buena parte de ese problema: como promedia muchos árboles distintos, sus manías se cancelan solas. Con más árboles (n_estimators) la clasificación se vuelve más estable y nunca empeora, aunque a partir de cierto punto deja de mejorar y solo cuesta más tiempo; cien es un buen punto de partida. Existen primos aún más potentes —el gradient boosting— que suelen ganar por poco a costa de más cuidado. Pero para domar tu primer modelo, el bosque es un caballo noble.

La victoria

Tienes todas las piezas: sabes aprender de ejemplos, partir en estudio y examen sin hacerte trampas, entrenar con fit, clasificar con predict, medir el acierto y comparar modelos. Es la hora de juntarlas en la victoria que pedía el encargo —que el ordenador adivine el género y que tú puedas comprobarlo—, y tiene dos mitades.

Antes, míralo todo junto. Este es el flujo entero de un proyecto de aprendizaje automático, de la primera línea a la clasificación, en una sola celda —la que resume la misión—. Léela despacio y reconoce cada paso:

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score

# 1) los datos, los generos y los rasgos
musica = pd.read_csv("musica.csv")
generos = ["classical", "reggaeton", "heavy-metal", "hip-hop", "jazz"]
canciones = musica[musica["track_genre"].isin(generos)].copy()
pistas = ["danceability", "energy", "valence", "tempo", "duration_ms"]
X = canciones[pistas].to_numpy(float)
y = canciones["track_genre"].to_numpy()

# 2) partir honesto (estudio y examen)
X_tr, X_te, y_tr, y_te = train_test_split(
    X, y, test_size=0.2, random_state=2026, stratify=y)

# 3) entrenar el mejor modelo
bosque = RandomForestClassifier(n_estimators=100, random_state=2026, n_jobs=-1)
bosque.fit(X_tr, y_tr)

# 4) medir el acierto sobre canciones que NO oyo
acc = accuracy_score(y_te, bosque.predict(X_te))
print("Aciertos en el examen:", round(acc * 100, 1), "%")

# 5) adivinar el genero de una cancion nueva (mucha energia, poco baile)
nueva = [[0.35, 0.95, 0.25, 150, 250000]]
print("Esta cancion es:", bosque.predict(nueva)[0])
Aciertos en el examen: 60.0 %
Esta cancion es: heavy-metal

Cinco pasos, y ya está todo: cargar, partir, entrenar, medir, clasificar. Este esqueleto —estas cinco piezas en este orden— es el mismo en cualquier proyecto de aprendizaje automático del mundo. Ahora desmenucemos las dos mitades de la victoria.

La primera mitad ya la tienes hecha: clasificar el examen —30 canciones que el modelo no oyó jamás— y medir el acierto. Tu mejor modelo, el bosque, acierta el género en el 60 % de canciones nuevas: el triple que la regla boba. Eso es un acierto comprobado: no te lo crees porque suene bien, lo sabes porque lo mediste en el examen. Para ver en qué acierta y en qué se lía, nada mejor que una matriz de confusión: una tabla que cruza el género real (filas) con el que predijo el bosque (columnas). La diagonal son los aciertos:

from sklearn.metrics import confusion_matrix

orden = sorted(generos)
matriz = confusion_matrix(y_te, pred_bosque, labels=orden)
print(orden)
print(matriz)
['classical', 'heavy-metal', 'hip-hop', 'jazz', 'reggaeton']
[[5 0 0 1 0]
 [0 5 0 1 0]
 [0 1 1 0 4]
 [0 1 1 3 1]
 [0 0 2 0 4]]

La figura 7.3 pinta esta misma tabla como un mapa de calor, que se lee de un vistazo. Cada fila es un género real; mira dónde caen sus seis canciones. La primera fila (clásica): cinco en su casilla, una escapada al jazz —casi perfecto—. La tercera (hip-hop): solo una en su sitio y cuatro coladas en reggaetón. ¡Ahí está la confusión que predijiste a ojo! El bosque distingue de maravilla lo que suena distinto (clásica, metal) y se lía con lo que suena parecido (hip-hop y reggaetón, gemelos bailables). No es tonto: se equivoca justo donde tú también dudarías.

Figura 7.3. Lo real frente a lo predicho. Matriz de confusión del bosque en el examen: cada fila es el género real de las canciones, cada columna el género que el modelo predijo. Las casillas de la diagonal (más oscuras) son los aciertos; las de fuera, las confusiones. Se ve al golpe que la clásica y el metal casi nunca se confunden, mientras que el hip-hop se le escapa casi siempre hacia el reggaetón: dos géneros con firmas acústicas casi idénticas.

La segunda mitad es la que da gustito: poner etiqueta a una canción que el modelo no ha oído en su vida. Le damos unos rasgos —mucha energía (0,95), poco bailable (0,35), tono más bien apagado, tempo rápido: la receta clásica de un temazo de heavy metal— y le pedimos su veredicto:

nueva = [[0.35, 0.95, 0.25, 150, 250000]]  # dance, energy, valence, tempo, dur
genero = bosque.predict(nueva)[0]
confianza = bosque.predict_proba(nueva)[0].max()
print(f"El bosque dice: {genero} ({round(confianza*100)} % de confianza)")
El bosque dice: heavy-metal (87 % de confianza)

Ahí está tu oído mecánico, que no es magia sino memoria bien ordenada: el modelo dice heavy metal, y con un 87 % de seguridad. Y lo mejor de todo: es un juicio que puedes comprobar. Búscate una canción de la que sepas el género, mira sus rasgos en Spotify, pásaselos al modelo y comprueba si acierta. Eso —clasificar algo nuevo y poder verificarlo después— es la victoria entera de la misión.

NotaPara saber más: por qué el éxito NO se predice por el sonido

Quizá pienses: «si el sonido predice el género, ¿predecirá también el éxito, la popularity de una canción?». Es una pregunta magnífica, y la respuesta esconde la trampa más importante de toda la ciencia de datos. Mira la correlación de cada rasgo con la popularidad:

for r in pistas:
    print(f"{r:14s} {canciones['popularity'].corr(canciones[r]):+.2f}")
danceability   +0.68
energy         +0.04
valence        +0.22
tempo          -0.03
duration_ms    -0.16

Sale que danceability correlaciona +0.68 con la popularidad: ¡parece que lo bailable predice el éxito! Pero es un espejismo. Mira la popularidad media por género: reggaetón 89, hip-hop 87… clásica 67, metal 57. Los géneros bailables no triunfan porque sean bailables; triunfan porque están de moda. El modelo no descubriría «este sonido gusta»: solo memorizaría «esto suena a reggaetón, y el reggaetón hoy arrasa». Es correlación, no causa. Cambia la moda —o coge una muestra justa con canciones populares y fracasadas de cada género— y la señal acústica se evapora: el éxito es social (en qué lista de reproducción entras, tu fama, un vídeo viral, la suerte), y nada de eso está en el audio. Por eso en esta misión predecimos el género (que sí vive en el sonido) y no el éxito. Lección de oro: un acierto alto puede esconder una explicación falsa. Antes de fiarte de qué predice un modelo, pregúntate por qué.

¿Lo lograste?

Como en cada misión, no te fías de que «parece que va»: lo compruebas con un guardián assert. Aquí verificamos las dos cosas que definen la victoria: que el bosque de verdad le gana al árbol, y que su acierto en el examen dobla con holgura el listón de la regla boba. Ejecuta la celda tal cual:

acc_bosque = accuracy_score(y_te, pred_bosque)
acc_arbol = accuracy_score(y_te, pred)
assert acc_bosque > acc_arbol, "El bosque deberia ganar al arbol."
assert acc_bosque > 2 * 0.20, "Casi: el bosque deberia doblar a la regla boba."
print("Lo lograste. Insignia desbloqueada: domador de modelos.")
Lo lograste. Insignia desbloqueada: domador de modelos.

Si ves ese mensaje, enhorabuena de verdad: has enseñado a una máquina a clasificar música, le has medido el acierto con honestidad sobre canciones que no oyó, has comparado dos modelos y te has quedado con el mejor con pruebas en la mano. Eso es domar un modelo, y por eso te llevas la insignia domador de modelos. Si en cambio salta un aviso, no es un fracaso: es el guardián haciendo su trabajo. Revisa que entrenaste con X_tr/y_tr y mediste con X_te/y_te —nunca al revés— y prueba otra vez.

La bitácora del analista

Abre tu bitácora —la que empezaste en la Misión 1— y añádele la entrada de hoy, que es de las gordas. Anota al menos:

  • La pregunta que resolviste: «¿de qué género es esta canción, dados su baile, energía, tono, tempo y duración?».

  • El listón y las notas, con sus números: regla boba 20 %, árbol 50 %, bosque 60 % de aciertos en el examen.

  • La trampa que aprendiste a evitar: mirarse el examen. Y por qué el árbol de estudio 100 % era, en realidad, uno de los peores modelos.

  • Tu clasificación de una canción nueva y la promesa de comprobarla: apunta un tema del que sepas el género, pásale sus rasgos al modelo y escribe al lado si acertó.

  • Una duda que te llevas. Las dudas anotadas son las que luego se resuelven.

Esa penúltima línea —la clasificación con hueco para comprobarla— es la más valiosa de toda tu bitácora: es la prueba, con fecha, de que hiciste ciencia y no adivinación.

Sube de nivel: juega con tu modelo

Un modelo entrenado no es un cuadro para colgar: es un juguete para interrogar. Y la mejor forma de entender lo que aprendió es hacerle preguntas y ver si sus respuestas tienen sentido. Vamos a darle tres canciones inventadas muy distintas y a mirar cómo reacciona.

def clasifica(dance, energy, val, tempo, dur):
    return bosque.predict([[dance, energy, val, tempo, dur]])[0]

print("Energia altisima, poco baile:", clasifica(0.35, 0.95, 0.25, 150, 250000))
print("Muy bailable, tempo medio:   ", clasifica(0.80, 0.65, 0.60,  95, 205000))
print("Suave, sin energia, larga:   ", clasifica(0.25, 0.10, 0.15, 110, 265000))
Energia altisima, poco baile: heavy-metal
Muy bailable, tempo medio:    reggaeton
Suave, sin energia, larga:    classical

Míralo pensar. Energía altísima y poco baile: heavy metal —potencia sin pista de baile—. Muy bailable y tempo medio: reggaetón —hecho para moverse—. Suave, sin energía y larga: clásica —un piano tranquilo—. El modelo no se ha aprendido estas canciones (son inventadas, no estaban en los datos); ha aprendido el patrón de cada género y lo aplica a casos nuevos con una lógica que reconocerías tú mismo. Eso es aprender, no memorizar.

¿De qué rasgo se fía más?

Aquí va un experimento revelador: ¿y si el modelo tuviera que clasificar mirando un solo rasgo? Entrenemos un bosque con cada rasgo por separado y veamos cuánto acierta, comparado con el bosque completo que usa los cinco:

for r in pistas:
    Xu = canciones[[r]].to_numpy(float)
    Xu_tr, Xu_te, yu_tr, yu_te = train_test_split(
        Xu, y, test_size=0.2, random_state=2026, stratify=y)
    b = RandomForestClassifier(n_estimators=100, random_state=2026, n_jobs=-1)
    b.fit(Xu_tr, yu_tr)
    acc = accuracy_score(yu_te, b.predict(Xu_te))
    print(f"solo {r:14s} -> {acc*100:5.1f} %")
solo danceability   ->  43.3 %
solo energy         ->  56.7 %
solo valence        ->  26.7 %
solo tempo          ->  23.3 %
solo duration_ms    ->  23.3 %

Ningún rasgo solo es gran cosa, pero hay diferencias enormes: la energy a solas ya acierta 56,7 % (¡casi como el bosque completo!, porque separa la clásica y el metal de un plumazo), mientras que el tempo o la duración a solas (23,3 %) apenas le ganan a la regla boba. Pero acuérdate: los cinco juntos daban 60 %. Esa es una idea profunda del aprendizaje automático —y de la vida—: los rasgos se combinan. Saber solo el tempo te dice poco; saber el tempo y que es poco bailable y con mucha energía, te dice «metal». El modelo brilla juntando pistas que por separado apenas susurran.

El bosque te dice en qué se fija

Hay una forma más directa de saber de qué se fía tu modelo: preguntárselo. El bosque, al entrenarse, lleva la cuenta de cuánto le ayuda cada rasgo, y te lo resume en su atributo feature_importances_ —unos números que reparten el «mérito» entre los rasgos y suman 1 (si los sumas redondeados a dos decimales te puede salir 1,01: es el redondeo, no un error)—.

for r, peso in sorted(zip(pistas, bosque.feature_importances_),
                      key=lambda par: -par[1]):
    print(f"{r:14s} {peso:.2f}")
energy         0.31
danceability   0.29
valence        0.17
duration_ms    0.13
tempo          0.11

El bosque se apoya sobre todo en la energy (0,31) y en lo bailable (0,29), justo los dos rasgos donde viste las firmas más claras. El valence ayuda algo (0,17) y el tempo es el que menos aporta (0,11). Esto se llama importancia de las características, y es una ventana preciosa al «cerebro» del modelo: te dice en qué se fija para decidir. Léela con una pizca de cautela —no significa que el tempo no distinga ningún género, solo que a este modelo, con estos cinco rasgos, le resulta el más prescindible—.

Tú contra la máquina: la regla a mano

En la Misión 1 escribías reglas de decisión a mano con if. Ahora que viste las firmas (clásica sin energía, metal con energía y sin baile, reggaetón bailable), quizá piensas: «yo podría escribir esas reglas y clasificar sin ningún modelo». Adelante, inténtalo —y luego mide tu regla en el mismo examen, sin piedad—:

def regla_a_mano(dance, energy, val, tempo, dur):
    if energy < 0.30:
        return "classical"            # sin energia = clasica
    elif energy > 0.80 and dance < 0.60:
        return "heavy-metal"          # mucha energia, poco baile = metal
    elif dance > 0.70:
        return "reggaeton"            # muy bailable = reggaeton
    else:
        return "jazz"                 # el resto, a jazz

pred_mano = np.array([regla_a_mano(*fila) for fila in X_te])
acc_mano = accuracy_score(y_te, pred_mano) * 100
acc_bosque = accuracy_score(y_te, pred_bosque) * 100
print("Tu regla a mano:", round(acc_mano, 1), "%")
print("El bosque:      ", round(acc_bosque, 1), "%")
Tu regla a mano: 63.3 %
El bosque:       60.0 %

¡Sorpresa! Tu regla casera acierta 63,3 % y hasta le saca un pelín al bosque. ¿La máquina no era imbatible? Aquí está la lección honesta, y es de las buenas: cuando el problema es fácil y tú ya viste las firmas, unas reglas a mano rinden estupendamente. Escribir a mano cuatro cortes sobre géneros tan distintos no es difícil. Pero fíjate en la trampa: pudiste escribir esas reglas porque miraste antes los datos y porque solo hay cinco géneros. Con los 114 géneros del catálogo completo, o con rasgos más sutiles, escribir a mano las reglas sería imposible, y ahí la máquina te barrería. Moraleja de analista de verdad: empieza siempre por una regla simple. Si una regla boba ya iguala a tu modelo, tu modelo no se está ganando su complejidad.

Y hay una letra pequeña deliciosa en tu propia regla: fíjate en que nunca devuelve hip-hop. No puede acertar ni uno solo —los manda todos a jazz o a reggaetón—, así que su techo es 24 de 30. Le gana al bosque en parte porque se retira de la categoría más difícil, justo donde el bosque se estrella. Una regla que no compite en lo difícil parte con ventaja en el promedio: otra razón para mirar el acierto género a género y no solo el total.

El modelo también duda: la confianza

El bosque no solo escupe un género: por dentro reparte votos. Con predict_proba puedes ver cuánto se la juega en cada etiqueta —su confianza—. Mira una canción del examen donde el modelo lo tuvo crudo:

probas = bosque.predict_proba(X_te)
for genero, p in zip(bosque.classes_, probas[0].round(2)):
    print(f"{genero:12s} {p}")
print("Real:", y_te[0])
classical    0.07
heavy-metal  0.01
hip-hop      0.17
jazz         0.36
reggaeton    0.39
Real: hip-hop

Aquí el bosque está hecho un lío: reparte los votos entre reggaetón (0,39), jazz (0,36) y hip-hop (0,17), sin decidirse. Se quedó con reggaetón por un pelo… y falló (era hip-hop). Compáralo con el heavy metal de antes, que clasificó con un 87 % de confianza a la primera. Esa cifra es oro: una clasificación al 39 % es poco más que echarlo a suertes; una al 90 % es sólida. En un sistema de verdad —el que decide si una foto es un tumor, pongamos—, saber cuánto duda el modelo importa tanto como su respuesta.

Sube otra vez: ¿qué géneros se le dan mejor?

Un porcentaje suelto —«acierta el 60 %»— convence a medias. Lo que de verdad impresiona (y enseña) es abrir esa media y ver género por género dónde brilla el modelo y dónde patina. Sacamos el acierto de cada género directamente de la matriz de confusión: los aciertos de su fila (la diagonal) entre el total de esa fila.

for i, g in enumerate(orden):
    aciertos = matriz[i, i]
    total = matriz[i].sum()
    print(f"{g:12s} {aciertos}/{total} = {round(aciertos/total*100)} %")
classical    5/6 = 83 %
heavy-metal  5/6 = 83 %
hip-hop      1/6 = 17 %
jazz         3/6 = 50 %
reggaeton    4/6 = 67 %

La figura 7.4 pinta estos porcentajes en barras, una por género, y cuenta una historia clarísima. La clásica y el heavy metal se los sabe de sobra (83 % cada uno): tienen firmas tan propias —energía mínima, energía máxima— que no hay confusión posible. En cambio el hip-hop es su talón de Aquiles (17 %): casi siempre lo manda al reggaetón, porque suenan como gemelos. El acierto global del 60 % no es un número plano: es la mezcla de géneros que el modelo domina y géneros que se le resisten. Y saber cuáles es tan valioso como el total: te dice exactamente dónde tendrías que darle más rasgos o más ejemplos para mejorar.

Figura 7.4. El acierto, género a género. Porcentaje de canciones del examen que el bosque coloca en su género correcto, desglosado por género. La clásica y el heavy metal (barras altas) tienen firmas acústicas únicas y casi no se confunden; el hip-hop (barra baja) se le escapa casi siempre hacia el reggaetón, su gemelo bailable. La media de todas estas barras es el 60 % global.

El peor patinazo: aprende de tus errores

Un analista curtido no huye de sus errores: los examina, porque son la mejor pista sobre los límites de su modelo. Busquemos la canción del examen donde el bosque se equivocó con más aplomo —la que clasificó mal pero con mucha confianza— y miremos sus rasgos a ver si entendemos por qué.

falla = pred_bosque != y_te
confianza = probas.max(axis=1)
i = np.where(falla, confianza, -1).argmax()   # el fallo mas seguro de si mismo
print("La cancion que mas confiado fallo:")
for r, v in zip(pistas, X_te[i]):
    print(f"  {r:14s} {v:.2f}")
print("Real:", y_te[i], "| Predije:", pred_bosque[i],
      "| Confianza:", round(confianza[i] * 100), "%")
La cancion que mas confiado fallo:
  danceability   0.74
  energy         0.85
  valence        0.55
  tempo          87.98
  duration_ms    143901.00
Real: hip-hop | Predije: reggaeton | Confianza: 70 %

¡Vaya patinazo con seguridad! El modelo dijo reggaetón al 70 % y era hip-hop. Pero mira sus rasgos y dale la razón al modelo: bailabilísima (0,74), con energía (0,85)… es el retrato robot del reggaetón. Ese hip-hop concreto suena tan a reggaetón que engañaría a mucha gente. El fallo no es de un mal modelo: es el recordatorio de que dos géneros vecinos comparten territorio, y ninguna frontera —ni la del modelo ni la tuya— los separa sin equivocarse alguna vez. El mundo real guarda siempre canciones a caballo entre dos etiquetas.

NotaPara saber más: por qué el acierto nunca llega al 100,%

¿Podríamos, con más datos y el mejor modelo del mundo, acertar el 100 % de los géneros? No. Y no es un defecto: es que algunas canciones de verdad viven en la frontera —un tema con base de reggaetón y flow de hip-hop, un cruce de jazz y soul—. Sus rasgos de audio son idénticos a los de dos géneros a la vez, así que ninguna máquina (ni ningún humano) puede separarlos siempre: la información que los distinguiría sencillamente no está en los cinco números. Esa parte imposible se llama ruido irreducible. Por eso el acierto se estanca por mucho que mejores el modelo: llegas al suelo que marca la propia realidad. Un buen analista sabe distinguir «puedo mejorar esto» de «esto ya es tan bueno como permiten los datos», y no se obsesiona persiguiendo un 100 % imposible.

¿Y si el reparto tuvo suerte?

Queda una duda honesta royéndonos: tu 60 % salió de un reparto concreto, el de la semilla 2026. ¿Y si ese reparto fue afortunado y con otro el modelo lo haría peor? La forma de dormir tranquilo es repetir la partición con varias semillas distintas y ver si el acierto se mantiene:

for semilla in [1, 7, 2026, 99]:
    xtr, xte, ytr, yte = train_test_split(
        X, y, test_size=0.2, random_state=semilla, stratify=y)
    b = RandomForestClassifier(n_estimators=100, random_state=2026, n_jobs=-1)
    b.fit(xtr, ytr)
    acc = accuracy_score(yte, b.predict(xte))
    print(f"semilla {semilla:5d} -> examen {acc*100:.1f} %")
semilla     1 -> examen 70.0 %
semilla     7 -> examen 56.7 %
semilla  2026 -> examen 60.0 %
semilla    99 -> examen 70.0 %

Aquí toca honestidad total: el acierto baila entre el 57 y el 70 % según el reparto. ¿Por qué tanto? Por lo de siempre: el examen es diminuto —solo 30 canciones—, así que cada acierto o fallo mueve el porcentaje más de tres puntos. Nuestro 60 % (semilla 2026) es una marca representativa, no una verdad al milímetro. Y sin embargo, la conclusión aguanta: en las cuatro semillas el bosque ronda el 60 % y siempre triplica de largo el 20 % de la regla boba. Cuando un número baila así, la lección no es «desconfía del modelo», sino «desconfía de un único número»: da un rango, no un decimal falso. Este truco —probar varios repartos y mirar si el resultado aguanta— es la semilla (nunca mejor dicho) de una técnica mayor, la validación cruzada, que aprenderás cuando quieras ir en serio.

El jefe de parte: tu detector de géneros

Hora de enfrentarte al jefe de parte, el miniproyecto que cierra la misión juntándolo todo. El encargo: convertir tu modelo en un detector de géneros de verdad —una función que reciba los rasgos de una canción y devuelva, además del género, la confianza y un veredicto en palabras: seguro o dudoso—. Es tu modelo de la Misión 7 dándose la mano con las funciones y los if de la Misión 1.

def detector_generos(dance, energy, val, tempo, dur):
    fila = [[dance, energy, val, tempo, dur]]
    probas = bosque.predict_proba(fila)[0]
    i = probas.argmax()
    genero = bosque.classes_[i]
    confianza = round(float(probas[i]) * 100)
    if confianza >= 60:
        veredicto = "seguro"
    else:
        veredicto = "dudoso"
    return genero, confianza, veredicto

print("Metal puro:   ", detector_generos(0.35, 0.95, 0.25, 150, 250000))
print("Piano suave:  ", detector_generos(0.25, 0.10, 0.15, 110, 265000))
print("Baile alegre: ", detector_generos(0.72, 0.70, 0.85, 120, 200000))
Metal puro:    ('heavy-metal', 87, 'seguro')
Piano suave:   ('classical', 93, 'seguro')
Baile alegre:  ('hip-hop', 59, 'dudoso')

Ahí tienes una herramienta de verdad: una sola función que, dada cualquier canción, adivina su género, se moja con un porcentaje y avisa cuándo no las tiene todas consigo. La canción bailable y alegre sale «dudoso» al 59 % —y con razón, porque el reggaetón y el hip-hop se le confunden—. Por dentro llama a bosque.predict_proba (tu modelo entrenado) y decide el veredicto con un if/else (tu Misión 1). Esto ya no es un ejercicio: es el corazón de cualquier app que reconozca música, voz o imágenes.

¿Lo lograste? (nivel jefe)

El jefe también se comprueba. Esta celda verifica que el detector reconoce un caso de manual (un piano suave tiene que ser clásica) y que se moja con confianza alta. Si los guardianes callan, has vencido:

genero, confianza, veredicto = detector_generos(0.25, 0.10, 0.15, 110, 265000)
assert genero == "classical", "Un piano sin energia deberia ser clasica."
assert veredicto == "seguro", "Con 93 % de confianza deberia decir 'seguro'."
print("Detector validado. Eres domador de modelos.")
Detector validado. Eres domador de modelos.

Dos guardianes, una victoria. Si los dos callan, tu detector de géneros es sólido: no lo crees porque «parece bien», lo sabes porque lo has comprobado —y esa, misión tras misión, es la diferencia entre un analista y alguien que tuvo suerte una vez—.

Retos: elige tu nivel

Ahora te toca a ti. Los retos suben de dificultad: \(\star\) es imprescindible, \(\star\star\) te reta y \(\star\star\star\) es de jefe. Haz al menos todos los de una estrella; sube hasta donde te apetezca. Y en todos, las dos reglas de oro: predice antes de ejecutar y el examen no se toca hasta el final.

  • Cambia la semilla. Repite la partición con random_state=7 en vez de 2026, reentrena el bosque y mira su acierto en el examen. ¿Cambia mucho? Antes de ejecutar, apuesta: ¿seguirá cerca del 60 % o se irá lejos?

  • Tu propia canción. Inventa los cinco rasgos de una canción (un temazo de metal, una nana tranquila…) y pásaselos a detector_generos. ¿El género y la confianza tienen sentido para esa canción?

  • Bate a la regla boba. Escribe en una línea cuánto mejora el bosque (60 %) frente a adivinar el género de moda (20 %): la resta, y también cuántas veces más acierta. Enséñaselo a alguien.

  • Lee la matriz. Mira la figura de la confusión y contesta por escrito: ¿con qué género confunde más el modelo al hip-hop? ¿Y por qué crees que pasa, mirando sus firmas acústicas?

  • ★★ El árbol justo. Prueba el DecisionTreeClassifier con max_depth igual a 2, 3 y 4. ¿Cuál da el mejor examen? Acabas de buscar a mano el punto dulce de la figura del sobreajuste.

  • ★★ Más árboles. Entrena bosques con n_estimators igual a 10, 100 y 300. Apunta el acierto y el tiempo de cada uno (puedes cronometrar a ojo). ¿Compensan los 300?

  • ★★ Quita un rasgo. Reentrena el bosque usando solo cuatro rasgos (quita energy). ¿Cuánto empeora el examen? Eso te dice cuánto aportaba la energía dentro del equipo.

  • ★★ Cambia los géneros. Sustituye jazz por techno en la lista y rehazlo todo. ¿Sube o baja el acierto? ¿El techno se confunde con alguno de los otros?

  • ★★ El más confiado. Recorre el examen y encuentra la canción que el bosque clasificó con más confianza (el mayor predict_proba). Imprime sus rasgos y su género: ¿acertó?

  • ★★★ El jefe: elige tú el modelo. Entrena los tres —regla boba, árbol y bosque—, guarda sus aciertos en un diccionario y escribe un bucle que imprima el nombre del ganador (el de mayor acierto) sin que tú digas cuál es. La máquina elige su propio campeón.

  • ★★★ El jefe: demuestra la trampa. Entrena un DecisionTreeClassifier sin límite de profundidad y mide su acierto en el estudio y en el examen. Enseña los dos números juntos y explica, con tus palabras, por qué el primero (100 %) es una mentira peligrosa.

  • ★★★ El jefe: detector con aviso. Amplía detector_generos para que, si el veredicto es «dudoso», añada con qué segundo género anda dudando (el de la segunda probabilidad más alta). Un detector que, además del género, confiesa entre qué dos titubea.

¿Te has atascado? En el apéndice C tienes, para cada reto, primero una pista y solo después la solución. Pero el pacto es este: entra ahí después de haberlo intentado de verdad.

Tu caja de herramientas

Mira todo lo que sabes hacer ahora y hace unas páginas no sabías. Esta es tu chuleta de aprendizaje automático: pégala en la bitácora y vuelve a ella cuando dudes.

  • Preparar: separa los rasgos (X) de la etiqueta (y).

  • Partir honesto: train_test_split con test_size=0.2, random_state fijo y stratify=y.

  • Entrenar: modelo.fit(X_tr, y_tr) (con el estudio, nunca con el examen).

  • Clasificar: modelo.predict(X_te); y predict_proba para la confianza.

  • Medir: accuracy_score —el porcentaje de aciertos— y la confusion_matrix para ver en qué se lía.

  • Modelos: DecisionTreeClassifier (transparente, el del dial max_depth) y RandomForestClassifier (el bosque que vota).

  • Vigilar el sobreajuste: compara acierto de estudio y de examen; si el primero es mucho mayor que el segundo, hay brecha (memoriza).

  • Comparar: mide dos o más modelos en el mismo examen y quédate con el de mayor acierto.

  • Reproducir: random_state en todo lo que lleve azar, para que salga igual cada vez.

No memorices esta lista: úsala. La memoria viene sola de tanto entrenar modelos.

Cuando quieras ir en serio

Lo que has hecho —partir, entrenar, medir en el examen, comparar— es el esqueleto de cualquier proyecto de aprendizaje automático, desde el que clasifica música hasta el que recomienda vídeos o detecta un tumor en una radiografía. Cambia el dato y el modelo; el método es este. Cuando quieras dar el salto, hay un mundo esperándote: encadenar la limpieza y el modelo en una sola tubería (pipeline) para que no se te cuele ninguna trampa; probar muchos ajustes de forma automática y elegir el mejor con validación cruzada (partir en varios exámenes y promediar, para no fiarlo todo a un reparto con suerte); y modelos más potentes —el gradient boosting, las redes neuronales— que aprenden patrones que ni un solo bosque alcanza. No lo necesitas todavía: con lo de esta misión ya clasificas y ya te mides con honestidad, que es lo que de verdad importa.

Y una última idea para llevarte, la que resume la misión entera: en aprendizaje automático, la pregunta nunca es «¿acierto en los datos que tengo?» —eso es fácil, basta memorizar— sino «¿acertaré en los que no tengo?». Todo tu oficio de domador de modelos consiste en no engañarte al responderla. El examen apartado, el acierto medido, la comparación honesta: no son burocracia, son la única forma de que tu juicio sobre una canción nueva signifique algo cuando suene.

Misión 7 completada. Insignia domador de modelos, desbloqueada. Has enseñado a una máquina a reconocer géneros a partir de ejemplos, la has medido sin hacerte trampas sobre canciones que no oyó, has desenmascarado al empollón que memoriza y has coronado, con pruebas, al mejor de dos modelos. Ya no solo cuentas y dibujas el pasado: clasificas lo nuevo y sabes exactamente cuánto puedes fiarte de tu modelo. Eso, exactamente eso, es lo que hace un científico de datos.