Addestrare un rilevatore di oggetti AI con Label Studio & MMDetection

Etichettatura e formazione richiedono un po' di colla.

Indice

Quando ho addestrato un rilevatore di oggetti AI qualche tempo fa - LabelImg era uno strumento molto utile, ma l’esportazione da Label Studio al formato COCO non era accettata dal framework MMDetection..

Aveva bisogno di alcuni strumenti e script per far funzionare tutto.

topimage

Elenco qui i pezzi mancanti e alcuni script alcuni dei quali ho trovato su internet e altri che ho scritto io stesso.

Passaggi base

Preparare, addestrare e utilizzare un AI coinvolge

  1. ottenere i dati di origine (immagini per il rilevamento degli oggetti e la classificazione)
  2. etichettare le immagini e preparare il dataset
  3. sviluppare un nuovo modello o trovare un modello esistente adatto
  4. addestrare il modello, a volte con l’aggiustamento dei parametri iper
  5. utilizzare il modello per prevedere le etichette per nuove immagini (ingerring)

Qui do un esempio esatto dei passaggi per etichettare i dati con Label Studio (passo 2) e addestrare con mmdetection e torchvision (passo 4), toccando l’inferenza (passo 5)

Label Studio

Ha alcune radici open source nel programma LabelImg ma ora è sviluppato e mantenuto in modo molto centralizzato. Ha anche una versione enterprise ovviamente.

Tuttavia è disponibile per l’hosting autonomo, che è molto bello.

Configurare e avviare

È possibile installare e avviare Label Studio in diversi modi, ad esempio con il pacchetto pip, o con un gruppo di contenitori docker compose. Qui sto utilizzando un singolo contenitore docker.

Preparare la cartella di origine

mkdir ~/ls-data
sudo chown -R 1001:1001 ~/ls-data10

Configurazione del archivio locale

mkdir ~/ai-local-store
mkdir ~/ai-local-labels

# impostare alcune autorizzazioni extra

In ~/ai-local-store si conservano i file immagine, ~/ai-local-labels - etichette sincronizzate.

Avviare il contenitore docker

docker run -it -p 8080:8080 \
    -e LABEL_STUDIO_HOST=http://your_ip_address:8080/ \
    -e LABEL_STUDIO_LOCAL_FILES_SERVING_ENABLED=true \
    -e LABEL_STUDIO_LOCAL_FILES_DOCUMENT_ROOT=/ai-local-store \
    -e DATA_UPLOAD_MAX_NUMBER_FILES=10000000 \
    -v /home/somename/ls-data:/label-studio/data \
    -v /home/somename/ai-local-store:/ai-local-store \
    -v /home/somename/ai-local-labels:/ai-local-labels \
    heartexlabs/label-studio:latest \
    label-studio \
    --log-level DEBUG

LABEL_STUDIO_HOST - a causa dei reindirizzamenti dell’interfaccia web di LS. DATA_UPLOAD_MAX_NUMBER_FILES … Django limitava il numero di file caricati a 100 e aveva un effetto terribile su Label Studio, quindi era necessario fornire questo nuovo limite. Tutte le altre configurazioni sono molto ben documentate nel Label Studio docs.

Importare la configurazione. Nelle impostazioni del progetto in Cloud Storage aggiungere un’origine di tipo File Locale simile a:

topimage

Non dimenticare di spuntare “Trattare ogni oggetto del bucket come file sorgente” se si prevede di sincronizzare le immagini (credo di sì), non i json. Se si hanno già alcune etichette per queste immagini in un json separato - si configura semplicemente questo Cloud Storage. Non spuntare Sync. E poi importare il json.

Configurare lo stesso per il Target Cloud Storage e ai-local-labels - se si desidera sincronizzarli.

Importare dati pre-etichettati in Label Studio

Amo il formato JSON COCO. Anche il formato Pascal VOC. Ma non sono direttamente compatibili con Label Studio, quindi è necessario convertirli prima nel formato proprietario di LS.

Ma prima, probabilmente sarà necessario filtrare il dataset. Potrebbe essere necessario solo alcuni etichette, non tutte. Mi piace lo script filter.py da coco-manager: https://github.com/immersive-limit/coco-manager/blob/master/filter.py

