勾配消失への対抗策の一つ，DenseNet．

# はじめに
本稿は2020年度の[映像メディア学](https://www.hal.t.u-tokyo.ac.jp/~yamasaki/lecture/index.html)の課題として出題されたものです． 
課題は2016~2020年の間にCVPR等のトップ会議・ジャーナルに投稿された内容を自力で実装せよ，さらにオンライン上のブログにて説明を行えというものでした．
今回私は[DenseNet](https://arxiv.org/abs/1608.06993)を選び[github](https://github.com/taketakeseijin/DenseNet)に実装を,ここに説明を公開します．

## DenseNetとは
DenseNetは2016年にCVPRに投稿された[Densely Connected Convolutional Networks](https://arxiv.org/abs/1608.06993)内で提案された画像のクラス分類問題を解くCNN(Convolutional Neural Networks)です．
[ベンチマークサイト](https://paperswithcode.com/sota/image-classification-on-cifar-10)によれば，外部データを用いないCIFAR-10データセットの分類精度において2016後半にSOTA(state-of-the-art)になりました．
当時はより深くより深層にすることが性能向上の鍵という風潮が強い一方，勾配消失問題への対策を求められている時期であったと記憶しています．その勾配消失問題への解決案として有名なのは，CNNにおいては[ResNet](https://arxiv.org/abs/1512.03385)，RNN(Recurent Neural Network)においては[LSTM](https://ja.wikipedia.org/wiki/%E9%95%B7%E3%83%BB%E7%9F%AD%E6%9C%9F%E8%A8%98%E6%86%B6)，もっと基礎で言ったら非線型関数はleakyReLUでPoolingはmeanPoolingといったところでしょう．
しかしながら解決案はそれだけではなく，本稿で説明するDenseNetや他にも[PyramidNet](https://arxiv.org/abs/1610.02915),[HighwayNet](https://arxiv.org/abs/1505.00387)などがあります．では実際にどのように勾配消失を解決していったのかを解説していきます．

## ResNetにおける解決策
深層化による勾配消失をResNetでは以下の式のように畳み込み処理をスキップするパスを持つResidual Blockを導入することで解決しています．

```math
x_{l+1} = x_{l} + f_l(x_l)
```
$x_l$は第$l$層目の入力で，$f_l$がl層目の処理です．
ネットワークはResidualな$f_l$の部分を学習し，**スキップするパスで勾配を伝搬する経路を確保**します．
このResidual Blockにより100層重ねても学習ができる大規模ネットワークが作れるようになりました．

## DenseNetにおける解決策
一方DenseNetは以下の式のようなDense Blockを使用した戦略をとります．

```math
x_{l+1} = f_l(x_0,\cdots,x_l)
```
先ほどと同じように$x_l$は第$l$層目の入力で，$f_l$がl層目の処理です．
任意の層の入力同士が直通のパスを持っていることがわかります．
特に**入力から出力までの直通のパスを用意する**ことで勾配の伝搬する経路を確保しています．
なお，実装上は$x_0,\cdots,x_l$の部分はチャンネル方向に結合して生成しています．

↓論文中より引用したDense Blockの図
<img width="400" alt="densenet.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546865/15641a8e-e55b-7f81-1d49-eb016fd2e637.png">

## Dense Blockの詳細な説明
### Transition層
VGGなどの多くのモデルは入力チャンネルサイズは層が深くなるにつれ大きくなっていきますが，同時に画像サイズ自体が小さくなっているので計算的に大きな問題にはなりません．
しかしながらDense Blockは画像サイズはそのままであるのに入力チャンネルサイズが層が深くなるにつれ大きくなってしまい計算量が増える問題があります．
そこでDenseNetではDense Blockをそのまま重ねていくだけではなく，Transition層という画像サイズと入力チャンネルサイズ両方を小さくする処理を間に挟みます．
Transition層ではチャンネル方向をpixel-width convolutionで，画像サイズをaverage poolingで半分のサイズにまで小さくします．
Transition層によって入力テンソルのサイズを小さくし，計算量を減らすことができます．
### Bottleneck層
全体として多くの畳み込みを行うDenseNetではTransition層を導入しても計算量やパラメータ数を減らす工夫が必要となります．（正直ResNetとパラメータ数競争したいだけだと思う．）
そのためDense Block内で利用する畳み込み計算をdepthwise separable convolutionのように２つの畳み込みに分解して計算を行います．一つ目の畳み込みはpixel-width convolutionで最終出力チャンネル数の４倍のチャンネル数にまで小さくし，その後に通常の畳み込みを用います．
### 全体の説明
DenseNetは下図にあるような形状をしており，Dense Blockを3または4層間にTransition層を挟みながら重ねていきます．（下図中において，Transition層はDense Blockの間のConvolutionとPoolingに相当．）
Dense Blockを通った後にGlobal Average Poolingを行った上で一層の線形層とsoftmaxをかけモデル全体となります．Dense BlockとTransition層を理解すればかなり単純な見た目をしています．

↓論文中より引用したDenseNetの概略図
![DenseNet_arcitecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546865/b352aae1-cb23-0b3b-3089-26cf74932ca5.png)

## DenseNetの追実験
[筆者のgithub](https://github.com/taketakeseijin/DenseNet)に０から自分で実装したコードがあります．今回の追実験はこのコードを利用して行いました．
今回はGPUの容量の都合もあり，CIFAR-10の画像分類問題をモデルに学習させました．
ハイパーパラメータ等として，$k(growth\ rate) = 12,L(depth)=100$でdata augumentaionありで学習を行い，その他は論文に準拠しました．
実験結果がこちらです．
![error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546865/8171865b-8705-c4eb-b987-8c7375bff05c.png)

|  元論文  |  追実験  |
| ---- | ---- |
|  95.5%  |  92.5%  |

結果として，92.5%程度の精度の分類器を得ることができました．
一方論文中では同じ条件で95.5%程度と報告されているので，追実験と開きがあることは確かです．
これは私の実装にバグがある訳ではなく，モデルの初期値や入力データの順番等のランダム性から生じる誤差だと考えられます．悪い見方をすれば，DenseNetは**ガチャ性がある**モデルであり，何度かやり直す必要があることが考えられます．