DenseNetの軽量版、PeleeNetをKerasで実装した

物体検出で軽量なモデルとして有効という話のPeleeNetをKerasで実装してみました。CIFAR-10で確かめた結果、簡単なData Augmentationで95%近い精度が出せることが確認できました。

## 元論文
R. J. Wang, X. Li, C. X. Ling. *Pelee: A Real-Time Object Detection System on Mobile Devices*. NIPS. 2018.
[https://arxiv.org/abs/1804.06882](https://arxiv.org/abs/1804.06882)

論文の解説とか
https://qiita.com/saneatsu/items/b372a2d71a33616f7355

図だけ読んで適当にやったら実装できたので、読み飛ばしている部分があるかもしれません。

## コード
[オリジナルのCaffe](https://github.com/Robert-JunWang/Pelee)をもとに自分がKerasに実装したものです。

**https://github.com/koshian2/PeleeNet-Keras**

## あらまし
PeleeNetと呼ばれる**DenseNetベースの軽量化なモデル**。係数が2.8Mと少ないながら、ImageNetでMobileNetV2以上の精度を出しています。PeleeNetがTop1:72.6%、MobileNetV2がTop1:72.0%。

![article_pelee_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/568798c9-2a92-5be2-2aa2-62a928159666.png)

このような構造。Stem BlockとDense Block, Transition Layerで構成されます。

StemBlockは入力のダウンサンプリング用のブロック。DenseBlockとはDenseNetの「知識の積み重ね、集約」といったConcatで少しずつチャンネル数が増えていく構造のブロック。Transition Layerとは全体に1x1Convを掛けてダウンサンプリングするレイヤー。ここらへんの発想はDenseNetと同じです。

![2019-06-17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/f9aefa98-754f-8309-111d-4965cb6da6a8.png)

この図は[DenseNetの論文](https://arxiv.org/pdf/1608.06993.pdf)より

## Stem Block
入力のダウンサンプリング用のブロック。

![article_pelee_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/22c1c67d-4d08-5ec8-d328-3b8000a9a5a1.png)

224→56へのダウンサンプリングですが、この構造がかなり精度に効いているようです。他のモデルに使いまわしできるのではないかと思います。論文内のImageNetの実験でも、StemBlockの有無でTop1精度が75.8％→76.8％と1％向上。

## Dense Layer
PeleeNetでは右図の「2-way dense layer」というのを使っています。左図はDenseNetのオリジナルで使われているDense Block。

![article_pelee_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/c6e90e7a-c298-d398-ce9e-0f6e4a08904f.png)

この図だと若干混乱しやすいのですが、Filter concatenateでは「Previous layer」と「左側の3x3 k/2, conv」「右側の3x3, k/2, conv」の3つのレイヤーを連結します。ここで**kはDenseNetにおける成長率（growth rate）で、実際の結合後のレイヤーの数とは異なります**。PeleeNetでは成長率は32を使っています。詳細はコード参照。

## MobileNetとの違い
「軽量で精度の出るモデルならMobileNetでいいじゃん」という疑問はもっともなのですが、PeleeNetの論文いわく、「MobileNetで使っているDepthwiseConvやSeparableConvはFP16だと高速化されない」とのこと。普段FP32のKerasでやってると「そんなことはないじゃん。DepthwiseやSeparableははええよ」と言いたくなるのですが、FP16で推論のような特殊な事情だとそうなのでしょう。

そのかわりに、PeleeNetでの高速化のポイントは、**1x1畳み込みを非常に多く使って理論的な計算量を減らしている**こと。これ自体はResNet派生やGoogLeNet時代からあるので、比較的使い古された手法です。やっていることはただのConv2Dなので、FP16でも対応できるはずです。

## 実験
CIFAR-10で以下の4パターンを計測しました。詳細な条件はコード見てください。

* Data Augmentationあり / Stem blockあり
* Data Augmentationあり / Stem blockなし
* Data Augmentationなし / Stem blockあり
* Data Augmentationなし / Stem blockなし

Data Augmentationは4ピクセル上下に動かして左右反転入れる、Standard Data Augmentation。

Stem blockの有無は、入力解像度・出力解像度が変わります。Stem blockありが本来のPeleeNetの構造です。なしの場合は、いきなりDenseLayerに入ります。

* Stem blockあり→入力 224 x 224 x 3：出力 7 x 7 x 704
* Stem blockなし→入力 32 x 32 x 3 : 出力 4 x 4 x 704

Stemありの場合は32→224に7倍のアップサンプリングを前処理に入れています。

各モデルのsummaryは自分のgithubを見てください。

* CIFAR-10, Stem blockあり　https://github.com/koshian2/PeleeNet-Keras/blob/master/summary/cifar10_use_stem.txt
* CIFAR-10, Stem blockなし　https://github.com/koshian2/PeleeNet-Keras/blob/master/summary/cifar10_no_stem.txt

## 結果（weight decayなし）
論文ではWeight Decayのパラメーター特になかったので、そのまま訓練しました。

![cifar10_no_weight_decay.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/390b74c2-6417-ca02-e955-509984a48e95.png)

あれ、なんか低くない？？　8割出ないってAlexNet以下では……。

ResNet系の論文でCIFAR-10にはWeight Decay入れてたのを思い出したので、Weight Decayを入れて再訓練してみました（ちなみにPeleeNetの論文にはCIFARに対する言及はありません）。

## 結果（weight decayあり）
**5e-4のWeight Decay（L2正則化）を入れて訓練**します。値は適当に入れたらうまく行ったものです。

![cifar10_5e-4_weight_decay.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/3919646b-0224-b630-b828-b18f015b54d0.png)

95%近く出た。わーい！　これならMobileNetといい勝負じゃないかと思います。

## 精度比較

| Augmentation | Stem Block | No weight Decay | 5e-4 Weight Decay |
|:------------:|:----------:|----------------:|------------------:|
|       ☓      |      ☓     |          0.7633 |            0.9247 |
|       ☓      |      ○     |          0.8280 |            0.9218 |
|       ○      |      ☓     |          0.8881 |            0.9446 |
|       ○      |      ○     |          0.8996 |            0.9410 |

Weight Decayありのほうは、Stem Blockがない方が精度が上がってしまいましたが、これはStem Blockなしのほうで7倍のアップサンプリングという少し特殊なことをしているので、ImageNetのような高解像度の入力一般で成立する現象ではないと思います（Weight DecayでStem Blockの有用性が減っている感は否めないけど）。

## まとめ
PeleeNet遊びで訓練した割には結構強かったです。精度や速度どうこうという視点も良いですが、**DenseNetのような「知識の積み重ね・集約」構造をお手軽に再現したいときは特に使える**モデルではないかと思われます。

少しモデルが深いのでWeight Decayや学習率の調整は難しいかもしれませんが、2.8Mという小型なモデルなので、普段使いにも便利そう。高速化手法も、1x1Convという割とありふれた手法ですからね。

ちなみに、DenseNet特有のGrowth rateに応じたちびちびFilter Concatenateをやめて、前のレイヤー全部をDense Layerに通す（係数は増えますが深くする必要性は減ります）構造も試してみましたが、あまりうまく行きませんでした（Weight Decay入れても7～8割）。なのでPeeleNetのDense Layerの構造は、DenseNetライクの構造ありきではないかと思います。

MobileNetとどっちがいいという話は、好きなほう使えばいいんじゃない？　という感じでした。MobileNetも進化してますからね。FP16ならPeleeNetありだと思います。