Ultralytics YOLO27:

Inferência segura para threads com modelos YOLO#

Para executar a inferência do Ultralytics YOLO com segurança em threads Python, instancia um modelo YOLO separado dentro de cada thread, em vez de partilhares uma única instância entre elas. Partilhar um único modelo causa condições de corrida que corrompem o seu estado interno e produzem resultados imprevisíveis, porque o módulo threading do Python executa as threads em simultâneo no mesmo objeto. Este guia explica por que motivo a partilha falha, mostra o padrão seguro por thread e aborda o decorador ThreadingLocked para os casos em que tens de partilhar uma instância.

Avança para por que motivo a partilha de um modelo falha, para o padrão seguro para threads ou para o decorador ThreadingLocked.



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

Compreender as threads do Python#

As threads do Python são uma forma de paralelismo que permite ao teu programa executar várias operações ao mesmo tempo. No entanto, o Bloqueio Global do Interpretador (GIL) do Python significa que apenas uma thread pode executar bytecode Python de cada vez.

Single-thread vs multi-thread inference

Embora isto pareça uma limitação, as threads ainda podem proporcionar concorrência, especialmente em operações limitadas por E/S ou ao utilizar operações que libertam o GIL, como as executadas pelas bibliotecas C subjacentes do YOLO.

O perigo de instâncias de modelos partilhadas#

Instanciar um modelo YOLO fora das tuas threads e partilhar essa instância entre várias threads pode causar condições de corrida, nas quais o estado interno do modelo é modificado de forma inconsistente devido a acessos simultâneos. Isto é particularmente problemático quando o modelo ou os seus componentes mantêm um estado que não foi concebido para ser seguro para threads.

Exemplo não seguro para threads: instância única do modelo#

Ao utilizar threads no Python, é importante reconhecer os padrões que podem causar problemas de concorrência. Eis o que deves evitar: partilhar uma única instância do modelo YOLO26 entre várias 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()

No exemplo acima, shared_model é utilizado por várias threads, o que pode causar resultados imprevisíveis porque predict pode ser executado simultaneamente por várias threads.

Exemplo seguro: uma instância dedicada por thread#

É perfeitamente aceitável ter várias instâncias de modelos separadas, desde que cada thread seja proprietária da sua instância e nunca a partilhe com outra thread. O facto de as instâncias abaixo serem criadas antes do início das threads não é relevante — o único padrão inseguro é partilhar uma instância entre 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()

Como cada thread trabalha com a sua própria instância dedicada, não existe estado partilhado do modelo que as threads possam corromper. Instanciar o modelo dentro de cada thread, como mostrado a seguir, é simplesmente a forma mais fácil de garantir que uma instância nunca seja partilhada acidentalmente.

Inferência segura para threads#

Para executar inferência de forma segura para threads, deves instanciar um modelo YOLO separado dentro de cada thread. Isto garante que cada thread tem a sua própria instância isolada do modelo, eliminando o risco de condições de corrida.

Exemplo seguro para threads#

Eis como instanciar um modelo YOLO dentro de cada thread para realizar inferência paralela com segurança:

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

Neste exemplo, cada thread cria a sua própria instância YOLO. Isto impede que qualquer thread interfira com o estado do modelo de outra, garantindo assim que cada thread realiza a inferência com segurança e sem interações inesperadas com as outras threads.

Utilizar o decorador ThreadingLocked#

A Ultralytics fornece um decorador ThreadingLocked que pode ser utilizado para garantir a execução segura para threads das funções. Este decorador utiliza um bloqueio para garantir que apenas uma thread de cada vez possa executar a função decorada.

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

O decorador ThreadingLocked é particularmente útil quando precisas de partilhar uma instância de modelo entre threads, mas queres garantir que apenas uma thread lhe possa aceder de cada vez.

Equilíbrio entre memória e concorrência

Partilhar uma instância de modelo bloqueada poupa memória em comparação com carregar um modelo em cada thread, mas reduz a concorrência, porque as threads ficam serializadas no bloqueio e aguardam pela sua vez. Prefere o padrão por thread quando tens memória disponível e queres obter o máximo paralelismo, e utiliza ThreadingLocked quando a memória do modelo é o fator limitante.

Conclusão#

Ao utilizar modelos YOLO com threading do Python, atribui a cada thread a sua própria instância dedicada do modelo e nunca partilhes uma instância entre threads. Instanciar o modelo dentro da thread que o utiliza é a forma mais simples de garantir isto, evitando condições de corrida e mantendo a fiabilidade das tuas tarefas de inferência.

Para cenários mais avançados e para otimizar ainda mais o desempenho da inferência multithread, considera utilizar paralelismo baseado em processos com multiprocessing ou uma fila de tarefas com processos de trabalho dedicados.

Perguntas frequentes#

  • Para evitar condições de corrida ao utilizar modelos Ultralytics YOLO num ambiente Python multithread, instancia um modelo YOLO separado dentro de cada thread. Isto garante que cada thread tem a sua própria instância isolada do modelo, evitando modificações simultâneas do estado do modelo.

    Exemplo:

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

    Para obter mais informações sobre como garantir a segurança entre threads, visita Inferência segura para threads com modelos YOLO.

  • Para executar inferência multithread de modelos YOLO com segurança no Python, segue estas melhores práticas:

    1. Instancia os modelos YOLO dentro de cada thread, em vez de partilhares uma única instância entre threads.
    2. Utiliza o módulo multiprocessing do Python para processamento paralelo, de modo a evitar problemas relacionados com o Bloqueio Global do Interpretador (GIL).
    3. Lembra-te de que as bibliotecas C subjacentes do YOLO (PyTorch, OpenCV) libertam automaticamente o GIL durante cálculos pesados, pelo que as threads ainda podem executar inferência em simultâneo.
    4. Considera utilizar o decorador ThreadingLocked para instâncias de modelos partilhadas quando a memória é uma preocupação.

    Exemplo de instanciação de modelos segura para threads:

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

    Para obter contexto adicional, consulta a secção sobre Inferência segura para threads.

  • Cada thread deve ter a sua própria instância de modelo YOLO para evitar condições de corrida. Quando uma única instância de modelo é partilhada entre várias threads, os acessos simultâneos podem causar comportamentos imprevisíveis e modificações do estado interno do modelo. Ao utilizar instâncias separadas, garantes o isolamento entre threads, tornando as tuas tarefas multithread fiáveis e seguras.

    Para obter orientações detalhadas, consulta as secções Exemplo não seguro para threads: instância única do modelo e Exemplo seguro para threads.

  • O Bloqueio Global do Interpretador (GIL) do Python permite que apenas uma thread execute bytecode Python de cada vez, o que pode limitar o desempenho de tarefas multithread limitadas pela CPU. No entanto, em operações limitadas por E/S ou processos que utilizam bibliotecas que libertam o GIL, como as bibliotecas C subjacentes do YOLO, ainda podes obter concorrência. Para melhorar o desempenho, considera utilizar paralelismo baseado em processos com o módulo multiprocessing do Python.

    Para saberes mais sobre threads no Python, consulta a secção Compreender as threads do Python.

  • Sim, utilizar o módulo multiprocessing do Python é mais seguro e frequentemente mais eficiente para executar inferência de modelos YOLO em paralelo. O paralelismo baseado em processos cria espaços de memória separados, evitando o Bloqueio Global do Interpretador (GIL) e reduzindo o risco de problemas de concorrência. Cada processo funciona de forma independente com a sua própria instância do modelo YOLO.

    Para obter mais detalhes sobre paralelismo baseado em processos com modelos YOLO, consulta a página sobre Inferência segura para threads.

Comentários