Ultralytics YOLO27:

Inferenza thread-safe con modelli YOLO#

Per eseguire l'inferenza di Ultralytics YOLO in sicurezza tra thread Python, crea un modello YOLO separato all'interno di ogni thread invece di condividere una singola istanza tra più thread. La condivisione di un singolo modello causa race condition che danneggiano il suo stato interno e producono risultati imprevedibili, perché il modulo threading di Python esegue i thread contemporaneamente sullo stesso oggetto. Questa guida spiega perché la condivisione non funziona, mostra il modello sicuro per singolo thread e descrive il decoratore ThreadingLocked per i casi in cui devi condividere un'istanza.

Vai a perché la condivisione di un modello non funziona, al pattern thread-safe o al decoratore ThreadingLocked.



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

Comprendere il threading in Python#

I thread Python sono una forma di parallelismo che consente al programma di eseguire più operazioni contemporaneamente. Tuttavia, il Global Interpreter Lock (GIL) di Python fa sì che un solo thread alla volta possa eseguire bytecode Python.

Single-thread vs multi-thread inference

Anche se può sembrare una limitazione, i thread possono comunque offrire concorrenza, soprattutto per le operazioni vincolate dall'I/O o quando si utilizzano operazioni che rilasciano il GIL, come quelle eseguite dalle librerie C sottostanti di YOLO.

Il rischio delle istanze di modello condivise#

Creare un modello YOLO al di fuori dei thread e condividere questa istanza tra più thread può causare race condition, in cui lo stato interno del modello viene modificato in modo incoerente a causa degli accessi concorrenti. Questo è particolarmente problematico quando il modello o i suoi componenti mantengono uno stato che non è progettato per essere thread-safe.

Esempio non thread-safe: istanza di modello singola#

Quando utilizzi i thread in Python, è importante riconoscere i modelli che possono causare problemi di concorrenza. Ecco cosa dovresti evitare: condividere una singola istanza del modello YOLO26 tra più thread.

# 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()

Nell'esempio precedente, shared_model viene utilizzato da più thread, il che può produrre risultati imprevedibili perché predict potrebbe essere eseguito contemporaneamente da più thread.

Esempio sicuro: un'istanza dedicata per ogni thread#

Più istanze di modello separate vanno bene, purché ogni thread sia proprietario della propria istanza e non la condivida mai con un altro thread. Non importa che le istanze seguenti vengano create prima dell'avvio dei thread: l'unico modello non sicuro è condividere una singola istanza tra più thread:

# 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()

Poiché ogni thread lavora con la propria istanza dedicata, non esiste uno stato del modello condiviso che i thread possano danneggiare. Creare il modello all'interno di ogni thread, come mostrato di seguito, è semplicemente il modo più facile per garantire che un'istanza non venga mai condivisa accidentalmente.

Inferenza thread-safe#

Per eseguire un'inferenza thread-safe, devi creare un modello YOLO separato all'interno di ogni thread. In questo modo ogni thread dispone di una propria istanza di modello isolata, eliminando il rischio di race condition.

Esempio thread-safe#

Ecco come creare un modello YOLO all'interno di ogni thread per un'inferenza parallela sicura:

# 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()

In questo esempio, ogni thread crea la propria istanza YOLO. In questo modo si impedisce a un thread di interferire con lo stato del modello di un altro, garantendo che ogni thread esegua l'inferenza in sicurezza e senza interazioni impreviste con gli altri thread.

Utilizzo del decoratore ThreadingLocked#

Ultralytics fornisce un decoratore ThreadingLocked che può essere utilizzato per garantire l'esecuzione thread-safe delle funzioni. Questo decoratore utilizza un lock per garantire che un solo thread alla volta possa eseguire la funzione decorata.

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

Il decoratore ThreadingLocked è particolarmente utile quando devi condividere un'istanza di modello tra più thread, ma vuoi garantire che vi acceda un solo thread alla volta.

Compromesso tra memoria e concorrenza

