Потокобезопасный инференс с моделями 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 в конкретный момент времени.
Хотя это звучит как ограничение, потоки все равно могут обеспечить параллельное выполнение, особенно для операций, связанных с вводом-выводом (I/O), или при использовании операций, которые освобождают GIL, подобных тем, что выполняются базовыми C-библиотеками YOLO.
Опасность совместного использования экземпляров моделей#
Создание экземпляра модели YOLO вне потоков и его совместное использование несколькими потоками может привести к состоянию гонки (race condition), при котором внутреннее состояние модели будет изменяться несогласованно из-за одновременного доступа. Это особенно проблематично, если модель или ее компоненты хранят состояние, не предназначенное для потокобезопасной работы.
Пример отсутствия потокобезопасности: один экземпляр модели#
При использовании потоков в 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 или задействования очереди задач с выделенными рабочими процессами.
FAQ#
Как мне избежать состояний гонки при использовании моделей YOLO в многопоточной среде Python?#
Чтобы предотвратить состояния гонки при использовании моделей 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 в Python, следуй этим лучшим практикам:
- Создавай экземпляры моделей YOLO внутри каждого потока, а не используй один экземпляр модели совместно в разных потоках.
- Используй модуль
multiprocessingв Python для параллельной обработки, чтобы избежать проблем, связанных с глобальной блокировкой интерпретатора (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?#
Каждый поток должен иметь свой собственный экземпляр модели YOLO для предотвращения состояний гонки. Когда один экземпляр модели совместно используется несколькими потоками, одновременный доступ может привести к непредсказуемому поведению и изменению внутреннего состояния модели. Используя отдельные экземпляры, ты обеспечиваешь изоляцию потоков, делая многопоточные задачи надежными и безопасными.
Для получения подробных инструкций ознакомься с разделами Пример без потокобезопасности: единственный экземпляр модели и Пример потокобезопасности.
Как глобальная блокировка интерпретатора (GIL) Python влияет на инференс моделей YOLO?#
Глобальная блокировка интерпретатора (GIL) в Python позволяет выполнять байт-код Python только одному потоку за раз, что может ограничивать производительность задач многопоточности, ограниченных процессором (CPU-bound). Однако для операций ввода-вывода (I/O-bound) или процессов, использующих библиотеки, освобождающие GIL (например, лежащие в основе YOLO библиотеки C), ты все равно можешь достичь параллелизма. Для повышения производительности рассмотри возможность использования параллелизма на основе процессов с модулем multiprocessing в Python.
Подробнее о многопоточности в Python см. в разделе Понимание многопоточности в Python.
Безопаснее ли использовать параллелизм на основе процессов вместо многопоточности для инференса моделей YOLO?#
Да, использование модуля multiprocessing в Python безопаснее и часто эффективнее для запуска параллельного инференса моделей YOLO. Параллелизм на основе процессов создает отдельные пространства памяти, избегая глобальной блокировки интерпретатора (GIL) и снижая риск возникновения проблем с параллелизмом. Каждый процесс будет работать независимо со своим собственным экземпляром модели YOLO.
Дополнительные сведения о параллелизме на основе процессов с моделями YOLO см. на странице Потокобезопасный инференс.