物体検出SSD詳しく紹介(三)OFFSET訓練

###背景
前章はSSDモデルから出力するOffset情報と信頼度情報の使い道について紹介しました。
Offset情報はデフォルトボックスをバウンディングボックスに変形するためなものです
信頼度情報はデフォルトボックスはどこカテゴリーに所属するかを表す数値です

実際に一つの画像の中に、教師データがあります。教師データとは物体を囲む正しい枠、物体の正しい所属カテゴリー
####目的####
***Offset情報を訓練する目的は、変形されたバウンディングボックスはできるだけ教師データと近くすることです***
図のように、たくさんのデフォルトボックスを一つの馬の教師データを目指して、近づこうとしています
![SSD物体検出2.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/8c318f0f-567f-8946-0d07-debaf6c9435f.jpeg)
図のように、ひとつのデフォルトボックスのoffset情報をたくさん訓練を受けて、offset情報がもっと有意になります。offset情報をつかって、デフォルトボックスから変形したバウンディングボックスが教師データと近づきました。
![SSD物体検出2.003.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/c2f4561d-f3f7-180c-8d90-c54a43e1f52f.jpeg)

###Offset情報の訓練###
####IOU####
ここで、IOUの概念の引用します。ボックスAとボックスBがあります。ボックスAとボックスBの共通の部分Cがあります。ボックスAとボックスBのすべての部分Dがあります。IOUの値はC/Dです。
***つまりIOUは二つのボックスがどれほど重ねってあることを表しています***

![SSD物体検出2.002.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/70035cd6-2102-ab3c-c819-015a5fe9baac.jpeg)
図のように、オレンジの部分は重ねってある部分、赤の部分はすべての部分。
![SSD物体検出2.004.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/761b79fd-9760-dc9f-682a-d06d7ac6cdc9.jpeg)
図のように、重ねってある部分がない、オレンジの部分がないです。赤の部分はすべての部分。

![SSD物体検出2.005.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/8f2edb60-4518-a694-ac3c-93c18c7ff36b.jpeg)
図のように、IOU = 重ねる部分の面積 / すべての部分の面積

####すべてのデフォルトボックスIOU計算####
我々はすでに8732個デフォルトボックスを用意しました。たとえここは一個教師データがあります。8732個デフォルトボックスは教師データにたいして、IOUを計算します。
<img width="293" alt="スクリーンショット 2020-04-29 6.15.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/81718c28-45b4-60d1-41c0-639e251b0eeb.png">
図のように、教師データと全然重ねってないボックスのIOUは0になります。他に教師データと重ねってあるデフォルトボックスのIOUが重ねる程度によって、IOUの数値が異なります。

####すべてのデフォルトボックスをIOUからフィルタリング####
そしてつぎの3ステップがおこないます
1. IOUが0より大きいデフォルトボックスを洗い出します。つまり教師データと重ねがあるデフォルトボックスを残らせます
2. IOUはαより大きいデフォルトボックスを洗い出します。ここのαが我々が設定する数値です。通常は0.5です。つまり、まり教師データと重ねがあるだけではなく、まり教師データとある程度重ねがあるデフォルトボックスを洗い出します。
3. 残りのデフォルトボックスをポジティブデフォルトボックスといいます
![SSD物体検出2.007.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/b6ecc2c0-53d6-b8ec-d3bc-03af5bf434ef.jpeg)
図のように、IOU>0、IOU>αのデフォルトボックスをスッテプずつ洗い出します。

![SSD物体検出2.008.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/4ce20d86-f5ee-08fa-b6a7-bfc6206515c1.jpeg)
図のように、ポジティブなデフォルトボックスを洗い出します。

####ポジティブデフォルトボックスの教師Offset情報の計算####
洗い出したポジティブデフォルトボックスはある程度教師データとかさねってありますが、実際にはまたまたすれています。そして、ポジティブデフォルトボックスから教師データに以下の算式で変形しようとしています。

