Google Compute Engine(GCE)を利用して、CUDAが使えるdarknet YOLOv4環境を構築してみたので、その手順と使って感じたことなどをまとめました。
GCEを使ってみようと思った動機としては、先日自宅のMac OSにdarknet YOLOv4環境を構築してみたものの、Mac環境ではCUDAが使えず速度が全く出なかったので、どうしようかと考えた結果、仮想GPUが気軽に使えるGoogle Compute Engineを使ってみよう、となった経緯があります。
ちなみに、NVIDIAのGPUが使えるレンタルサーバーというのもあるみたいですが、初期費用が何十万円もするので、学習目的としてはさすがにナシでした。😂
Google Compute Engineとは
Google Compute Engine(GCE)はGoogleのクラウドサービスであるGoogle Cloud Platform(GCP)のうちの1つで、仮想マシンをGoogleクラウド上に作成することができます。
仮想プライベートサーバー(VPS)と概念的には似ていますが、一般的なVPSとは異なりGCEはGPUを活用することができるため、機械学習をする上で有利です。
Google Cloudには他にも、機械学習ソリューションとしてAI Platformなども提供しているのですが、今回はYOLOv4を動かすことが目的だったためOSまるごと好きに使えるGCEを選択しました。
一連の作業はLinux VM の使用に関するクイックスタートを参考にしています。
Google Cloudのアカウントがまだ有効になっていない方はこのあたりを見ながらアカウントを有効にするところから始める必要があります。
機械学習用のプロジェクトを作成
Googleさんも推奨していることですが、何か新しいことを試す時は、他のプロジェクトとは別でプロジェクトを作成しておき、試し終わったらプロジェクトごと削除する、といった運用にすることで意図しない課金リソースが残らなくて安心です。
ホーム>ダッシュボード>プロジェクトを作成
GPUリソースの割り当て上限を増やしておく
GPUを使用するGoogle Compute EngineのVMインスタンスを作成するためには、事前にGPUリソースの割り当て上限を増やしておく必要があります。デフォルトでは上限0になっていて、1つも使用することができないからです。
IAMと管理→割り当て
割り当てが必要なリソースは、「GPUs(all regions)」と「NVIDIA A100 GPUs」です。目的のリソースを見つけるために検索します。フィルタをクリックして「上限名」を選択します。
「GPU」と入力すると候補にいい感じで出てくるので、GPUs(all regions)を選択します。
「すべての割り当て量」>「グローバル」にチェックを入れる>「割り当てを編集」
と進んで、新しい上限を入力します。GPUが活用できるかどうかを試す目的だけでよければ1で十分ですが、googleからの承認に時間が掛かることがあるので、リスクと相談しながら大きめの数字を入れてもいいかもしれません。
「リクエストの説明」は目的や必要性について詳しく書いた方がいいかもしれません。僕の場合「machine learning用」と超簡潔に入力したせいか、googleからの承認がなかなか下りずサポートに連絡するハメになりました。何度かメールやりとりをしてようやく承認してもらうことができましたが、1週間以上かかってしまいました。。
同様に、「NVIDIA A100 GPUs」の割り当ても変更します。もしCompute Engineで異なるGPUを選択するのであれば、そちらのGPUリソースに対して行います。2021年4月24日時点でのCompute EngineデフォルトはNVIDIA A100 GPUです。
残念ながら日本のリージョンではGPUが使えないので、us-central1を使うことにしました。
割り当ての変更依頼を出した後、Googleから「承認するために最大2日ほどかかるよ」といった内容のメールが来ますが、その直後に「承認完了!」というメールも来ることもあります。僕の場合はリクエストの説明が不適切だったせいか、先述のとおり2日以上待っても承認されず、サポートに連絡するハメになりましたが。。
これでリソースの割り当て作業は完了です。これらの割り当てが完了していないとCompute Engine VMインスタンスを作成するときにエラーになります。
Compute EngineのVMインスタンスを作成する
IAMによるリソース割り当てが承認されたら、ようやくGoogle Compute Engine(GCE)のVMインスタンスを作成することができます。
ナビゲーションメニュー>コンピューティング>Compute Engine>VMインスタンス
と選択してVMインスタンス画面を表示します。
「インスタンスを作成リンク」をクリック
マシンの構成とOSをこんな感じで設定
- GPUの割り当て上限はus-central1リージョンに対して実施したのでそこに合わせる
- GPUのタイプはデフォルトのままNVIDIA Tesla A100
OSはDeep Learning Imageを使用。
作成ボタンを押すと、Compute EngineのVMインスタンスが作成されます。
Compute Engine VMインスタンスに接続する
こちらの接続ドキュメントを参照しながら接続しました。作成されたVMインスタンスの「SSH」をクリックします。
するとブラウザの新しいウィンドウでリモートのCUI画面が表示されます。
他にも接続方法として、sshコマンドを使って直接接続する方法や、gcloudというGCPコマンド経由で接続する方法などがあります。しかし上記のようにブラウザから接続する手順と比べると、次の理由からオススメしません。
- sshコマンドで直接接続:VM側・クライアント側でいくつか設定が必要(ややこしかったのでドキュメント読み飛ばした)
- gcloudコマンドで接続:gcloudをインストールする必要がある。ファイルのアップロードやダウンロードはgcloud compute scpコマンドを使う必要がある。
darknetをビルドする
前準備は終わりで、darknetをビルドしていきます。前準備が長かったように感じるかもしれませんが、ここまでにやったこととしては
- AIM設定でGPUリソースの割り当て上限を設定しておく
- Compute EngineのVMを作成する
- 接続する
だけです。複雑な手順も無かったので、次回からは一瞬で出来そうだという安心感があります。
依存ライブラリをインストールする
YOLOv4をインストールする前に、依存ライブラリをインストールしておきます。といっても、インストールが必要なのはopencvだけです。
instance-1:~$ sudo apt-get install libopencv-dev
darknetはcudaやcudnnなどにも依存しているので、本当ならこれらもインストールする必要があって、しかもここがかなりの難所らしいです(原因不明のエラーや、パッケージ間の複雑な依存関係、etc.)。しかしgoogleが用意してくれているDeep Learning ImageからVMを作成しているので、これらは最初からインストールされています。めっちゃありがたい。
darknetをダウンロード・ビルドする
依存ライブラリさえインストールされていれば、Linux上でのdarknetのビルドはWindowsと比べてはるかに簡単です。
How to compile on Linux (using make
)を参照しながら作業しました。
作業フォルダを作成して、darknetをダウンロード。
instance-1:~$ mkdir work
instance-1:~$ cd work
instance-1:~/work$ git clone https://github.com/AlexeyAB/darknet.git
instance-1:~/work/darknet$ cd darknet
Makefileを開いて、GPUとOpenCVを有効にして上書き保存します。
instance-1:~/work/darknet$ nano --linenumbers Makefile
GPU=1
CUDNN=1
CUDNN_HALF=1
OPENCV=1
# 他はデフォルト
# それと33行目・34行目あたりにある、GPUに対応した部分のコメントを解除
# Tesla A100 (GA100), DGX-A100, RTX 3080
ARCH= -gencode arch=compute_80,code=[sm_80,compute_80]
そしたら後は、makeするだけです!ドーン!
instance-1:~/work/darknet$ make
・・・いつものことですが、darknetのビルド中はwarningがてんこ盛りで表示されてハラハラします。😅
instance-1:~/work/darknet$ ls -l
total 4280
drwxr-xr-x 4 jima jima 4096 Apr 25 00:18 3rdparty
-rw-r--r-- 1 jima jima 22916 Apr 25 00:18 CMakeLists.txt
-rw-r--r-- 1 jima jima 1406 Apr 25 00:18 DarknetConfig.cmake.in
-rw-r--r-- 1 jima jima 515 Apr 25 00:18 LICENSE
-rw-r--r-- 1 jima jima 5936 Apr 25 00:23 Makefile
-rw-r--r-- 1 jima jima 61687 Apr 25 00:18 README.md
drwxr-xr-x 2 jima jima 4096 Apr 25 00:23 backup
drwxr-xr-x 3 jima jima 4096 Apr 25 00:18 build
-rwxr-xr-x 1 jima jima 13691 Apr 25 00:18 build.ps1
drwxr-xr-x 3 jima jima 4096 Apr 25 00:18 cfg
drwxr-xr-x 3 jima jima 4096 Apr 25 00:18 cmake
-rwxr-xr-x 1 jima jima 4149048 Apr 25 00:24 darknet
-rw-r--r-- 1 jima jima 10334 Apr 25 00:18 darknet.py
-rw-r--r-- 1 jima jima 9467 Apr 25 00:18 darknet_images.py
-rw-r--r-- 1 jima jima 5227 Apr 25 00:18 darknet_video.py
drwxr-xr-x 3 jima jima 4096 Apr 25 00:18 data
-rwxr-xr-x 1 jima jima 110 Apr 25 00:18 image_yolov3.sh
-rwxr-xr-x 1 jima jima 110 Apr 25 00:18 image_yolov4.sh
drwxr-xr-x 2 jima jima 4096 Apr 25 00:18 include
-rwxr-xr-x 1 jima jima 345 Apr 25 00:18 json_mjpeg_streams.sh
-rwxr-xr-x 1 jima jima 159 Apr 25 00:18 net_cam_v3.sh
-rwxr-xr-x 1 jima jima 159 Apr 25 00:18 net_cam_v4.sh
drwxr-xr-x 2 jima jima 4096 Apr 25 00:24 obj
drwxr-xr-x 2 jima jima 4096 Apr 25 00:18 results
drwxr-xr-x 4 jima jima 4096 Apr 25 00:18 scripts
drwxr-xr-x 2 jima jima 4096 Apr 25 00:18 src
-rw-r--r-- 1 jima jima 2609 Apr 25 00:18 vcpkg.json
-rwxr-xr-x 1 jima jima 108 Apr 25 00:18 video_yolov3.sh
-rwxr-xr-x 1 jima jima 108 Apr 25 00:18 video_yolov4.sh
darknetが作成されていればおそらく成功です。(出力内容を見てもぐちゃぐちゃでワケが分からないので)
CUDA、cuDNN、OpenCVともに有効になっています。
instance-1:~/work/darknet$ ./darknet -h
CUDA-version: 11000 (11000), cuDNN: 8.0.5, CUDNN_HALF=1, GPU count: 1
CUDNN_HALF=1
OpenCV version: 3.2.0
Not an option: -h
YOLOv4の動作確認をしてみる
実際に検出できるか確認してみましょう。CUI環境なので、検出結果を画像上で確認するためには結果をダウンロードして確認する必要があります。少々面倒ですね。
yolov4.weightsファイルをダウンロードしておきます。
instance-1:~/work/darknet$ wget
https://github.com/AlexeyAB/darknet/releases/download/darknet_yolo_v3_optimal/yolov4.weights
2021-04-25 00:37:00 (97.3 MB/s) - ‘yolov4.weights’ saved [257717640/257717640]
続いてAlexeyAB darknetのHow to use on the command lineを参考に、動作確認をします。
instance-1:~/work/darknet$ ./darknet detector test cfg/coco.data
cfg/yolov4.cfg yolov4.weights -dont_show -thresh 0.25 data/dog.jpg
Loading weights from yolov4.weights...
seen 64, trained: 32032 K-images (500 Kilo-batches_64)
Done! Loaded 162 layers from weights-file
Detection layer: 139 - type = 28
Detection layer: 150 - type = 28
Detection layer: 161 - type = 28
data/dog.jpg: Predicted in 590.037000 milli-seconds.
bicycle: 92%
dog: 98%
truck: 92%
pottedplant: 33%
やはりGPUを使うと590msという短時間で終わりますね!ちなみに同環境でGPU無効にしたビルドだと、19415msかかっていました。
-dont_showオプションを付けて実行しているのは、CUI環境で結果のプレビューが表示できないからです。検出結果が描画された画像はpredictions.jpgというファイル名で保存されているので、これを実際に見て確認する必要があります。
ブラウザから起動したCUI画面なら、右上の歯車アイコン(⚙)からダウンロードできます。
⚙>ファイルをダウンロード
ファイルはフルパスで指定しないと認識されません。ここ、ハマりどころです。フルパスはreadlinkコマンドの結果を貼るとすんなりです。
instance-1:~/work/darknet$ readlink -f predictions.jpg
/home/kendimaru2/work/darknet/predictions.jpg
predictions.jpgをダウンロードしてローカルで確認。
検出、できてますね!
ちなみにpredictions.jpgが生成されているかどうかだけでなく、内容もきちんと確認することをオススメします。
僕が実際に確認したところ、検出結果が反映されていないただの写真…といった状態のものがダウンロードされていました。
AlexeyAB darknetにあるyolov4.weightsファイルのダウンロードリンクのうち、Google-drive mirrorのリンクがNot foundになっていて、wgetコマンドを使ってダウンロードしたyolov4.weightsファイルが正しくないことに気づかないまま検出かけてしまったことが原因でした。ダウンロードの手間がかかるので確認を省きたい気持ちもわかるけど、確認省いたらダメネ!
性能を確認する
せっかくGPUを有効化した環境なので、darknetからどれくらいGPUを活用できているのか確認しておきます。
1000枚のdog.jpgに対して検出をかけてみる
方法として、安直ですがdog.jpgを1000ファイルくらいにコピー・増殖させたものをいっきに検出にかけてみます。
dog.jpgの1000個のコピーを作ります。
instance-1:~/work/darknet$ mkdir dogs
instance-1:~/work/darknet$ for i in {0001..1000}
> do
> cp data/dog.jpg dogs/$i.jpg
> echo $i.jpg >> dogs/files.txt
> done
ssh接続をもう一つ作成して、nvidia-smiコマンドでGPUの使用率を監視しておきます。-lオプションを使用すれば、断続的に状態を把握することができます。
instance-1:~$ nvidia-smi -l 2
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 450.80.02 Driver Version: 450.80.02 CUDA Version: 11.0 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|===============================+======================+======================|
| 0 A100-SXM4-40GB Off | 00000000:00:04.0 Off | 0 |
| N/A 32C P0 47W / 400W | 0MiB / 40537MiB | 0% Default |
| | | Disabled |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=============================================================================|
| No running processes found |
+-----------------------------------------------------------------------------+
darknetの画像検出コマンドを実行します。
instance-1:~/work/darknet$ cat dogs/files.txt | ./darknet detector test
cfg/coco.data cfg/yolov4.cfg yolov4.weights -dont_show -thresh 0.25
1000枚のdog.jpgを処理している状態のGPU使用率は、最高で64%ほどでした。
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 450.80.02 Driver Version: 450.80.02 CUDA Version: 11.0 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|===============================+======================+======================|
| 0 A100-SXM4-40GB Off | 00000000:00:04.0 Off | 0 |
| N/A 40C P0 150W / 400W | 2871MiB / 40537MiB | 64% Default |
| | | Disabled |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=============================================================================|
| 0 N/A N/A 4335 C ./darknet 2868MiB |
+-----------------------------------------------------------------------------+
検出にかかる時間は、1画像あたり10msとなっていて、この数字だけ見ると1秒間に100画像に対して処理ができる、つまり100FPSほどの速度が出ているということになります。
Enter Image Path: Cannot load image 0998.jpg
Detection layer: 139 - type = 28
Detection layer: 150 - type = 28
Detection layer: 161 - type = 28
0998.jpg: Predicted in 9.647000 milli-seconds.
Enter Image Path: Cannot load image 0999.jpg
Detection layer: 139 - type = 28
Detection layer: 150 - type = 28
Detection layer: 161 - type = 28
0999.jpg: Predicted in 9.665000 milli-seconds.
Enter Image Path: Cannot load image 1000.jpg
Detection layer: 139 - type = 28
Detection layer: 150 - type = 28
Detection layer: 161 - type = 28
1000.jpg: Predicted in 9.654000 milli-seconds.
dog.jpg × 1000フレーム分の動画に対して検出をかけてみる
ファイル1000枚だとディスクIOがボトルネックになっていてGPU使用率が7割弱しか使えなかった可能性があるので、今度は動画に対して検出処理をかけてみます。本当はカメラなどから直接入力したほうが高速になる気はしますが、クラウド上のマシンだとカメラ使えないので。
先ほど作成した0001.jpg〜1000.jpgを使って、ffmpegで動画を作成します。
instance-1:~/work/darknet$ ffmpeg -i dogs/%04d.jpg -framerate 1 dogs/movie.mp4
プレビューを表示しない-dont_show
オプション、代わりに結果をファイルに出力する-out_filename results/dogs.mp4
辺りが忘れがちポイント。
instance-1:~/work/darknet$ ./darknet detector demo
cfg/coco.data cfg/yolov4.cfg yolov4.weights -dont_show
-ext_output -out_filename results/dogs.mp4 dogs/movie.mp4
中略...
pottedplant: 35%(left_x: 681 top_y: 109 width: 37 height: 45)
dog: 98% (left_x: 128 top_y: 225 width: 184 height: 316)
truck: 92% (left_x: 464 top_y: 77 width: 220 height: 93)
bicycle: 92% (left_x: 114 top_y: 127 width: 457 height: 299)
FPS:95.7 AVG_FPS:94.7
cvWriteFrame
Objects:
pottedplant: 35%(left_x: 681 top_y: 109 width: 37 height: 45)
dog: 98% (left_x: 128 top_y: 225 width: 184 height: 316)
truck: 92% (left_x: 464 top_y: 77 width: 220 height: 93)
bicycle: 92% (left_x: 114 top_y: 127 width: 457 height: 299)
FPS:95.6 AVG_FPS:94.7
Stream closed.
cvWriteFrame
input video stream closed.
closing... closed!output_video_writer closed.
標準出力を見ると、IOを含めた全体で見ても100FPS近くの性能が出ているようです。ちなみにCUDAが使えないMac環境で同様の手順で試してみたところ、その速度はゼロFPSでした😇
その間のGPU使用率は85%ほどでした。GPUメモリ的にもまだまだ余裕がありそうな感じです。もっと高い解像度じゃないと本気出すまでもないゼって感じですね。
+-----------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=============================================================================|
| 0 N/A N/A 5152 C ./darknet 2868MiB |
+-----------------------------------------------------------------------------+
Sun Apr 25 05:16:24 2021
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 450.80.02 Driver Version: 450.80.02 CUDA Version: 11.0 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|===============================+======================+======================|
| 0 A100-SXM4-40GB Off | 00000000:00:04.0 Off | 0 |
| N/A 41C P0 175W / 400W | 2871MiB / 40537MiB | 85% Default |
| | | Disabled |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=============================================================================|
| 0 N/A N/A 5152 C ./darknet 2868MiB |
+-----------------------------------------------------------------------------+
余談ですが、作成されたresults/dogs.mp4をダウンロードして内容を確認したところ、面白い現象が見られました。
1000フレームとも同じ画像のはずなのに、前半と後半でcarとtruckの検出結果に若干の違いがありました。なぜだろう。
Compute Engineを停止する
一通り使い終わったら、Compute Engineの停止を忘れないようにしましょう。起動している間は継続的に課金が積み上がっていきます。ただ、停止しようとすると軽い脅しメッセージが出てきます。
色々と環境を苦労して作り上げた後に壊れたりするときっと発狂するので、念のため数回停止と起動を繰り返してみましたが、特に問題は起こりませんでした。きっと壊れてほしくない時に限って壊れるに違いない、うん。