python filter.py --input_json instances_train2017.json --output_json filtered.json --categories person dog cat
python -m venv env
source env/bin/activate
git clone https://github.com/heartexlabs/label-studio-converter.git
cd label-studio-converter
pip install -e . 

Convertire da COCO e impostare la cartella corretta

label-studio-converter import coco -i your-input-file.json -o output.json

Importare il output.json cliccando sul pulsante Import in Label Studio.

Etichettatura

Molto lavoro creativo estremamente è fatto qui.

Esportazione

Dopo aver cliccato sul pulsante Esporta in Label Studio e selezionato il formato di esportazione COCO - dare un’occhiata all’interno di questo file e ammirare i nomi delle immagini. Saranno simili a questo se hai importato le etichette prima senza sovrascrivere il percorso base delle immagini

  "images": [
    {
      "width": 800,
      "height": 600,
      "id": 0,
      "file_name": "\/data\/local-files\/?d=\/iteration1001\/123.jpg"
    },

O saranno simili a questo se hai sincronizzato l’archiviazione cloud esterna.

  "images": [
    {
      "width": 800,
      "height": 600,
      "id": 0,
      "file_name": "http:\/\/localhost:8080\/data\/local-files\/?d=iteration1001\/123.jpg"
    },

Questi non sono molto belli. vorremmo qualcosa come

  "images": [
    {
      "width": 800,
      "height": 600,
      "id": 0,
      "file_name": "iteration1001/123.jpg"
    },

Per correggere i nomi dei file ho utilizzato degli splendidi script. Essi sovrascrivono il file result.json, quindi se hai bisogno di un backup prima - prendi cura di te stesso:

sed -i -e 's/\\\/data\\\/local-files\\\/?d=\\\///g' ~/tmp/result.json
sed -i "s%http:\\\/\\\/localhost:8080\\\/data\\\/local-files\\\/?d=%%" ~/tmp/result.json
sed -i "s%http:\\\/\\\/your_ip_address:8080\\\/data\\\/local-files\\\/?d=%%" ~/tmp/result.json

Backup dei dati e del database di Label Studio

Fermare con cura il contenitore docker di Label Studio e poi eseguire qualcosa come

cp ~/ls-data ~/all-my-backups
cp ~/ai-local-store ~/all-my-backups
cp ~/ai-local-labels ~/all-my-backups

Unire

A volte è necessario unire diversi dataset in uno, specialmente se si eseguono diverse iterazioni.

Ho utilizzato lo strumento COCO-merger. Dopo l’installazione e l’esecuzione con il parametro -h:

python tools/COCO_merger/merge.py -h

COCO Files Merge Usage

python -m COCO_merger.merge --src Json1.json Json2.json --out OUTPUT_JSON.json

Argument parser

usage: merge.py [-h] --src SRC SRC --out OUT

Merge two annotation files to one file

optional arguments:
  -h, --help     show this help message and exit
  --src SRC SRC  Path of two annotation files to merge
  --out OUT      Path of the output annotation file

Sì. si può unire solo due file. Quindi se si hanno 10 iterazioni - è necessario fare uno sforzo extra. Tuttavia lo apprezzo.

MMDetection

topimage

Divisione del dataset

Per dividere il dataset in training e test ho utilizzato lo strumento COCOSplit.

git clone https://github.com/akarazniewicz/cocosplit.git
cd cocosplit
pip install -r requirements

Non c’è molto da fare:

$ python cocosplit.py -h
usage: cocosplit.py [-h] -s SPLIT [--having-annotations]
                    coco_annotations train test

Splits COCO annotations file into training and test sets.

positional arguments:
  coco_annotations      Path to COCO annotations file.
  train                 Where to store COCO training annotations
  test                  Where to store COCO test annotations

optional arguments:
  -h, --help            show this help message and exit
  -s SPLIT              A percentage of a split; a number in (0, 1)
  --having-annotations  Ignore all images without annotations. Keep only these
                        with at least one annotation
  --multi-class         Split a multi-class dataset while preserving class
                        distributions in train and test sets

Per eseguire la divisione del COCO:

python cocosplit.py --having-annotations \
  --multi-class \
  -s 0.8 \
  source_coco_annotations.json \
  train.json \
  test.json

Ricorda semplicemente di aggiungere la proprietà licenses all’inizio del dataset json, da qualche parte dopo il primo “{”. Questo strumento di divisione lo vuole veramente.

  "licenses": [],

Configurazione

Sì, le configurazioni dei modelli sono complicate.

Ma mask-rcnn è abbastanza veloce e ha un tasso di rilevamento ragionevole. Vedere qui per i dettagli della configurazione: https://mmdetection.readthedocs.io/en/latest/user_guides/train.html#train-with-customized-datasets

# La nuova configurazione eredita una configurazione base per sottolineare la modifica necessaria
_base_ = '/home/someusername/mmdetection/configs/mask_rcnn/mask-rcnn_r50-caffe_fpn_ms-poly-1x_coco.py'

# Dobbiamo anche modificare il num_classes nel head per adattarlo all'annotazione del dataset
model = dict(
    roi_head=dict(
        bbox_head=dict(num_classes=3),
        mask_head=dict(num_classes=3)))

# Modificare le impostazioni relative al dataset
data_root = '/home/someusername/'
metainfo = {
    'classes': ('MyClass1', 'AnotherClass2', 'AndTheLastOne3'),
    'palette': [
        (220, 20, 60),
        (20, 60, 220),
        (60, 220, 20),
    ]
}
train_dataloader = dict(
    batch_size=1,
    dataset=dict(
        data_root=data_root,
        metainfo=metainfo,
        ann_file='train.json',
        data_prefix=dict(img='')))
val_dataloader = dict(
    dataset=dict(
        data_root=data_root,
        metainfo=metainfo,
        ann_file='test.json',
        data_prefix=dict(img='')))
test_dataloader = val_dataloader

# Modificare le impostazioni relative alla metrica
val_evaluator = dict(ann_file=data_root+'test.json')
test_evaluator = val_evaluator

# Possiamo utilizzare il modello pre-addestrato Mask RCNN per ottenere prestazioni più elevate
load_from = 'https://download.openmmlab.com/mmdetection/v2.0/mask_rcnn/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco/mask_rcnn_r50_caffe_fpn_mstrain-poly_3x_coco_bbox_mAP-0.408__segm_mAP-0.37_20200504_163245-42aa3d00.pth'

# se ti piacciono i film lunghi
# il valore predefinito qui, se ricordo bene, è 12
train_cfg = dict(max_epochs=24) 

Qualcosa da guardare se non sono necessari i mask: https://mmdetection.readthedocs.io/en/latest/user_guides/single_stage_as_rpn.html

Addestramento

supponiamo che tu abbia la tua configurazione del modello in /home/someusername/myproject/models/configs/mask-rcnn_r50-caffe_fpn_ms-poly-1x_v1.0.py . Lo script di addestramento è un normale richiamo allo strumento mmdetection:

cd ~/mmdetection
python tools/train.py \
    /home/someusername/myproject/models/configs/mask-rcnn_r50-caffe_fpn_ms-poly-1x_v1.0.py \
    --work-dir /home/someusername/myproject/work-dirs/my-object-detector-v1.0-mask-rcnn_r50-caffe_fpn_ms-poly-1x

Inferenza

Alcuni documenti sono qui: https://mmdetection.readthedocs.io/en/latest/user_guides/inference.html

from mmdet.apis import DetInferencer

inferencer = DetInferencer(
    model='/home/someusername/myproject/models/configs/mask-rcnn_r50-caffe_fpn_ms-poly-1x_v1.0.py',
    weights='/home/someusername/myproject/work-dirs/my-object-detector-v1.0-mask-rcnn_r50-caffe_fpn_ms-poly-1x/epoch_12.pth')

# eseguilo per un singolo file:
# inferencer('demo/demo.jpg', out_dir='/home/someusername/myproject/test-output/1.0/', show=True)

# o per l'intera cartella
inferencer('/home/someusername/myproject/test-images/', out_dir='/home/someusername/myproject/test-output/1.0/', no_save_pred=False)

Spero che questo ti aiuti in qualche modo.

Altri utili letture si trovano su