物体検出SSD-2 : 物体検出で使う用語の整理(2)

# 修正
L2Normalaizationに説明を加えました。(赤い字になっています)正規化した値に対してチャネルごとにスケーリング用の学習可能なパラメータをかけることでチャネルごとの重要度を表現することが出来ます。(2024 12/14)
# 参考文献
* 新納浩幸『PyTorchによる物体検出』オーム社, 2020年
* Wei Liu, et al. "SSD: Single Shot MultiBox Detector", ECCV, pp. 21-37, 2016.
# 前回の内容
https://qiita.com/Syota-Ymauchi/items/b97c6420cdaaba93a5ce

前回分を見ていない方はこちらから。

#### SSDの原論文
https://arxiv.org/pdf/1512.02325

原論文はこちらから

# 今回の内容
* SSDのネットワークモデル
* VGG
* L2Normalization
* 補足1: 局所的な情報とグローバルな情報の両方を得るとは
* 補足2: 膨張畳み込み層

# SSDのネットワークモデル
概要図は下のようになる
![スクリーンショット 2024-11-28 185346.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3897045/d64a584d-ec0f-30c7-93bd-06a5d3e94cf6.png)


* 大きく分けてVgg16、extras、loc、confの4つの要素(この後それぞれについて説明)
* vgg1では10回の畳み込み演算を行い、これにL2正規化をほどこしたものがout1
* 特徴マップのw×hは38×38
* vgg2ではvgg1の出力に対して5回の畳み込みを行いサイズを19×19にする
* これがout2であり、Vgg16の出力
* out２がextrasの入力になる
* extrasでは各々2回ずつ畳み込み演算を行いout3、out4、out5、out6を出力
* out2->2回畳み込み演算->out3->2回畳み込み演算->out4->...->out6ということ
* 出力を6個保持して、各セルに対応する形でDBoxを作成
* out1 : 各セルに4つ
* out2 : 各セルに6つ
* out3 : 各セルに6つ
* out4 : 各セルに6つ
* out5 : 各セルに4つ
* out6 : 各セルに4つ
* DBoxの総数は以下のように計算できる
```math
(4 × 38 × 38) + (6 × 19 × 19) + (6 × 10 × 10) + (6 × 5 × 5) + (4 × 3 × 3)+ (4 × 1 × 1)\ = 8732
 ```
 * このout1 ~ out6に対してlocモジュールを適用してBBoxとのオフセットの予測値を求める
 * このout1 ~ out6に対してconfモジュールを適用してクラスの信頼度を求める(ただしsoftmax適用前のlogits)

# VGG
* CNNの有名モデルの一つ
* 3×3のカーネルを何層にも重ねることで局所的な情報とグローバルな情報の両方を得ることが出来る(補足1へ)
* SSDではVGGのモデルの一つであるVGG16を使用

![スクリーンショット 2024-11-27 194218.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3897045/f55d3516-a43e-4c5c-acd3-56d5f72b6829.png)

![スクリーンショット 2024-11-27 195211.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3897045/6785001b-ad35-900d-ba1e-106f7c71ce2c.png)


* ただしVGGの最後から2番目は膨張畳み込みを使用している(補足2へ)

# L2Normalization
* 各データ(1つの特徴量マップ)の各チャネルごとのL2ノルムの合計を1にする正規化(正規化後は正規化した値に対して<font color=red>チャネルごとに</font>学習可能なスケーリングパラメータをかける)
![スクリーンショット 2024-11-26 162729.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3897045/1caa6e00-97e1-89d0-3c26-b094ee32a798.png)
例
* ある3チャネル特徴量マップのあるセルの値が[1, 2, 3]であったとする
```math
L2norm = \sqrt(1^{2}+2^{2}+3^{3}) = \sqrt{9}=3
```
この値で割ることで正規化する
```math
[1, 2, 3] → [\frac{1}{3}, \frac{2}{3}, \frac{3}{3}]
```
実際に、正規化後のL2ノルムの合計は
```math
L2norm = \sqrt((\frac{1}{3}^{2})+(\frac{2}{3}^{2})+(\frac{3}{3}^{2})) = \sqrt{\frac{9}{9}}=1
```
* この値に学習可能なパラメータを<font color=red>チャネルごとに</font>適用する
* SSDは複数の解像度で計算された特徴マップを扱うので、L2Normを使用することで、計算された特徴マップに対して、チャネル間で同一のスケールを揃えることになる<font color=red>(L2正規化により)</font>
* 異なる解像度でも一貫したスケールで特徴量を使用できる
* <font color=red>空間情報を壊しずらい(h,wの情報を維持できる。他の正規化手法だとこれが難しい)</font>
* <font color=red>これにより学習を安定させる</font>
* <font color=red>これに対してスケーリングしてチャネル間の重要度を表現する</font>

