Ultralytics YOLO27 :

Inférence thread-safe avec des modèles YOLO#

Pour exécuter l’inférence Ultralytics YOLO en toute sécurité entre des threads Python, instancie un modèle YOLO distinct dans chaque thread au lieu de partager une même instance entre eux. Le partage d’un seul 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 les threads simultanément sur le même objet. Ce guide explique pourquoi le partage échoue, présente le modèle sûr par thread et décrit le décorateur ThreadingLocked pour les cas où tu dois partager une instance.

Accède directement à la raison pour laquelle le partage d’un modèle échoue, au modèle thread-safe ou au décorateur ThreadingLocked.



Watch: How to Perform Thread Safe Inference with Ultralytics YOLO Models in Python | Multi-Threading 🚀

Comprendre le threading Python#

Les threads Python sont une forme de parallélisme qui permet à ton programme d’exécuter plusieurs opérations à la fois. Toutefois, le verrouillage global de l’interpréteur (GIL) de Python signifie qu’un seul thread peut exécuter du bytecode Python à la fois.

Single-thread vs multi-thread inference

Bien que cela puisse sembler être une limitation, les threads peuvent tout de même fournir de la concurrence, en particulier pour les opérations limitées par les entrées-sorties ou lors de l’utilisation d’opérations qui libèrent le GIL, comme celles effectuées par les bibliothèques C sous-jacentes de YOLO.

Le danger des instances de modèle partagées#

Instancier un modèle YOLO en dehors de tes threads et partager cette instance entre plusieurs threads peut provoquer des conditions de concurrence, dans lesquelles l’état interne du modèle est modifié de manière incohérente en raison d’accès concurrents. 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 : instance unique du modèle#

Lorsque tu utilises des threads en Python, il est important d’identifier les modèles susceptibles de provoquer des problèmes de concurrence. Voici ce que tu dois éviter : partager une seule instance de modèle YOLO26 entre plusieurs threads.

# Unsafe: Sharing a single model instance across threads
from threading import Thread

from ultralytics import YOLO

# Instantiate the model outside the 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)
    # Process results

# Starting threads that share the same model instance
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 produire des résultats imprévisibles, car predict peut ê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 tant 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 non sûr consiste à partager une instance entre plusieurs threads :

# Safe: each thread uses its own dedicated model instance
from threading import Thread

from ultralytics import YOLO

# Instantiate one model per 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)
    # Process results

# Each thread uses a separate, dedicated model instance
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 partagé ne peut être corrompu par les threads. Instancier le modèle dans chaque thread, comme indiqué ensuite, est simplement le moyen le plus facile de garantir qu’une instance ne soit jamais partagée accidentellement.

Inférence thread-safe#

Pour effectuer une inférence 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 une inférence parallèle en toute sécurité :

# Safe: Instantiating a single model inside each 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)
    # Process results

# Starting threads that each have their own model instance
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 l’inférence en toute sécurité, sans interactions inattendues avec les autres threads.

Utiliser le décorateur ThreadingLocked#

Ultralytics fournit un décorateur ThreadingLocked qui peut être utilisé pour garantir l’exécution thread-safe des fonctions. Ce décorateur utilise un verrou afin qu’un seul thread à la fois puisse exécuter la fonction décorée.

from ultralytics import YOLO
from ultralytics.utils import ThreadingLocked

# Create a model instance
model = YOLO("yolo26n.pt")

# Decorate the prediction function to make it thread-safe
@ThreadingLocked()
def thread_safe_predict(image_path):
    """Thread-safe prediction using a shared model instance."""
    results = model.predict(image_path)
    return results

# Now you can safely call this function from multiple threads

Le 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 puisse y accéder à la fois.

Compromis entre mémoire et concurrence

Partager une instance de modèle verrouillée é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 sérialisent au niveau du verrou et attendent leur tour. Privilégie le modèle par thread lorsque tu disposes de suffisamment de mémoire et que tu veux maximiser le parallélisme, et utilise ThreadingLocked lorsque la mémoire du modèle constitue le 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 maintenir la fiabilité de tes tâches d’inférence.

Pour les scénarios plus avancés et pour optimiser davantage les performances de ton inférence multithread, envisage d’utiliser le parallélisme basé sur les processus avec multiprocessing ou une file d’attente de tâches avec des processus workers dédiés.

FAQ#

  • Pour éviter les conditions de concurrence lors de l’utilisation de 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 les modifications concurrentes 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)
        # Process results
    
    Thread(target=thread_safe_predict, args=("image1.jpg",)).start()
    Thread(target=thread_safe_predict, args=("image2.jpg",)).start()

    Pour plus d’informations sur la garantie de la sécurité des threads, consulte la page Inférence thread-safe avec des modèles YOLO.

  • Pour exécuter en toute sécurité une inférence multithread avec un modèle YOLO en Python, suis ces bonnes pratiques :

    1. Instancie les modèles YOLO dans chaque thread plutôt que de partager une seule instance de modèle entre plusieurs threads.
    2. Utilise le module multiprocessing de Python pour le traitement parallèle afin d’éviter les problèmes liés au verrouillage global de l’interpréteur (GIL).
    3. 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’exécuter simultanément l’inférence.
    4. Envisage d’utiliser le décorateur ThreadingLocked pour les instances de modèle partagées lorsque la mémoire est une contrainte.

    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)
        # Process results
    
    # Initiate multiple 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 l’inférence thread-safe.

  • Chaque thread doit disposer de sa propre instance de modèle YOLO afin d’éviter les conditions de concurrence. Lorsqu’une seule instance de modèle est partagée entre plusieurs threads, les accès concurrents peuvent entraîner un comportement imprévisible et des modifications de l’état interne du modèle. En utilisant des instances distinctes, tu garantis l’isolation des threads, ce qui rend tes tâches multithread fiables et sûres.

    Pour obtenir des instructions détaillées, consulte les sections Exemple non thread-safe : instance unique du modèle et Exemple thread-safe.

  • Le verrouillage global de l’interpréteur (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 limitées par le processeur. Toutefois, pour les opérations limitées par les entrées-sorties ou les processus qui utilisent des bibliothèques libérant le GIL, comme les bibliothèques C sous-jacentes de YOLO, tu peux tout de même bénéficier de la concurrence. Pour améliorer les performances, envisage d’utiliser le parallélisme basé sur les processus avec le module multiprocessing de Python.

    Pour en savoir plus sur le threading en Python, consulte la section Comprendre le threading Python.

  • Oui, utiliser le module multiprocessing de Python est plus sûr et souvent plus efficace pour exécuter l’inférence des modèles YOLO en parallèle. Le parallélisme basé sur les processus crée des espaces mémoire distincts, ce qui évite le verrouillage global de l’interpréteur (GIL) et réduit le risque de problèmes de concurrence. Chaque processus fonctionne indépendamment avec sa propre instance de modèle YOLO.

    Pour plus de détails sur le parallélisme basé sur les processus avec les modèles YOLO, consulte la page sur l’inférence thread-safe.

Commentaires