可変ブログ

色々メモとか。とりあえず自分が分かるように。その後、なるべく人が見て分かるように。可変。

YOLOv2を使って自前のデータを学習させて認識させるまで。

精度、処理速度がいいと噂のYOLOv2を使って自分が検出させたいものを学習させます。
自分も試しながら書いていったので、きれいにまとまっていなくて分かりにくいです。そのうちもっとわかりやすくまとめたいですねー。
ほぼこちらにURLに書かれている通りです。英語が読めるならこちらの方を参考にしたほうがいいです。



環境

  • Ubuntu14.04
  • OpenCV2
  • Quadro6000



流れ

  • CUDAをインストールする。
  • OpenCVをインストールする。
  • YOLOv2をインストールして、とりあえずYOLOを試す。
  • 学習させるデータを用意して正解データを作る。
  • YOLO用に学習データを変換する。
  • 学習パラメータとか諸設定。
  • 学習させる。
  • 認識させる。



CUDAをインストールする。

CPU環境でも一応動くのですが、webカメラなど使って検出させると重すぎてまともに動かないのでGPU環境を使うことをお勧めします。
CUDAのインストールは下記の記事の『CUDAをインストールする。(Nvidiaドライバのインストール含む。)』の項目を参考にしてください。ついでにcuDNNもインストールするとよいでしょう。



OpenCVをインストールする。

実はYOLOを使うだけならOpenCVは必要ありません。
ただし検出結果を画像や映像として出力するためにはOpenCVが必要なります。
OpenCV3~は対応していないようで、OpenCV2系をインストールする必要があります。
インストール方法は下記の記事を参考にしてください。下記の記事はOpenCV3系をインストールしているので適宜OpenCV2系と読み替えてください。
今はOpenCV3に対応してるっぽいです。



YOLOv2をインストールして、とりあえずYOLOを試す。

YOLOv2をインストールして、ちゃんとインストールできたかの確認の為とりあえず試してみます。
gitからYOLOv2のソースをcloneします。

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

cloneしたディレクトリに移動してMakefileを編集します。

cd darknet
gedit Makefile

最初の数行にある、GPU=0とOPENCV=0を1に書き換えます。
保存したら、

make

サンプル画像で検出を試す。

./darknet detect cfg/yolo.cfg yolo.weights data/dog.jpg

webカメラから

./darknet detector demo cfg/coco.data cfg/yolo.cfg yolo.weights



学習させるデータを用意して正解データを作る。

【学習させるデータの形式】

まずは学習させるデータの形式を説明します。
YOLOで学習させるデータは主に4種類あります。

  • 画像データ(認識させたいものが写っているもの)

拡張子はjpg等(拡張子によって学習データ作成ツールのソースを変更する必要があります。JPEGならソース変更少なく済みます。今回はjpg想定で進めます)です。

  • 座標データ

画像の中のどこに認識させたいものが写っているかの座標が記載されたテキストデータで、拡張子はtxtです。
画像と座標データはそれぞれ1対1で対応しています。
つまり、1つの画像データ(example01.jpg)に対して1つのテキストデータ(example01.txt)が必要であり、
拡張子より前の部分は同じファイル名でなくてはなりません。

  • 画像リスト

学習させる画像のファイル名のリスト(ファイルのパス含む)になります。これも拡張子はtxtです。
このリストはテストデータ用と訓練データ用に2つに分けます。

  • クラスリスト

クラスのリストです。犬や猫といった認識させるものを列挙したファイルで、拡張子はnames(もしくはlabels)です。


次に、それぞれのデータの書式についてです。

  • 画像データ

画像についてはjpg形式のデータとなり、特に説明もいらないでしょう。サイズについては特にバラバラでも問題ないと思います。

  • 座標データ

2つ目の座標のデータの書式ですが、以下のようになります。

カテゴリ番号 オブジェクトの中心x座標 オブジェクトの中心y座標 オブジェクトの幅 オブジェクトの高さ

カテゴリ番号は0から始まる整数数字で、次に示す座標が何のクラス(カテゴリ)に所属しているかを表します。(例えば、犬:0、猫:1、人間:2など)
いくつが何のクラスを表すかは後ほど別の設定ファイルに書き込みます。
また、それぞれの座標については画像の幅、高さを1としたときの割合となっています。
例として、画像の左上の方にクラス番号1としたものが画像の10分の1くらいの大きさで写っている場合、

1 0.2 0.2 0.1 0.1

といった風になります。もし1枚の画像中に複数のオブジェクトが写っている場合は、

1 0.2 0.2 0.1 0.1
2 0.8 0.4 0.1 0.3
0 0.3 0.3 0.2 0.1
    .
    .
    .

というように、2行3行と連なっていきます。

  • 画像リスト

