DarknetはCで書かれたディープラーニングフレームワークである。物体検出のYOLOというネットワークの著者実装がDarknet上で行われている。
もともとはLinux等で動かすもののようだが、ありがたいことにWindowsでコンパイルできるようにしたフォークが存在している:
github.com
これを利用してWindowsで動かしてみる。
事前準備
Visual Studio 2017 Community
まずVisual Studio 2017 Communityをインストール。
プロジェクトがVisual Studio 2015のものなので、v140ツールセットも入れる。インストーラを起動し、「変更」を押し、右側のペインの「C++によるデスクトップ開発」の下のオプションから「デスクトップ用VC++ 2015.3 v14.00 (v140) ツールセット」にチェックを入れ、「変更」を押してインストール。
コンパイル
ここからソースコード一式をダウンロードしてくる。ReleasesからYolo_v3のタグがついたものをダウンロードしてきたが、git cloneしても問題ないはず。
ダウンロードしてきたものを展開し、build/darknet/darknet.slnをVisual Studio 2017 (Community)で開く。
開いた際にツールセットを変換するか聞かれたのだが変換しないようにした。(これはどうするのが最適なのか不明)
ソリューション構成・プラットフォームの変更
構成: Release, プラットフォーム: x64に変更。
カスタム ビルド ツールの指定
プロジェクト名(darknet)を右クリックして「ビルドの依存関係」→「ビルドのカスタマイズ」を開き、「CUDA 9.1」のチェックを外し、代りに「CUDA 9.2」をオンにする。
試す
ちゃんと動くか確認する。
YOLOのページ(https://pjreddie.com/darknet/yolo/)からYOLOv3のweightsファイルをダウンロードしてきてbuild\darknet\x64
以下に入れる。
コマンドプロンプトでbuild\darknet\x64
を開き、
darknet.exe detector test data/coco.data yolov3.cfg yolov3.weights -i 0 -thresh 0.25 dog.jpg
を実行する。これはdarknet_yolo_v3.cmd
の中身でもある。認識がうまくいくと次のような認識結果が表示される。
トレーニング準備
トレーニングについては
に詳しく書いてある。
また、データセットの見本として、
のページの「The data set I composed for this article can be found here (19.4Mb).」と書かれているところからダウンロードできるデータを参考にできる。(これをそのまま使う場合、改行コードがWindows用\r\nになっているため変換が必要になるかもしれないらしい)
今回はイラストと顔の領域を指定したデータを用意し、イラストの顔検出をやってみる。
学習データを用意
アノテーションはLabelImg等を使うと楽。形式が異なっている場合は適宜変換する。
学習データは、画像ファイル(.jpg)群*1と(拡張子以外)同名のテキストファイル(.txt)を用意する。画像ファイルとテキストファイルは同一のディレクトリに入れる。テキストファイルはアノテーションを定義する。
アノテーションの形式は、
<クラスNo.> <領域の中心のx座標> <領域の中心のy座標> <領域の幅> <領域の高さ>
である。クラスNo.は.namesファイルで定義したものに対応する番号である(0オリジン)。今回はクラスが1つだけの場合を考えるのでクラスNoは必ず0になる。
座標や幅・高さは左上を(0.0, 0.0), 右下を(1.0, 1.0)とする割合で示す。2,3番目の座標は領域の中心の座標であることに注意。一行に一つの領域をかき、領域が複数になる場合は同様の内容を複数行書くことになる。
一例をあげると
0 0.2 0.2 0.1 0.1
は0番目のクラス、領域の中心は画像の左から2/10、上から2/10の位置で、幅が画像の1/10、高さが画像の1/10となる。
画像ファイルのリスト
学習用(train.txt
)とテスト用(test.txt
)に、画像ファイルの一覧を書いたファイルを用意する。darknet.exe
からの相対パスを一行一ファイルとして羅列したものとなる。画像ファイル群を適当な割合で分割して学習用とテスト用にする。今回は1000個弱の画像ファイルを学習:テスト=9:1に分けた。
画像ファイルは最終的にbuild\darknet\x64\data\obj
以下に入れるとすると、train.txt
やtest.txt
は次のような内容になる。
data/obj/00001.jpg data/obj/00002.jpg data/obj/00003.jpg
これらをbuild\darknet\x64\data\obj\train.txt
, build\darknet\x64\data\obj\test.txt
に用意した。
クラス名の設定
.names
ファイルがクラス名の設定である。一行に一つのクラスを書く。今回は一つのクラスだけにするので、obj.names
ファイルを
face
とだけ書いて保存する。
.dataファイルの設定
classes= 1 train = data/obj/train.txt valid = data/obj/test.txt #difficult = data/difficult_2007_test.txt names = data/obj.names backup = backup/
クラス数を1に変更、train
とvalid
に先ほど用意したtrain.txt
, test.txt
を指定。
クラスの名前を書いたファイルをnames
に指定。backupはトレーニング結果の重みの出力先となる。
cfgファイルの設定
yolov3.cfg
をコピーしてyolov3-obj.cfg
とし、編集する。
max_batches
は何回繰り返すかを指定する。今回は10000とした(トレーニングに丸1日程度かかった)。
GPUのメモリが6GBと少ないのでsubdivisionを増やす。メモリが十分ある場合は小さい値(8や16)で問題ないが、メモリ関係のエラーで止まる場合は32, 64と値を増やすといいらしい。
subdivision=16
→subdivision=32
クラスの数に合わせて、3ヵ所について次の変更を施す。filters
はfilters=(クラス数+5)×3で計算し、クラスが1つの場合は18となる。
classes=80
→classes=1
filters=255
→filters=18
トレーニング
ネットワークの重みの初期値としてdarknet53.conv.74
をダウンロードしてきて使う。
これをbuild\darknet\x64
に入れる。
build\darknet\x64
に移動して、次のコマンドを実行する。
darknet.exe detector train data/obj.data yolov3-obj.cfg darknet53.conv.74
lossが下がっていくのを眺めながら、トレーニングが終了するまで待つ(あるいは適当なところで終了する)。
トレーニング途中でdarknetが終了してしまう場合
darknet.exe
と同じディレクトリにbad.list
というファイルが生成され、問題となった画像ファイルが追記されるようだ。このファイルを用意したtrain.txt
やtest.txt
から取り除いてトレーニングし直すといいっぽい。これを学習が止まらなくなるまで繰り返す。
結果の確認
トレーニング結果は
darknet.exe detector test data/obj.data yolov3-obj.cfg backup/yolov3-obj_final.weghts -i 0
で確認できる。動画だと
darknet.exe detector test data/obj.data yolov3-obj.cfg backup/yolov3-obj_final.weghts moviefile.mp4 -i 0 -out_filename result.avi
とか。
OpenCV/Pythonで動かす
最新のOpenCVにはDNNモジュールがあり、darknetのネットワークも利用できる。
ただし、YOLOv3(内部で利用しているshortcutレイヤ)を使うためにはOpenCV 3.4.2より前のバージョンでは対応していないので、最新版をインストールする必要がある。Python版はpip install opencv-python
などで入れられる。
OpenCV公式のsamples/dnn/object_detection.pyにDNNモジュールによる物体検出のサンプルがあるので、これ改変して動くものをつくった。
備考
入力に与えたい画像と、ネットワークの受け付ける入力の形が異なるので、cv2.dnn.blobFromImage関数を利用してデータの橋渡しをしてやる必要がある、ということのようだ。.cfgファイルがネットワークを定義しているので、入力画像の大きさについてもここに定義されている(今回は(416, 416))。
また、スケーリングを行い入力が0.0~1.0の範囲になるようにしているようだ。
OpenCVがBGRなので、(BGRでなく)RGB順を使うよう指定している。
動作画面
まあ動く。
参考サイト
*1:.jpg以外の形式も使えるかは確認してない。