本番運用におけるPyTorch+CUDAサーバーでの「Unknown Error」問題とその対策

本番運用におけるPyTorch+CUDAサーバーでの「Unknown Error」問題とその対策

こんにちは!Qualitegプロダクト開発部です。

今日は、GPUをつかった商用サービスにて悩ましい、テストは全部通るけど、長時間運用をしていると急に起こる「CUDA error: unknown error」についての内容です。

これ、出会うと残念な気持ちになりますが、けっこうGPU商用サービス界隈では「あるある」なんです。

原因を真面目に探るには CUDAバージョン、PyTorchバージョンの調合具合、実際のアプリケーションコードまですべてソースまで追う必要があるのですが、多くの場合、運用でカバーします。

なぜなら仮に1つ原因をみつけて対処できたとしても、CUDAバージョンはしょっちゅうあがりますし、PyTorchもそれに追従して頻繁に更新されます。さらにやっかいなことに、1日、2日、いや1週間くらいは安定的に動作しているようにみえて、数週間後にとつぜんエラーが出るといった具合なので、修正確認の難易度が高いんです。

そこで本日は「開発環境や実験環境」ではなく「本番環境」で発生しがちなこのCUDA Unknown Error について問題の原因と実践的な対策について解説します。

問題の具体例

典型的なエラーは次のようなスタックトレースとして現れます:

RuntimeError: CUDA error: unknown error
CUDA kernel errors might be asynchronously reported at some other API call, so the stacktrace below might be incorrect.
For debugging consider passing CUDA_LAUNCH_BLOCKING=1
Compile with `TORCH_USE_CUDA_DSA` to enable device-side assertions.

特に多く見られるのは、NumPy配列からPyTorchテンソルへの変換時に発生するエラーです:

img = torch.from_numpy(img.copy()).to(device, dtype=torch.float32)

なぜこの問題が発生するのか

この問題が発生する主な理由は以下のようなものがかんがえられます

1. CUDA・PyTorchのバージョン互換性の問題

CUDAとPyTorchのバージョンをアップグレードした後に、このようなエラーが突然発生するケースが多く見られます。

特にマイナーアップデートでさえ、内部実装の変更によって未知の互換性問題が引き起こされることがあります。例えば、CUDA 12.4から12.8へのアップグレード後、それに追従したPyTorchのアップグレード、また、そのために Pythonのバージョンをアップグレードしたときに付随して問題が発生することがよくあります。

2. GPUメモリの管理とリーク

PyTorchはGPUメモリを効率的に使用するためにキャッシュ機構を採用していますが、このキャッシュは完璧ではありません。

長時間の実行中に、小さなメモリリークが蓄積したり、メモリの断片化が進行したりします。

さらに、PyTorchのメモリ管理はアプリケーション層から詳細に制御することができません。
ざっくりとした解放命令やGC要求はできますが、それ以上の細かい制御はできずメモリの解放、予約領域の解放など簡単にはアプリ層からは触れず、あるいいみPyTorchまかせです。

2. CUDA非同期処理の特性

CUDAの多くの操作は非同期で行われるため、エラーが実際に発生した場所と報告される場所が異なることがあります。

エラーメッセージにも記載されている通り、実際のエラーは別のAPIコールでしれっと非同期的に報告されることがあります。

これによりデバッグがさらに困難になります。

3. 長時間実行の影響

商用サーバーアプリケーションは通常、数日から数週間(あるいはそれ以上)にわたって実行され続けます。

この間、小さな問題が徐々に蓄積し、最終的にはクリティカルなエラーとなって現れることがあります。

4. モデルの複雑性と負荷

当社サービスでも頻繁に実行される顔検出や画像認識などの複雑なモデルでは、大量のGPUリソースが必要とされ、長時間の高負荷状態がこの問題を引き起こす可能性が高まります。

実用的な対策

この問題に対処するための実践的なアプローチをいくつか紹介します

0.バージョンアップグレード前の長時間テスト

CUDAやPyTorchのバージョンをアップグレードする前に、必ず本番環境と同じ条件で長時間のテストを実施することが重要です。

特に注意すべき点は以下の通りです

  • 実際の負荷条件での検証 実際の運用時と同様の負荷をかけたテストを行います
  • 最低24時間の継続実行 メモリリークなどは時間経過とともに発生するため、短時間のテストでは発見できません
  • 繰り返しの処理 同じ処理を数千回繰り返すことで、安定性を確認します
# 最低でも24時間は同じ負荷で継続的に実行し、安定性を確認
for i in {1..10000}; do
    python your_processing_script.py --batch-size 32
    sleep 5
done
  • リソース使用量の監視 テスト中はGPUメモリやCPU使用率を継続的に監視し、徐々に増加する傾向がないか確認します
     このとき PyTorchやPythonの組み込み関数だけでなく、NVML(NVIDIA Management Library)を使用すて定期的に実行して監視するのがおすすめです。プロセス横断での使用メモリ量をより性格に取得することができます。
  • エラーログの監視 わずかな警告メッセージでも見逃さないようにログを注意深く監視します