画像のファイル名のリストになりますが、これは、

data/dog/dog00.jpg
data/dog/dog01.jpg
data/dog/dog02.jpg
    .
    .
    .

というような画像の所在を含めたファイル名のリストになります。
この時の相対パスはdarknetのバイナリからの相対パスになります。

  • クラスリスト

犬、猫といったクラス名を一行に一つずつ書いていきます。

dog
cat
bird
  .
  .
  .

このクラスリストの一行目が座標データに使われているカテゴリ番号の0に対応しています。

これで学習させるデータの形式についての説明は終わりになります。

【学習させるデータの作り方】

では、実際に学習させるデータを作っていきます。
コツコツと自分でピクセルを測ってデータを作ってもいいのですが、非常に面倒なのでツールを使います。
BBox-Label-Toolとconvert.pyというツールを使うことで楽に学習データを作れます。(楽と言っても大量のデータを作るのはやはり大変ですけどね)

まず依存関係をインストールします。(pipとかpyenvとか仮想環境使って入れたほうがいいかもしれません。)

sudo apt-get install python-tk python3-tk python-imaging-tk

BBox-Label-Toolのリポジトリからソースをクローンします。

git clone https://github.com/puzzledqs/BBox-Label-Tool.git

クローンしたディレクトリの中に、Images/001とLabels/001というディレクトリができていると思います。
このImages/001の中に入力となる画像を入れておき、ツールによって生成された座標のテキストデータがLabels/001の中に生成されます。
この時に注意して欲しいのは、画像の拡張子が大文字の『JPEG』でないとこのツールが認識してくれないという点です。
ですので『jpg』で認識するように、main.pyの中に記載されている『JPEG』をすべて『jpg』に置換してください。(たぶん3か所くらい置換するところがあると思います。)
また、このツールで作れるデータはオブジェクトのクラス数が1つの場合のみです。
multi-classというブランチを使えば複数のクラスに対応できるみたいですが、この後に使うconvert.pyツールがたぶん対応してないです。(何かいい方法知っている方がいましたら教えてください。)

では、実際の使い方ですが、
main.pyをpython2で実行します。python2系じゃないと対応してないみたいなので注意してください。

python main.py

すると、ウィンドウが立ち上がると思います。
上のImageDir欄にImagesディレクトリ内のディレクトリ、デフォルト状態なら『001』を記入してLoadを押すと001内の画像が読み込まれると思います。
読み込まれたらあとは対象となる物体をクリックで囲んでいってください。あとの使い方は感覚でわかると思います。
この作業が終わるとLabels/001に物体の座標情報のテキストが出力されていると思います。


YOLO用に学習データを変換する。

実は見てもらうと分かると思いますが、BBox-Label-Toolで作ったデータはYOLOで使える形式とは違った形式になっています。
これをYOLOの形式に変換するためconvert.pyというものを使います。

ここからコードをコピーしてconvert.pyというファイル名で保存してください。
そしたらこのconvert.pyを少し編集します。
このツールも画像の拡張子が大文字の『JPEG』でないと認識してくれないので、
・『JPEG』と書かれているところをすべて『jpg』に書き換えてください。(たぶん2か所)
また、改行コードが一か所Windows用になっている場所がありますので、(ここハマりポイントです。後に説明しますが他から既存のデータを持ってきたときは注意)
・『\r\n』と書かれているところをすべて『\n』に書き換えてください。(たぶん1か所)

次に『images』と『labels』という名前のディレクトリをconvert.pyがあるディレクトリに作ります。
labelsの中に『stopsign』と『stopsign_original』という名前のディレクトリを作ります。
そしたらimagesの中に画像のデータを、stopsign_originalの中にBBox-Label-Toolで作ったテキストデータを入れます。
ちょっとわかりにくいかもしませんがこういう感じです。

convert.py

images
  |-dog00.jpg
  |-dog01.jpg
  |-dog02.jpg

labels
  |-stopsign
  |-stopsign_original
      |-dog00.txt
      |-dog01.txt
      |-dog02.txt


準備ができたら、データを変換します。

python convert.py

stopsignの中に変換後のデータが生成されます。
また、convert.pyと同じディレクトリに画像のリストがテキストで生成されますが、今回はこれは利用しません。
学習させる画像はパスの指定がちゃんと正しければどこに置いても構わないのですが、今回は便宜上darknetディレクトリのdataの中にディレクトリを作ってそこに入れたいと思います。つまり、convert.pyで生成されたリストに載っているパスとは違うところに画像を保管するためにconvert.pyで生成されたリストは使えません。
では、学習させる画像を正しい場所に移して、リストを作りましょう。
『darknet/data/』にimagesというディレクトリを作って画像を入れます。

