COCOデータセットから必要なクラスを抽出して統合し、独自データを追加してYOLOの転移学習のために編集するのに苦労した

![カスタムh5_Truck_bus6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140663/0e4bd37e-8fd5-bc7c-5c72-81a5bd16b98d.jpeg)

# サマリー 
ここではYOLO系を使った画像や映像からの物体検出において、COCOデータを自分に都合よく編集して学習させた方法を解説します。具体的には以下の内容となります。
・COCOデータセットで元々クラス定義されているcar, bus, truckの3クラス分けが気に入らないのでbusとtruckを合体させて、SmallCar/LargeCarの2クラスとした
・COCOデータセットのcarの画像が日本の車両（軽四など日本独自の車種）判別向きではないのでデータを追加して判別できるようにした。
・自分の好きなように編集したCOCOデータセットを使って転移学習を行い、成果が出た。

# なぜCOCOデータに不満があるのか？ 
COCO Data Setは物体検出系の学習に多く用いられている優れた画像とアノテーションデータのセットです。まずはCOCOデータセットをあがめます。すごいです。公開していただき、ありがとうございます。
Yolov4で物体検出させると、元データが次の場合、
![Truck_bus6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140663/713705fb-b0bd-ea07-ac32-85301706e810.jpeg)
こんか感じでちゃんと物体検出してくれます。
![オリジナルh5_Truck_bus6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140663/9ad43107-6ff6-cf3e-7c4d-cab003c0a10f.jpeg)

が、不満があります。例えば自動車関連ではcar, bus, truckの3種類にクラスが分類されていますが、国土交通省の道路センサスでは「小型車両」「大型車両」の2種類に分けております。つまり[car, bus, truck]ではなく、[Smallcar, Largecar]と分けたいのです。上の画像では、画面左の黄色のトラックと画面中央の白いバスを両方ともLargecarと分類したいところです。

