Ultralytics YOLO27:
Get Started

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

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

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



Смотри: Как выполнять потокобезопасный инференс с моделями Ultralytics YOLO в Python | Многопоточность 🚀

Основы потоков Python#

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

Single-thread vs multi-thread inference

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

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

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

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

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

# Небезопасно: совместное использование одного экземпляра модели в нескольких потоках
from threading import Thread

from ultralytics import YOLO

# Создание экземпляра модели вне потока
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)
    # Обработать результаты

# Запуск потоков, использующих один и тот же экземпляр модели
Thread(target=predict, args=("image1.jpg",)).start()
Thread(target=predict, args=("image2.jpg",)).start()

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

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

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

# Безопасно: каждый поток использует собственный экземпляр модели
from threading import Thread

from ultralytics import YOLO

# Создание отдельной модели для каждого потока
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)
    # Обработать результаты

# Каждый поток использует отдельный экземпляр модели
Thread(target=predict, args=(model_1, "image1.jpg")).start()
Thread(target=predict, args=(model_2, "image2.jpg")).start()

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

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

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

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

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

# Безопасно: создание отдельного экземпляра модели в каждом потоке
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)
    # Обработать результаты

# Запуск потоков, каждый из которых использует собственный экземпляр модели
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

# Создание экземпляра модели
model = YOLO("yolo26n.pt")

# Добавление декоратора к функции предсказания для обеспечения потокобезопасности
@ThreadingLocked()
def thread_safe_predict(image_path):
    """Thread-safe prediction using a shared model instance."""
    results = model.predict(image_path)
    return results

# Теперь эту функцию можно безопасно вызывать из нескольких потоков

Декоратор 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)
        # Обработать результаты
    
    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. Помни, что базовые библиотеки C, используемые YOLO (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)
        # Обработать результаты
    
    # Запуск нескольких потоков
    Thread(target=thread_safe_predict, args=("image1.jpg",)).start()
    Thread(target=thread_safe_predict, args=("image2.jpg",)).start()

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

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

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

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

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

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

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

Комментарии