Ultralytics YOLO27:
Get Started

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

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

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



視聴: PythonでUltralytics YOLOモデルを使ったスレッドセーフな推論を行う方法 | マルチスレッド 🚀

Pythonのスレッド処理について#

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

Single-thread vs multi-thread inference

制約のように思えるかもしれませんが、スレッドでも並行処理は可能です。特に、I/Oバウンドの処理や、YOLOの基盤となるCライブラリが実行する処理のようにGILを解放する処理を使う場合に有効です。

モデルインスタンスを共有する危険性#

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が複数のスレッドで同時に実行される可能性があるため、予測できない結果につながることがあります。

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

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

# 安全: 各スレッドが専用のモデルインスタンスを使用
from threading import Thread

from ultralytics import YOLO

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

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デコレーターは、モデルインスタンスをスレッド間で共有する必要があるものの、一度にアクセスできるスレッドを1つに制限したい場合に特に便利です。

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

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

結論#

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

さらに高度なシナリオに対応し、マルチスレッド推論の性能を最適化するには、multiprocessingによるプロセスベースの並列処理や、専用のワーカープロセスを備えたタスクキューの活用を検討してください。

よくある質問#

  • マルチスレッドの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)
        # 結果を処理する
    
    Thread(target=thread_safe_predict, args=("image1.jpg",)).start()
    Thread(target=thread_safe_predict, args=("image2.jpg",)).start()

    スレッドセーフを確保する方法について詳しくは、YOLOモデルによるスレッドセーフな推論をご覧ください。

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

    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)
        # 結果を処理する
    
    # 複数のスレッドを開始
    Thread(target=thread_safe_predict, args=("image1.jpg",)).start()
    Thread(target=thread_safe_predict, args=("image2.jpg",)).start()

    詳しい説明については、スレッドセーフな推論のセクションを参照してください。

  • 競合状態を防ぐには、各スレッドに独自のYOLOモデルインスタンスを用意する必要があります。単一のモデルインスタンスを複数のスレッドで共有すると、同時アクセスによって予測できない動作やモデルの内部状態の変更が発生する可能性があります。個別のインスタンスを使用すれば、スレッドを分離できるため、マルチスレッドタスクの信頼性と安全性が高まります。

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

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

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

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

    YOLOモデルを使ったプロセスベースの並列処理について詳しくは、スレッドセーフな推論のページをご覧ください。

コメント