Ultralytics YOLO27:

YOLOモデルによるスレッドセーフな推論#

Pythonスレッド間でUltralytics YOLO推論を安全に実行するには、複数のスレッドで1つのインスタンスを共有するのではなく、各スレッド内で個別のYOLOモデルをインスタンス化します。1つのモデルを共有すると、競合状態によって内部状態が破損し、予測できない結果が生じます。これは、Pythonのthreadingモジュールが同じオブジェクトに対してスレッドを並行実行するためです。このガイドでは、共有が失敗する理由、安全なスレッドごとのパターン、そしてインスタンスを共有する必要がある場合のThreadingLockedデコレーターについて説明します。

モデルの共有が失敗する理由スレッドセーフなパターン、またはThreadingLockedデコレーターに移動します。



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

Pythonスレッドの概要#

Pythonスレッドは、プログラムで複数の操作を同時に実行できる並列化の一形態です。ただし、Pythonのグローバルインタープリターロック (GIL) により、一度にPythonバイトコードを実行できるスレッドは1つだけです。

Single-thread vs multi-thread inference

これは制限のように聞こえますが、I/Oバウンドの操作や、YOLOの基盤となるCライブラリが実行する処理のようにGILを解放する操作を使用する場合、スレッドによって並行性を実現できます。

共有モデルインスタンスの危険性#

スレッドの外部でYOLOモデルをインスタンス化し、そのインスタンスを複数のスレッドで共有すると、競合状態が発生する可能性があります。これは、同時アクセスによってモデルの内部状態が一貫性なく変更されるためです。モデルまたはそのコンポーネントがスレッドセーフに設計されていない状態を保持している場合、特に問題になります。

スレッドセーフではない例: 単一モデルインスタンス#

Pythonでスレッドを使用する場合、並行性の問題につながるパターンを認識することが重要です。避けるべきなのは、1つの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が複数のスレッドによって同時に実行され、予測できない結果につながる可能性があります。

安全な例: スレッドごとに専用のインスタンス#

各スレッドが自身のインスタンスを所有し、別のスレッドと共有しない限り、複数の個別モデルインスタンスを使用しても問題ありません。以下のインスタンスがスレッドの開始前に作成されていても問題ありません。安全でない唯一のパターンは、1つのインスタンスを複数のスレッドで共有することです。

# 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デコレーターを提供しています。このデコレーターはロックを使用し、一度に1つのスレッドだけがデコレートされた関数を実行できるようにします。

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デコレーターは、スレッド間でモデルインスタンスを共有する必要がある一方で、一度に1つのスレッドだけがアクセスできるようにしたい場合に特に役立ちます。

メモリと並行性のトレードオフ

ロックされた1つのモデルインスタンスを共有すると、各スレッドでモデルを読み込む場合と比較してメモリを節約できますが、スレッドがロック上で直列化され、順番を待つため、並行性は低下します。メモリに余裕があり、最大限の並列性が必要な場合はスレッドごとのパターンを優先し、モデルのメモリ使用量がボトルネックになる場合はThreadingLockedを使用します。

まとめ#

PythonのthreadingでYOLOモデルを使用する場合は、各スレッドに専用のモデルインスタンスを割り当て、1つのインスタンスを複数のスレッドで共有しないでください。モデルを使用するスレッド内でインスタンス化することが、この要件を保証する最も簡単な方法です。これにより、競合状態を回避し、推論タスクの信頼性を維持できます。

より高度なシナリオやマルチスレッド推論のパフォーマンスをさらに最適化するには、multiprocessingによるプロセスベースの並列化を使用するか、専用のワーカープロセスを備えたタスクキューの利用を検討してください。

FAQ#

  • マルチスレッドのPython環境でUltralytics YOLOモデルを使用する際に競合状態を防ぐには、各スレッド内で個別の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モデルによるスレッドセーフな推論をご覧ください。

  • PythonでマルチスレッドのYOLOモデル推論を安全に実行するには、次のベストプラクティスに従ってください。

    1. 1つのモデルインスタンスを複数のスレッドで共有するのではなく、各スレッド内でYOLOモデルをインスタンス化します。
    2. グローバルインタープリターロック (GIL) に関連する問題を回避するため、並列処理にはPythonのmultiprocessingモジュールを使用します。
    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モデルインスタンスを持たせる必要があります。1つのモデルインスタンスを複数のスレッドで共有すると、同時アクセスによって予測できない動作やモデルの内部状態の変更が発生する可能性があります。個別のインスタンスを使用することでスレッドを分離し、マルチスレッドタスクの信頼性と安全性を確保できます。

    詳しいガイダンスについては、スレッドセーフではない例: 単一モデルインスタンススレッドセーフな例のセクションを確認してください。

  • Pythonのグローバルインタープリターロック (GIL) では、一度にPythonバイトコードを実行できるスレッドが1つだけであるため、CPUバウンドのマルチスレッドタスクのパフォーマンスが制限される可能性があります。ただし、I/Oバウンドの操作や、YOLOの基盤となるCライブラリのようにGILを解放するライブラリを使用するプロセスでは、並行性を実現できます。パフォーマンスを向上させるには、Pythonのmultiprocessingモジュールによるプロセスベースの並列化を検討してください。

    Pythonのスレッド処理について詳しくは、Pythonスレッドの概要のセクションをご覧ください。

  • はい、Pythonのmultiprocessingモジュールを使用すると、YOLOモデルの推論を並列実行する際に、より安全で、多くの場合より効率的です。プロセスベースの並列化では個別のメモリ空間が作成されるため、グローバルインタープリターロック (GIL) を回避し、並行性に関する問題のリスクを低減できます。各プロセスは独立して動作し、それぞれ独自のYOLOモデルインスタンスを持ちます。

    YOLOモデルによるプロセスベースの並列化の詳細については、スレッドセーフな推論のページを参照してください。

コメント