Ir al contenido

Etiquetado con el modelo en el ciclo: preetiquetas y reentrenamiento

Muestra las respuestas de un modelo entrenado como preetiquetas, revisa primero las preetiquetas con menos confianza y reentrena el modelo a medida que los anotadores corrigen las respuestas.

Novedad de la v2.14.0

Este tutorial recorre el ciclo con el modelo en el ciclo en un estudio de sentimiento. Un modelo se entrena con las primeras etiquetas, sus predicciones aparecen a los anotadores como preetiquetas, los revisores corrigen las preetiquetas con menos confianza y las correcciones alimentan la siguiente ejecución. El tutorial está pensado para responsables de estudios que ya han entrenado un primer modelo, como en Entrena tu primer modelo, y ahora quieren que los anotadores trabajen junto al modelo. Todos los pasos salvo uno se ejecutan en un portátil con sklearn-text. El paso de adquisición entrena un transformer pequeño y tarda unos minutos en una CPU.

El proyecto de sentimiento

El proyecto es el ejemplo de entrenamiento de modelos de Potato, con 160 reseñas sintéticas de productos y un esquema radio llamado sentiment. Una preetiqueta es una etiqueta que un modelo propuso para un ítem antes de que ninguna persona lo etiquetara. La escritura de vuelta es el paso que convierte las predicciones de una ejecución de entrenamiento en preetiquetas en la página de anotación. Al final del tutorial, el proyecto entrena un clasificador con las primeras 120 etiquetas, publica preetiquetas en las reseñas restantes, sirve primero las reseñas con menos confianza, reentrena a medida que llegan etiquetas nuevas y conserva una partición reservada como versión de un conjunto de datos.

Las preetiquetas aceleran la anotación y también inclinan a los anotadores hacia la respuesta del modelo. Etiquetado asistido por un modelo entrenado con tus etiquetas explica cuándo compensa ese intercambio, y Cómo distinguir la revisión del sellado automático en datos preetiquetados trata de los anotadores que aceptan las preetiquetas sin leerlas. La configuración siguiente excluye desde el principio de todas las preetiquetas a una parte de los anotadores. Las respuestas de los excluidos permiten al estudio medir esa inclinación.

Preparar el proyecto

Instala Potato. sklearn-text no necesita nada más allá del paquete base:

bash
pip install --upgrade potato-annotation

Copia examples/ai-assisted/model-training/ del repositorio de Potato a una carpeta nueva. La carpeta contiene data/reviews.json, con las 160 reseñas, y data/gold.json, las etiquetas a partir de las cuales responde el simulador. Sustituye el config.yaml del ejemplo por esta configuración:

yaml
annotation_task_name: "Review Sentiment (model in the loop)"
 
data_files:
  - "data/reviews.json"
 
item_properties:
  id_key: "id"
  text_key: "text"
 
task_dir: "."
output_annotation_dir: "annotation_output"
 
require_password: false
admin_api_key: "demo-admin-key"
 
num_annotators_per_item: 1
assignment_strategy: llm_confidence
 
annotation_schemes:
  - annotation_type: "radio"
    name: "sentiment"
    description: "Is this review positive or negative?"
    labels:
      - "positive"
      - "negative"
    sequential_key_binding: true
 
ai_support:
  holdout:
    fraction: 0.2
    salt: "review-sentiment"
 
datasets:
  enabled: true
 
model_training:
  enabled: true
  writeback:
    schemas: ["sentiment"]
    include_unlabeled: true
    min_confidence: 0.6

