Inférence thread-safe avec les modèles YOLO#
Pour exécuter en toute sécurité l’inférence Ultralytics YOLO sur plusieurs threads Python, instancie un modèle YOLO distinct dans chaque thread au lieu de partager la même instance entre plusieurs threads. Le partage d’une même instance de modèle provoque des conditions de concurrence qui corrompent son état interne et produisent des résultats imprévisibles, car le module threading de Python exécute simultanément les threads sur le même objet. Ce guide explique pourquoi le partage échoue, présente le modèle sûr avec une instance par thread et décrit le décorateur ThreadingLocked pour les cas où tu dois partager une instance.
Accède directement aux sections Pourquoi le partage d’un modèle échoue, Modèle thread-safe ou décorateur ThreadingLocked.
À regarder : Comment effectuer une inférence thread-safe avec les modèles Ultralytics YOLO en Python | Multithreading 🚀
Comprendre le multithreading en Python#
Les threads Python sont une forme de parallélisme qui permet à ton programme d’exécuter plusieurs opérations à la fois. Cependant, le Global Interpreter Lock (GIL) de Python signifie qu’un seul thread peut exécuter du bytecode Python à la fois.
Même si cela peut sembler limitant, les threads permettent tout de même la concurrence, en particulier pour les opérations liées aux E/S ou celles qui libèrent le GIL, comme les opérations effectuées par les bibliothèques C sous-jacentes de YOLO.
Les risques liés au partage d’instances de modèle#
Instancier un modèle YOLO en dehors de tes threads et partager cette instance entre plusieurs threads peut entraîner des conditions de concurrence, où l’état interne du modèle est modifié de manière incohérente en raison d’accès simultanés. Cela pose particulièrement problème lorsque le modèle ou ses composants contiennent un état qui n’est pas conçu pour être thread-safe.
Exemple non thread-safe : une seule instance de modèle#
Lorsque tu utilises des threads en Python, il est important de repérer les modèles susceptibles d’entraîner des problèmes de concurrence. Voici ce que tu dois éviter : partager une seule instance du modèle YOLO26 entre plusieurs threads.
# Dangereux : partager une seule instance de modèle entre les threads
from threading import Thread
from ultralytics import YOLO
# Instancier le modèle en dehors du thread
shared_model = YOLO("yolo26n.pt")
def predict(image_path):
"""Predicts objects in an image using a preloaded YOLO model, take path string to image as argument."""
results = shared_model.predict(image_path)
# Traiter les résultats
# Démarrer des threads qui partagent la même instance de modèle
Thread(target=predict, args=("image1.jpg",)).start()
Thread(target=predict, args=("image2.jpg",)).start()Dans l’exemple ci-dessus, shared_model est utilisé par plusieurs threads, ce qui peut entraîner des résultats imprévisibles, car predict pourrait être exécuté simultanément par plusieurs threads.
Exemple sûr : une instance dédiée par thread#
Plusieurs instances de modèle distinctes conviennent, à condition que chaque thread possède sa propre instance et ne la partage jamais avec un autre thread. Peu importe que les instances ci-dessous soient créées avant le démarrage des threads : le seul modèle dangereux consiste à partager une instance entre plusieurs threads.
# Sûr : chaque thread utilise sa propre instance de modèle dédiée
from threading import Thread
from ultralytics import YOLO
# Instancier un modèle par thread
model_1 = YOLO("yolo26n.pt")
model_2 = YOLO("yolo26n.pt")
def predict(model, image_path):
"""Runs prediction on an image using a specified YOLO model, returning the results."""
results = model.predict(image_path)
# Traiter les résultats
# Chaque thread utilise une instance de modèle distincte et dédiée
Thread(target=predict, args=(model_1, "image1.jpg")).start()
Thread(target=predict, args=(model_2, "image2.jpg")).start()Comme chaque thread utilise sa propre instance dédiée, aucun état de modèle n’est partagé entre les threads, qui ne peuvent donc pas le corrompre. Instancier le modèle dans chaque thread, comme indiqué ci-dessous, est simplement le moyen le plus simple de garantir qu’une instance ne sera jamais partagée par inadvertance.
Inférence thread-safe#
Pour effectuer des inférences thread-safe, tu dois instancier un modèle YOLO distinct dans chaque thread. Ainsi, chaque thread dispose de sa propre instance de modèle isolée, ce qui élimine le risque de conditions de concurrence.
Exemple thread-safe#
Voici comment instancier un modèle YOLO dans chaque thread pour effectuer des inférences parallèles en toute sécurité :
# Sûr : instancier un seul modèle dans chaque thread
from threading import Thread
from ultralytics import YOLO
def thread_safe_predict(image_path):
"""Predict on an image using a new YOLO model instance in a thread-safe manner; takes image path as input."""
local_model = YOLO("yolo26n.pt")
results = local_model.predict(image_path)
# Traiter les résultats
# Démarrer des threads ayant chacun leur propre instance de modèle
Thread(target=thread_safe_predict, args=("image1.jpg",)).start()
Thread(target=thread_safe_predict, args=("image2.jpg",)).start()Dans cet exemple, chaque thread crée sa propre instance YOLO. Cela empêche tout thread d’interférer avec l’état du modèle d’un autre thread et garantit ainsi que chaque thread effectue ses inférences en toute sécurité, sans interactions inattendues avec les autres threads.
Utiliser le décorateur ThreadingLocked#
Ultralytics fournit un décorateur ThreadingLocked qui permet de garantir l’exécution thread-safe des fonctions. Ce décorateur utilise un verrou pour garantir qu’un seul thread à la fois peut exécuter la fonction décorée.
from ultralytics import YOLO
from ultralytics.utils import ThreadingLocked
# Créer une instance de modèle
model = YOLO("yolo26n.pt")
# Décorer la fonction de prédiction pour la rendre thread-safe
@ThreadingLocked()
def thread_safe_predict(image_path):
"""Thread-safe prediction using a shared model instance."""
results = model.predict(image_path)
return results
# Tu peux maintenant appeler cette fonction en toute sécurité depuis plusieurs threadsLe décorateur ThreadingLocked est particulièrement utile lorsque tu dois partager une instance de modèle entre plusieurs threads tout en garantissant qu’un seul thread à la fois puisse y accéder.
Partager une instance de modèle protégée par un verrou économise de la mémoire par rapport au chargement d’un modèle dans chaque thread, mais réduit la concurrence, car les threads se succèdent en attendant leur tour pour accéder au verrou. Privilégie le modèle avec une instance par thread si tu disposes de suffisamment de mémoire et souhaites maximiser le parallélisme ; utilise ThreadingLocked lorsque la mémoire nécessaire au modèle constitue un goulot d’étranglement.
Conclusion#
Lorsque tu utilises des modèles YOLO avec threading de Python, attribue à chaque thread sa propre instance de modèle dédiée et ne partage jamais une instance entre plusieurs threads. Instancier le modèle dans le thread qui l’utilise est le moyen le plus simple de garantir cela, d’éviter les conditions de concurrence et de fiabiliser tes tâches d’inférence.
Pour les scénarios plus avancés et pour optimiser davantage les performances de tes inférences multithread, envisage le parallélisme fondé sur des processus avec multiprocessing ou l’utilisation d’une file de tâches avec des processus de travail dédiés.
FAQ#
Pour éviter les conditions de concurrence lorsque tu utilises des modèles Ultralytics YOLO dans un environnement Python multithread, instancie un modèle YOLO distinct dans chaque thread. Ainsi, chaque thread dispose de sa propre instance de modèle isolée, ce qui évite toute modification simultanée de l’état du modèle.
Exemple :
from threading import Thread from ultralytics import YOLO def thread_safe_predict(image_path): """Predict on an image in a thread-safe manner.""" local_model = YOLO("yolo26n.pt") results = local_model.predict(image_path) # Traiter les résultats Thread(target=thread_safe_predict, args=("image1.jpg",)).start() Thread(target=thread_safe_predict, args=("image2.jpg",)).start()Pour en savoir plus sur les méthodes permettant de garantir la sécurité des threads, consulte la page Inférence thread-safe avec les modèles YOLO.
Pour effectuer des inférences multithread avec un modèle YOLO en toute sécurité en Python, suis ces bonnes pratiques :
- Instancie les modèles YOLO dans chaque thread au lieu de partager une seule instance de modèle entre plusieurs threads.
- Utilise le module
multiprocessingde Python pour le traitement parallèle afin d’éviter les problèmes liés au Global Interpreter Lock (GIL). - N’oublie pas que les bibliothèques C sous-jacentes de YOLO (PyTorch, OpenCV) libèrent automatiquement le GIL lors des calculs intensifs, ce qui permet aux threads d’effectuer des inférences simultanément.
- Envisage d’utiliser le décorateur
ThreadingLockedpour les instances de modèle partagées lorsque la mémoire est une préoccupation.
Exemple d’instanciation thread-safe d’un modèle :
from threading import Thread from ultralytics import YOLO def thread_safe_predict(image_path): """Runs inference in a thread-safe manner with a new YOLO model instance.""" local_model = YOLO("yolo26n.pt") results = local_model.predict(image_path) # Traiter les résultats # Lancer plusieurs threads Thread(target=thread_safe_predict, args=("image1.jpg",)).start() Thread(target=thread_safe_predict, args=("image2.jpg",)).start()Pour plus de contexte, consulte la section sur les inférences thread-safe.
Chaque thread devrait disposer de sa propre instance de modèle YOLO pour éviter les conditions de concurrence. Lorsqu’une seule instance de modèle est partagée entre plusieurs threads, les accès simultanés peuvent entraîner un comportement imprévisible et modifier l’état interne du modèle. En utilisant des instances distinctes, tu assures l’isolation des threads et la fiabilité et la sécurité de tes tâches multithread.
Pour obtenir des instructions détaillées, consulte les sections Exemple non thread-safe : une seule instance de modèle et Exemple thread-safe.
Le Global Interpreter Lock (GIL) de Python n’autorise qu’un seul thread à exécuter du bytecode Python à la fois, ce qui peut limiter les performances des tâches multithread gourmandes en CPU. Cependant, les opérations liées aux E/S ou les processus qui utilisent des bibliothèques libérant le GIL, comme les bibliothèques C sous-jacentes de YOLO, permettent tout de même d’atteindre la concurrence. Pour améliorer les performances, envisage le parallélisme fondé sur des processus avec le module
multiprocessingde Python.Pour en savoir plus sur le multithreading en Python, consulte la section Comprendre le multithreading en Python.
Oui, utiliser le module
multiprocessingde Python est plus sûr et souvent plus efficace pour effectuer des inférences avec un modèle YOLO en parallèle. Le parallélisme fondé sur des processus crée des espaces mémoire distincts, évitant le Global Interpreter Lock (GIL) et réduisant le risque de problèmes de concurrence. Chaque processus fonctionne indépendamment avec sa propre instance de modèle YOLO.Pour en savoir plus sur le parallélisme fondé sur des processus avec les modèles YOLO, consulte la page consacrée aux inférences thread-safe.