paint-brush
Comment former un modèle de détection d'objets à l'aide d'un ensemble de données personnalisé avec MinIO et YOLOpar@minio
8,522 lectures
8,522 lectures

Comment former un modèle de détection d'objets à l'aide d'un ensemble de données personnalisé avec MinIO et YOLO

par MinIO13m2023/11/22
Read on Terminal Reader

Trop long; Pour lire

Dans cet article, nous allons créer un ensemble de données d'image personnalisé, puis former un modèle You-Only-Look-Once (YOLO) pour la tâche omniprésente de détection d'objets. Nous implémenterons ensuite un système utilisant MinIO Bucket Notifications qui peut effectuer automatiquement une inférence sur une nouvelle image.
featured image - Comment former un modèle de détection d'objets à l'aide d'un ensemble de données personnalisé avec MinIO et YOLO
MinIO HackerNoon profile picture
0-item
1-item

La vision par ordinateur reste une application extrêmement intéressante de l’intelligence artificielle. Qu’il s’agisse de reconnaître des éléments sur un champ de bataille ou de prédire les rendements des cultures, la vision par ordinateur est sans doute l’un des domaines de l’IA les plus précieux sur le plan commercial (et socialement important).


Cependant, la complexité associée à la construction d'un ensemble de données et à la conception d'un système simple de bout en bout qui exécutera votre tâche de vision par ordinateur sur une nouvelle image constitue souvent un frein à l'adoption des meilleures capacités de vision par ordinateur.


Dans cet article de blog, nous examinerons étape par étape comment résoudre ces problèmes avec les meilleurs outils de leur catégorie tels que CVAT et MinIO Bucket Notifications. À la fin de cet article, vous serez en mesure de former un modèle de détection d'objets sur un ensemble de données personnalisé et de l'utiliser pour faire des prédictions chaque fois qu'une nouvelle image apparaît.

La prémisse

Disons que nous voulons pouvoir reconnaître les types d'avions présents dans les images satellite. Supposons également que nous partions de zéro : pas d'ensembles de données prédéfinis, pas de modèles pré-entraînés. Voici deux exemples d’avions que nous souhaitons détecter et reconnaître dans nos images satellite :


De gauche à droite : (1) Su-30, (2) Tu-95


Les étapes décrites dans cet article peuvent être généralisées à presque n’importe quel domaine. Au lieu de détecter les types d’avions, nous pourrions classer l’utilisation des terres ou effectuer une régression pour prédire les rendements des cultures. Au-delà des images traditionnelles, nous pourrions également former et effectuer des inférences sur d'autres types de données multidimensionnelles comme les nuages de points LiDAR ou les images sismiques 3D ; cela devient simplement une question de savoir à quoi ressemblent les données de formation (et potentiellement un modèle d'apprentissage en profondeur différent au lieu de YOLO). Si vous avez d'autres questions sur ce à quoi cela ressemblerait pour un cas d'utilisation spécifique, n'hésitez pas à poser un problème sur le dépôt GitHub !


Étape 1 : Acquisition et gestion des échantillons de formation