互換性マトリックスの確認

PyTorchとCUDAのバージョン互換性を事前に確認します。PyTorch公式サイトには互換性マトリックスが公開されていますが、実際のアプリケーションでの互換性は環境によって異なることがあります。

当社ブログ「PyTorchがサポートするGPUの Compute Capability」 より

1. 運用面での対策

定期的な再起動の自動化

最も単純かつ効果的な対策は、サービスを定期的に再起動するスケジュールを設定することです。

# crontabの例: 毎日午前3時にサービスを再起動
0 3 * * * systemctl restart your_service

ヘルスチェックと自動再起動

定期的な再起動に加え、アプリケーションの状態を監視し、エラーが発生したら自動的に再起動するようにします

try:
    # メイン処理
    process_images()
except RuntimeError as e:
    if "CUDA error" in str(e):
        # ログ記録
        logging.error("CUDA error detected, restarting service")
        # プロセス再起動コード
        os.execv(sys.executable, ['python'] + sys.argv)

GPUリソースのモニタリング

NVML をつかってGPU使用率やメモリ消費を定期的に監視し、問題の予兆を検知します

import pynvml

def monitor_gpu():
    pynvml.nvmlInit()
    handle = pynvml.nvmlDeviceGetHandleByIndex(0)
    info = pynvml.nvmlDeviceGetMemoryInfo(handle)
    used_percent = info.used / info.total * 100
    
    # 使用率が90%を超えたら警告
    if used_percent > 90:
        logging.warning(f"GPU memory usage high: {used_percent:.2f}%")
        # 対策を実施 (キャッシュクリア、プロセス再起動など)

2. コード面での対策

コード面での一般的な対策も記載しておきましょう。
これは今回の根深い問題の処方箋ではありませんが、一般論としてご紹介いたします

CUDAキャッシュのクリア

定期的にPyTorchのCUDAキャッシュをクリアすることで、メモリリークの影響を減らすことができます

def clear_gpu_memory():
    torch.cuda.empty_cache()

# 一定回数の処理ごとにキャッシュクリア
for i, batch in enumerate(data_loader):
    process_batch(batch)
    if i % 100 == 0:
        clear_gpu_memory()

バッチサイズの最適化

こちらも一般論ですが、大きなバッチサイズはGPUメモリを圧迫します。より小さなバッチサイズを使用することで、メモリ問題を軽減できることがあります

# より小さなバッチサイズを設定
data_loader = DataLoader(dataset, batch_size=8, shuffle=True)

CPUフォールバックの実装

GPU処理に失敗した場合に自動的にCPUにフォールバックするロジックを実装すると、サービスの継続性が向上します

def process_with_fallback(image):
    try:
        # GPUで処理
        return process_on_gpu(image)
    except RuntimeError as e:
        if "CUDA error" in str(e):
            logging.warning("Falling back to CPU processing")
            # CPUで処理
            return process_on_cpu(image)
        else:
            raise

デバッグフラグの使用

エラーメッセージで推奨されているように、CUDA_LAUNCH_BLOCKING=1フラグを設定することで、非同期エラーの正確な位置を特定しやすくなります

CUDA_LAUNCH_BLOCKING=1 python your_script.py

3. アーキテクチャ面での対策

マイクロサービス化と水平スケーリング

モノリシックなアプリケーションを小さなマイクロサービスに分割することで、一部に問題が発生しても全体のサービスが継続できるようになります。

また、個別のサービスごとに再起動戦略を実装できます。

当社サービスも小さな単位でマイクロサービス化されており、同一の機能を提供するマイクロサービスが水平展開されています。マイクロサービスの定期的な再起動タイミングが重ならないようにスケジューリングすることでサービスの継続性を向上させることができます。

つまり複数のサーバーインスタンスを使用してロードバランシングすることで、一部のインスタンスが失敗しても、他のインスタンスがリクエストを処理できるようになります。

Kubernetes等のコンテナオーケストレーションツールを使用すると、こうした構成を効率的に管理できます。

まとめ

「CUDA error: unknown error」は、PyTorchとCUDAを使用した長時間実行サーバーでよく見られる問題です。

特にそれまでは安定していたのに、バージョンアップグレード後に突然発生することが多いため、事前の十分な検証と長時間テストが重要です。

この問題は完全に防ぐことは難しいものの、適切な運用戦略と予防策を組み合わせることで、その影響を最小限に抑えることができます。

バージョン互換性の慎重な検証、定期的な再起動の自動化、リソースモニタリング、コード最適化、適切なアーキテクチャ設計など、複数のアプローチを組み合わせて、信頼性の高いGPU対応サービスを構築することが重要です。

特に本番環境へのデプロイ前には、必ず数日間の安定性テストを行い、長時間実行時の問題を事前に発見することが、予期せぬダウンタイムを防ぐ鍵となります。

このような対策を導入することで、ディープラーニングやコンピュータビジョンのサーバーアプリケーションを、より安定して運用することができるでしょう。

