¿Basta con el porcentaje de aciertos?
Evaluar un modelo es medir cuánto acierta y qué fallos comete. Para eso se usan la matriz de confusión, la precisión y la exhaustividad (recall en inglés). Un solo porcentaje de aciertos puede esconder un modelo inútil.
Una empresa de alquiler de bicicletas quiere avisar los días movidos. Una regla acierta mucho, pero ¿a qué coste?
Al final de esta lección
Nota
Puedo juzgar un modelo con la matriz de confusión, la precisión, la exhaustividad y un conjunto de prueba.
Palabras clave
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Matriz de confusión | una tabla de aciertos y fallos | |
| Falso positivo | el modelo dice sí y era no | una falsa alarma |
| Falso negativo | el modelo dice no y era sí | un caso perdido |
| Precisión | de los sí del modelo, la parte que era sí | |
| Exhaustividad | de los sí reales, la parte que el modelo encontró | |
| Conjunto de prueba | datos que el modelo no usó para aprender |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
La matriz de confusión cuenta cuatro casos
Un modelo dice sí o no, y la realidad también. Eso da cuatro casos. Dos son aciertos y dos son fallos.
Un verdadero positivo es un sí que era sí. Un falso positivo es un sí que era no. Un falso negativo es un no que era sí. Un verdadero negativo es un no que era no.
Ejemplo resuelto: la regla de 12 grados
| Dijo movido | Dijo tranquilo | Total | |
|---|---|---|---|
| Era movido | 109, acierto | 22, fallo | 131 |
| Era tranquilo | 48, fallo | 186, acierto | 234 |
| Total | 157 | 208 | 365 días |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Cuenta los cuatro casos con dos umbrales
docker exec jupyter python3 -c "import csv; d=list(csv.DictReader(open('hires.csv'))); y=[int(r['hires'])>=300 for r in d]; t=[float(r['temp_c']) for r in d]; print([(k, sum(a and b>=k for a, b in zip(y, t)), sum((not a) and b>=k for a, b in zip(y, t)), sum(a and b<k for a, b in zip(y, t)), sum((not a) and b<k for a, b in zip(y, t))) for k in (12, 15)])"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
La precisión y la exhaustividad miran fallos distintos
La precisión pregunta: cuando el modelo dice sí, ¿cuántas veces tiene razón? Es verdaderos positivos entre todos los sí del modelo.
La exhaustividad pregunta: de los sí reales, ¿cuántos encontró? Es verdaderos positivos entre todos los sí reales. Subir una suele bajar la otra.
Ejemplo resuelto: dos umbrales de temperatura
| Regla | Precisión | Exhaustividad | Qué pasa |
|---|---|---|---|
| 12 grados o más | 109 / 157 = 0,69 | 109 / 131 = 0,83 | Encuentra casi todos, con falsas alarmas |
| 15 grados o más | 76 / 99 = 0,77 | 76 / 131 = 0,58 | Acierta más, pero pierde muchos |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Calcula la precisión y la exhaustividad
docker exec jupyter python3 -c "import csv; d=list(csv.DictReader(open('hires.csv'))); y=[int(r['hires'])>=300 for r in d]; t=[float(r['temp_c']) for r in d]; print([(k, round(sum(a and b>=k for a, b in zip(y, t)) / sum(b>=k for b in t), 2), round(sum(a and b>=k for a, b in zip(y, t)) / sum(y), 2)) for k in (12, 15)])"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Un error muy común
Advertencia
Mucha gente cree que un 99 % de aciertos es siempre un buen modelo. Si solo 1 de cada 100 operaciones es un fraude, decir siempre «no es fraude» acierta el 99 %. Su exhaustividad es 0: no encuentra ningún fraude. Mira siempre la precisión y la exhaustividad.
Cuenta los casos de seis días
# Una regla: «movido si hace k grados o más». Cuenta los cuatro casos.
days = [[18, True], [9, False], [14, True], [11, True], [16, False], [7, False]]
k = 12
tp = 0
fp = 0
fn = 0
tn = 0
for day in days:
predicted = day[0] >= k
real = day[1]
if predicted and real:
tp = tp + 1
elif predicted and not real:
fp = fp + 1
elif real:
fn = fn + 1
else:
tn = tn + 1
print("tp", tp, "fp", fp, "fn", fn, "tn", tn)
print("precisión", round(tp / (tp + fp), 2))
print("exhaustividad", round(tp / (tp + fn), 2))
# Cambia k a 10 y vuelve a ejecutar. ¿Qué cifra sube y cuál baja?
Se juzga con datos que no vio
Si eliges la regla y la mides con los mismos días, el resultado es demasiado optimista. Por eso se separan los datos.
Con los primeros 292 días se elige la temperatura. Con los últimos 73, el conjunto de prueba, se juzga. Esos días no pudieron influir en la elección.
Elige la temperatura y mídela con días nuevos
docker exec jupyter python3 -c "import csv; d=list(csv.DictReader(open('hires.csv'))); y=[int(r['hires'])>=300 for r in d]; t=[float(r['temp_c']) for r in d]; a=lambda lo, hi, k: sum((t[i]>=k)==y[i] for i in range(lo, hi))/(hi-lo); k=max(range(26), key=lambda k: (a(0, 292, k), -k)); print(k, round(a(0, 292, k), 3), round(a(292, 365, k), 3), round(1-sum(y[292:])/73, 3))"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Para ir más lejos: el mejor corte
Un modelo da un número y tú pones el corte. Prueba 10, 12, 14, 16 y 18 grados con el programa de la lección. Apunta la precisión y la exhaustividad de cada uno. ¿Qué corte elegirías si una falsa alarma cuesta poco y perder un día movido cuesta mucho?