Pular para o conteúdo

Rotulagem com modelo no ciclo, com pré-rótulos e retreinamento

Mostre as respostas de um modelo treinado como pré-rótulos, revise primeiro os pré-rótulos de menor confiança e retreine o modelo conforme os anotadores corrigem as respostas.

Novo na v2.14.0

Este tutorial conduz um estudo de sentimento pelo ciclo com o modelo no ciclo. Um modelo treina com os primeiros rótulos, as predições do modelo aparecem aos anotadores como pré-rótulos, revisores corrigem os pré-rótulos de menor confiança e as correções alimentam a execução seguinte. O tutorial é para responsáveis por estudos que já treinaram um primeiro modelo, como em Treine o seu primeiro modelo, e agora querem que os anotadores trabalhem ao lado do modelo. Todas as etapas, exceto uma, rodam em um laptop com sklearn-text. A etapa de aquisição treina um pequeno transformer e leva alguns minutos em uma CPU.

O projeto de sentimento

O projeto é o exemplo de treinamento de modelos do Potato, com 160 avaliações sintéticas de produtos e um esquema radio chamado sentiment. Um pré-rótulo é um rótulo que um modelo propôs para um item antes que qualquer pessoa o rotulasse. A gravação de volta é a etapa que transforma as predições de uma execução de treinamento em pré-rótulos na página de anotação. Ao final do tutorial, o projeto treina um classificador com os primeiros 120 rótulos, publica pré-rótulos nas avaliações restantes, serve primeiro as avaliações de menor confiança, retreina conforme chegam novos rótulos e guarda uma divisão reservada como versão de um conjunto de dados.

Os pré-rótulos aceleram a anotação e também puxam os anotadores para a resposta do modelo. Rotulagem assistida por modelo com um modelo treinado nos seus rótulos trata de quando vale a pena fazer essa troca, e Como distinguir revisão de carimbo em dados pré-rotulados trata dos anotadores que aceitam pré-rótulos sem ler. A configuração abaixo reserva desde o início uma parcela dos anotadores sem nenhum pré-rótulo. As respostas desses anotadores permitem ao estudo medir essa atração.

Monte o projeto

Instale o Potato. sklearn-text não precisa de nada além do pacote base:

bash
pip install --upgrade potato-annotation

Copie examples/ai-assisted/model-training/ do repositório do Potato para uma nova pasta. A pasta contém data/reviews.json, com as 160 avaliações, e data/gold.json, os rótulos a partir dos quais o simulador responde. Substitua o config.yaml do exemplo por esta configuração:

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 bloco acrescentado à configuração do exemplo cumpre uma função:

  • assignment_strategy: llm_confidence serve todos os itens, começando pelo pré-rótulo de menor confiança. Os itens sem predição vêm por último, na ordem da fila.
  • ai_support.holdout retém toda forma de assistência de IA, pré-rótulos inclusive, de 20% dos pares anotador-item. Defina a reserva antes de a anotação começar, porque mudar fraction ou salt no meio do estudo reembaralha os grupos.
  • datasets.enabled permite que a divisão de uma execução seja salva como versão de um conjunto de dados na última etapa.
  • writeback.schemas nomeia os esquemas cujas predições os anotadores veem. A lista vem vazia por padrão, e ligar o treinamento não muda nada na página de anotação até que a lista nomeie um esquema.
  • include_unlabeled: true faz cada execução prever também as avaliações que ninguém rotulou ainda. Sem essa configuração, uma execução prevê apenas os itens reservados val e test da execução. Esses itens já têm rótulos humanos.
  • min_confidence: 0.6 mantém fora da vista dos anotadores uma predição com confiança abaixo de 0,6. O Potato ainda armazena a predição junto com a execução.

writeback.field mantém o padrão, predictions, o campo do item em que os pré-rótulos são gravados. Com o padrão, nenhum bloco pre_annotation é necessário. Verifique a configuração antes de iniciar o servidor:

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

Rotule um conjunto inicial

Um modelo treinado nos seus rótulos precisa primeiro de rótulos. sklearn-text se recusa a ajustar o modelo com menos de 10 itens na divisão de treinamento. Rotule algumas avaliações em http://localhost:8000 ou deixe três anotadores simulados rotularem 40 avaliações cada um a partir do arquivo 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 dá a todo anotador simulado o perfil good, que acerta 80–90% das avaliações. Com num_annotators_per_item: 1, os três anotadores cobrem 120 avaliações diferentes e deixam 40 sem rótulo para o modelo.