Cada bloque añadido a la configuración del ejemplo cumple una función:

  • assignment_strategy: llm_confidence sirve todos los ítems, empezando por la preetiqueta con menos confianza. Los ítems sin predicción van al final, en el orden de la cola.
  • ai_support.holdout retiene toda forma de asistencia de IA, preetiquetas incluidas, en el 20 % de los pares anotador-ítem. Define la exclusión antes de que empiece la anotación, porque cambiar fraction o salt a mitad del estudio vuelve a repartir los grupos.
  • datasets.enabled permite guardar la partición de una ejecución como versión de un conjunto de datos en el último paso.
  • writeback.schemas nombra los esquemas cuyas predicciones ven los anotadores. La lista está vacía de forma predeterminada, y activar el entrenamiento no cambia nada en la página de anotación hasta que la lista nombra un esquema.
  • include_unlabeled: true hace que cada ejecución prediga sobre las reseñas que nadie ha etiquetado todavía. Sin este ajuste, una ejecución predice solo sobre los ítems reservados de val y test. Esos ítems ya tienen etiquetas humanas.
  • min_confidence: 0.6 mantiene fuera de la vista de los anotadores una predicción con una confianza inferior a 0,6. Potato sigue almacenando la predicción junto con la ejecución.

writeback.field conserva el valor predeterminado, predictions, el campo del ítem en el que se escriben las preetiquetas. Con el valor predeterminado no hace falta ningún bloque pre_annotation. Comprueba la configuración antes de iniciar el servidor:

bash
potato validate config.yaml
potato start config.yaml -p 8000

Etiquetar un conjunto semilla

Un modelo entrenado con tus etiquetas necesita primero etiquetas. sklearn-text se niega a ajustar el modelo con menos de 10 ítems en la partición de entrenamiento. Etiqueta algunas reseñas en http://localhost:8000, o deja que tres anotadores simulados etiqueten 40 reseñas cada uno a partir del archivo gold:

bash
python -m potato.simulator --server http://localhost:8000 --users 3 \
  --strategy gold_standard --competence good=1.0 --max-annotations 40 \
  --gold-file data/gold.json --sequential --fast-mode --no-export

--competence good=1.0 asigna a cada anotador simulado el perfil good, que acierta el 80–90 % de las reseñas. Con num_annotators_per_item: 1, los tres anotadores cubren 120 reseñas distintas y dejan 40 sin etiquetar para el modelo.

Entrenar y leer el informe de escritura de vuelta

Abre http://localhost:8000/admin/training con la clave de administrador, elige sklearn-text y el esquema sentiment, y haz clic en Train now. La misma ejecución se inicia desde la línea de comandos:

bash
curl -X POST -H "X-API-Key: demo-admin-key" -H "Content-Type: application/json" \
  http://localhost:8000/admin/training/api/runs \
  -d '{"trainer": "sklearn-text", "schemas": ["sentiment"]}'

La ejecución tarda unos segundos. Potato divide las reseñas etiquetadas en train, val y test (70 %, 15 % y 15 % de forma predeterminada), ajusta el clasificador sobre train y lo puntúa sobre val y test. La ruta de estado informa entonces de las puntuaciones y del informe de escritura de vuelta:

bash
curl -H "X-API-Key: demo-admin-key" http://localhost:8000/admin/training/api/status

Una ejecución de este tutorial devolvió lo siguiente, reducido a los campos que importan aquí:

json
{
  "metrics": {
    "heldout_metric": "val macro_f1",
    "heldout_score": 0.7333,
    "n_train": 76.0,
    "val_n": 20.0,
    "test_n": 24.0,
    "test_macro_f1": 0.8222
  },
  "writeback": {
    "n_read": 84,
    "n_written": 78,
    "n_skipped_low_confidence": 6,
    "n_skipped_train_split": 0,
    "n_skipped_rejected": 0,
    "n_skipped_schema_not_enabled": 0,
    "summary": "Published 78 prediction(s) as prelabels."
  }
}

La ejecución predijo sobre 84 reseñas, las 44 reseñas de val y test más las 40 que nadie había etiquetado. Seis predicciones quedaron por debajo del umbral de 0,6, y las otras 78 se convirtieron en preetiquetas. Tus puntuaciones y recuentos diferirán de una ejecución a otra, porque los anotadores simulados cometen errores al azar y etiquetan reseñas distintas. Cada contador de omisión nombra una de las cuatro comprobaciones de la escritura de vuelta, que Configuración lista en orden.