また、当社ではGPU商用サービスをご検討中または、商用サービス運用中でお悩みをお持ちの事業者様へのGPUテクニカルコンサルティング・アドバイザリーのご提供も行っておりますので、お気軽にご相談くださいませ。

Read more

システムとcondaのC++標準ライブラリ(libstdc++)のバージョン違い問題による事象と対処法解説

システムとcondaのC++標準ライブラリ(libstdc++)のバージョン違い問題による事象と対処法解説

こんにちは! 先日、dlibをつかったPythonアプリケーション(conda環境で動作する)作っていたところ、以下のようなエラーに遭遇しました。 ImportError: /home/mlu/anaconda3/envs/example_env/bin/../lib/libstdc++.so.6: version `GLIBCXX_3.4.32' not found (required by /home/mlu/anaconda3/envs/example_env/lib/python3.10/site-packages/_dlib_pybind11.cpython-310-x86_64-linux-gnu.so) 「dlib_pybind11モジュールがGLIBCXX_3.4.32を要求してるけど、みつからない!」という感じのエラーですね。

By Qualiteg プロダクト開発部
LLM推論基盤プロビジョニング講座 第2回 LLMサービスのリクエスト数を見積もる

LLM推論基盤プロビジョニング講座 第2回 LLMサービスのリクエスト数を見積もる

こんにちは! 今回はLLM推論基盤プロビジョニング講座 第2回です! STEP2 LLMサービスへのリクエスト数見積もり それでは、早速、LLM推論基盤プロビジョニングの第2ステップである「リクエスト数見積もり」の重要性と方法を解説いたします。 LLMサービスを構築する際に必要となるGPUノード数を適切に見積もるためには、まずサービスに対して想定されるリクエスト数を正確に予測する必要があります。 リクエスト数見積もりの基本的な考え方 LLMサービスへの想定リクエスト数から必要なGPUノード数を算出するプロセスは、サービス設計において非常に重要です。過小評価すればサービス品質が低下し、過大評価すれば無駄なコストが発生します。このバランスを適切に取るための基礎となるのがリクエスト数の見積もりです。 想定リクエスト数の諸元 リクエスト数を見積もるための5つの重要な要素(諸元)をみてみましょう。 1. DAU(Daily Active Users): 1日あたりの実際にサービスを利用するユーザー数です。これはサービスの規模を示す最も基本的な指標となります。 2. 1日

By Qualiteg コンサルティング
Zoom会議で肩が踊る?自動フレーミング映像安定化とAIによる性能向上の可能性

Zoom会議で肩が踊る?自動フレーミング映像安定化とAIによる性能向上の可能性

こんにちは! 本日は、自動フレーミング映像の安定化に関するアルゴリズム・ノウハウを解説いたします 第1章 問題の背景と目的 バストアップ映像を撮影する際、特にオンラインミーティングやYouTubeなどのトーク映像では、人物がうなずく、首を振るなどの自然な動作をした際に「首まわりや肩がフレーム内で上下に移動してしまう」という現象がしばしば起こります。これは、多くの場合カメラや撮影ソフトウェアが人物の「目や顔を画面中央に保とう」とする自動フレーミング機能の働きに起因します。 撮影対象の人物が頭を下げた際に、映像のフレーム全体が相対的に上方向へシフトし、その結果、本来動いていないはずの肩の部分が映像内で持ち上がっているように見えてしまう現象です。 本稿では、この問題を撮影後の後処理(ポストプロセッシング)のみを用いて、高速、高い精度かつロバストに解決する手法をご紹介します。 前半では、従来のCV(コンピュータービジョン)の手法を使い高速に処理する方法をご紹介します。後半では、AIを使用してより安定性の高い性能を実現する方法について考察します。 第2章 古典手法による肩の上下

By Qualiteg 研究部
LLM推論基盤プロビジョニング講座 第1回 基本概念と推論速度

LLM推論基盤プロビジョニング講座 第1回 基本概念と推論速度

こんにちは! 本日は LLMサービスの自社構築する際の推論基盤プロビジョニング、GPUプロビジョニングについて数回にわけて解説いたします。 はじめに LLMの進化に伴い、ChatGPTやClaudeといったパブリックなLLMの活用は企業においても急速に広がってきました。しかし先進的な企業はこれらの汎用LLMに加えて、「領域特化型」「ドメイン特化型」といった専用LLMの構築へと歩みを進めています。こうした動きの背景には、企業固有の専門知識への対応力強化と情報セキュリティの確保という二つの重要なニーズがあります。 一般的なパブリックLLMでは対応できない企業固有の専門知識や機密情報の取り扱いが必要なケースが増えているため、自社LLMの構築や自社サーバーでの運用を検討する企業が急増しています。特に金融、医療、製造、法務といった専門性の高い領域では、業界特化型の独自LLMが競争優位性をもたらすと認識されています。 しかし、業界特化型のLLMを自社で運用することは簡単ではありません。自社運用を決断した場合、まず最初に取り組むべきは適切な推論環境の整備です。オンプレミス環境を構築するに

By Qualiteg コンサルティング