Publicado el 5 min de lectura

Elige umbrales de revisión de IA con cobertura y tasas de error

En esta página

Enviar a una persona las predicciones inciertas de una IA parece eficiente, hasta que la cola de revisión supera la capacidad del equipo. Un umbral más estricto puede reducir los errores automatizados y, a la vez, generar más trabajo del que se puede atender.

Crea una comparación local de umbrales para un clasificador de solicitudes con IA. Medirás conjuntamente la cobertura automatizada, los errores entre las predicciones aceptadas y la carga de revisión. Basta con Python 3.10 o posterior; los datos sintéticos no necesitan paquetes, llamadas a API ni información privada.

Tarjetas de documentos verde azulado atraviesan un umbral de cristal ajustable, mientras tarjetas ámbar se acumulan en una bandeja de revisión humana junto a una lupa.

1. Separa la predicción del permiso para automatizar

El clasificador elige question o bug. Una puntuación de selección independiente determina si se automatiza esa predicción o si el sistema se abstiene y solicita revisión. Abstenerse no equivale a elegir una tercera etiqueta ni implica que la persona revisora vaya a acertar.

Es el equilibrio entre cobertura y riesgo que estudia la clasificación selectiva. A diferencia de nuestra evaluación de clasificación de incidencias, aquí preguntamos qué predicciones aceptar, no solo qué categoría se eligió.

En este ejemplo, una puntuación más alta indica mayor confianza para seleccionar la predicción, en una escala de cero a uno. Son señales sintéticas de ordenación, no probabilidades calibradas. Que un chatbot diga «confianza de 0.95» no demuestra un 95 % de aciertos. La guía de calibración de scikit-learn explica qué requieren las probabilidades calibradas; nuestro tutorial de la puntuación de Brier examina por separado la calidad de las probabilidades.

2. Define los denominadores y el presupuesto de revisión

Para cada umbral, acepta las predicciones cuya puntuación sea mayor o igual que ese valor.

  • Cobertura: solicitudes aceptadas divididas entre todas las solicitudes.
  • Error selectivo: predicciones aceptadas incorrectas divididas entre las predicciones aceptadas.
  • Número de revisiones: todas las solicitudes menos las aceptadas.

Si no se acepta ninguna solicitud, el error selectivo queda indefinido; no es cero. Muestra ese resultado de forma explícita.

Para este ejercicio de ocho solicitudes, exigimos un error máximo de 0.10 y no más de cuatro revisiones. Son restricciones ilustrativas, no criterios de producción. En la práctica, calcula el presupuesto de revisión según las solicitudes entrantes y el personal disponible en un intervalo definido.

3. Ejecuta la comparación de umbrales

Guarda lo siguiente como review_thresholds.py. Cada identificador corresponde a una etiqueta revisada, la predicción congelada y su puntuación de selección. Antes de adaptar el ejemplo, empareja los registros reales por identificador.

import math

# request_id: (reviewed label, predicted label, selection score)
ROWS = {
    "r1": ("question", "question", 0.98),
    "r2": ("bug", "bug", 0.95),
    "r3": ("bug", "question", 0.90),
    "r4": ("question", "question", 0.85),
    "r5": ("bug", "bug", 0.75),
    "r6": ("question", "bug", 0.65),
    "r7": ("question", "question", 0.55),
    "r8": ("bug", "question", 0.40),
}
LABELS = {"question", "bug"}


def unit_score(value):
    return (type(value) in (int, float)
            and math.isfinite(value) and 0 <= value <= 1)


def evaluate(rows, threshold):
    if not rows or not unit_score(threshold):
        raise ValueError("nonempty rows and threshold in [0, 1] required")
    accepted = errors = 0
    for actual, predicted, score in rows.values():
        if actual not in LABELS or predicted not in LABELS:
            raise ValueError("unknown label")
        if not unit_score(score):
            raise ValueError("score must be finite and in [0, 1]")
        if score >= threshold:
            accepted += 1
            errors += actual != predicted
    review = len(rows) - accepted
    coverage = accepted / len(rows)
    error = errors / accepted if accepted else None
    return accepted, review, coverage, error


if __name__ == "__main__":
    for threshold in (0.00, 0.80, 0.95, 1.00):
        accepted, review, coverage, error = evaluate(ROWS, threshold)
        ready = error is not None and error <= 0.10 and review <= 4
        rate = "NA" if error is None else f"{error:.3f}"
        print(f"t={threshold:.2f} auto={accepted} review={review} "
              f"coverage={coverage:.3f} error={rate} "
              f"gate={'PASS' if ready else 'HOLD'}")

Ejecuta:

python3 -B review_thresholds.py

Resultado esperado:

t=0.00 auto=8 review=0 coverage=1.000 error=0.375 gate=HOLD
t=0.80 auto=4 review=4 coverage=0.500 error=0.250 gate=HOLD
t=0.95 auto=2 review=6 coverage=0.250 error=0.000 gate=HOLD
t=1.00 auto=0 review=8 coverage=0.000 error=NA gate=HOLD

4. Valida conjuntamente la calidad y la carga de trabajo

La referencia sin abstención acepta las ocho solicitudes y comete tres errores: 0.375. Con 0.80 se aceptan cuatro solicitudes, pero una es incorrecta, por lo que el error es 0.250. Con 0.95 no hay errores entre las dos predicciones aceptadas, pero seis solicitudes necesitan revisión: más de las permitidas.

Ninguno de los umbrales mostrados cumple ambos requisitos. Rechazar todas las solicitudes también mantiene la decisión pendiente: NA no permite establecer la calidad de las predicciones. Observar cero errores en dos casos no demuestra un rendimiento fiable en producción.

Como control positivo, cambia únicamente la etiqueta predicha de r3 a bug. La fila de 0.80 debería mostrar cuatro aceptadas, cuatro revisadas, error 0.000 y PASS. Después, restaura los datos originales. Una entrada vacía, etiquetas desconocidas, puntuaciones no finitas o fuera de rango deberían provocar ValueError; los booleanos tampoco se aceptan como puntuaciones.

El programa original termina con código 0 y sin salida de error estándar. HOLD es un informe, no un bloqueo del despliegue mediante el código de salida.

5. Resuelve los problemas antes de elegir un umbral

  • El error aumenta con un umbral más estricto: examina los casos incorrectos con alta confianza. La cobertura disminuye al subir el umbral; el error no tiene por qué disminuir de forma monótona.
  • Se dispara la carga de revisión: no descartes silenciosamente las abstenciones. Mejora el modelo, limita las solicitudes aptas o aumenta la capacidad de revisión.
  • Las puntuaciones significan cosas distintas: usa la confianza en la etiqueta elegida, no la probabilidad de una clase positiva fija. Comprueba cómo se comportúan las puntuaciones con ejemplos revisados.
  • Los resultados parecen demasiado buenos: elige los umbrales con registros de validación separados, congela la elección y después evalúa registros de prueba intactos. Scikit-learn advierte contra reutilizar datos de entrenamiento para ajustar umbrales.

Con muestras mayores, informa de los recuentos aceptados y los errores por clase e idioma. Registra por separado las demoras y los fallos de quienes revisan: este programa no mide la exactitud final del proceso con intervención humana. Tras cambiar un prompt o modelo, repite una comparación de regresiones emparejada y vuelve a comprobar el umbral congelado.

Conclusión práctica

Elige una política de automatización, no solo un corte de confianza llamativo. Mantén visibles la cobertura, los errores entre los casos aceptados y la capacidad de revisión; si ningún umbral cumple los requisitos, conserva el flujo bajo revisión.

Artículos relacionados

Últimos artículos