Formation d'un détecteur d'objets AI avec Label Studio & MMDetection
L’étiquetage et l’entraînement nécessitent un peu de collage.
Quand j’ai entraîné un détecteur d’objets AI il y a un certain temps - LabelImg était un outil très utile, mais l’exportation depuis Label Studio vers le format COCO n’était pas acceptée par le framework MMDetection..
Il avait besoin d’outils et de scripts pour que tout fonctionne.
Liste ici les éléments manquants et certains scripts certains que j’ai trouvés sur Internet et d’autres que j’ai écrits moi-même.
Étapes de base
Préparer, entraîner et utiliser un AI implique
- obtenir des données sources (images pour la détection d’objets et la classification)
- étiqueter les images et préparer le dataset
- développer un nouveau modèle ou trouver un modèle existant adapté
- entraîner le modèle, parfois avec un réglage des hyperparamètres
- utiliser le modèle pour prédire des étiquettes pour de nouvelles images (ingerring)
Ici, je donne les étapes exactes pour étiqueter des données avec Label Studio (étape 2) et entraîner avec mmdetection et torchvision (étape 4), et aborder l’inférence (étape 5)
Label Studio
Il a certaines racines open source dans le programme LabelImg mais maintenant développé et maintenu de manière très centralisée. Et a bien sûr une version d’entreprise.
Toutefois, il est disponible pour le déploiement en interne, ce qui est très pratique.
Configurer et exécuter
On peut installer et exécuter Label Studio de plusieurs façons, comme par exemple via le package pip, ou via un groupe de conteneurs docker compose. Ici, j’utilise un seul conteneur docker.
Préparer le dossier source
mkdir ~/ls-data
sudo chown -R 1001:1001 ~/ls-data10
Configuration du stockage local
mkdir ~/ai-local-store
mkdir ~/ai-local-labels
# configurer quelques permissions supplémentaires
Dans ~/ai-local-store, vous conservez les fichiers d’images, ~/ai-local-labels - les étiquettes synchronisées.
Démarrer le conteneur 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 - à cause des redirections du LS webui. DATA_UPLOAD_MAX_NUMBER_FILES … Django a restreint le nombre de fichiers uploadés à 100 et cela avait un effet terrible sur label studio, donc il fallait fournir cette nouvelle limite. Toutes les autres configurations sont très bien documentées dans les docs Label Studio.
Importer la configuration. Dans les paramètres du projet, dans le Stockage Cloud, ajouter un Stockage Source de type Fichiers Locaux similaire à :
N’oubliez pas de cocher “Traiter chaque objet de bucket comme un fichier source” si vous prévoyez de synchroniser des images (je crois que oui), pas les jsons. Si vous avez déjà certaines étiquettes pour ces images dans un json séparé - vous configurez simplement ce Stockage Cloud. Ne pas cliquer sur Synchroniser. Et ensuite importer le json.
Configurer de la même façon le Stockage Cloud cible et ai-local-labels - si vous souhaitez les synchroniser.
Importer des données pré-étiquetées dans Label Studio
J’adore le format JSON COCO. Un format Pascal VOC aussi. Mais ils ne sont pas directement compatibles avec Label Studio, il faut donc les convertir d’abord au format propre à LS.
Mais avant, vous pourriez probablement avoir besoin de filtrer le dataset. Vous pourriez avoir besoin uniquement de certaines étiquettes, pas de toutes. J’aime le script filter.py du 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
D’accord, maintenant, installez le convertisseur. Comme le site officiel du convertisseur le recommande :
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 .
Convertir depuis COCO et configurer le dossier correct
label-studio-converter import coco -i your-input-file.json -o output.json
Importer le output.json en cliquant sur le bouton Importer dans Label Studio.
Étiquetage
Beaucoup de travail créatif extrêmement intense est fait ici.
Export
Après avoir cliqué sur le bouton Exporter dans Label Studio et avoir sélectionné le format d’exportation COCO - jetez un œil à l’intérieur de ce fichier et admirez les noms des images. Ils ressembleraient à ceci si vous avez importé des étiquettes avant sans remplacer le chemin de base des images
"images": [
{
"width": 800,
"height": 600,
"id": 0,
"file_name": "\/data\/local-files\/?d=\/iteration1001\/123.jpg"
},
Ou ressembleraient à ceci si vous avez synchronisé un stockage cloud externe.
"images": [
{
"width": 800,
"height": 600,
"id": 0,
"file_name": "http:\/\/localhost:8080\/data\/local-files\/?d=iteration1001\/123.jpg"
},
Ce sont assez peu agréables. Nous voulons quelque chose comme
"images": [
{
"width": 800,
"height": 600,
"id": 0,
"file_name": "iteration1001/123.jpg"
},
Pour corriger les noms de fichiers, j’ai utilisé des scripts très agréables. Ils remplacent le fichier result.json, donc si vous avez besoin d’une sauvegarde avant - assurez-vous de la faire vous-même :
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
Sauvegarde des données et de la base de données de Label Studio
Arrêtez soigneusement votre conteneur docker Label Studio et exécutez ensuite quelque chose comme
cp ~/ls-data ~/all-my-backups
cp ~/ai-local-store ~/all-my-backups
cp ~/ai-local-labels ~/all-my-backups
Fusionner
Parfois, il faut fusionner plusieurs datasets en un seul, surtout si vous faites plusieurs itérations.
J’ai utilisé l’outil COCO-merger. Après l’installation et l’exécution avec le paramètre -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
Oui, on peut fusionner uniquement deux fichiers. Donc si vous avez 10 itérations - il faut faire un effort supplémentaire. Toutefois, j’aime cette méthode.
MMDetection
Division du dataset
Pour diviser le dataset en ensembles d’entraînement et de test, j’ai utilisé l’outil COCOSplit.
git clone https://github.com/akarazniewicz/cocosplit.git
cd cocosplit
pip install -r requirements
Il n’y a pas trop de choses à faire :
$ python cocosplit.py -h
usage: cocosplit.py [-h] -s SPLIT [--having-annotations]
coco_annotations train test
Divise le fichier d'annotations COCO en ensembles d'entraînement et de test.
arguments positionnels :
coco_annotations Chemin vers le fichier d'annotations COCO.
train Où stocker les annotations d'entraînement COCO
test Où stocker les annotations de test COCO
arguments optionnels :
-h, --help affiche ce message d'aide et quitte
-s SPLIT Pourcentage de la division ; un nombre entre 0 et 1
--having-annotations Ignorer toutes les images sans annotations. Conserver uniquement celles avec au moins une annotation
--multi-class Diviser un dataset multi-classe tout en préservant la distribution des classes dans les ensembles d'entraînement et de test
Pour exécuter la division COCO :
python cocosplit.py --having-annotations \
--multi-class \
-s 0.8 \
source_coco_annotations.json \
train.json \
test.json
N’oubliez pas d’ajouter la propriété licenses au début du fichier json du dataset, quelque part après le premier “{”. Ce outil de division veut vraiment cela.
"licenses": [],
Configuration
Oui, les configurations de modèles sont complexes.
Mais mask-rcnn est assez rapide et a un taux de détection raisonnable. Voir ici pour les détails de la configuration : https://mmdetection.readthedocs.io/en/latest/user_guides/train.html#train-with-customized-datasets
# La nouvelle configuration hérite d'une configuration de base pour mettre en évidence les modifications nécessaires
_base_ = '/home/someusername/mmdetection/configs/mask_rcnn/mask-rcnn_r50-caffe_fpn_ms-poly-1x_coco.py'
# Nous devons également changer le nombre de classes dans la tête pour qu'il corresponde aux annotations du dataset
model = dict(
roi_head=dict(
bbox_head=dict(num_classes=3),
mask_head=dict(num_classes=3)))
# Modifier les paramètres liés au 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
# Modifier les paramètres liés aux métriques
val_evaluator = dict(ann_file=data_root+'test.json')
test_evaluator = val_evaluator
# Nous pouvons utiliser le modèle Mask RCNN pré-entraîné pour obtenir une meilleure performance
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'
# si vous aimez les longs films
# la valeur par défaut ici, si je me souviens bien, est 12
train_cfg = dict(max_epochs=24)
Quelque chose à voir si les masques ne sont pas nécessaires : https://mmdetection.readthedocs.io/en/latest/user_guides/single_stage_as_rpn.html
Entraînement
supposons que vous ayez votre configuration de modèle dans /home/someusername/myproject/models/configs/mask-rcnn_r50-caffe_fpn_ms-poly-1x_v1.0.py . Le script d’entraînement est un appel standard à l’outil 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
Inférence
Quelques documents sont ici : 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')
# exécuter pour un seul fichier :
# inferencer('demo/demo.jpg', out_dir='/home/someusername/myproject/test-output/1.0/', show=True)
# ou pour tout un dossier
inferencer('/home/someusername/myproject/test-images/', out_dir='/home/someusername/myproject/test-output/1.0/', no_save_pred=False)
Liens utiles
J’espère que cela vous aidera de quelque manière que ce soit.
D’autres lectures utiles, veuillez consulter
- Site Label Studio : https://labelstud.io/
- Docs MMDetection : https://mmdetection.readthedocs.io/en/latest/get_started.html
- Feuille de triche Bash
- Détection des barres d’armature en béton avec tensorflow
- Feuille de triche Python
- Feuille de triche Conda
- Flux texte vers image
- Feuille de triche Ollama
- Feuille de triche Docker
- Lambda en couches avec AWS SAM et Python
- Génération de PDF en Python - Bibliothèques et exemples"