##### 補足1: 局所的な情報とグローバルな情報の両方を得るとは
* CNNは層を深くすると、「フィルタの受容野が拡大」する
* 「フィルタの受容野が拡大」とはどういうことか具体例で説明
![スクリーンショット 2024-11-27 183032.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3897045/e55f61d3-5f4c-d160-0eea-f25a4dca4949.png)
* 畳み込み層である特徴マップを畳み込みする時にstride=1, kernel size=3, padding=0であるとする
* 分かりやすくするため、特徴マップのセルに番号を振っている
* 1回目のカーネル適用でマス目1, 2, 3を見ている
![スクリーンショット 2024-11-27 183434.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3897045/87a21a41-800c-06ff-f31b-f3fdce3ad420.png)
* 2回目では2, 3, 4マス目を見ている
![スクリーンショット 2024-11-27 183634.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3897045/bde22c3f-b989-93f1-7574-c54ef8ac7ea1.png)
* 3回目では3, 4, 5マス目を見ている
![スクリーンショット 2024-11-27 183843.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3897045/a848d1ec-9bd4-b821-90df-e8541b8186bc.png)
* 次の層で畳み込み(kernel size=3, stride=1)を行う時は上の3マス
* これは前の層の5マスに該当する
* つまり、同じ画像に対する「今見ているセルの数」は、層が深くなるにつれて多くなる
* これが「フィルタの受容野が拡大」するということ
* VGGでカーネルサイズ3×3を重ねて「局所的な情報とグローバルな情報の両方を得ることが出来る」とは1回目で「局所的な情報」を(1,2,3のマスの情報)得て、さらに畳み込みをすることで広い情報(1, 2, 3, 4, 5のマスの情報)を得ることが出来るということ

##### 膨張畳み込み(Dilated Convolution)
* 通常の畳み込みにおいてカーネルの受容野を拡大する手法
* 各カーネルの要素に「隙間」を持たせる
* 具体例で説明
![スクリーンショット 2024-11-28 193239.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3897045/7be5d2e7-4ba8-c422-72e2-08cc493e2626.png)

* このようにカーネルに隙間を持たせる
* pytorchのnn.Conv2dではデフォルトでdilation = 1だがこれをdilation >= 2でカーネルに隙間を持たせる膨張畳み込み層となる
* 補足1ではdelation=1で、これは一般的な畳み込み層である
* カーネルを適用した時に関わるセルが広くなっている(dilation = 1では1,2,3であったが、今回は1,3,5)
![スクリーンショット 2024-11-28 193853.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3897045/ccbf8811-66e6-8568-fdfc-534c6c5a1eb2.png)

* stride=1でずらして、再びカーネル適用
* 今回も見ているセルが広くなった(dilation = 1では2,3,4であったが、今回は2,4,6)
* より広い範囲を考慮した特徴マップの出力を得ることが出来る

なお、dilationを考慮した出力サイズは以下のように計算する
* kをカーネルサイズ,dilationを考慮したカーネルサイズをk'とする
* dilationの大きさをdとする
* 入力サイズをIとする
* 出力サイズをOとする
* パディングサイズをpとする
* ストライドの大きさをsとする
* []で余りは無視

```math
k' = k + (k-1)(d-1)
```
```math
O = [\frac{I - k' + 2p}{s}] + 1
```
* 今回はI=6, s = 1, p= 0, d = 2, k = 3なのでO = 2

![スクリーンショット 2024-11-28 195338.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3897045/c9632104-0fea-423d-1272-83c3b4986f55.png)



# 次回
* IoU
* SSDの損失関数
* Non Maximum Suppression