プロジェクト向けGPU · KYC不要の暗号資産決済 レンタル方法
日本語
コンソールを開く
実践ガイド / KERNODECK

DataLoader がブロックする:workers より先にデータを確認する

num_workers=0 と固定順序から始め、まずサンプルを、次にバッチ全体を検証し、読み込み・変換・組み立て・GPU 転送を分離してください。その後、ワーカーを段階的に戻します。待機中の GPU はストレージが遅い証拠にはなりません。データエラー、シリアライズ、あるいはコストの高い組み立てが、計算の前でチェーンを止めている可能性があります。

2 分で読了 · 開発者向けガイド

ローダーが提供すべきものを定義する

最適化の前に出力契約を書いてください。要素数、各フィールドの型、次元、ターゲットの範囲、不完全な入力の扱いを定めます。サンプルの識別子とバッチ内での位置を区別します。変換によって形状が変わることや入力がフィルタリングされることがあります。それが許可されるかを学習プログラムが把握している必要があります。

通常のファイル、境界ケース、データセットの最後の要素を含む代表的なサンプルを取得します。Dataset と全く同じ前処理で各要素を開きます。次にそれらのアセンブルを確認します。個別のアクセスが成功しても、複数の結果をスタックできるとは限りません。テキストの場合は padding とマスクを、画像の場合はチャネル、次元、軸の順序を文書化します。

範囲を定めます。データがローカルかリモートか、デコードを含むか否か、変換が固定かランダムか。2つの設定間でそれを維持してください。見かけ上の向上は、削除された作業に起因する可能性があります。

単一プロセスに戻してエラーを読む

まず num_workers=0、shuffle=False、小さなバッチで再現します。すると読み込みはメインプロセス内で行われ、エラーのトレースは通常より読みやすくなります。DataLoader のドキュメントはデバッグのためにこの設定を推奨しています。失敗した要素の識別子をデコード前に記録しますが、その機密内容をログにコピーしないでください。

分離して進めます。生アクセス、変換、collate_fn、そして転送です。転送の前で処理が失敗するなら、CUDA の変更が最初の手がかりではありません。複数の workers の場合にのみブロックするのなら、それらのプロセスに渡されるオブジェクトとリソースを確認します。最初のイテレーションとその次を比較します。workers の起動は初回の待機を説明できますが、継続的な問題を立証するものではありません。

timeout は待機を可視化できますが、利用できないソースやブロックした worker を修復するものではありません。この遅延を無限に増やすのではなく、最後に把握できたステップを保ち、入力数を減らします。

実例: 期待される3チャンネル、異なる画像

4つの教育用レコードを考えます。最初の3つは形状 [3, 16, 16] のテンソルを与え、4番目は [1, 16, 16] です。3チャンネルを義務付ける契約では、4番目の要素はスタックの前に識別されなければなりません。このシナリオはここでは実行されておらず、選ばれた形状に基づく期待される結果を記述しています。

以下の関数は、各レコードが id、x、y フィールドを持ち、x が CPU テンソルであり、y が整数インデックスであることを前提としています。矛盾を黙って削除するのではなく拒否します。あなたのプロジェクトでは、モノクロ画像を3チャンネルに変換すべきか、インポート時に拒否すべきかを明示的に決定してください。この決定はデータの意味とモデルが期待する前処理に依存します。

修正後、4つの識別子はすべて存在し続けなければならず、組み立てられたテンソルは形状 [4, 3, 16, 16] でなければなりません。ターゲットに適したガードを追加してください。正しく次元付けされた画像でも、無効なアノテーションを持つ可能性があります。

提案された教育用アセンブル、未実行
import torch
from torch.utils.data import DataLoader

def assemble(records):
    for item in records:
        if tuple(item["x"].shape) != (3, 16, 16):
            raise ValueError(f"予期しない形状です: {item['id']}")
    return {
        "ids": [item["id"] for item in records],
        "x": torch.stack([item["x"] for item in records]),
        "y": torch.tensor([item["y"] for item in records],
                          dtype=torch.long),
    }

# dataset は、ここで説明したレコードを生成するあなたの Dataset です。
# マルチプロセススクリプトでは、main ガードの下で loader を作成してください。
if __name__ == "__main__":
    loader = DataLoader(dataset, batch_size=4, num_workers=0,
                        shuffle=False, collate_fn=assemble)
    iterator = iter(loader)
    batch = next(iterator)

データを変えずに workers を再導入する

batch、順序、変換を維持したまま、ゼロから少数の worker へと進めましょう。まず 1 エポック全体をテストし、続いて 2 回目をテストします。一部のエラーは、イテレータの再起動時やリソースが消費された後にのみ現れるからです。並列性を高めることが役立つのは、前処理の作業が実際に並行して進められる場合だけです。

