Ultralytics YOLO27:

Потокобезопасный инференс с моделями YOLO#

Чтобы безопасно выполнять инференс Ultralytics YOLO в потоках Python, создавай отдельную модель YOLO внутри каждого потока вместо использования одного экземпляра в нескольких потоках. Совместное использование одной модели приводит к состояниям гонки, которые нарушают её внутреннее состояние и вызывают непредсказуемые результаты, поскольку модуль threading в Python одновременно выполняет потоки с одним и тем же объектом. В этом руководстве объясняется, почему совместное использование не работает, демонстрируется безопасный шаблон для каждого потока и рассматривается декоратор ThreadingLocked для случаев, когда необходимо использовать один экземпляр совместно.

Перейди к разделу о том, почему совместное использование модели не работает, потокобезопасному шаблону или декоратору ThreadingLocked.



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

Основы работы с потоками в Python#

Потоки Python — это форма параллелизма, которая позволяет программе выполнять несколько операций одновременно. Однако глобальная блокировка интерпретатора (GIL) в Python означает, что в каждый момент времени только один поток может выполнять байт-код Python.

Single-thread vs multi-thread inference

Хотя это может показаться ограничением, потоки всё равно обеспечивают конкурентное выполнение, особенно для операций, ограниченных вводом-выводом, или при использовании операций, освобождающих GIL, например выполняемых базовыми библиотеками YOLO на C.

Опасность совместного использования экземпляров моделей#

Создание модели YOLO вне потоков и совместное использование этого экземпляра в нескольких потоках может привести к состояниям гонки, при которых внутреннее состояние модели непоследовательно изменяется из-за одновременного доступа. Это особенно проблематично, когда модель или её компоненты хранят состояние, не рассчитанное на потокобезопасную работу.

Непотокобезопасный пример: один экземпляр модели#

При использовании потоков в Python важно распознавать шаблоны, которые могут привести к проблемам конкурентного выполнения. Вот чего следует избегать: совместного использования одного экземпляра модели YOLO26 в нескольких потоках.

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

В приведённом выше примере shared_model используется несколькими потоками, что может привести к непредсказуемым результатам, поскольку predict может одновременно выполняться несколькими потоками.

Безопасный пример: отдельный экземпляр для каждого потока#

Несколько отдельных экземпляров моделей допустимы, если каждый поток владеет своим экземпляром и никогда не передаёт его другому потоку. Не имеет значения, что показанные ниже экземпляры создаются до запуска потоков — небезопасным является только совместное использование одного экземпляра в нескольких потоках:

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

Поскольку каждый поток работает со своим отдельным экземпляром, общего состояния модели, которое потоки могли бы повредить, нет. Создание модели внутри каждого потока, как показано далее, — это просто самый удобный способ гарантировать, что экземпляр никогда случайно не используется совместно.

Потокобезопасный инференс#

Чтобы выполнять потокобезопасный инференс, создавай отдельную модель YOLO внутри каждого потока. Это гарантирует, что у каждого потока будет собственный изолированный экземпляр модели, и устраняет риск состояний гонки.

Потокобезопасный пример#

Вот как создать модель YOLO внутри каждого потока для безопасного параллельного инференса:

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

В этом примере каждый поток создаёт собственный экземпляр YOLO. Это не позволяет одному потоку вмешиваться в состояние модели другого потока и гарантирует, что каждый поток безопасно выполняет инференс без неожиданных взаимодействий с другими потоками.

Использование декоратора ThreadingLocked#

Ultralytics предоставляет декоратор ThreadingLocked, который можно использовать для обеспечения потокобезопасного выполнения функций. Этот декоратор использует блокировку, чтобы в каждый момент времени только один поток мог выполнять декорированную функцию.

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

Декоратор ThreadingLocked особенно полезен, когда нужно совместно использовать экземпляр модели в нескольких потоках, но требуется разрешить доступ к нему только одному потоку за раз.