Treine e leia o relatório de gravação de volta

Abra http://localhost:8000/admin/training com a chave de administrador, escolha sklearn-text e o esquema sentiment e clique em Train now. A mesma execução começa pela linha de comando:

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"]}'

A execução leva alguns segundos. O Potato divide as avaliações rotuladas em train, val e test (70%, 15% e 15% por padrão), ajusta o classificador em train e pontua o classificador em val e test. A rota de status informa então as pontuações e o relatório de gravação de volta:

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

Uma execução deste tutorial retornou o seguinte, reduzido aos campos que importam aqui:

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."
  }
}

A execução previu 84 avaliações, as 44 avaliações de val e test mais as 40 que ninguém havia rotulado. Seis predições ficaram abaixo do limite de 0,6, e as outras 78 viraram pré-rótulos. As suas pontuações e contagens vão variar de execução para execução, porque os anotadores simulados cometem erros aleatórios e rotulam avaliações diferentes. Cada contador de itens ignorados nomeia uma das quatro verificações da gravação de volta, que a Configuração lista em ordem.

A gravação de volta nunca altera um rótulo humano. As predições vão para os dados do item, e as anotações permanecem no estado de cada anotador. Entre com um novo nome de usuário para ver o resultado. A avaliação seguinte chega com a resposta do modelo selecionada:

Uma avaliação de produto na página de anotação do Potato com Positive já selecionado pelo modelo, antes de o anotador responderUm novo anotador vê a resposta do modelo selecionada e a confirma ou a altera

Um pré-rótulo não é salvo enquanto o anotador apenas olha o pré-rótulo. O pré-rótulo passa a ser a resposta do anotador quando o anotador o edita ou passa para a avaliação seguinte. Uma avaliação sem resposta, portanto, não conta como concluída, e a resposta de um modelo não retorna ao treinamento como rótulo humano.

Leia a pontuação em itens reservados antes de mostrar pré-rótulos aos anotadores. Uma pontuação val de 0,73 de macro-F1 em 20 avaliações deixa muito espaço para pré-rótulos errados. A referência original sobre gravação de volta lista os demais contadores de itens ignorados e como cada tipo de anotação mostra um pré-rótulo.

Revise os pré-rótulos de menor confiança

Com llm_confidence, o próximo anotador recebe as 40 avaliações sem rótulo ordenadas pela confiança do pré-rótulo publicado, da menor para a maior. Uma segunda estratégia serve pré-rótulos para controle de qualidade, e não para rotulagem. assignment_strategy: model_review serve apenas itens que têm um pré-rótulo, começando pelo de menor confiança, e deixa de fora os itens sem predição. Passe para model_review em uma rodada de revisão da saída do modelo e permaneça em llm_confidence enquanto todos os itens ainda precisarem de um rótulo. A referência original sobre revisão de modelos explica as duas filas.

O relatório de revisão de modelos mostra a fila e o que o modelo deixou de fora:

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

O relatório da execução acima, com as listas abreviadas:

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
      }
    }
  }
}

A posição de um item em queue é a menor confiança de predição do item entre os esquemas. worst_first é o 20% menos confiante da fila, nunca com menos de um item. empty_prediction_ids lista os itens sem pré-rótulo. Neste projeto, os 82 itens vazios são as 76 avaliações de treinamento e as 6 predições abaixo do limite. As avaliações de treinamento nunca recebem um pré-rótulo.

provenance registra a origem de cada pré-rótulo. Para cada item da fila cujo pré-rótulo uma execução de treinamento gravou, a entrada nomeia a execução, o treinador e a pontuação em itens reservados da execução. Um pré-rótulo que veio com o arquivo de dados não tem entrada. Quando um pré-rótulo parece errado, o ID da execução leva à página da execução em /admin/training/runs/<run_id>, que mostra os parâmetros e a divisão de cada item.

Registrando vereditos