# もっともお手軽な方法
COCOデータセットを使って学習させたWeightファイル(Keras Yolo系ではh5ファイル）は、非常に優秀なので普通なら、手を入れる必要があまり感じられません。ただ今回のようにトラックとバスを「LargeCar(大型車両)」としてひとまとめにしたい場合、いくつかの方法があります。

一番ズルい方法はcoco_classes.txtとかの値を変えてしまうことです。
物体検出はIDの数値でしか返ってこないため、各IDに紐づくcoco_classes.txtを変えてしまうとbusとtruckを同じ表示にできる、という訳ですね。

例えばですが、
```
person
bicycle
car
motorbike
aeroplane
bus
train
truck
boat
traffic light
fire hydrant(以下略)
```
とあるのを、
```
person
bicycle
*smallcar
motorbike
aeroplane
*largecar
train
*largecar
boat
traffic light
fire hydrant(以下略)
```
とすればよいわけですね(*は変更箇所の意）。しかし結果はこんな風に出ます。
![cocoクラス名変更インチキ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140663/ad7488c2-4851-aeb0-0e60-19b7e1bc114e.png)

左の黄色いトラックと中央の白っぽいバスはいずれも「largecar」としてDetectされていますが、バウンディングボックスの色が違っていて、いやな感じです。ちょっと気持ち悪い。あと右端のバスのお尻が変な感じにDetectされています。
仕方ないので、大型バスと大型トラックを一緒にして、転移学習させようと考えました。

# COCOデータセットから必要なクラスのみ抽出する
ここではYolov4の学習向けに説明していますが、基本的にはCOCOデータセットを学習に使う場合はYOLOXでも同じ方式でいけます。
Yolov4の学習用として、COCOデータセットからクラスを抽出する方法についてはこちらを参考にさせていただきました。
[「COCOデータセットをダウンロードし，特定のクラスを抽出する」](https://www.hamlet-engineer.com/posts/object_detection01.html)
これは便利だ！クソ重たいCOCOデータセットから、必要なクラスと画像およびそのアノテーションデータのみ抜けるわけですね。ではcar, bus, truckの必要データのみさっさと抜いてみましょう。

もともとのCOCOデータセットのアノテーション情報()はこんな感じです。
```
"coco_url": "http://images.cocodataset.org/train2017/000000250108.jpg","height": 481,"width": 640,"date_captured": "2013-11-15 17:13:57","flickr_url": "http://farm1.staticflickr.com/18/92714041_ea63d5d400_z.jpg","id": 250108},{"license": 3,"file_name": "000000315601.jpg",
"coco_url": "http://images.cocodataset.org/train2017/000000315601.jpg","height": 424,"width": 640,"date_captured": "2013-11-15 17:35:04","flickr_url": "http://farm9.staticflickr.com/8092/8393450656_d03daf4b03_z.jpg","id": 315601},{"license": 1,"file_name": "000000437218.jpg",
"coco_url": "http://images.cocodataset.org/train2017/000000437218.jpg","height": 480,"width": 640,"date_captured": "2013-11-15 17:37:36","flickr_url": "http://farm2.staticflickr.com/1329/1017578715_853fd4bdab_z.jpg","id": 437218},{"license": 2,"file_name": "000000503707.jpg",
"coco_url": "http://images.cocodataset.org/train2017/000000503707.jpg","height": 427,"width": 640,"date_captured": "2013-11-15 17:55:21","flickr_url": "http://farm9.staticflickr.com/8340/8239634401_6079e1b043_z.jpg","id": 503707},{"license": 4,"file_name": "000000074331.jpg",
"coco_url": "http://images.cocodataset.org/train2017/000000074331.jpg","height": 640,"width": 427,"date_captured": "2013-11-15 18:38:36","flickr_url": "http://farm6.staticflickr.com/5287/5351312216_04d4d6dcae_z.jpg","id": 74331},{"license": 1,"file_name": "000000579664.jpg
```
・・・ちょっと何書いてるか、よく分かりませんね。COCOデータセットはすべてのクラスが1個のinstances_train2017.jsonファイルにまとまっているので、「自動車だけ物体検出したい」という人には不向きです。

なお上記で参考にしたWEBページのコードでは、4番目のmotorbikeクラスだけを抜き出して転移学習させていますが、例えば3番目のクラスであるcarで抜き出すとこんな感じになります。
```
keras_car\train\000000000064.jpg 52,387,228,545,0
keras_car\train\000000000071.jpg 467,216,495,231,0 522,223,547,235,0 436,217,460,226,0 586,231,613,245,0 497,223,520,235,0 601,216,618,222,0 617,237,640,250,0 479,220,503,232,0 387,200,403,205,0 366,196,386,204,0 348,198,368,203,0 499,219,519,225,0 460,217,479,228,0
keras_car\train\000000000094.jpg 356,275,399,313,0
keras_car\train\000000000149.jpg 278,311,288,317,0 311,316,321,321,0 295,316,303,320,0 256,321,275,328,0
keras_car\train\000000000247.jpg 55,186,141,221,0 514,179,549,197,0 478,175,509,198,0 544,172,552,178,0 533,171,541,178,0 503,169,519,177,0
```
このうち「,0」のところがクラスIDを示しています。つまり3番目のクラスであるcarで抜き出してもすべて3ではなく0になってしまいます。bus, truckで抜き出しても同様です。ただこれは利点でもあります。

要するにbus, truckのクラスで抽出したデータがすべて「,0」となっているので、これを「,1」にそれぞれ変換してテキストデータを合体させれば、もともとbusとtruckに分かれていたクラスを合体して「LargeCar」という単体のクラスとして転移学習できる、ということですね。

まとめると手順はこんな感じになります。
１）carで抽出したデータ　⇒　「,0(改行)」または「,0 」でTXT(1)ファイルとして出力される
２）busで抽出したデータ　⇒　「,0(改行)」または「,0 」でTXT(2)ファイルとして出力される
⇒TXT(2)ファイル内の「,0(改行)」または「,0 」をすべて「,1(改行)」・「,1 」に変換してTXT(2)'ファイルを作成する。
３）truckで抽出したデータ　⇒　「,0(改行)」または「,0 」でTXT(3)ファイルとして出力される
⇒TXT(3)ファイル内の「,0(改行)」または「,0 」をすべて「,1(改行)」・「,1 」に変換してTXT(3)'ファイルを作成する。
４）TXT(1)ファイル/TXT(2)'ファイル/TXT(3)'ファイルを合体させて、TXT(4)ファイルを作成する。

これでできたTXT(4)ファイルをTrainファイルとして使用すればよいです。同様にValファイルも作成します。
なおTXT(4)ファイルの画像のパスは絶対パスに一括置換するとより作業がしやすいです。

ところで私はWindowsで作業をしているのですが、
・そもそもCOCOデータセットがダウンロードできない(Chrome⇒Edgeにブラウザを切り替えてなんとかダウンロードできた）
・ファイル数が多すぎてハードディスクが壊れそうなので、外部ハードディスクを用意した
ので皆さんもご注意ください。COCOデータセットは解凍するとサイズは100GB近く、ファイル数も29万近くあります。同一フォルダに入れてサムネール表示したら、OSの制限次第では外部記憶装置が壊れてしまいますのでご注意ください。間違ってもＣドライブとかで起動ディスクにはCOCOデータセットを入れないようにしてください。

# COCOデータセットの画像がイケてないので修正する
COCOデータセットは素晴らしいのですが、たぶんアメリカで撮影した画像が多いようで、日本独自の軽自動車のボックスカーとか誤認識します。例えばこんな感じです。

**・検出前**
![Truck_bus2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140663/2a101cf5-fb0a-deac-b194-a478ab85eeee.jpeg)

**・検出結果**
![オリジナルh5_Truck_bus2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140663/bb2910da-e2c7-dcc6-dbf9-dcc8a793904e.jpeg)

手前のシルバーの軽ボックスカーが「truck」として検出されています。これはまずいですね。
そこでVottをつかって、「日本独自の車両」について「car」としてアノテーションしたものを1500件ほど用意します。これはVottからPascal VOC形式で出力したデータをYolov4などに同梱されているvoc_annotation.pyを使うと、前項目の「TXT(1)ファイル」と同じ形式で出力されるわけです。
これについて前に作成したファイルとクラスの表記（「,0(改行)」または「,0 」）が一致するよう気を付け、必要に応じて一括置換した後で先ほどのTXT(4)ファイルの中身に合体させればよいわけですね。

これによりできたTXT(4)ファイルは、
１）busとtruckクラスを合体させた
２）carクラスに日本独自の軽ボックスやワンボックスカーのデータを追加した
ものとなりました。もとのcarクラスの名称をSmallCar、busとtruckを合体させたものをLargeCarとして2クラスで転移学習してみます。

# 転移学習・通常学習の結果
学習させました。対象画像は22810となり、手元機材では1エポック当たり1時間半くらいかかったので待ちきれず、95エポックあたりで止めてしまいました・・・。lossが7.453とまだ高止まりしていますが、作成できたWeight(h5)ファイルをつかって物体検出したのが以下の結果です。

**・検出前画像(1)**
![Test_BusTrack05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140663/4b9181a8-4a8f-cbd2-8729-2b6add50f723.jpeg)

**・オリジナルのWeight(h5)ファイルによる検出結果(1)**
![オリジナルh5_Test_BusTrack05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140663/44065de8-5109-f750-b836-f276b63595df.jpeg)
なぜかクリーム色のタクシーがtruckとcarの2重判定されている。
また手前の屋根のはしごを積んだワンボックスカーもtruckとcarの2重判定されている。

**・転移学習させたWeight(h5)ファイルによる検出結果(1)**
![カスタムh5_Test_BusTrack05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140663/b1049eeb-4053-684d-3630-1dc1d2391abc.jpeg)
両方とも「SmallCar」として検出できている。

**・検出前画像(2)**
![Test_BusTrack04.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140663/579e19e1-5c82-4a48-427c-beaa03206267.jpeg)

**・オリジナルのWeight(h5)ファイルによる検出結果(2)**
![オリジナルh5_Test_BusTrack04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140663/598198a6-5086-b18a-cdc0-9efd6c54499a.png)
左側の軽のボックスカーがtruckとして検出されている。
またその下の白いボックスカーがcarとtruckの2重検出となっている。

**・転移学習させたWeight(h5)ファイルによる検出結果(2)**
![カスタムh5_Test_BusTrack04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140663/dbc80a89-ef99-f6e4-cf8f-7157a80a241e.png)
左側の軽のボックスカーは小型車(SmallCar)として検出されている。
しかしその下の白いボックスカーは大型車(LargeCar)として認識されている。lossが7.453と高止まりしていたので、学習が足りなかったかもしれない。

# まとめ
・検出スコアはオリジナルのWeight(h5)ファイルの方が高かったが、バスとトラックを同じLargeCarとして認識させる、という試みは成功した。
・軽トラや軽のボックスカー、ワンボックスカーなど日本独自の車両についてアノテーションデータを追加してSmallCarとしてDetectさせる手法も有効であると思われる。
・今回は実施しなかったが、SmallCarとLargeCarの判別精度を高めるには、既に登録されているCOCOデータの中から、不要なものを削除する工程を入れた方が良いかもしれない。
・それにしてもオリジナルのWeight(h5)ファイルは出来がいい。どんな環境（GPUボードとか）で何エポックくらい回したのか知りたいところ。

以上