Pre-etichette e punteggi held-out da un modello che già esegui
Valuta sulle etichette del tuo studio un modello servito da vLLM, Ollama, Hugging Face, Roboflow, CVAT o un backend ML di Label Studio, poi rivedi le sue risposte come pre-etichette.
Novità nella v2.14.0
Questo tutorial è per i team che eseguono già un modello fuori da Potato e vogliono sapere quanto il modello concorda con i propri annotatori prima di mostrare a qualcuno le sue risposte. L'addestramento di modelli di Potato include cinque trainer, chiamati bridge, che non addestrano nulla. Un bridge invia gli item esclusi dall'addestramento dello studio a un modello servito altrove, valuta le risposte rispetto alle etichette dello studio e le riscrive come pre-etichette da rivedere, come fa l'esecuzione di un modello addestrato. I passaggi seguenti coprono un modello visione-linguaggio dietro un'API compatibile con OpenAI, un modello Roboflow, una funzione serverless di CVAT, un modello Hugging Face e un backend ML di Label Studio, con una configurazione validata per ciascuno.
Che cosa invia e valuta l'esecuzione di un bridge
Un bridge è un trainer il cui passaggio di adattamento verifica soltanto che il servizio risponda. L'esecuzione invia poi al servizio gli item delle partizioni val e test dell'esecuzione, e con writeback.include_unlabeled: true anche gli item che nessuno ha etichettato. Potato trasforma le risposte nelle forme proprie dello schema, cioè span o scelte, e le valuta sugli item esclusi dall'addestramento rispetto alle etichette umane risolte, con le stesse metriche held-out di un modello addestrato (mAP50 per i box, F1 sugli span, macro-F1 per le scelte singole). Il write-back, la coda di revisione e l'ID dell'esecuzione di ogni pre-etichetta funzionano come dopo qualsiasi altra esecuzione.
| Trainer | Schemi | Il modello che chiama |
|---|---|---|
openai-vlm-boxes | image_annotation | Un modello visione-linguaggio dietro un'API di chat compatibile con OpenAI (vLLM, Ollama, LM Studio, il server di llama.cpp, OpenAI) |
roboflow | image_annotation | Un modello Roboflow di rilevamento o segmentazione di istanze, ospitato o su un server Inference self-hosted |
cvat-nuclio | image_annotation | Una funzione serverless di CVAT eseguita su Nuclio |
hf-inference | radio, select, multiselect, span, image_annotation | Un Hugging Face Inference Endpoint o un modello sulla HF Inference API serverless |
label-studio-ml | radio, select, multiselect, span, image_annotation | Un backend ML di Label Studio in esecuzione |
La partizione di un item dipende solo dall'id dell'item e da model_training.split_seed, quindi l'esecuzione di un bridge e quella di un modello addestrato vengono valutate sugli stessi item val e test finché il seed resta fisso. La tabella Runs in /admin/training mette così un modello che già possiedi accanto a un modello addestrato sulle etichette dello studio. Gli item nella partizione train non ricevono alcuna predizione da un bridge, e il write-back non pre-etichetta mai un item della partizione train. Con la ripartizione predefinita 70/15/15, un bridge viene quindi valutato su circa il 30% degli item etichettati.
Prima di iniziare
I quattro bridge diversi da label-studio-ml richiedono requests e Pillow sul server Potato, e la pagina Training elenca un bridge come "not installed" finché entrambi i pacchetti non si importano. label-studio-ml non richiede nulla oltre a Potato. Nessuno dei cinque bridge installa un modello, una libreria GPU o torch, perché il modello gira nel servizio.
Le immagini devono essere file locali dentro il progetto, come per ogni trainer di immagini. Un bridge esclude un item la cui immagine è un URL http(s) remoto, e un item con un'etichetta e senza predizione conta come errato nel punteggio held-out. L'esecuzione riporta la quota di item esclusi dall'addestramento che hanno ricevuto una predizione come val_coverage e test_coverage, quindi una copertura bassa indica immagini che il bridge non ha potuto inviare.
Le chiavi restano nelle variabili d'ambiente
Una chiave non va mai nella configurazione. Ogni bridge che invia una chiave accetta un parametro che nomina la variabile d'ambiente contenente la chiave (api_key_env, token_env per Hugging Face, o i due nomi basic_auth_*_env per Label Studio), e la variabile deve essere impostata nell'ambiente del server Potato prima che il server parta. cvat-nuclio non invia alcuna chiave e non accetta un parametro del genere. I quattro bridge che accettano una chiave rifiutano un valore che non è il nome di una variabile, e l'errore non ripete il valore, perché un valore in quel punto è con ogni probabilità la chiave stessa. Quando il corpo di un errore del servizio o un errore di connessione riecheggia la richiesta, i bridge sostituiscono il valore della chiave con [redacted] prima di mostrare l'errore. I corpi di errore del /predict di un backend ML di Label Studio vengono ripuliti allo stesso modo. Viene sostituito anche tutto ciò che ha la forma di una chiave in una stringa di query o in un'intestazione Authorization.
Le esecuzioni di hf-inference e roboflow sono solo locali. Entrambi i bridge leggono la chiave sul server Potato, e un'esecuzione su un backend ssh, SLURM o agent dovrebbe spedire la chiave a quell'host, quindi Potato rifiuta di avviare un'esecuzione di questo tipo lì. openai-vlm-boxes, cvat-nuclio e label-studio-ml girano su qualsiasi backend che raggiunga il servizio, con la variabile della chiave impostata sull'host di quel backend.
Passaggio 1: uno studio di immagini con tre bridge
La configurazione seguente imposta uno studio di foto di cantiere in cui gli annotatori riquadrano ogni elmetto e ogni testa scoperta. model_training.trainer_params contiene i valori predefiniti per più trainer insieme, quindi un'unica configurazione porta le impostazioni di tutti e tre i bridge per immagini, e ogni esecuzione usa il blocco del proprio trainer. Il campo Parameters della pagina Training si compila con questi valori predefiniti quando scegli un trainer.
annotation_task_name: "Hard hats on site photos"
task_dir: .
media_directory: media
output_annotation_dir: annotation_output
admin_api_key: "replace-with-a-long-random-string"
data_files:
- data/photos.jsonl
item_properties:
id_key: id
text_key: image_url
annotation_schemes:
- annotation_type: image_annotation
name: ppe
description: "Box every hard hat and every bare head."
source_field: image_url
tools: [bbox, polygon]
labels:
- name: helmet
- name: head
model_training:
enabled: true
writeback:
schemas: [ppe]
include_unlabeled: true
trainer_params:
openai-vlm-boxes:
url: http://localhost:8000/v1
model: Qwen/Qwen2.5-VL-7B-Instruct
coordinate_format: pixels
roboflow:
model_id: site-safety/3 # your Roboflow project and version
url: https://detect.roboflow.com
api_key_env: ROBOFLOW_API_KEY
confidence: 0.4
label_map: {hardhat: helmet, no-hardhat: head} # the model's class names on the left
cvat-nuclio:
url: http://localhost:32768
threshold: 0.5
label_map: {person: null}Ogni riga di data/photos.jsonl indica un'immagine sotto media/:
{"id": "site_001", "image_url": "media/site_001.jpg"}writeback.schemas è ciò che rende visibili agli annotatori i box del modello, e include_unlabeled invia al servizio anche le foto che nessuno ha etichettato. Lascia fuori entrambe le chiavi per la prima esecuzione se vuoi il punteggio held-out prima che qualcuno veda una pre-etichetta. label_map associa i nomi delle classi del servizio alle etichette dello schema, e una classe associata a null viene scartata. Una classe senza voce va all'etichetta con lo stesso nome, e il confronto ignora le maiuscole. I box con qualsiasi altra classe restano fuori, e il log dell'esecuzione conta i box esclusi.
Passaggio 2: un modello visione-linguaggio con openai-vlm-boxes
openai-vlm-boxes riduce ogni immagine a un massimo di 1.280 pixel sul lato più lungo (max_image_side) e la invia alla route delle chat completions con un prompt che nomina le etichette dello schema. Potato legge la risposta come box JSON. Funziona qualsiasi server con un'API compatibile con OpenAI. Il comando seguente serve Qwen2.5-VL con vLLM, la cui documentazione collega i client a http://localhost:8000/v1:
vllm serve Qwen/Qwen2.5-VL-7B-InstructCon vLLM sulla porta 8000, avvia Potato su un'altra porta:
potato start config.yaml -p 9000I modelli visione-linguaggio rispondono con la propria convenzione di coordinate qualunque cosa chieda il prompt, e due parametri dicono a Potato come leggere i numeri. coordinate_format è fraction (da 0 a 1 dell'immagine), per_mille (da 0 a 1.000) o pixels dell'immagine come inviata. box_order è xyxy per [x0, y0, x1, y1] o yxyx per [y0, x0, y1, x1]. Qwen2.5-VL risponde in pixel, quindi la configurazione qui sopra imposta coordinate_format: pixels. Con auto, Potato indovina il formato una volta per risposta dalla dimensione dei numeri. I valori fino a 1.05 si leggono come frazioni, i valori oltre la dimensione dell'immagine inviata si leggono come per mille e tutto il resto si legge come pixel. Un box per mille che sta dentro un'immagine grande si legge quindi come pixel, perciò indica il formato ogni volta che conosci la convenzione del modello.
Gemma e Gemini rispondono con box per mille con la y per prima, [ymin, xmin, ymax, xmax], anche quando il prompt chiede frazioni. Con entrambi i parametri su auto, Potato legge i box di un modello il cui nome contiene gemma o gemini come per mille con la y per prima. Su un progetto di prova con Gemma 4 12B su quadrati rossi e blu, il team di Potato ha misurato una mAP50 di 0.0 con i box letti con la x per prima e di 1.0 con i box letti con la y per prima. Ollama serve Gemma 3 con un nome che contiene gemma, quindi auto si applica:
ollama run gemma3:12bSostituisci il blocco openai-vlm-boxes nella configurazione con il blocco seguente:
model_training:
trainer_params:
openai-vlm-boxes:
url: http://localhost:11434/v1
model: gemma3:12b
json_mode: objectUn fine-tune di Gemma o Gemini servito con un nome privo di entrambe le parole riceve la lettura con la x per prima sotto auto. Imposta coordinate_format: per_mille e box_order: yxyx per un modello di questo tipo. json_mode: object chiede a Ollama JSON con response_format: json_object. Il valore predefinito, schema, invia uno schema JSON con le etichette come enum. vLLM e OpenAI rispettano lo schema. Un'esecuzione in cui nessuna risposta si interpreta come JSON fallisce, e l'errore suggerisce json_mode: object o un prompt più semplice (prompt, dove {labels} diventa le etichette dello schema).
Un modello visione-linguaggio non dà alcun punteggio con i suoi box, quindi i box non hanno confidenza. writeback.min_confidence lascia passare ogni predizione di openai-vlm-boxes, e la revisione del modello non può ordinare le predizioni dalla meno alla più confidente. Il riferimento di openai-vlm-boxes elenca api_key_env per un'API ospitata e le impostazioni di token e timeout.
Passaggio 3: avviare un'esecuzione e leggere il punteggio
Apri http://localhost:9000/admin/training, scegli openai-vlm-boxes e lo schema ppe e fai clic su Train now. La stessa esecuzione parte dall'API:
curl -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
http://localhost:9000/admin/training/api/runs \
-d '{"trainer": "openai-vlm-boxes", "schemas": ["ppe"]}'Il passaggio di adattamento elenca i modelli del server tramite /models e registra quale modello l'esecuzione interroga e come legge i box del modello. Il passaggio di predizione invia poi un'immagine per richiesta. Quando l'esecuzione termina, la mAP50 held-out compare nella tabella Runs, e la pagina dell'esecuzione elenca val_map_50, test_map_50, i valori di copertura e un avviso "Not used" che conta le risposte che non erano JSON, i box con etichette che lo schema non ha e i box fuori dall'immagine. Quando il punteggio è vicino a 0 mentre la copertura è alta e l'avviso è vuoto, controlla prima coordinate_format e box_order.
Modelli Roboflow
roboflow invia ogni immagine con POST a <url>/<model_id>, dove model_id è il progetto e la versione come project/version. La chiave va per impostazione predefinita in ROBOFLOW_API_KEY (api_key_env), e l'esecuzione rifiuta di partire senza la chiave:
export ROBOFLOW_API_KEY=... # in the shell that starts Potato
potato start config.yaml -p 9000L'url predefinito di Potato è https://detect.roboflow.com, e la configurazione qui sopra indica quello predefinito. Un'esecuzione per la segmentazione di istanze imposta url: https://outline.roboflow.com. url è un parametro, quindi un'esecuzione può anche indicare https://serverless.roboflow.com, l'host che il riferimento sul rilevamento di oggetti di Roboflow riporta per la stessa route project/version. Il team di Potato non ha provato quell'host con questo bridge. Un server Roboflow Inference self-hosted ascolta per impostazione predefinita sulla porta 9001 (url: http://localhost:9001).
L'API di Roboflow riceve la chiave nella stringa di query. Potato non scrive l'URL della richiesta nel log dell'esecuzione né in un messaggio di errore. Potato invia confidence e overlap (valori predefiniti 0.4 e 0.3) all'API come percentuali, e il riferimento di Roboflow documenta entrambi su una scala da 0 a 100. shapes: auto scrive un contorno dove il modello restituisce un contorno e lo schema ha uno strumento poligono, e un box altrimenti. Il controllo di integrità invia un'immagine bianca di 8 × 8, e Roboflow conta la richiesta nell'utilizzo dell'account.
Funzioni serverless di CVAT
cvat-nuclio usa un detector già distribuito per CVAT come funzione Nuclio, come le funzioni YOLOv7 o Faster R-CNN in cvat/serverless. Potato invia ogni immagine con POST all'URL proprio della funzione come {"image": <base64>, "threshold": t}, senza passare da CVAT. nuctl get functions elenca le funzioni distribuite con le loro porte, e il comando seguente è la forma riportata dalla documentazione di CVAT:
nuctl get functions --platform localLa funzione risponde con forme in pixel. Un rectangle diventa un box, e un polygon diventa un poligono, o il box di delimitazione del poligono quando lo schema non ha uno strumento poligono. Le forme mask vengono escluse e contate. La funzione scarta le forme con punteggio inferiore a threshold (predefinito 0.5). Lo schema non ha un'etichetta person, quindi label_map: {person: null} nella configurazione scarta le persone di un detector COCO.
Modelli Hugging Face con hf-inference
hf-inference chiama un modello distribuito su Hugging Face, sia un Inference Endpoint dedicato tramite il suo URL https:// (endpoint_url), sia l'id di un modello sulla HF Inference API serverless (model). Imposta endpoint_url o model, e non entrambi. Il compito segue il tipo dello schema:
- Classificazione di testo per
radio,selectemultiselect. Uno schema a scelta singola prende l'etichetta migliore del modello. Quando quell'etichetta non è una delle etichette dello schema dopolabel_map, l'item non riceve predizione anziché la seconda classificata. Uno schemamultiselectprende ogni etichetta con punteggio di almenothreshold(predefinito 0.5). - Classificazione di token per
span, con i word piece raggruppati in entità tramiteaggregation_strategy(simple,first,averageomax) e gli offset di carattere presi dal modello. - Rilevamento di oggetti per
image_annotation. I box con punteggio inferiore athresholdvengono scartati.
La configurazione seguente chiede a dslim/bert-base-NER gli span. La model card descrive un modello BERT sottoposto a fine-tuning per le entità nominate sull'inglese di CoNLL-2003. HF Inference serve un sottoinsieme dei modelli dell'Hub, quindi controlla che la pagina del modello elenchi HF Inference prima di affidarti all'API serverless.
annotation_task_name: "Named entities in news sentences"
task_dir: .
output_annotation_dir: annotation_output
admin_api_key: "replace-with-a-long-random-string"
data_files:
- data/sentences.jsonl
item_properties:
id_key: id
text_key: text
annotation_schemes:
- annotation_type: span
name: entities
description: "Highlight every person, organization, and place."
labels: [person, organization, location]
model_training:
enabled: true
writeback:
schemas: [entities]
include_unlabeled: true
trainer_params:
hf-inference:
model: dslim/bert-base-NER
token_env: HF_TOKEN
aggregation_strategy: simple
label_map: {PER: person, ORG: organization, LOC: location, MISC: null}Le entità PER, ORG e LOC del modello vengono associate alle etichette dello schema, e MISC viene scartata perché lo schema non ha un'etichetta simile. Quando il nome completo di un'entità non corrisponde a nulla, Potato elimina un prefisso B- o I- e riprova. Un classificatore di sentiment che risponde LABEL_0 e LABEL_1 richiede nello stesso modo label_map: {LABEL_0: negative, LABEL_1: positive}. Il controllo di integrità è una richiesta reale, una frase breve o un'immagine di 8 × 8, e wait_for_model: true (il valore predefinito) attende mentre un modello serverless si carica anziché fallire con un 503.
Un backend ML di Label Studio
label-studio-ml usa un backend ML di Label Studio in esecuzione. Un backend avviato dagli esempi del repository con docker-compose up serve un modello via HTTP su http://localhost:9090. L'esecuzione controlla il GET /health del backend, invia gli item a POST /predict in batch da 16 (batch_size) e legge le regioni restituite con lo stesso convertitore usato da potato import -f label_studio. Gli offset di span UTF-16 di Label Studio diventano code point, e le coordinate in percentuale diventano quelle del widget immagine.
annotation_task_name: "Review sentiment"
task_dir: .
output_annotation_dir: annotation_output
admin_api_key: "replace-with-a-long-random-string"
data_files:
- data/reviews.jsonl
item_properties:
id_key: id
text_key: text
annotation_schemes:
- annotation_type: radio
name: sentiment
description: "Is this review positive or negative?"
labels: [positive, negative]
model_training:
enabled: true
writeback:
schemas: [sentiment]
include_unlabeled: true
min_confidence: 0.6
trainer_params:
label-studio-ml:
url: http://localhost:9090
basic_auth_user_env: LS_ML_USER
basic_auth_pass_env: LS_ML_PASSL'unica autenticazione di un backend standard è HTTP Basic, impostata da BASIC_AUTH_USER e BASIC_AUTH_PASS sul backend. basic_auth_user_env e basic_auth_pass_env nominano le variabili sul server Potato che contengono gli stessi due valori, e i due parametri vanno insieme. api_key_env invia invece Authorization: Token <value>, per un backend dietro un proxy che si aspetta un token. Un backend standard risponde a quell'intestazione con un 500. Potato genera dallo schema la configurazione di etichettatura inviata con ogni richiesta (Choices, Labels, RectangleLabels o PolygonLabels), e label_config sostituisce l'XML generato con il tuo.
Un backend per immagini richiede un'altra impostazione. Senza media_url_prefix, Potato invia ogni immagine come percorso assoluto sulla macchina Potato, e un backend costruito sull'SDK di Label Studio legge data.image come un URL e fallisce con un percorso semplice. Per un backend sulla stessa macchina, aggiungi il prefisso seguente e avvia il backend con LOCAL_FILES_DOCUMENT_ROOT impostato alla cartella del task:
model_training:
trainer_params:
label-studio-ml:
url: http://localhost:9090
media_url_prefix: /data/local-files/?d=Per un backend su un'altra macchina, servi la cartella del task via HTTP e indica l'URL di quel server come prefisso. Un'immagine fuori dalla cartella del task non ha un percorso relativo alla cartella, quindi Potato non invia l'immagine, e il log dell'esecuzione indica quante ne sono rimaste fuori.
Potato invia al backend richieste di predizione e nessun evento di annotazione, quindi l'addestramento proprio del backend nel suo gestore /webhook non parte da Potato. potato import -f label_studio sposta in Potato un intero progetto Label Studio con la configurazione di etichettatura del progetto, le risposte degli annotatori e le predizioni memorizzate (Importare progetti). Migrating from Label Studio to Potato tratta i tag che l'importatore lascia fuori.
Rivedere le pre-etichette
Le predizioni di un bridge passano attraverso gli stessi controlli di write-back di quelle di un modello addestrato. Il write-back salta gli item della partizione train e gli schemi assenti da writeback.schemas, salta le coppie item-schema che un revisore ha rifiutato e trattiene le predizioni sotto writeback.min_confidence. Per box e span, la confidenza di una predizione è la confidenza della forma o dello span meno confidente, quindi la soglia trattiene l'intera predizione di un item quando un qualsiasi box o span dell'item ottiene un punteggio inferiore. Il confidence di Roboflow e il threshold di CVAT agiscono invece per box, presso il servizio, e il threshold di hf-inference agisce per box o per etichetta multiselect. Il tutorial sul modello nel ciclo tratta la coda di revisione, i verdetti accept e reject e il riaddestramento. La coda e i verdetti funzionano allo stesso modo per un bridge.
Per ogni item e schema viene conservata una predizione, quindi un'esecuzione successiva di un altro bridge o di un modello addestrato sostituisce le pre-etichette dell'esecuzione precedente. Per ritirare le pre-etichette di un'esecuzione, invia DELETE /admin/training/api/runs/<run_id>/predictions. Il riferimento dei bridge di inferenza elenca ogni parametro dei quattro bridge, e il riferimento di label-studio-ml elenca i parametri di Label Studio.
Ulteriori letture
- Addestramento di modelli tratta il ciclo e la pagina Training.
- I trainer elenca tutti i 38 trainer per compito, per un modello addestrato sulle etichette dello studio da confrontare con il punteggio di un bridge.
- Configurazione documenta partizioni, write-back e route.
- Annotazione di immagini e Annotazione di span documentano gli schemi che i bridge compilano.
- Supporto IA visivo mostra su richiesta a un annotatore i suggerimenti di un modello di visione, senza un punteggio held-out.