Um veredito registra o julgamento de um revisor sobre um pré-rótulo, separadamente da anotação do revisor. "O modelo acertou" e "uma pessoa escolheu este rótulo de forma independente" são fatos diferentes, e mantê-los separados mantém a precisão do modelo mensurável. O Potato recebe vereditos por uma única rota, do anotador a quem o item está atribuído ou de um administrador com a chave de 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 o modelo acertou, correct significa que uma pessoa alterou a resposta e reject significa que o modelo errou. Um reject retira o pré-rótulo do item de imediato, e as execuções seguintes não publicam de novo uma predição para esse item e esquema. O reject continua contando contra a precisão do modelo, como um pré-rótulo rejeitado e não como um rótulo que o modelo deixou de dar, e enviar o mesmo veredito de novo não altera nenhuma contagem. Um veredito correct move o item para a divisão train de toda execução posterior, como descreve adiante a seção sobre correções. Registrar um segundo veredito no mesmo item substitui o primeiro, e um veredito desconhecido é recusado com HTTP 400.

Ordene a fila por uma pontuação de aquisição

A ordem de confiança serve primeiro os itens em que a resposta principal do modelo é mais fraca. Uma função de aquisição no aprendizado ativo classifica os itens sem rótulo pelo quanto um rótulo em cada item ajudaria o modelo seguinte. hf-text-classification e hf-image-classification aceitam um parâmetro acquisition que grava essa pontuação em cada predição, e llm_confidence serve então primeiro a maior pontuação. O treinador precisa do extra de treinamento de texto:

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

Inicie uma execução com o parâmetro, pelo campo Parameters da página Training ou pela 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"}}'

O parâmetro aceita cinco valores:

acquisitionPontuação gravada em cada predição
least_confidentO padrão. Nenhuma pontuação é gravada, e os itens mantêm a ordem de confiança.
margin1 menos a diferença entre as duas maiores probabilidades.
entropyEntropia da distribuição prevista, escalonada para 0–1.
baldInformação mútua entre o rótulo e os pesos do modelo, a partir de mc_samples (padrão 10) passagens diretas com dropout ligado. O método é de Houlsby et al. (2011). A pontuação é alta onde faltam dados ao modelo e baixa onde um item é ambíguo quaisquer que sejam os dados.
coresetA distância de cada item ao item de treinamento mais próximo, ou ao item já escolhido mais próximo, nos embeddings do modelo. O método é de Sener e Savarese (2018). A pontuação distribui a fila pelos dados, de modo que dez quase cópias de um item difícil não ocupem as dez primeiras posições.

Uma predição da execução com margem traz a pontuação ao lado da confiança:

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

Com llm_confidence, vêm primeiro os itens com pontuação de aquisição, depois os itens apenas com confiança, depois os itens cuja predição não tem confiança e por último os itens sem predição. acquisition funciona em esquemas de rótulo único (radio, select, likert). A ordem lê os pré-rótulos publicados, então o esquema precisa estar em writeback.schemas, e include_unlabeled: true precisa estar definido para que os itens sem rótulo recebam alguma pontuação. model_review ignora a pontuação e mantém a ordem de confiança, porque uma rodada de revisão procura as respostas erradas do modelo. A seção original sobre escolher o que perguntar em seguida traz os detalhes.

Compare a pontuação em itens reservados da nova execução com a da execução de sklearn-text antes de manter os pré-rótulos da nova execução. O Potato guarda uma predição por item e esquema, e a predição de uma execução mais nova substitui a de uma mais antiga. Onde a predição da execução mais nova é armazenada mas não mostrada, por exemplo abaixo de min_confidence, o pré-rótulo da execução mais antiga também sai do item, como descreve a Configuração. Em uma execução deste tutorial em uma CPU, três épocas do modelo padrão (epochs: 3 em params) obtiveram 0,52 de macro-F1 em test com 84 avaliações de treinamento, abaixo da execução de sklearn-text nos mesmos rótulos, e a execução publicou 37 pré-rótulos. Para retirar os pré-rótulos de uma execução, apague as predições da execução:

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

Retirar remove as predições armazenadas da execução e tira os pré-rótulos da execução dos itens. Os pré-rótulos de uma execução anterior não voltam, então treine de novo para republicar.

Retreine automaticamente

auto_retrain inicia uma execução por conta própria conforme chegam anotações. Adicione o bloco a model_training quando a pontuação em itens reservados de uma execução justificar mostrar pré-rótulos:

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

O Potato verifica após cada anotação salva. Uma execução começa quando 25 anotações chegaram desde a última execução automática e 600 segundos se passaram desde o fim da última execução. Os dois valores são os padrões, e trainer e schemas são obrigatórios. params passa parâmetros do treinador, como faz o campo Parameters da página Training.

Ligue auto_retrain depois da primeira execução manual, e não desde a primeira anotação. Um projeto sem nenhuma execução ainda treina assim que tem update_frequency anotações. Com update_frequency: 20 definido desde o início, a primeira execução automática deste projeto treinou com 12 avaliações, obteve 1,0 de macro-F1 em val com 3 avaliações e publicou 72 pré-rótulos. Uma pontuação em itens reservados de 3 avaliações diz quase nada sobre as 72 seguintes.

Quando uma verificação decide não iniciar uma execução, skip_reason em /admin/training/api/status dá o motivo, como "waiting for 3 more annotation(s)" ou "waiting 299 more second(s) since the last run". As execuções automáticas não entram na fila de execuções. A próxima anotação salva depois que a fila esvazia dispara uma nova verificação. Os contadores ficam na memória, e após uma reinicialização um projeto que já teve execuções conta as novas anotações a partir da reinicialização. Um treinador cuja licença precisa de confirmação (ultralytics-detect, ultralytics-segment, layoutlmv3) também precisa de accept_licence: true no bloco, ou toda execução automática é ignorada. A configuração completa com os dois blocos passa em potato validate config.yaml --strict.

Itens corrigidos e fixados no treinamento

Dois tipos de item vão para a divisão train de toda execução posterior, qualquer que seja o lugar do item na divisão estável. O primeiro tipo é um item com veredito correct. As correções vêm dos itens em que o modelo foi pior, e em val ou test as correções pontuariam o modelo em uma amostra dos próprios erros do modelo. Os vereditos accept e reject não movem um item. O segundo tipo é um item fixado, que um administrador envia ao treinamento à mão:

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"]}'

Um item fixado treina assim que alguém o rotula, e até lá cada execução conta o item como em espera. Com curation: {enabled: true}, o botão Train on these, ao lado de uma fatia na página Catalog, fixa tudo o que a fatia reúne, como a fatia embutida model-found-nothing. DELETE /admin/training/api/pinned desafixa.

Uma execução que moveu itens reservados para train registra um aviso com a contagem. Após um veredito correct, a execução seguinte deste tutorial registrou o seguinte:

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.

As contagens estão no manifest.json do pacote da execução, em stats.forced_train. Compare as pontuações em itens reservados entre execuções tendo o aviso em mente, já que as duas execuções pontuaram itens diferentes.

Salve a divisão test de uma execução como conjunto de dados

Uma pontuação em itens reservados muda quando as divisões mudam, e um conjunto fixo de itens é o que torna dois modelos comparáveis. A seção Keep this split as a dataset da página da execução salva os itens val, test ou train de uma execução, com os rótulos humanos acordados desses itens, como uma nova versão de um conjunto de dados. Escolha a divisão, digite um nome para o conjunto de dados e clique em Save as dataset version. A rota por trás do botão aceita a mesma requisição pela linha de comando:

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"}'

A resposta nomeia a nova versão e o número de itens, aqui 23 avaliações de test salvas como a versão v0001. Uma versão não muda depois de salva, então modelos e prompts de LLM posteriores, no executor de experimentos dos conjuntos de dados, podem ser pontuados exatamente nesses itens. Salvar em um conjunto de dados que já tem versões inicia uma nova versão com apenas essa divisão, de modo que os itens val de uma execução posterior não se misturam com itens em que a execução posterior treinou. As entradas de cada exemplo deixam de fora o campo do pré-rótulo, então uma avaliação não consegue ler a resposta do modelo. A rota retorna HTTP 400 quando datasets.enabled não está definido.

Mantendo anotadores fora dos pré-rótulos

O bloco ai_support.holdout da configuração mantém 20% dos pares anotador-item livres de assistência desde a primeira anotação. Para um par reservado, a página de anotação não mostra pré-rótulo, e os dados do item enviados ao navegador deixam de fora tanto o writeback.field (aqui predictions) quanto o pre_annotation.field. O sorteio é um hash com sal por anotador e item, então um par permanece no mesmo grupo em toda visita e após uma reinicialização. Com vários anotadores por avaliação, a maioria das avaliações recebe alguns anotadores assistidos e alguns reservados. A aba AI influence do painel de administração compara os dois grupos quanto à frequência com que as respostas coincidem com o modelo, à frequência com que coincidem com o gold e à concordância dentro de cada grupo. Com num_annotators_per_item: 1, cada avaliação cai em apenas um grupo, então aumente o valor para mais de 1 em um estudo que mede o efeito. Rotulagem assistida por modelo com um modelo treinado nos seus rótulos explica como ler a comparação.

Leitura complementar

Referências

  • 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