Darknetの概要


DarknetはYoloの開発者が開発しているディープラーニングのフレームワークです。C++で記述されており、簡単にビルドすることができます。Yoloの歴史についてはYOLO9000: Better, Faster, Strongerの資料が詳しいです。

Darknetを使用してYoloの係数を学習するチュートリアルは以下が詳しいです。
How to train YOLOv2 to detect custom objects

Darknetのインストール


環境構築はMacかUbuntuが簡単です。まず、リポジトリをCloneします。

git clone https://github.com/AlexeyAB/darknet.git

GNUmakeを使用してmakeします。darknetのバイナリが生成されます。

make

nVidiaのGPUであればMakefileを書き換えることでCUDAを有効にすることができます。今回はAMDのGPUを搭載したMacなため、CPUで動かします。

学習させるデータの準備


学習させるデータをダウンロードします。上記記事のサンプルデータがテストには最適です。
https://timebutt.github.io/content/other/NFPA_dataset.zip

ただし、pos-234.jpgの拡張子が大文字のJPGになっているため、小文字のjpgに変換する必要があります。また、train.txtとtest.txtの改行コードをWindows StyleからLinux Styleに変換する必要があります。

データセットには、入力画像のjpgと、バウンディングボックスのtxtが含まれています。バウンディングボックスのtxtのフォーマットは以下のようになります。画像のどの位置にどのカテゴリのオブジェクトが存在するかが記載されています。同様の形式で、jpgとtxtのペアを準備することで、任意の画像で学習させることができます。

[category number] [object center in X] [object center in Y] [object width in X] [object width in Y]

YoloV2での学習


次に、obj.data、obj.names、yolo-obj.cfgを準備します。

obj.dataには学習のための基本情報が含まれます。上から、識別クラス数、入力画像のファイルリスト、評価画像のファイルリスト、クラス名称、係数の出力ディレクトリです。trainやvalidはdarknetのバイナリからの相対パスです。

classes=1
train=data/nfpa/train.txt
valid=data/nfpa/test.txt
names=data/nfpa/obj.names
backup=backup/

obj.namesにはカテゴリ名称が含まれます。

NFPA

yolo-obj.cfgにはYoloのネットワーク構成が含まれます。yolo-voc.cfgをコピーして、batchを64に、subdivisionsを8に、classesを識別させたいクラス数に書き換えます。最終段のfiltersを(classes + 5)*5に書き換えます。

[net]
# Testing
batch=64 #書き換える
subdivisions=8 #書き換える
# Training
# batch=64
# subdivisions=8
height=416
width=416
channels=3

省略...

[convolutional]
size=1
stride=1
pad=1
filters=30 #書き換える
activation=linear

[region]
anchors =  1.3221, 1.73145, 3.19275, 4.00944, 5.05587, 8.09892, 9.47112, 4.84053, 11.2364, 10.0071
bias_match=1
classes=1 #書き換える
coords=4
num=5
softmax=1
jitter=.3
rescore=1

省略...

収束を速めるため、初期係数列をダウンロードしておきます。
darknet19_448.conv.23

最後に、以下のコマンドで学習を行います。

./darknet detector train data/nfpa/obj.data data/nfpa/yolo-obj.cfg darknet19_448.conv.23

学習にはハイエンドのGPUで1時間かかるということで、CPUだけだと厳しい感はあります。
学習結果は以下のコマンドで確認します。

./darknet detector test data/nfpa/obj.data data/nfpa/yolo-obj.cfg yolo-obj1000.weights testimage.jpg

学習済みのTiny-Yoloを動かしてみるには、ofxDarknetからtiny-yolo-voc.weightsをダウンロードした後、以下のコマンドで確認します。

./darknet detector test cfg/voc.data cfg/tiny-yolo-voc.cfg tiny-yolo-voc.weights data/person.jpg

上記ページのCOCO版のtiny-yolo.weightは学習時のcfgが異なるためか、正しく動作しないため、digitalbrain79/pyyoloのウエイトファイルを使用します。