```
cx = cx_d+0.1*△cx*w_d)
cy = cy_d+0.1*△cy*w_h)
w = w_d*exp(0.2*△w)
h = h_d*exp(0.2*△h)
```
左のcx,cy,w,hは教師データバウンディングボックスの座標情報。右側のcx_d,cy_d,w_d,h_dはデフォルトボックスの座標情報。
逆算すれば、教師データバウンディングボックスに変形するためなoffset情報を以下の算式から取り出せます。

```
△cx = (cx - cx_d) / (0.1 * w_d)
△cy = (cy - cy_d) / (0.1 * w_h)
△w = log(w / w_d) / 0.2
△h = log(h / h_d) / 0.2
```

***以上の計算結果は教師データのOffsetTになります***

![SSD物体検出2.009.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/290a1032-457f-00af-a71c-fcada2deab40.jpeg)
ずのように教師データバウンディングボックスに変形するためなoffset情報を計算する

####ポジティブデフォルトボックスのSSDモデルOffset情報計算####
さっき、offsetTを計算しました。でも、あれは目指す目標です。実際につかうのはSSDモデルから出力したOffset情報です。

実際につかうのはSSDモデルから出力したOffset情報は教師データバウンディングボックスに変形するためなoffset情報を目指しています
ここはとても重要なものです。
***Offset情報訓練の教師データはさっき計算したOffsetTになります***
***Offset情報訓練の訓練データはSSDモデルから出力したOffsetSになります***
***訓練するのは、変形したバインディングボックスと教師データボックスと比較することではなりません***
***訓練するのは、デフォルトボックスの変形するための材料Offset情報の訓練です***

![SSD物体検出2.010.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/bba4ce7c-53ec-e1c6-0737-0f7e6497a215.jpeg)
この図のように、38*38*4のデフォルトボックス[一番右側の４個のデフォルトボックス][一番手前のデフォルトボックス]に対して、４個のoffset情報(△cx,△cy,△w,△h)を提供しています。デフォルトボックすは座標情報(cx_d,cy_d,w_d,h_d)を持ち、offset情報(△cx,△cy,△w,△h)を使い、赤いボックスに変形します
変形する算式は

####Offset情報の損失計算####
では、手元に用意したのは教師データOffsetTと変形データOffsetSです。Offset情報訓練の訓練データはSSDモデルから出力したOffsetSになります。そのため、OffsetTとOffsetSと比較するための損失関数を用意する必要があります
![SSD物体検出2.011.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/6e834a1f-21d6-cf0f-ed37-87829374e3f2.jpeg)
図のように、左側逆算で計算したOffsetTは△cx,△cy,△w,△hを提供しています。右側SSDモデルから出力したOffsetSは△cx,△cy,△w,△h。
OffsetTとOffsetSの比較は△cx,△cy,△w,△hの比較になります。
OffsetTとOffsetSとともに、損失関数に投げます

ここで少し損失関数を紹介します。
![SSD物体検出2.012.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/6b33e822-42d6-571c-26fb-103587ac786e.jpeg)
図のように、xは普通訓練データ、xtは教師データ。
xとxtの絶対差は1以下なら、損失値が0.5*(x-xt)**2です。
xとxtの絶対差は1以上なら、損失値はabs( x - xt ) - 0.5になります。

実際にxはSSDモデルから出力した△cx,△cy,△w,△h
xtは教師データから逆算で計算された△cx,△cy,△w,△h
代入したら、以下のことになります
![SSD物体検出2.013.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/f12ad8fe-12cd-e2c3-0576-30553e25acd6.jpeg)
図のようにoffsetSとoffsetTの計算は△cx,△cy,△w,△hになります。最後にsumします。

![SSD物体検出2.014.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/61652c77-700e-c02a-ab51-84f0e90919ec.jpeg)
図のように△cx,△cy,△w,△h一対一でsmooth_l1_lossに代入して、最後にsumします

![SSD物体検出2.015.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/d7016ebb-adea-b4bc-f389-a4923d673e24.jpeg)
ずのように△cx,△cy,△w,△hの実際のデータを使って、計算する。