起動方式はシステムと Python のバージョンによって異なります。spawn を使う場合は、プログラムのエントリを if __name__ == '__main__' で保護し、Dataset、collate_fn、worker 関数をローカルのラムダではなくモジュールレベルで定義してください。プロセスのドキュメントでは、継承されたロックやスレッドがデッドロックを引き起こす理由も説明されています。ライブラリが要求する場合は、プロセスごとに固有のアクセス初期化を行ってください。

IterableDataset の場合、識別子を使って workers 間の分割を検証します。複数のワーカーがそれぞれ同じストリーム全体を消費してはなりません。バッチの数だけで判断せず、重複や欠落した要素も確認してください。

明確な単位で待ち時間とスループットを測る

2つの補完的な観測を使用してください。ローダー単体の実行では、定義された区間中に準備されたサンプルを数えます。統合された実行では、モデルがそのデータを消費する際に何が起こるかを調べます。前者は準備を切り分けるのに役立ちますが、自動的にトレーニングのスループットを表すものではありません。

プロトコルでは、実際に配信されたサンプル数を数え、経過した秒数で割ります。起動時に除外した分、データのキャッシュ、変換、繰り返し回数を明記してください。最良の値だけを選ぶのではなく、各回の値をそのまま保持します。以下の表は記録用のシートであり、性能の数値はあらかじめ埋められていません。

形状が変化する場合、1秒あたりのサンプル数は負荷の変化を覆い隠す可能性があります。デコードされたピクセル数や実際に準備されたトークン数など、関連する単位を加えつつ、サンプル数も保持してください。完全なループ内での待ち時間を特定するには、次のバッチの読み取りを計算とは別に名付けてください。

表をスクロールしてすべての列を表示してください。
自分の負荷で記入するためのシート。性能の値は仮定していません
設定確認する項目観測された所要時間期待される結論
workers=0識別子、形状、ターゲット秒単位で測定するもの正しい基準
少数の workers同じ入力集合秒単位で測定するもの実際の利得または追加コスト
同じ設定、第二の時代欠落も重複もなし秒単位で測定するもの起動とキャッシュの影響

メモリ、先読み、転送を別々に扱う

ワーカーと待機中のバッチはホストメモリを消費します。VRAMだけが重要だと結論づける前に、試用中はこれを監視してください。より深いプリロードは待機を移動させる一方で占有率を高めることがありますが、1秒あたりの結果が増えることを保証するものではありません。まず疑わしい変数を減らし、同じ範囲で比較してください。

pin_memory と非ブロッキング転送は、アクセラレータへのデータの受け渡しに関係します。PyTorch の最適化レシピでは、これらはハードウェアと負荷に応じて検討すべきレバーとして紹介されています。デコードの誤りを修正するものではありません。まず workers 内で CPU 上のデータを扱い、その後、計算を駆動するプロセスで転送を整理してください。利点と実際のオーバーラップは、想定ではなく観測する必要があります。

persistent_workers を使用する場合は、エポック間で保持されるリソースと状態に注意してください。単一のバッチで良好な設定が得られても、ファイルのクローズやデータソースの再読み込みを検証するには不十分です。

データが正しいままである場合にのみ設定を採用する

期待される結果は、選択した枠組みの中で、予定されたすべての入力をサイレントエラーなしで受け取るループです。最適化の前後で識別子とターゲットを比較してください。最後の不完全なバッチを除外する場合は drop_last を説明してください。変換がランダムな場合は、そのポリシーに反するピクセルの一致を要求するのではなく、ポリシー自体を確認してください。

測定された要件を満たす最もシンプルな設定を維持してください。ストレージ、デコード、またはモデルがすでに制限を課している場合、ワーカー数を増やしても改善しないことがあります。サーバーの CPU、RAM、ストレージのリソースは、その GPU の名前から推測することはできません。Kernodeck 環境を準備する際は、これらの要件を別途明示してください。

よくある質問

num_workers=0 で GPU トレーニングは無効になりますか?

いいえ。データ読み込みがメインプロセス内で行われます。モデルは引き続き GPU で計算できます。この設定は主に、読み込み、変換、アセンブルのエラーをより直接的に確認するために役立ちます。

ワーカー数は CPU コア数と同じにする必要がありますか?

自動的にそうなるわけではありません。適切な設定は、前処理の作業、メモリ、データアクセス、モデルの消費速度によって異なります。同じ負荷を保ち、配信される入力を確認しながら、いくつかの値を比較してください。

バッチを小さくすればワーカーが停止する問題は解決しますか?

メモリ負荷は変わるかもしれませんが、無効なアノテーション、シリアライズできないリソース、読み取り不能なファイルは修正されません。まずワーカーをゼロにして再現し、原因となるステップを特定してください。

next(iterator) にかかる時間はディスクを測定していますか?

いいえ。iterator=iter(loader) の後、next(iterator) はバッチを待ちます。デコード、変換、アセンブル、プロセス間通信、プリフェッチが関与する可能性があります。読み取りのみの測定と完全なループのトレースでは、答える問いが異なります。