./darknet detector test cfg/coco.data cfg/tiny-yolo.cfg tiny-yolo.weights data/person.jpg

YoloV1での学習


係数をCaffemodelに変換するには、pytorch-caffe-darknet-convertを使用します。変換にはCaffeのInstallが必要です。

python darknet2caffe.py tiny-yolo.cfg tiny-yolo.weights out.prototxt out.caffemodel

ただし、YoloV2にはCaffeではサポートされていないRegion Layerが含まれています。そのため、Caffeで動かす場合は、cfg/yolov1を使用する必要があります。Tiny-Cocoのweightsはyolov1のページからダウンロードできます。

動作テスト
./darknet coco test cfg/yolov1/tiny-coco.cfg tiny-coco.weights data/giraffe.jpg

Caffeモデルへの変換
python darknet2caffe.py tiny-coco.cfg tiny-coco.weights out.prototxt out.caffemodel

Yolov1で学習するには以下のコマンドを使用します。使用する画像の設定はsrc/yolo.cで行う必要があります。(You Only Look Once:Unified, 統合されたリアルタイムオブジェクトの検出

char *train_images = "/home/pjreddie/data/voc/test/train.txt";
char *backup_directory = "/home/pjreddie/backup/";

cfgのclassesとconnectedのoutputを書き換えます。outputはcfgのside*side*((coords+1)*n+classes)です。nfpaの1カテゴリでtiny-yoloを使用する場合は539になります。

VOCの学習
./darknet yolo train cfg/yolov1/yolo.train.cfg

yolo.cfgとyolo.train.cfgの違いはbatchとsubdivisionsだけです。

nfpaの学習
./darknet yolo train cfg/yolov1/tiny-yolo-nfpa.cfg

学習はMac Pro 2013だとシングルスレッドで24時間で200 Epoc、OpenMPで12時間で1400Epocでした。GTX1070を使えば、1時間で2000Epoc回すことができました。Darknetで2クラスの物体をトレーニングによると8000Epoc近く回す必要があるみたいなので、CPUで8000Epoc回すには3日程度必要です。GPUだと4時間程度で終わります。

クラス数1の学習結果のテストを行うにはyolo.cのdraw_detectionsの最後の引数の20をl.classesに書き換える必要があります。

学習結果のテスト
./darknet yolo test cfg/yolov1/tiny-yolo-nfpa.cfg backup/tiny-yolo-nfpa_200.weights data/nfpa/pos-1.jpg

500Epoc
500
1000Epoc
1000
2000Epoc
2000

2000 Epocまでいけばわりと認識できるようです。

(補足)Caffeのインストール


darknet2caffe.pyの実行にはCaffeが必要です。CaffeのインストールにはMakefile.configに以下の設定が必要でした。PYTHON_LIBのパスが合ってないと、import CaffeでSegmentation Faultが起きるようです。リンカフラグへのlibstdc++の設定は不要でした。

CPU_ONLY := 1
OPENCV_VERSION := 3
WITH_PYTHON_LAYER := 1
PYTHON_LIB :=  /usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/
INCLUDE_DIRS += /usr/local/Cellar/boost@1.59/1.59.0/include /usr/local/Cellar/boost-python@1.59/1.59.0/include
LIBRARY_DIRS += /usr/local/Cellar/boost@1.59/1.59.0/lib /usr/local/Cellar/boost-python@1.59/1.59.0/lib
INCLUDE_DIRS += /usr/local/lib/python2.7/site-packages/numpy/core/include

(補足)Darknetのマルチスレッド化


Darknetはデフォルトでは1スレッドで動作します。CPUでマルチスレッド化するには、OpenMPを有効化します。XcodeのデフォルトではOpenMPが使用できないため、最新のLLVMをインストールします。
brew install llvm

Makefileを書き換えます。
OPENMP=1
CC=/usr/local/opt/llvm/bin/clang
CPP=/usr/local/opt/llvm/bin/clang++
LDFLAGS+= -L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib