YOLO Vision 2026 :

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

Pour exécuter l'inférence Ultralytics YOLO en toute sécurité dans des threads Python, instancie un modèle YOLO distinct à l'intérieur de chaque thread au lieu d'en partager une seule instance. Le partage d'un modèle unique 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 en même temps sur le même objet. Ce guide explique pourquoi le partage échoue, présente le modèle sécurisé par thread et aborde le décorateur ThreadingLocked pour les cas où tu dois impérativement partager une instance.

Accède à la section pourquoi le partage d'un modèle échoue, au modèle sécurisé pour les threads 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. Cependant, le Global Interpreter Lock (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 offrir de la concurrence, notamment pour les opérations liées aux E/S 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 entraîner des conditions de concurrence (race conditions), où l'état interne du modèle est modifié de manière incohérente en raison d'accès simultanés. C'est particulièrement problématique lorsque le modèle ou ses composants conservent un état qui n'est pas conçu pour être thread-safe.

Exemple non thread-safe : instance de modèle unique#

Lorsque tu utilises des threads en Python, il est important d'identifier les schémas qui peuvent entraîner des problèmes de concurrence. Voici ce qu'il faut é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, l'objet 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écurisé : une instance dédiée par thread#

Plusieurs instances de modèle distinctes sont acceptables 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 dangereux est le partage d'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()

Parce que chaque thread travaille avec sa propre instance dédiée, il n'y a pas d'état de modèle partagé que les threads pourraient corrompre. Instancier le modèle à l'intérieur de chaque thread, comme montré ensuite, est simplement le moyen le plus simple de garantir qu'une instance n'est jamais partagée par accident.

Inférence thread-safe#

Pour effectuer une inférence thread-safe, tu dois instancier un modèle YOLO distinct au sein de chaque thread. Cela garantit que chaque thread possède sa propre instance de modèle isolée, éliminant ainsi le risque de conditions de concurrence.

Exemple thread-safe#

Voici comment instancier un modèle YOLO à l'intérieur de chaque thread pour une inférence parallèle sécurisée :

# 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, garantissant ainsi que chaque thread effectue son inférence en toute sécurité et 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 sécurisée des fonctions entre les threads. Ce décorateur utilise un verrou pour s'assurer qu'un seul thread à la fois peut 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 des threads tout en t'assurant qu'un seul thread peut y accéder à la fois.

Compromis entre mémoire et concurrence

Le partage d'une instance de modèle verrouillée permet d'économiser de la mémoire par rapport au chargement d'un modèle dans chaque thread, mais cela réduit la concurrence car les threads se sérialisent sur le verrou et patientent à tour de rôle. Privilégie le modèle par thread si tu as de la mémoire à revendre et que tu souhaites un parallélisme maximal, et tourne-toi vers ThreadingLocked lorsque la mémoire du modèle est le facteur limitant.

Conclusion#

Lorsque tu utilises des modèles YOLO avec le module threading de Python, attribue à chaque thread sa propre instance de modèle dédiée et ne partage jamais une même instance entre les threads. Instancier le modèle à l'intérieur du thread qui l'utilise est le moyen le plus simple de le garantir, ce qui évite les conditions de concurrence et assure la fiabilité de tes tâches d'inférence.

Pour des 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 de tirer parti d'une file d'attente de tâches avec des processus de travail dédiés.

FAQ#

  • Pour éviter les conditions de concurrence lors de l'utilisation des modèles Ultralytics YOLO dans un environnement Python multi-thread, instancie un modèle YOLO distinct dans chaque thread. Cela garantit que chaque thread dispose de sa propre instance de modèle isolée, évitant ainsi la 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)
        # 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 façon de garantir la sécurité des threads, consulte la page Inférence sécurisée pour les threads avec les modèles YOLO.

  • Pour exécuter une inférence de modèle YOLO multi-thread en toute sécurité en Python, suis ces meilleures pratiques :

    1. Instancie les modèles YOLO au sein de chaque thread plutôt que de partager une instance unique entre les threads.
    2. Utilise le module multiprocessing de Python pour le traitement en parallèle afin d'éviter les problèmes liés au verrou global de l'interpréteur (GIL).
    3. Rappelle-toi que les bibliothèques C sous-jacentes de YOLO (PyTorch, OpenCV) libèrent automatiquement le GIL pendant les calculs intensifs, donc les threads peuvent toujours exécuter l'inférence simultanément.
    4. Pense à utiliser le décorateur ThreadingLocked pour les instances de modèles partagées lorsque la mémoire est un problème.

    Exemple pour une instanciation de modèle thread-safe :

    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 un contexte supplémentaire, reporte-toi à la section sur l'inférence sécurisée pour les threads.

  • Chaque thread devrait avoir sa propre instance de modèle YOLO pour éviter les conditions de concurrence. Lorsqu'une instance unique de modèle est partagée entre plusieurs threads, les accès simultanés peuvent conduire à un comportement imprévisible et à des modifications de l'état interne du modèle. En utilisant des instances séparées, tu garantis l'isolation des threads, rendant tes tâches multi-thread fiables et sécurisées.

    Pour des conseils détaillés, consulte les sections Exemple non sécurisé pour les threads : instance de modèle unique et Exemple sécurisé pour les threads.

  • Le verrou global de l'interpréteur (GIL) de Python permet à un seul thread d'exécuter du bytecode Python à la fois, ce qui peut limiter les performances des tâches multithread liées au processeur. Cependant, pour les opérations d'E/S 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 obtenir de la concurrence. Pour des performances accrues, envisage d'utiliser le parallélisme basé sur les processus avec le module multiprocessing de Python.

    Pour en savoir plus sur les threads en Python, consulte la section Comprendre les threads en Python.

  • Oui, l'utilisation du module multiprocessing de Python est plus sûre et souvent plus efficace pour exécuter l'inférence d'un modèle YOLO en parallèle. Le parallélisme basé sur les processus crée des espaces mémoire séparés, ce qui évite le verrou global de l'interpréteur (GIL) et réduit les risques de problèmes de concurrence. Chaque processus fonctionnera de manière indépendante 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, reporte-toi à la page sur l'inférence sécurisée pour les threads.

Commentaires