ラビットチャレンジ　レポート（その13 深層学習Day4 Section5~6）

# 深層学習day4

## Section5 Transformer

### ニューラル機械翻訳
RNNやseq2seqの部分で出てきたEncoder-Decoderモデルは機械翻訳にも使えるモデルである。
しかし、長さに弱いという問題点があり、翻訳元の文の内容を一つのベクトルで表現するので、文長が長くなると表現力が足りなくなってしまっていた。

### Attention
そこで提案されたのがAttention。
翻訳先の各単語を選択する際に、翻訳元の文中の各単語の隠れ状態を利用する。
![深層学習Day4 Attention1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2437549/7d02c76e-0e8f-43cf-bb1b-4ac77a5ed7aa.png)

このように各単語の重みを計算し、単語との加重平均をとったベクトルを計算することで、「出力しようとしている単語と関連度が高い単語」を選択しやすくする。

このattentionの計算例が以下。
![深層学習day4 Attention2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2437549/a450874a-792b-c14e-70fd-e1da70da4248.png)

attentionの計算を簡略化すると、queryに一致するkeyを索引し、対応するvalueを取り出す操作をみなすことができる。
![Attention.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2437549/4fc7cac6-49e6-34f9-ec73-301f7a637a1a.jpeg)

このattention機構によって、RNN＋Attentionの構造を利用したEncoder-Decoderモデルが頻繁に使われていたが、それでもRNNを使用している関係上、学習の遅さや計算量がネックとなっていた。

### Transformer
そこで、Attention機構のみを利用したEndcoder-Decoderモデルが提案された。これがTransformerである。

![Transformer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2437549/a6932c78-b093-a84f-4e39-5fc6bce05d09.png)

Attentionには2種類あるが、EncoderのMulti-Head attentionに使われているのが、Self-Attention（query,key,valueが全て同じベクトル由来）。一方で、DecoderのMulti-Head Attentionに使われているのが、Source-Target Attention（queryとkey・valueが別々のベクトル由来）である。

ここで、Multi-Head Attentionというのは、重みパラメータの異なる8個のヘッドを利用し、それぞれのヘッドの計算値を後で結合して次の層に伝えるという仕組みである。これにより、それぞれのヘッドが異なる種類の情報を収集することが期待できる。

Feed Forwardネットワークの部分は、2層の全結合ニューラルネットワークを用いる。

また、図中のAdd&Normの部分は、Residual ConnectionとLayer Normalizationを示している。

Encoder・Decoder共にEmbeddingの後にしているPositional Encodingというのは、位置情報を付加する処理である。RNNを用いないため、語順の情報が扱えないため、疑似的にそれを付加している。これは三角関数を用いた式で表される。

### 実装演習
PyTorchを用いて、Transformerのモデル本体部分を実装。
PyTorchにはあらかじめTransformerEncoderが用意されているので、それを利用。

```python
import math
import torch
import torch.nn as nn
import torch.nn.functional as F

#Transformerモデルの中身本体
class TransformerModel(nn.Module):

    def __init__(self, ntoken, ninp, nhead, nhid, nlayers, dropout=0.5):
        super(TransformerModel, self).__init__()
        from torch.nn import TransformerEncoder, TransformerEncoderLayer
        self.model_type = 'Transformer'
        self.pos_encoder = PositionalEncoding(ninp, dropout)
        encoder_layers = TransformerEncoderLayer(ninp, nhead, nhid, dropout)
        self.transformer_encoder = TransformerEncoder(encoder_layers, nlayers)
        self.encoder = nn.Embedding(ntoken, ninp)
        self.ninp = ninp
        self.decoder = nn.Linear(ninp, ntoken)

        self.init_weights()

    def generate_square_subsequent_mask(self, sz):
        mask = (torch.triu(torch.ones(sz, sz)) == 1).transpose(0, 1)
        mask = mask.float().masked_fill(mask == 0, float('-inf')).masked_fill(mask == 1, float(0.0))
        return mask

    def init_weights(self):
        initrange = 0.1
        self.encoder.weight.data.uniform_(-initrange, initrange)
        self.decoder.bias.data.zero_()
        self.decoder.weight.data.uniform_(-initrange, initrange)

    def forward(self, src, src_mask):
        src = self.encoder(src) * math.sqrt(self.ninp)
        src = self.pos_encoder(src)
        output = self.transformer_encoder(src, src_mask)
        output = self.decoder(output)
        return output

#位置情報のためのPositional Encoding
class PositionalEncoding(nn.Module):

    def __init__(self, d_model, dropout=0.1, max_len=5000):
        super(PositionalEncoding, self).__init__()
        self.dropout = nn.Dropout(p=dropout)

        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)
        pe = pe.unsqueeze(0).transpose(0, 1)
        self.register_buffer('pe', pe)

    def forward(self, x):
        x = x + self.pe[:x.size(0), :]
        return self.dropout(x)
```