La escritura de vuelta nunca cambia una etiqueta humana. Las predicciones van a los datos del ítem, y las anotaciones permanecen en el estado de cada anotador. Inicia sesión con un nombre de usuario nuevo para ver el resultado. La siguiente reseña llega con la respuesta del modelo seleccionada:

Una reseña de un producto en la página de anotación de Potato con Positive ya seleccionado por el modelo, antes de que el anotador haya respondidoUn anotador nuevo ve seleccionada la respuesta del modelo y la confirma o la cambia

Una preetiqueta no se guarda mientras el anotador solo la mira. La preetiqueta pasa a ser la respuesta del anotador cuando este la edita o pasa a la siguiente reseña. Por eso una reseña sin responder no cuenta como terminada, y la respuesta de un modelo no vuelve al entrenamiento como etiqueta humana.

Lee la puntuación sobre ítems reservados antes de mostrar preetiquetas a los anotadores. Una puntuación de 0,73 de macro-F1 en val sobre 20 reseñas deja mucho margen para preetiquetas erróneas. La referencia original sobre la escritura de vuelta lista los demás contadores de omisión y cómo muestra una preetiqueta cada tipo de anotación.

Revisar las preetiquetas con menos confianza

Con llm_confidence, el siguiente anotador recibe las 40 reseñas sin etiquetar ordenadas por la confianza de la preetiqueta publicada, de menor a mayor. Una segunda estrategia sirve preetiquetas para el control de calidad y no para etiquetar. assignment_strategy: model_review sirve solo los ítems que llevan una preetiqueta, empezando por la de menor confianza, y deja fuera los ítems sin predicción. Cambia a model_review para una pasada de revisión sobre la salida del modelo, y quédate en llm_confidence mientras todos los ítems sigan necesitando una etiqueta. La referencia original de revisión del modelo explica ambas colas.

El informe de revisión del modelo muestra la cola y lo que el modelo dejó fuera:

bash
curl -H "X-API-Key: demo-admin-key" http://localhost:8000/admin/model-review

El informe de la ejecución anterior, con las listas recortadas:

json
{
  "n_items": 160,
  "n_prelabelled": 78,
  "queue": ["review_062", "review_024", "review_044", "review_122", "..."],
  "worst_first": ["review_062", "review_024", "review_044", "..."],
  "n_empty": 82,
  "empty_prediction_ids": ["review_006", "review_007", "review_008", "..."],
  "provenance": {
    "review_062": {
      "sentiment": {
        "run_id": "20261104T043431-de5686",
        "trainer": "sklearn-text",
        "heldout_metric": "val macro_f1",
        "heldout_score": 0.7333
      }
    }
  }
}

La posición de un ítem en queue es la confianza de predicción más baja del ítem entre todos los esquemas. worst_first es el 20 % menos seguro de la cola, y nunca tiene menos de un ítem. empty_prediction_ids lista los ítems sin preetiqueta. En este proyecto, los 82 ítems vacíos son las 76 reseñas de entrenamiento y las 6 predicciones por debajo del umbral. Las reseñas de entrenamiento nunca reciben una preetiqueta.

provenance registra de dónde vino cada preetiqueta. Para cada ítem de la cola cuya preetiqueta escribió una ejecución de entrenamiento, la entrada nombra la ejecución, el entrenador y la puntuación sobre ítems reservados de la ejecución. Una preetiqueta que llegó con el archivo de datos no tiene entrada. Cuando una preetiqueta parece errónea, el id de la ejecución lleva a la página de la ejecución en /admin/training/runs/<run_id>, que muestra los parámetros y la partición de cada ítem.

Registrar veredictos

Un veredicto registra el juicio de un revisor sobre una preetiqueta, por separado de la anotación del revisor. «El modelo acertó» y «una persona eligió esta etiqueta de forma independiente» son hechos distintos, y mantenerlos separados permite medir la precisión del modelo. Potato recibe los veredictos por una sola ruta, del anotador al que está asignado el ítem o de un administrador con la clave de la API:

bash
curl -X POST -H "Content-Type: application/json" -H "X-API-Key: demo-admin-key" \
  -d '{"instance_id": "review_062", "verdict": "correct", "schema_name": "sentiment"}' \
  http://localhost:8000/api/model-review/verdict

accept significa que el modelo acertó, correct significa que una persona cambió la respuesta y reject significa que el modelo se equivocó. Un reject quita la preetiqueta del ítem al instante, y las ejecuciones posteriores no vuelven a publicar una predicción para ese ítem y esquema. El reject sigue contando contra la precisión del modelo, como una preetiqueta rechazada y no como una etiqueta que el modelo omitió, y enviar de nuevo el mismo veredicto no cambia ningún recuento. Un veredicto correct mueve el ítem a la partición train de todas las ejecuciones posteriores, como describe más abajo la sección sobre correcciones. Registrar un segundo veredicto sobre el mismo ítem sustituye al primero, y un veredicto desconocido se rechaza con HTTP 400.

Ordenar la cola por una puntuación de adquisición

El orden por confianza sirve primero los ítems en los que la mejor respuesta del modelo es más débil. Una función de adquisición del aprendizaje activo ordena los ítems sin etiquetar según cuánto ayudaría al siguiente modelo una etiqueta de cada ítem. hf-text-classification y hf-image-classification admiten un parámetro acquisition que escribe una puntuación así en cada predicción, y llm_confidence sirve entonces primero la puntuación más alta. El entrenador necesita el extra de entrenamiento de texto:

bash
pip install "potato-annotation[train-text]"

Inicia una ejecución con el parámetro, desde el campo Parameters de la página Training o desde la API:

bash
curl -X POST -H "X-API-Key: demo-admin-key" -H "Content-Type: application/json" \
  http://localhost:8000/admin/training/api/runs \
  -d '{"trainer": "hf-text-classification", "schemas": ["sentiment"],
       "params": {"acquisition": "margin"}}'

El parámetro admite cinco valores:

acquisitionPuntuación escrita en cada predicción
least_confidentEl valor predeterminado. No se escribe ninguna puntuación, y los ítems conservan el orden por confianza.
margin1 menos la diferencia entre las dos probabilidades más altas.
entropyEntropía de la distribución predicha, escalada a 0–1.
baldInformación mutua entre la etiqueta y los pesos del modelo, a partir de mc_samples (10 de forma predeterminada) pasadas hacia delante con dropout activado. El método es de Houlsby et al. (2011). La puntuación es alta donde al modelo le faltan datos y baja donde un ítem es ambiguo sean cuales sean los datos.
coresetDistancia de cada ítem al ítem de entrenamiento más cercano o al ya elegido, sobre los embeddings del modelo. El método es de Sener y Savarese (2018). La puntuación reparte la cola por los datos, de modo que diez copias casi idénticas de un ítem difícil no ocupan los diez primeros puestos.

Una predicción de la ejecución con margen lleva la puntuación junto a la confianza:

json
{"label": "positive", "confidence": 0.5207,
 "probabilities": {"negative": 0.4793, "positive": 0.5207},
 "acquisition": 0.9586}

Con llm_confidence, van primero los ítems con puntuación de adquisición, después los ítems con solo confianza, después los ítems cuya predicción no tiene confianza y por último los ítems sin predicción. acquisition funciona con esquemas de una sola etiqueta (radio, select, likert). El orden lee las preetiquetas publicadas, así que el esquema debe estar en writeback.schemas, y include_unlabeled: true debe estar definido para que los ítems sin etiquetar reciban siquiera una puntuación. model_review ignora la puntuación y mantiene el orden por confianza, porque una pasada de revisión busca las respuestas erróneas del modelo. La sección original sobre cómo elegir qué preguntar a continuación tiene los detalles.