ここからprocess.pyというpythonスクリプトのコードをコピーして必要ならば編集して、imagesの中に保存します。
path_dataはdarknetのバイナリから見た、画像が入っているディレクトリまでの相対パスです。つまりこの場合は『data/images/』です。(最後の『/』忘れずに)
percentage_testはテストデータの割合%です。
画像の拡張子がjpgじゃない人は適宜合わせましょう。
編集が終わったら、スクリプトを実行します。これをすると、自動的に指定した割合でランダムにテストデータと訓練データに分けてリストを作ってくれます。

python process.py

すると、test.txtとtrain.txtが生成されると思います。


最後にクラスリストを作ります。
今回はクラスは1種類なので

dog

とだけ書いて、obj.namesとして保存します。

それでは今まで作ったものを適切な場所に配置しましょう。
画像とprocess.pyで作ったtest.txtとtrain.txtはもう入っていると思いますが、darknet/data/images/の中に、
convert.pyで作った座標のテキストファイルも、画像が入っているdarknet/data/images/の中にいれます。
obj.namesもdarknet/data/images/の中にいれます。


【手っ取り早く学習を試してみたい人へ】

こちらにYOLO用に用意された学習データがあります。
The data set I composed for this article can be found here (19.4Mb).
と書かれたところのリンクからダウンロードできますが、一つ注意があります。
ここからダウンロードできるデータは改行コードがWindows用(\r\n)となっています。
そのため、いざYOLOで学習をさせようとした時にエラーが出ますので改行コードをLinux用(\n)に変更する必要があります。
変更の仕方は以下参照ください。



学習パラメータとか諸設定。

パラメータとかを設定するファイルを用意します。
設定に必要なファイルは三つ。

cfg/obj.data        クラスの数やデータの所在等を指定しています。
cfg/yolo-obj.cfg    ニューラルネットモデルです。これはもとから用意されているものを少しいじって利用します。

obj.dataの書式は、

classes=1                          クラス数。今回は1種類なので1
train = data/images/train.txt      訓練用の画像リスト
valid = data/images/test.txt       テスト用の画像リスト
labels = data/images/obj.names     クラスリスト
backup = backup/                   学習した重みが保存される場所。
top = 2                            よくわかりません。なくてもあまり問題なさそうです。


cfg/yolo-obj.cfgはもとからcfgディレクトリの中にある.cfgファイルをコピーしてちょっと編集してyolo-obj.cfgとします。今回はyolo-voc.cfgファイルをコピーして使いますが、他のモデル(tiny-yolo.cfg等)が使いたかったら適宜変えてください。
コピーしてリネームしたyolo-obj.cfgを少し編集します。
3行目:batch=64 にします。学習ステップごとに使い画像の枚数です。
4行目:subdivisions=8 にします。バッチが8で除算されます。強力なGPUを積んでればこれを減らすか、バッチを増やすことができます。
244行目:classes=1 にします。クラス数です。
237行目:filters=30 にします。フィルターの大きさです。filters=(classes + 5) * 5 と決まっています?


学習させる。

必要なものはそろったので学習させます。
重みの初期値として適切なものがあると学習がうまくいきやすいです。
ここからdarknet19_448.comv.23をダウンロードし、初期値として使用します。


学習させます。

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

うまくいけば数字がずらずら出てくると思います。
大事なのは何行かに一回出てくるこんな感じの数字、

2: 2.950644, 15.939886 avg, 0.001000 rate, 2.813000 seconds, 128 images

最初の数字が反復回数です。通常2000回は必要みたいです。
そしてavgの手前の数字がが小なればなるほど精度が高まっていると考えてよいです。
avgの数字が減らなくなってきたら学習を止めましょう。
反復1000回までは100回ごとに重みがそれぞれ保存されていき、1000を超えると1000ごとに最新の重みが更新されます。
学習の早期終了についてはこちら。
GitHub - AlexeyAB/darknet: YOLOv4 (v3/v2) - Windows and Linux version of Darknet Neural Networks for object detection (Tensor Cores are used)
途中で間違って学習を止めてしまった場合でも、途中まで保存された重みを初期値として再度学習すれば続きを学習できます。

認識させる。

backup/にある重みを使って認識させる。
使う重みは自分で選んで、認識させる画像は自分で用意してください。

./darknet detector test cfg/obj.data cfg/yolo-obj.cfg yolo-obj.backup data/testimage.jpg



ちなみに

Windowsでやりたい場合はこのリポジトリからできるっぽいです。
しかもこれを使えばYOLOをAPI的に使えるっぽいです。
できたらAPI的な使い方についても今度記事にしたいですね。
GitHub - AlexeyAB/darknet: YOLOv4 (v3/v2) - Windows and Linux version of Darknet Neural Networks for object detection (Tensor Cores are used)