## Section6 物体検知・セグメンテーション

### 画像認識タスク
画像を用いた物体認識タスクは、以下の種類に分けられる。

|タスク|出力|
|:---:|:---:|
|分類|（画像に対し、単一or複数の）ラベル|
|物体検知|Bounding Box|
|意味領域分割|（各ピクセルに対し単一の）ラベル|
|個体領域分割|（各ピクセルに対し単一の）ラベル|

この表では、上から下に行くほどタスクの難易度は高い。分類では物体の位置に関係なく、画像に着けられたラベルを当てるが、個体領域分割では、画像中の物体が一つ一つ別物であることを区別して、さらにラベルを当てなければならないので、これらのタスクの差は大きい。

### データセット
この画像認識のタスクに使われる代表的なデータセットの一覧が以下。

|データセット名|クラス|訓練/検証のデータ数|Box/画像|
|:--:|:--:|:--:|:--:|
|VOC12|20|11540|2.4|
|ILSVRC17|200|476668|1.1|
|MS COCO18|80|123287|7.3|
|OICOD18|500|1743042|7.0|

またこのうち、ILSVRC17以外はインスタンスに毎にアノテーションがつけられている。

### 評価指標
一般的には、混同行列を用いたラベルの正解率・再現率などを用いることが多い。
しかし、物体検出タスクにおいては、クラスラベルだけでなく、物体位置の予測精度も評価したい。

そこで提案されたのがIoU(Intersection over Union)

```math
IoU = \frac{Area \; of \;Overlap}{Area \; of  \;Union} = \frac{TP}{TP+FP+FN}
```
![深層学習Day4 IoU.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2437549/6db1958d-6769-594d-bb1f-80772ed57588.png)

他にもいくつか評価指標が存在する。

**Average Precision**
![深層学習Day4 Average Precision.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2437549/201852c2-62cc-e2f0-0bf6-d1acc8dfc5a0.png)

**mean Average Precision**
![深層学習Day4-mean Average Precision.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2437549/12055629-8624-a120-e260-9f41d6517420.png)

### フレームワーク
物体検知フレームワークは大きく分けて2種類ある。

#### 1段階検出器
DetectorNet, YOLO, SSD, YOLO9000, RetinaNet, CornerNet

  * 候補領域の検出とクラス推定を同時に行う
  * 相対的に精度が低い
  * 相対的に計算量が小さく、推論も早い傾向

#### 2段階検出器
RCNN, SPPNet, Fast RCNN, Faster RCNN, RFCN, FPN, Mask RCNN

   * 候補領域の検出とクラス推定を別々に行う
   * 相対的に精度が高い傾向
   * 相対的に計算量が大きく推論も遅い傾向

### SSD(Single Short Detector)
物体検出モデルの一つ。
1度のCNN演算で「領域候補検出」と「クラス分類」の両方を行うため、従来モデルに比べて処理が高速。
構造は以下。
![深層学習Day4 SSD.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2437549/c2aec429-e9d8-ad55-b2e6-e2edb735953f.png)

最初のレイヤーでは、VGG-16をベースとする構造を用いている。
従来のVGG-16の最後にあった全結合層を畳み込み層に変更し、さらにその後ろに畳み込み層を追加している。
この各畳み込み層ごとに異なるスケールで物体検出を行っている。

### 実装演習
pytorchの学習済みモデルのYOLOを簡単に試す

```python
import torch

# Model
model = torch.hub.load('ultralytics/yolov3', 'yolov3')  # or yolov3-spp, yolov3-tiny, custom

# Images
img = 'https://ultralytics.com/images/zidane.jpg'  # or file, Path, PIL, OpenCV, numpy, list

# Inference
results = model(img)

# Results
results.print()  # or .show(), .save(), .crop(), .pandas(), etc.
```