Compara la puntuación sobre ítems reservados de la nueva ejecución con la de la ejecución de sklearn-text antes de conservar las preetiquetas de la nueva. Potato conserva una predicción por ítem y esquema, y la predicción de una ejecución más reciente sustituye a la de una anterior. Cuando la predicción de la ejecución más reciente se almacena pero no se muestra, p. ej., por min_confidence, la preetiqueta de la ejecución anterior también sale del ítem, como describe Configuración. En una ejecución de este tutorial en una CPU, tres épocas del modelo predeterminado (epochs: 3 en params) obtuvieron 0,52 de macro-F1 en test sobre 84 reseñas de entrenamiento, por debajo de la ejecución de sklearn-text con las mismas etiquetas, y la ejecución publicó 37 preetiquetas. Para retirar las preetiquetas de una ejecución, borra las predicciones de la ejecución:

bash
curl -X DELETE -H "X-API-Key: demo-admin-key" \
  http://localhost:8000/admin/training/api/runs/<run_id>/predictions

Retirar elimina las predicciones almacenadas de la ejecución y quita de los ítems las preetiquetas de la ejecución. Las preetiquetas de una ejecución anterior no vuelven, así que entrena de nuevo para publicarlas otra vez.

Reentrenar automáticamente

auto_retrain inicia una ejecución por sí solo a medida que llegan anotaciones. Añade el bloque a model_training cuando la puntuación sobre ítems reservados de una ejecución respalde mostrar preetiquetas:

yaml
model_training:
  enabled: true
  writeback:
    schemas: ["sentiment"]
    include_unlabeled: true
    min_confidence: 0.6
  auto_retrain:
    enabled: true
    trainer: sklearn-text
    schemas: ["sentiment"]
    update_frequency: 25
    min_interval_s: 600

Potato comprueba tras cada anotación guardada. Una ejecución empieza cuando han llegado 25 anotaciones desde la última ejecución automática y han pasado 600 segundos desde que terminó la última ejecución. Ambos valores son los predeterminados, y trainer y schemas son obligatorios. params pasa parámetros del entrenador, como hace el campo Parameters de la página Training.

Activa auto_retrain tras la primera ejecución manual y no desde la primera anotación. Un proyecto sin ninguna ejecución entrena en cuanto el proyecto tiene update_frequency anotaciones. Con update_frequency: 20 definido desde el principio, la primera ejecución automática de este proyecto entrenó con 12 reseñas, obtuvo un macro-F1 de 1,0 en val sobre 3 reseñas y publicó 72 preetiquetas. Una puntuación sobre ítems reservados con 3 reseñas no dice casi nada sobre las 72 siguientes.

Cuando una comprobación decide no iniciar una ejecución, skip_reason en /admin/training/api/status da el motivo, como «waiting for 3 more annotation(s)» o «waiting 299 more second(s) since the last run». Las ejecuciones automáticas no entran en la cola de ejecuciones. La siguiente anotación guardada después de que la cola se vacíe desencadena una nueva comprobación. Los contadores viven en memoria, y tras un reinicio un proyecto que ya ha ejecutado cuenta las anotaciones nuevas desde el reinicio. Un entrenador cuya licencia necesita confirmación (ultralytics-detect, ultralytics-segment, layoutlmv3) necesita además accept_licence: true en el bloque, o se omite cada ejecución automática. La configuración completa con ambos bloques pasa potato validate config.yaml --strict.

Ítems corregidos y fijados en el entrenamiento

Dos tipos de ítem van a la partición train de todas las ejecuciones posteriores, sea cual sea el lugar del ítem en la partición estable. El primer tipo es un ítem con un veredicto correct. Las correcciones vienen de los ítems en los que el modelo fue peor, y en val o test las correcciones puntuarían al modelo sobre una muestra de sus propios errores. Los veredictos accept y reject no mueven un ítem. El segundo tipo es un ítem fijado, que un administrador envía al entrenamiento a mano:

bash
curl -X POST -H "X-API-Key: demo-admin-key" -H "Content-Type: application/json" \
  http://localhost:8000/admin/training/api/pinned \
  -d '{"instance_ids": ["review_007", "review_008"]}'