Condividere un'unica istanza di modello protetta da lock consente di risparmiare memoria rispetto al caricamento di un modello in ogni thread, ma riduce la concorrenza perché i thread vengono serializzati sul lock e aspettano il proprio turno. Preferisci il modello per singolo thread quando hai memoria disponibile e vuoi il massimo parallelismo, e usa ThreadingLocked quando la memoria del modello rappresenta il collo di bottiglia.

Conclusioni#

Quando utilizzi modelli YOLO con threading di Python, assegna a ogni thread una propria istanza di modello dedicata e non condividere mai una singola istanza tra più thread. Creare il modello all'interno del thread che lo utilizza è il modo più semplice per garantirlo, evitando race condition e mantenendo affidabili le attività di inferenza.

Per scenari più avanzati e per ottimizzare ulteriormente le prestazioni dell'inferenza multi-thread, valuta l'utilizzo del parallelismo basato sui processi con multiprocessing oppure di una coda di attività con processi worker dedicati.

FAQ#

  • Per prevenire le race condition quando utilizzi modelli Ultralytics YOLO in un ambiente Python multi-thread, crea un modello YOLO separato all'interno di ogni thread. In questo modo ogni thread dispone di una propria istanza di modello isolata, evitando modifiche concorrenti allo stato del modello.

    Esempio:

    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()

    Per ulteriori informazioni su come garantire la sicurezza dei thread, visita la pagina Inferenza thread-safe con modelli YOLO.

  • Per eseguire in sicurezza l'inferenza di modelli YOLO multi-thread in Python, segui queste best practice:

    1. Crea i modelli YOLO all'interno di ogni thread invece di condividere una singola istanza del modello tra più thread.
    2. Utilizza il modulo multiprocessing di Python per l'elaborazione parallela, così da evitare i problemi legati al Global Interpreter Lock (GIL).
    3. Ricorda che le librerie C sottostanti di YOLO (PyTorch, OpenCV) rilasciano automaticamente il GIL durante i calcoli pesanti, consentendo ai thread di eseguire comunque l'inferenza in concorrenza.
    4. Valuta l'utilizzo del decoratore ThreadingLocked per le istanze di modello condivise quando la memoria rappresenta un problema.

    Esempio di creazione di un modello 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()

    Per ulteriori informazioni, consulta la sezione Inferenza thread-safe.

  • Ogni thread dovrebbe avere una propria istanza del modello YOLO per prevenire le race condition. Quando una singola istanza del modello viene condivisa tra più thread, gli accessi concorrenti possono causare comportamenti imprevedibili e modifiche allo stato interno del modello. Utilizzando istanze separate, garantisci l'isolamento dei thread, rendendo le attività multi-thread affidabili e sicure.

    Per indicazioni dettagliate, consulta le sezioni Esempio non thread-safe: istanza di modello singola ed Esempio thread-safe.

  • Il Global Interpreter Lock (GIL) di Python consente a un solo thread alla volta di eseguire bytecode Python, limitando così le prestazioni delle attività multi-thread vincolate dalla CPU. Tuttavia, per le operazioni vincolate dall'I/O o per i processi che utilizzano librerie che rilasciano il GIL, come le librerie C sottostanti di YOLO, puoi comunque ottenere concorrenza. Per prestazioni migliori, valuta l'utilizzo del parallelismo basato sui processi con il modulo multiprocessing di Python.

    Per ulteriori informazioni sul threading in Python, consulta la sezione Comprendere il threading in Python.

  • Sì, l'utilizzo del modulo multiprocessing di Python è più sicuro e spesso più efficiente per eseguire in parallelo l'inferenza dei modelli YOLO. Il parallelismo basato sui processi crea spazi di memoria separati, evitando il Global Interpreter Lock (GIL) e riducendo il rischio di problemi di concorrenza. Ogni processo opera in modo indipendente con la propria istanza del modello YOLO.

    Per ulteriori dettagli sul parallelismo basato sui processi con i modelli YOLO, consulta la pagina Inferenza thread-safe.

Commenti