Компромисс между памятью и конкурентным выполнением

Совместное использование одной модели с блокировкой экономит память по сравнению с загрузкой модели в каждом потоке, но снижает конкурентность, поскольку потоки выстраиваются в очередь на блокировке и ждут своей очереди. Выбирай шаблон с отдельным экземпляром для каждого потока, если у тебя достаточно памяти и нужна максимальная степень параллелизма, а ThreadingLocked используй, когда память, необходимая модели, становится узким местом.

Заключение#

При использовании моделей YOLO с threading в Python выделяй каждому потоку собственный отдельный экземпляр модели и никогда не используй один экземпляр совместно в нескольких потоках. Создание модели внутри потока, который её использует, — самый простой способ гарантировать это, избежать состояний гонки и сохранить надёжность задач инференса.

Для более сложных сценариев и дальнейшей оптимизации производительности многопоточного инференса рассмотри параллелизм на основе процессов с использованием multiprocessing или очереди задач с выделенными рабочими процессами.

Часто задаваемые вопросы#

  • Чтобы предотвратить состояния гонки при использовании моделей Ultralytics YOLO в многопоточной среде Python, создавай отдельную модель YOLO внутри каждого потока. Это гарантирует, что у каждого потока будет собственный изолированный экземпляр модели, и исключает одновременное изменение состояния модели.

    Пример:

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

    Чтобы узнать больше о потокобезопасности, перейди к разделу Потокобезопасный инференс с моделями YOLO.

  • Чтобы безопасно выполнять многопоточный инференс моделей YOLO в Python, следуй этим рекомендациям:

    1. Создавай модели YOLO внутри каждого потока, а не используй один экземпляр совместно в нескольких потоках.
    2. Используй модуль multiprocessing Python для параллельной обработки, чтобы избежать проблем, связанных с глобальной блокировкой интерпретатора (GIL).
    3. Помни, что базовые библиотеки YOLO на C (PyTorch, OpenCV) автоматически освобождают GIL во время интенсивных вычислений, поэтому потоки всё равно могут одновременно выполнять инференс.
    4. Если память ограничена, рассмотри использование декоратора ThreadingLocked для экземпляров моделей, используемых совместно.

    Пример создания потокобезопасного экземпляра модели:

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

    Дополнительные сведения см. в разделе Потокобезопасный инференс.

  • У каждого потока должен быть собственный экземпляр модели YOLO, чтобы предотвратить состояния гонки. Когда один экземпляр модели используется несколькими потоками, одновременный доступ может привести к непредсказуемому поведению и изменению внутреннего состояния модели. Использование отдельных экземпляров обеспечивает изоляцию потоков, делая многопоточные задачи надёжными и безопасными.

    Подробные рекомендации см. в разделах Непотокобезопасный пример: один экземпляр модели и Потокобезопасный пример.

  • Глобальная блокировка интерпретатора (GIL) Python позволяет только одному потоку одновременно выполнять байт-код Python, что может ограничивать производительность задач многопоточности, требовательных к CPU. Однако для операций, ограниченных вводом-выводом, или процессов, использующих библиотеки, освобождающие GIL, например базовые библиотеки YOLO на C, всё ещё можно добиться конкурентного выполнения. Для повышения производительности рассмотри параллелизм на основе процессов с модулем multiprocessing Python.

    Подробнее о потоках в Python см. в разделе Основы работы с потоками в Python.

  • Да, использование модуля multiprocessing Python безопаснее и часто эффективнее для параллельного инференса моделей YOLO. Параллелизм на основе процессов создаёт отдельные адресные пространства, исключает влияние глобальной блокировки интерпретатора (GIL) и снижает риск проблем с конкурентным выполнением. Каждый процесс работает независимо со своим экземпляром модели YOLO.

    Дополнительные сведения о параллелизме на основе процессов с моделями YOLO см. на странице Потокобезопасный инференс.

Комментарии