Un ítem fijado se entrena cuando alguien lo ha etiquetado, y hasta entonces cada ejecución lo cuenta como en espera. Con curation: {enabled: true}, el botón Train on these junto a un segmento de la página del Catálogo fija todo lo que el segmento incluye, como el segmento integrado model-found-nothing. DELETE /admin/training/api/pinned quita la fijación.

Una ejecución que movió ítems reservados a train registra una advertencia con el recuento. Tras un veredicto correct, la siguiente ejecución de este tutorial registró lo siguiente:

text
1 held-out item(s) moved to train (corrected: 1). Held-out scores are computed without them, so they are not directly comparable with a run that had them.

Los recuentos están en el manifest.json del paquete de la ejecución, bajo stats.forced_train. Compara las puntuaciones sobre ítems reservados entre ejecuciones teniendo presente la advertencia, ya que las dos ejecuciones puntuaron ítems distintos.

Guardar la partición test de una ejecución como conjunto de datos

Una puntuación sobre ítems reservados cambia cuando cambian las particiones, y un conjunto fijo de ítems es lo que hace comparables dos modelos. La sección Keep this split as a dataset de la página de la ejecución guarda los ítems de val, test o train de una ejecución, con las etiquetas humanas acordadas de los ítems, como una nueva versión de un conjunto de datos. Elige la partición, escribe un nombre de conjunto de datos y haz clic en Save as dataset version. La ruta que hay detrás del botón admite la misma petición desde la línea de comandos:

bash
curl -X POST -H "X-API-Key: demo-admin-key" -H "Content-Type: application/json" \
  http://localhost:8000/admin/training/api/runs/<run_id>/pin-split \
  -d '{"dataset": "sentiment-test", "split": "test"}'

La respuesta nombra la nueva versión y el número de ítems, aquí 23 reseñas de test guardadas como versión v0001. Una versión no cambia después de guardarse, así que luego se pueden puntuar modelos y prompts de LLM en el ejecutor de experimentos de los conjuntos de datos sobre exactamente esos ítems. Guardar en un conjunto de datos que ya tiene versiones inicia una versión nueva que contiene solo esa partición, de modo que los ítems de val de una ejecución posterior no se mezclan con ítems con los que esa ejecución entrenó. Las entradas de cada ejemplo omiten el campo de preetiquetas, así que una evaluación no puede leer la respuesta del modelo. La ruta devuelve HTTP 400 cuando datasets.enabled no está definido.

Excluir a anotadores de las preetiquetas

El bloque ai_support.holdout de la configuración mantiene el 20 % de los pares anotador-ítem libres de asistencia desde la primera anotación. Para un par excluido, la página de anotación no muestra ninguna preetiqueta, y los datos del ítem enviados al navegador omiten tanto writeback.field (aquí predictions) como pre_annotation.field. El sorteo es un hash con sal por anotador e ítem, así que un par permanece en el mismo grupo en cada visita y tras un reinicio. Con varios anotadores por reseña, la mayoría de las reseñas tienen algunos anotadores asistidos y otros excluidos. La pestaña AI influence del panel de administración compara los dos grupos según la frecuencia con que las respuestas coinciden con el modelo, la frecuencia con que coinciden con gold y el acuerdo dentro de cada grupo. Con num_annotators_per_item: 1, cada reseña cae en un solo grupo, así que sube el valor por encima de 1 en un estudio que mida el efecto. Etiquetado asistido por un modelo entrenado con tus etiquetas explica cómo leer la comparación.

Más información

Referencias

  • Houlsby, N., Huszár, F., Ghahramani, Z., and Lengyel, M. (2011). Bayesian Active Learning for Classification and Preference Learning. arXiv:1112.5745. https://arxiv.org/abs/1112.5745
  • Sener, O., and Savarese, S. (2018). Active Learning for Convolutional Neural Networks: A Core-Set Approach. ICLR 2018. https://arxiv.org/abs/1708.00489