YOLOモデルを用いたスレッドセーフな推論

マルチスレッド環境でYOLOモデルを実行する場合、スレッドセーフを確保するために慎重な検討が必要です。Pythonのthreadingモジュールを使用すると複数のスレッドを並行して実行できますが、これらのスレッド間でYOLOモデルを使用する際には、注意すべき重要な安全性の問題があります。このページでは、スレッドセーフなYOLOモデル推論を作成する方法について説明します。



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モデルをスレッドの外でインスタンス化し、このインスタンスを複数のスレッド間で共有すると、競合状態(race conditions)が発生する可能性があります。これは、同時アクセスによってモデルの内部状態が矛盾した形で変更されてしまう現象です。モデルやそのコンポーネントがスレッドセーフでない状態を保持している場合、特にこの問題が顕著になります。

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

Pythonでスレッドを使用する際は、並行性の問題を引き起こす可能性のあるパターンを認識することが重要です。単一のYOLOモデルインスタンスを複数のスレッド間で共有することは避けてください。

# 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が複数のスレッドから同時に実行される可能性があるため、予測不能な結果を招く恐れがあります。

スレッドセーフでない例:複数のモデルインスタンス

同様に、複数のYOLOモデルインスタンスを使用する場合の安全でないパターンを以下に示します。

# Unsafe: Sharing multiple model instances across threads can still lead to issues
from threading import Thread

from ultralytics import YOLO

# Instantiate multiple models outside the thread
shared_model_1 = YOLO("yolo26n_1.pt")
shared_model_2 = YOLO("yolo26n_2.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

# Starting threads with individual model instances
Thread(target=predict, args=(shared_model_1, "image1.jpg")).start()
Thread(target=predict, args=(shared_model_2, "image2.jpg")).start()

2つの個別のモデルインスタンスを使用している場合でも、依然として並行性の問題が発生するリスクがあります。YOLOの内部実装がスレッドセーフでない場合、たとえ個別のインスタンスを使用したとしても、それらのインスタンスがスレッドローカルではない基盤リソースや状態を共有していれば、競合状態を防止できない可能性があります。

スレッドセーフな推論

スレッドセーフな推論を実行するには、各スレッド内で個別の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 predict method 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つのスレッドしかアクセスできないように制限したい場合に特に役立ちます。この手法は、スレッドごとに新しいモデルインスタンスを作成するよりもメモリを節約できる可能性がありますが、スレッドがロックの解放を待機する必要があるため、並行性が低下する可能性があります。

結論

PythonのthreadingでYOLOモデルを使用する際は、スレッドセーフを確保するために、常にそのモデルを使用するスレッド内でインスタンス化してください。この慣行により、競合状態を回避し、推論タスクを確実に実行できます。

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

FAQ

マルチスレッドPython環境でYOLOモデルを使用する際、競合状態を避けるにはどうすればよいですか?

マルチスレッド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モデル推論を実行するためのベストプラクティスは何ですか?

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

  1. 単一のモデルインスタンスをスレッド間で共有するのではなく、スレッドごとにYOLOモデルをインスタンス化してください。
  2. グローバルインタープリターロック(GIL)に関連する問題を回避するために、並列処理にはPythonのmultiprocessingモジュールを使用してください。
  3. YOLOの基盤となるCライブラリによる操作を使用して、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."""
    model = YOLO("yolo26n.pt")
    results = 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モデルインスタンスを持つ必要があります。単一のモデルインスタンスが複数のスレッド間で共有されている場合、同時アクセスによって予測不能な動作やモデルの内部状態の変更が発生する可能性があります。個別のインスタンスを使用することで、スレッドの分離が確保され、マルチスレッドタスクが信頼性と安全性を備えたものになります。

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

Pythonのグローバルインタープリターロック(GIL)は、YOLOモデルの推論にどのような影響を与えますか?

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

Pythonのスレッドに関する詳細については、「Pythonのスレッディングについて」セクションを参照してください。

YOLOモデルの推論において、スレッドよりもプロセスベースの並列処理を使用する方が安全ですか?

はい、Pythonのmultiprocessingモジュールを使用する方が安全であり、YOLOモデルの推論を並列実行する場合に効率的であることが多いです。プロセスベースの並列処理は個別のメモリ空間を作成するため、グローバルインタープリターロック(GIL)を回避し、競合問題のリスクを軽減できます。各プロセスは独自のYOLOモデルインスタンスを使用して独立して動作します。

YOLOモデルでのプロセスベースの並列処理に関する詳細は、「スレッドセーフな推論」のページを参照してください。

コメント