####ネガティブデフォルトボックスの教師Offset情報の計算####
以上をまとめてみると、IOUからポジティブボックスを洗い出し、そしてポジティブボックスにたいして、Offset損失を計算します
では、残るのネガティブデフォルトボックスはどうしますかというと、
***結論はネガティブデフォルトボックスOffset損失を計算しません。***
ネガティブデフォルトボックスはOffsetTとOffsetSもあります。Offset損失を計算しないことは、もともと教師データとのずれることが結構大きいため、
***ネガティブデフォルトボックスを背景の予測とします***
***推論するときに、そのまま教師データとのずれて、背景として出力させないです***
![SSD物体検出2.017.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/9de11b2d-4790-66d0-a0f8-386f4cb72e03.jpeg)
図のように、デフォルトボックスは教師データと重ねがありますが、IOUはαより小さいため、ネガティブボックスにさせて、背景ボックスとして取り扱います。
![SSD物体検出2.018.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/93ca669d-2b93-3d97-4d25-0ceaa9f88901.jpeg)
図のように、このデフォルトボックスは、教師データと重ねがないため、IOU=0,ネガティブボックスにさせて、背景ボックスとして取り扱います。
***IOUは0ですが、重ねない教師ボックスは一番重ねがある教師データなので、OffsetTはこの教師データの座標から逆算で計算します***
つまり、ネガティブデフォルトボックスはOffsetSとOffsetTを持っています。

![SSD物体検出2.019.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/f3ff2044-c88f-d5b0-3d7a-a33869d77e05.jpeg)
図のように、ネガティブデフォルトボックスはoffsetの損失に参加しません。

####教師データが複数のある場合####
教師データが複数がある場合には、各デフォルトボックスに対して、IOUの計算はちょっとかんがえないといけないです。
![SSD物体検出2.020.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/e8d764fe-7564-2fa3-1ad4-457cf592769c.jpeg)
図のように、教師データは馬ではなく、猫と犬もあります。
各デフォルトボックスに対して、IOUは教師データボックスと重ねる程度を表すものです。
***複数教師データがある場合、各デフォルトボックスに対して、IOUは一番重ねってある教師データボックスとの重ねる程度を表しています***

ここでは、馬、猫、犬三つの教師データがあります。デフォルトボックスを分身させ、三つのlayerに分身します。各layerに馬、猫、犬三つの教師データがそれぞれあります。
三つのlayerごとに、教師データとのIOUを計算します
![SSD物体検出2.021.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/8d3fe662-e300-0525-8104-5e8642dee26b.jpeg)

そして、各デフォルトボックスは馬、猫、犬三つの教師データとのIOUから一番高いIOUだけ残らせます
そして、このデフォルトボックスとのIOUが一番高い教師データがこのデフォルトボックスのOffsetTの逆算ざいりょうになります。
![SSD物体検出2.023.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/320072ff-9162-bf46-8135-bce1ac008240.jpeg)
図のように、このデフォルトボックスは馬とのIOUは0.14、猫とのIOUは0.39、犬とのIOUは0.52になります。
0.52が一番高くて、残ります。このデフォルトボックスはこれから犬の枠に変形しようと訓練します。

![SSD物体検出2.024.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/0274198f-cde6-9629-dd35-f35082db24b4.jpeg)
ずのように、複数の教師データの場合、ポジティブボックスも複数の教師データを目指して、変形しようとします。

###ソースコード###
####IOUの計算####