Pour ce projet, en grande partie parce que je ne possédais pas de satellite d'imagerie à la demande, j'ai visité des aérodromes sur Google Earth et pris plusieurs captures d'écran des zones où certains de ces avions étaient visibles. L'assemblage de cet ensemble d'images a pris un certain temps, je les ai donc toutes stockées dans un compartiment sur mon serveur MinIO intitulé « détection d'objet ». Dans un environnement de production, les avantages du stockage de vos échantillons collectés sur MinIO deviennent encore plus prémonitoires. La réplication active-active, les niveaux de cryptage les plus élevés et les GET/PUT ultra rapides (pour n'en nommer que quelques-uns) signifient que vos échantillons collectés avec diligence seront hautement disponibles, sûrs et sécurisés.

Étape 2 : Création de l'ensemble de données

Création de l'ensemble de données


Afin de former un modèle de détection d'objets pour votre cas d'utilisation, un ensemble de données étiqueté (ou « annoté ») est nécessaire. Un excellent outil pour cela est CVAT par OpenCV. Une fonctionnalité intéressante est que CVAT fournit un utilitaire pour connecter votre bucket MinIO en tant que « stockage cloud » afin de transmettre les images de votre bucket directement à l'outil d'annotation de l'ensemble de données. Pour ce faire, assurez-vous que l'hôte de votre serveur MinIO est accessible au serveur CVAT, surtout si vous exécutez MinIO Server sur site ou localement sur votre ordinateur portable. De plus, à titre de remarque, il existe deux façons d'utiliser CVAT : (1) en utilisant l'application Web fournie sur app.cvat.ai ou (2) en l'exécutant localement. Dans les deux cas, une fois CVAT ouvert, cliquez sur « Cloud Storages » dans la barre de menu. À partir de là, vous pouvez remplir un formulaire pour joindre votre bucket MinIO (compatible S3) :


Créer un stockage cloud


Créons maintenant notre nouvelle tâche d'étiquetage sous « Tâches » :


Tâche d'étiquetage


Vous devriez être invité à remplir un formulaire :


Créer une nouvelle tâche


Lors de la création de la tâche, il est important de définir correctement les étiquettes de classe (j'ai défini deux étiquettes Rectangle intitulées « SU30 » et « TU95 », correspondant aux deux plans que je voulais détecter) :


Définir des étiquettes de classe


Il ne reste plus qu'à attacher notre compartiment MinIO précédemment ajouté en tant que source de données. Sous « Sélectionner les fichiers », cliquez sur « Cloud Storage » et indiquez le nom que vous avez fourni précédemment pour cette source. J'ai utilisé le nom « minio-cv-bucket » ci-dessus.


Attacher le bucket MinIO en tant que source de données


Le processus de téléchargement prendra quelques minutes. Une fois terminé, vous devriez pouvoir voir votre travail d'annotation disponible sous « Tâches ».


Téléchargement terminé


Désormais, en cliquant sur le travail, vous pouvez commencer à annoter chacune de vos images. Attention : cela peut prendre un temps disproportionné. Généralement, dans un environnement de production avec des besoins d'annotation importants, il peut être préférable de confier cette tâche à une équipe interne dédiée ou à une société tierce d'étiquetage des données.


Commencer à annoter


Une fois que vous avez terminé d'annoter, exportez l'ensemble de données au format YOLO.


Exporter des données

Étape 3 : Organisation des données de formation

Votre ensemble de données exporté se présentera sous la forme d'un fichier zip. Une fois que vous l'avez décompressé, les fichiers texte d'annotation au format YOLO se trouveront dans un dossier fermé. N'hésitez pas à y jeter un oeil. Au format YOLO, les annotations de chaque image se trouvent dans un fichier texte où chaque ligne contient deux coins d'un cadre de délimitation et la classe. Le numéro de classe correspond à l'ordre dans lequel vous avez défini les libellés lors de la création de la tâche. Ainsi, dans cet exemple, 0 correspondrait au Su-30 et 1 correspondrait au Tu-95.


À ce stade, créez un nouveau répertoire de travail (ou entrez-en un que vous avez déjà créé). Dans ce répertoire, créez un sous-répertoire appelé « dataset ». Dans « ensemble de données », créez des répertoires tels que votre répertoire de travail ressemble à ceci :


 my_cv_project (WORKING DIRECTORY) |---- dataset |----images |----train |----val |----test |----annotations |----train |----val |----test



Vous devrez maintenant remplir les sous-répertoires train, val et test pour les deux images et leurs annotations correspondantes (les fichiers texte). C'est à vous de décider comment vous souhaitez récupérer et diviser vos échantillons. Une bonne pratique consiste à diviser votre quantité totale d’échantillons de formation en 80 % de formation, 10 % de validation et 10 % de tests. Assurez-vous de mélanger aléatoirement vos images avant de les partitionner.


Personnellement, j'ai utilisé le mc cp de MinIO Client dans la ligne de commande pour récupérer rapidement toutes les images de mon bucket « détection d'objet ». Alternativement, si vous avez déjà tous vos exemples d’images au même endroit sur votre ordinateur local, vous pouvez directement travailler avec cela. Une fois que j'ai eu tous mes échantillons au même endroit, j'ai utilisé un script Python pour mélanger, diviser et déplacer mes images et annotations vers les répertoires train, val et test. Voici le script fourni pour plus de commodité . Si vous avez des questions sur la façon de l'utiliser, n'hésitez pas à poser un problème dans le repo !


En fin de compte, assurez-vous que pour chaque image que vous avez placée dans images/train, images/val ou images/test, le fichier d'annotation .txt correspondant se trouve également dans le sous-répertoire correspondant du répertoire annotations/. Par exemple:


 my_cv_project (WORKING DIRECTORY) |---- dataset |----images |----train - 5.png - 3.png - 2.png |----val - 4.png |----test - 1.png |----annotations |----train - 5.txt - 3.txt - 2.txt |----val - 4.txt |----test - 1.txt


Désormais, nos données sont en place. Il est temps de jeter un œil à notre modèle de détection d'objets et de commencer la formation.

Étape 4 : Le modèle de détection d'objets

La référence actuelle (en termes de performances et de facilité d’utilisation) en matière de reconnaissance d’objets est la classe de modèles YOLO (You Only Look Once). Au moment de la rédaction, YOLOv8 est la dernière version et est maintenue en open source par Ultralytics. YOLOv8 fournit une API simple que nous pouvons exploiter pour entraîner le modèle sur nos annotations nouvellement créées (et éventuellement exécuter également l'inférence).


Téléchargeons YOLOv8 :


 $ pip install ultralytics


Nous pouvons désormais utiliser l'outil CLI YOLOv8 ou le SDK Python pour entraîner, valider et prédire. Reportez-vous à la documentation YOLOv8 pour plus d'informations.

Étape 5 : Formation

Dans votre répertoire de travail, définissez un fichier YAML qui spécifie les emplacements de l'ensemble de données et les détails sur les classes. Remarquez que les chemins sont les mêmes que ceux que j'ai créés précédemment dans le répertoire de travail. J'ai appelé mon fichier ' objdetect.yaml .' Notez également que les deux étiquettes de classe d'avion doivent être définies dans le même ordre que dans CVAT.


 train: ./dataset/images/train/ val: ./dataset/images/val/ test: ./dataset/images/test/ # number of classes nc: 2 # class names names: ["SU-30","TU-95"]


Commencez à entraîner le modèle YOLOv8 sur notre ensemble de données avec la commande suivante (à l'aide de l'outil YOLO CLI). Reportez-vous à la documentation YOLO pour en savoir plus sur toutes les différentes options que vous pouvez configurer pour la formation. Ici, je lance une formation sur 100 époques et je définis une taille d'image de 640 pixels (toutes nos images de formation seront mises à l'échelle en conséquence pendant la formation) :


 $ yolo task=detect \ mode=train \ model=yolov8s.pt \ data=objdetect.yaml \ epochs=100 \ imgsz=640


La formation prendra un certain temps, surtout si vous travaillez sur un ordinateur portable (comme moi), c'est donc le bon moment pour faire une pause (ou lire à l'avance 😀) !


À la fin de la boucle d'entraînement, votre modèle entraîné, ainsi que d'autres graphiques et diagrammes intéressants, seront stockés dans un répertoire généré automatiquement appelé « exécutions ». La sortie du terminal (comme ci-dessous) indiquera l'emplacement spécifique des résultats de la dernière exécution. Chaque fois que vous entraînez un modèle, un répertoire similaire sera généré dans « runs/detect/ ».


 Results saved to runs/detect/train


Remarque : runs/detect/train/weights/ contiendra les fichiers PT avec les poids exacts entraînés. Mémorisez cet emplacement pour plus tard.

Étape 5B : Validation et test du modèle

Vous pouvez exécuter la validation avec la commande suivante :


 $ yolo task=detect \ mode=val \ model=path/to/best.pt \ data=objdetect.yaml


Les résultats seront automatiquement stockés dans un dossier de votre répertoire de travail avec un chemin du formulaire « runs/detect/val ».


Pour effectuer une inférence sur l'ensemble de test, vous pouvez utiliser la commande suivante :


 $ yolo task=detect \ mode=predict \ model=path/to/best.pt \ conf=0.5 \ source=dataset/images/test


Les résultats seront stockés dans « runs/detect/predict ». Voici quelques résultats de prédiction sur l’ensemble de test :


Résultats de prédiction


Étape 6 : Nouvelle inférence d'image à l'aide des notifications de compartiment MinIO

Maintenant que nous disposons d'un modèle entraîné capable de reconnaître certains types d'avions présents dans une image satellite, comment pouvons-nous l'utiliser de manière simple pour de nouvelles images ?


Utilisation des notifications du compartiment MinIO


MinIO Bucket Notifications est un outil parfait pour cela. Nous pouvons créer un système capable d'effectuer automatiquement une inférence de détection d'objet sur une nouvelle image déposée dans notre compartiment à l'aide d'un webhook.


À un niveau élevé, nous avons 3 étapes. Tout d'abord, nous devons définir un point de terminaison qui peut servir de webhook pour effectuer la détection d'objets sur une nouvelle image avec notre modèle entraîné. Deuxièmement, nous devons configurer certaines variables d'environnement pour notre déploiement de serveur MinIO qui lui demandent d'atteindre notre point de terminaison webhook lorsqu'un événement se produit. Troisièmement, nous devons configurer les types d'événements de compartiment (c'est-à-dire PUT) sur lesquels nous voulons agir. Passons en revue étape par étape.


Voici le code d'un simple serveur basé sur Flask ( détection_server.py ) qui exécute l'inférence sur une nouvelle image ajoutée au compartiment MinIO :


 """ This is a simple Flask inference server implementation that serves as a webhook for the event of a new image being added to a MinIO bucket. Object detection using YOLO will be performed on that image and the resulting predictions will be returned. """ from flask import Flask, request, abort, make_response from ultralytics import YOLO import tempfile from minio import Minio # Make sure the following are populated with your MinIO details # (Best practice is to use environment variables!) MINIO_ENDPOINT = '' MINIO_ACCESS_KEY = '' MINIO_SECRET_KEY = '' model = YOLO('/PATH/TO/best.pt') # load a custom model (path to trained weights) client = Minio( MINIO_ENDPOINT, access_key=MINIO_ACCESS_KEY, secret_key=MINIO_SECRET_KEY, ) app = Flask(__name__) @app.route('/', methods=['POST']) async def inference_bucket_webhook(): """ This endpoint will be called when a new object is placed in your inference bucket """ if request.method == 'POST': # Get the request event from the 'POST' call event = request.json bucket = event['Records'][0]['s3']['bucket']['name'] obj_name = event['Records'][0]['s3']['object']['key'] with tempfile.TemporaryDirectory() as temp_dir: temp_file_name = temp_dir+'/'+obj_name client.fget_object(bucket, obj_name, temp_file_name) # See https://docs.ultralytics.com/modes/predict/ for more information about YOLO inference options results = model.predict(source=temp_file_name, conf=0.5, stream=False) # A list of bounding boxes (if any) is returned. # Each bounding box is in the format [x1, y1, x2, y2, probability, class]. result = {"results": results[0].boxes.data.tolist()} print(result) resp = make_response(result, 200) return resp else: abort(400) if __name__ == '__main__': app.run()


Démarrons le serveur d'inférence :


 $ python detection_server.py * Serving Flask app 'detection_server' * Debug mode: off * Running on http://127.0.0.1:5000 Press CTRL+C to quit


Notez le nom d'hôte et le port sur lesquels l'application Flask est exécutée.


Ensuite, commençons à travailler sur la configuration des webhooks côté MinIO. Tout d’abord, définissez les variables d’environnement suivantes. Remplacez <YOURFUNCTIONNAME> par un nom de fonction de votre choix. Par souci de simplicité, j'ai opté pour « inférence ». Assurez-vous également que la variable d'environnement du point de terminaison est définie sur l'hôte et le port corrects pour votre serveur d'inférence. Dans ce cas, http://localhost:5000 est l'endroit où notre application Flask s'exécute.


 $ export MINIO_NOTIFY_WEBHOOK_ENABLE_<YOURFUNCTIONNAME>=on $ export MINIO_NOTIFY_WEBHOOK_ENDPOINT_<YOURFUNCTIONNAME>=http://localhost:5000


Maintenant, redémarrez le serveur MinIO à l'aide de la commande mc admin service restart ALIAS ou si vous démarrez le serveur pour la première fois, vous pouvez également simplement utiliser la commande du serveur minio . Pour plus d'informations sur le redémarrage du serveur MinIO, consultez la documentation MinIO. Remarque : ALIAS doit être remplacé par l'alias de votre déploiement de serveur MinIO. Pour plus d'informations sur la façon de définir un alias ou d'afficher les alias existants, consultez la documentation .


Enfin, ajoutons le bucket et l'événement dont nous souhaitons être informés. Dans notre cas, nous souhaitons être avertis des événements ` put` (création de nouveaux objets) dans notre bucket. J'ai créé un tout nouveau seau vide à cet effet, intitulé « détecter-inférence », je vais donc le remplacer par « BUCKET ».


 $ mc event add ALIAS/BUCKET arn:minio:sqs::<YOURFUNCTIONNAME>:webhook --event put


Vous pouvez vérifier que vous avez configuré le type d'événement correct pour les notifications du compartiment en vérifiant si « s3:ObjectCreated:* » est généré lorsque vous exécutez cette commande :


 $ mc event ls local/detect-inference arn:minio:sqs::<YOURFUNCTIONNAME>:webhook


Pour une explication plus détaillée de la publication des événements du bucket sur un webhook, consultez la documentation . Nous sommes maintenant prêts à essayer la détection d'objets sur une toute nouvelle image !

Essayer notre système d'inférence

Voici la nouvelle image (intitulée « 1.png ») sur laquelle je souhaite effectuer une inférence :


Essayer le système d'inférence


Je dépose la nouvelle image dans mon bucket « détection-inférence » :


Déposer une image dans un nouveau compartiment


Presque instantanément, je peux voir les résultats suivants sur mon serveur Flask :


 $ python detection_server.py * Serving Flask app 'detection_server' * Debug mode: off * Running on http://127.0.0.1:5000 Press CTRL+C to quit image 1/1 /var/folders/xf/q7x0z8sn5nvckccp1g0m1vpm0000gn/T/tmpo6jx3w8u/1.png: 448x736 2 SU-30s, 101.0ms Speed: 4.1ms preprocess, 101.0ms inference, 5.8ms postprocess per image at shape (1, 3, 448, 736) {'results': [[1927.78369140625, 627.7123413085938, 1995.090576171875, 715.3443603515625, 0.8142037987709045, 0.0], [1735.740234375, 477.2108154296875, 1809.181640625, 555.767578125, 0.7766116261482239, 0.0]]} 127.0.0.1 - - [14/Sep/2023 15:39:21] "POST / HTTP/1.1" 200 -


Notez que chaque cadre de délimitation détecté dans la liste des résultats est au format YOLO [x1, y1, x2, y2, probabilité, classe]. Voici les cadres de délimitation et les classes prédites superposées à l'image d'origine :


Les deux autres avions ne sont pas des Su-30. Mon ensemble de données initial comprenait environ 100 images, j'ai donc été surpris que le modèle soit déjà capable de capter les nuances entre des avions d'apparence similaire. Je suppose que la leçon à retenir ici est la suivante : ne sous-estimez jamais la descente de pente !


Remarque : Pour les environnements de production et/ou les grands modèles d'apprentissage automatique, c'est une bonne idée d'utiliser un cadre de service de modèles établi comme PyTorch Serve ou Triton Server pour rendre l'inférence plus robuste et fiable. Si cela vous intéresse, consultez l'article précédent sur l'optimisation du service de modèles d'IA avec MinIO et PyTorch Serve .

Réflexions finales

Nous l'avons fait! Nous avons expliqué comment MinIO et CVAT se sont réunis pour assurer la sécurité et la disponibilité de nos échantillons d'images collectés, ainsi que comment créer notre ensemble de données de détection d'objets personnalisé. Ensuite, nous avons formé notre propre modèle YOLO personnalisé pour notre tâche personnalisée. Enfin, avec un peu plus de 50 lignes de code, nous avons mis en place un serveur d'inférence utilisant MinIO Bucket Notifications qui pourrait exécuter une nouvelle image au-delà de notre modèle de détection d'objets formés personnalisé.


MinIO et YOLO déployés en périphérie.


De plus, pour la plupart des applications critiques de vision par ordinateur, il est préférable d’effectuer des inférences en périphérie. Sinon, ces applications sont vulnérables à la latence associée au téléchargement de nouvelles données vers le cloud public et à l'attente d'un serveur d'inférence dans le cloud pour répondre – sans parler des risques d'une connexion réseau défectueuse. Pour cette raison, un pipeline de vision par ordinateur centré sur MinIO en tant que couche de données est beaucoup plus logique. Imaginez un drone survolant un aérodrome, capable de capturer, de stocker et d'utiliser notre modèle entraîné sur de nouvelles images avec du matériel et des logiciels entièrement intégrés. Avec un déploiement local du serveur MinIO, le système d'inférence basé sur Bucket Notification que nous avons construit à la fin de l'article fonctionne parfaitement pour ce scénario et d'innombrables autres scénarios similaires.


Si vous avez des questions, rejoignez notre chaîne Slack ou envoyez-nous un message à [email protected] . Nous sommes là pour vous aider.


Également publié ici .