Потокобезопасный инференс с моделями 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.
Хотя это может показаться ограничением, потоки всё равно обеспечивают конкурентное выполнение, особенно для операций, ограниченных вводом-выводом, или при использовании операций, освобождающих 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, следуй этим рекомендациям:
- Создавай модели YOLO внутри каждого потока, а не используй один экземпляр совместно в нескольких потоках.
- Используй модуль
multiprocessingPython для параллельной обработки, чтобы избежать проблем, связанных с глобальной блокировкой интерпретатора (GIL). - Помни, что базовые библиотеки YOLO на C (PyTorch, OpenCV) автоматически освобождают GIL во время интенсивных вычислений, поэтому потоки всё равно могут одновременно выполнять инференс.
- Если память ограничена, рассмотри использование декоратора
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, всё ещё можно добиться конкурентного выполнения. Для повышения производительности рассмотри параллелизм на основе процессов с модулем
multiprocessingPython.Подробнее о потоках в Python см. в разделе Основы работы с потоками в Python.
Да, использование модуля
multiprocessingPython безопаснее и часто эффективнее для параллельного инференса моделей YOLO. Параллелизм на основе процессов создаёт отдельные адресные пространства, исключает влияние глобальной блокировки интерпретатора (GIL) и снижает риск проблем с конкурентным выполнением. Каждый процесс работает независимо со своим экземпляром модели YOLO.Дополнительные сведения о параллелизме на основе процессов с моделями YOLO см. на странице Потокобезопасный инференс.