```python
def jaccard(box_a, box_b):
    inter = intersect(box_a, box_b)
    area_a = ((box_a[:, 2]-box_a[:, 0]) *
              (box_a[:, 3]-box_a[:, 1])).unsqueeze(1).expand_as(inter)  # [A,B]
    area_b = ((box_b[:, 2]-box_b[:, 0]) *
              (box_b[:, 3]-box_b[:, 1])).unsqueeze(0).expand_as(inter)  # [A,B]
    union = area_a + area_b - inter
    return inter / union  # [A,B]

def intersect(box_a, box_b):
    A = box_a.size(0)
    B = box_b.size(0)
    max_xy = torch.min(box_a[:, 2:].unsqueeze(1).expand(A, B, 2),
                       box_b[:, 2:].unsqueeze(0).expand(A, B, 2))
    min_xy = torch.max(box_a[:, :2].unsqueeze(1).expand(A, B, 2),
                       box_b[:, :2].unsqueeze(0).expand(A, B, 2))
    inter = torch.clamp((max_xy - min_xy), min=0)
    return inter[:, :, 0] * inter[:, :, 1]
```

####教師データからOffsetTの逆算####
```python
def encode(matched, priors, variances):
    # dist b/t match center and prior's center
    g_cxcy = (matched[:, :2] + matched[:, 2:])/2 - priors[:, :2]
    # encode variance
    g_cxcy /= (variances[0] * priors[:, 2:])
    # match wh / prior wh
    g_wh = (matched[:, 2:] - matched[:, :2]) / priors[:, 2:]
    g_wh = torch.log(g_wh) / variances[1]
    # return target for smooth_l1_loss
    return torch.cat([g_cxcy, g_wh], 1)  # [num_priors,4]
```

####各デフォルトボックスのIOUを計算####

```python
best_truth_overlap, best_truth_idx = overlaps.max(0, keepdim=True)
```
overlaps -> [n,8732]:すべてなデフォルトボックスから各教師データとのIOU
nは教師データの数

```
overlaps = tensor([[0.0000, 0.0000, 0.0000,  ..., 0.0719, 0.1068, 0.0991],
        [0.0000, 0.0000, 0.0000,  ..., 0.1028, 0.1526, 0.1526],
        [0.0000, 0.0000, 0.0000,  ..., 0.1574, 0.2337, 0.2167]],
       device='cuda:0')
```
best_truth_overlap->[1,8732]:8732個デフォルトボックス、各デフォルトボックスから一番大きいIOU

```
best_truth_overlap = tensor([[0.0000, 0.0000, 0.0000,  ..., 0.1574, 0.2337, 0.2167]],
       device='cuda:0')
```
best_truth_idx->[1,8732]:8732個デフォルトボックス、各デフォルトボックスから一番大きいIOUの教師データID

```
best_truth_idx = tensor([[0, 0, 0,  ..., 2, 2, 2]], device='cuda:0')
```



####各デフォルトボックスの一番重ねってある教師データの座標####
```
matches = truths[best_truth_idx]      
```
best_truth_idxは教師データのID、そして、truthsは教師データの座標をもっています。通常はcx1,cy1,cx2,cy2です。

```
truths = tensor([[0.2342, 0.1980, 1.3273, 0.6320],
        [0.5435, 0.1000, 1.0721, 0.4720]], device='cuda:0')
```
8732個のデフォルトボックスはそれぞれの教師データに対して、IOUが一番高い教師データを選びます。そうしたら、matchesに納めます。matchesはたくさんの重複なデータがあります。それは、たくさんのデフォルトボックスから同じ教師データを選んだからです。
***このmatchesは教師データの座標をもっているため、デフォルトボックスの座標と比較して、教師offset情報をencode関数で逆算します***

```

matches = tensor([[0.2342, 0.1980, 1.3273, 0.6320],
        [0.2342, 0.1980, 1.3273, 0.6320],
        [0.2342, 0.1980, 1.3273, 0.6320],
        ...,
        [0.2342, 0.1980, 1.3273, 0.6320],
        [0.2342, 0.1980, 1.3273, 0.6320],
        [0.2342, 0.1980, 1.3273, 0.6320]], device='cuda:0')
```

###まとめ###
デフォルトボックスを変形させるためのOffset情報を訓練する方法を紹介しました
IOUを使う目的は以下です
1. 使えそうなデフォルトボックスを洗い出します
2. デフォルトボックスの変形目標を探します
Offset情報を計算するための損失関数はsmooth_l1_lossとします

次の章は信頼度訓練を紹介したいと思います。