背景###
前章はSSDモデルから出力するOffset情報の訓練を紹介しました。IOUという計算式をつかって、各段階デフォルトボックスの教師データを特定し、そして、損失関数を計算する流れです。
SSDモデルは各デフォルトボックスに対して、Offset情報と信頼度情報を提供していいます。Offset情報はデフォルトボックスを教師データと同じようなバインディングボックスに変形するための材料です。
信頼度情報はこのデフォルトボックスはすべての21種類のカテゴリーに属する可能性を表しています。
目的###
- 各デフォルトボックスの教師信頼度データを特定する
つまり、デフォルトボックスはどのカテゴリーに属しています - SSDモデルから出力する信頼度情報と教師信頼度情報の損失を計算する
訓練###
デフォルトボックスの教師データを探し出す####
まず、IOUの計算に戻ります
各デフォルトボックスからすべての教師データGround TruthsとIOU計算して、自分と一番IOUが高い教師データGround Truthを教師データとして取り扱います。
教師データが一つの場合#####
図のように各デフォルトボックスは唯一なGround TruthとIOUけいさんして、IOU>αのデフォルトボックスを残らせます。ポジティブボックスとして扱います。
残らせられたポジティブボックスにっとては、一番IOUが高い教師データGround Truthは唯一な馬、他にはないから、教師データの信頼度を馬にします
図のように、他のデフォルトボックスにとっては、一番IOUが高い教師データGround Truthは唯一な馬かもしれませんが、IOU<αのため、教師データの信頼度を背景(background)にします***
教師データが複数ある場合#####
ケース1######
ずのように、教師データは馬だけではなく、猫、犬もあります
ずのように、一つのデフォルトボックスを例にします
このデフォルトボックスからすべて教師データ(馬、猫、犬)とのIOUを計算します
それぞれは
IOU馬->0.14
IOU猫->0.39
IOU犬->0.52
ずのように、一番IOUが高いのは犬ですので、このデフォルトボックスの教師データを犬にします。
ケース2######
図のように、もう一つのデフォルトボックスを例にします。今回のデフォルトボックスはすべての教師データGround Truthどっちでも重ねることがないです。
図のように、このデフォルトボックスにとっては、教師データを犬、猫、馬どっちにされても、可能性があります。
図のように、
オレンジのデフォルトボックスは教師データが猫に属するデフォルトボックス
緑のデフォルトボックスは教師データが犬に属するデフォルトボックス
水色のデフォルトボックスは教師データが馬に属するデフォルトボックス
どちでも重ねがないデフォルトボックスはランダムから馬、猫、犬から教師データを選びます
ずのように、各デフォルトボックスはIOUと属する教師データを特定したあと、IOU<αのデフォルトボックスの教師データを背景(background)にします
信頼度の損失計算####
損失関数#####
ずのように、損失計算の方法は、softmax+cross_entropy_error
ポジティブボックスの損失#####
図のように、デフォルトボックスは教師データ馬を特定して、信頼度の教師データは図のようになります
図のように、SSDモデルはこのデフォルトボックスにたいして、提供する信頼度のデータは図のようになります
そして、SSDモデル提供する信頼度データと信頼度の教師データを比較して、損失計算を準備します
ネガテイブボックスの損失#####
図のように、教師データには背景(background)は1.0になります
Hard Negative Mining####
SSDはHard Negative Miningが行います。
ようするには、信頼度損失を計算する際に、各デフォルトボックスの信頼度の損失値を計算して、合計します。
でも、ポジティブボックスは少なく、ネガテイブボックスは圧倒的に多い現状です。
そのバランスを維持するためには、以下の流れをします
1.一回すべてのデフォルトボックスの信頼度損失を計算します
2.ポジティブデフォルトボックスの信頼度損失を0にします
3.すべてのデフォルトボックスの信頼度を降順にします
4.ポジティブデフォルトボックスの数をpnにします、indexをIndexPに保存します
5.降順になったデフォルトボックスの信頼度を上からpn*3をとって、indexをIndexNに保存します
6.すべてのデフォルトボックスに、indexはindexPとIndexNのデフォルトボックスだけとって、もう一度信頼度損失計算します.
7.そうしたら、ポジティブボックスとネガティブボックスの比例は1:3になります
8.信頼度損失を2回計算したわけです。1回目はすべてのデフォルトボックスに対して、2回目は特定されたデフォルトボックスに対します。
図のように、まずはポジティブボックスとネガティブボックスにわけます。
ずのように、実際にネガティブボックスはポジティブボックスより、圧倒的に多い
ずのように、1回目の損失計算:すべてのデフォルトボックスの信頼度損失を計算します
ずのように、ポジティブボックスの数は6個です。そして、ネガティブボックスの中から損失の降順で上から18このネガティブボックスをとります
ずのように、前のステップ残ったのデフォルトボックスに対して、もう一度損失を計算します(ここの必要があるかと疑っています、1回目計算できた損失値をそのまま使えると思いますけど)
ソースコード###
各デフォルトボックスの信頼度教師データを特定します
conf = labels[best_truth_idx] + 1
conf[best_truth_overlap < threshold] = 0 -->ポジティブボックスの信頼度を0にする
best_truth_idx = tensor([0, 0, 0, ..., 2, 2, 2], device='cuda:0')->各デフォルトボックスの信頼度教師データの番号
labels = tensor([14., 14., 17.], device='cuda:0')->教師データのカテゴリー番号(教師データGroundTruthは3個)
conf = tensor([15., 15., 15., ..., 18., 18., 18.], device='cuda:0')->各デフォルトボックスの信頼度教師データのカテゴリー番号
1回目の信頼度計算
loss_c = F.cross_entropy(batch_conf, conf_t_label.view(-1), reduction='none')
各デフォルトボックスのIOU
batch_conf = tensor([[ 1.2041, -0.0737, 1.2133, ..., -1.1512, 0.5440, 1.4540],
[ 0.3814, -0.8049, 0.7727, ..., -0.5996, -0.5597, -0.3455],
[-0.4916, 0.5868, -0.1321, ..., 0.0529, 0.7314, 1.1589],
...,
[-0.0170, 0.0476, 0.3374, ..., -0.2304, -0.1694, 0.0337],
[ 0.3133, 0.1897, -0.1472, ..., 0.1327, 0.2246, -0.0078],
[ 0.0589, 0.2930, 0.2262, ..., 0.2082, -0.0045, -0.2921]],
device='cuda:0', grad_fn=<ViewBackward>)
各デフォルトボックスの教師データカテゴリー番号
conf_t_label = tensor([[0, 0, 0, ..., 8, 0, 8],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 3, 0, 3],
[0, 0, 0, ..., 0, 8, 8],
[0, 0, 0, ..., 0, 0, 0]], device='cuda:0')
ポジティブボックスの信頼度を0にします
num_pos = pos_mask.long().sum(1, keepdim=True)
loss_c = loss_c.view(num_batch, -1) # torch.Size([num_batch, 8732])
loss_c[pos_mask] = 0
ここは、デフォルトボックスの信頼度はすべての信頼度の中に何番名なのかをidx_rankに納めました
_, loss_idx = loss_c.sort(1, descending=True)
_, idx_rank = loss_idx.sort(1)
デフォルトボックスの順番を変わらず、中身の内容が自分の信頼度のランキングです
idx_rank = tensor([[7832, 6827, 3786, ..., 8730, 7021, 8731],
[1026, 2675, 7253, ..., 5144, 6300, 5666],
[1565, 3243, 7233, ..., 5716, 6670, 5968],
...,
[5329, 3652, 1536, ..., 8730, 6505, 8731],
[1120, 2669, 7308, ..., 5538, 8730, 8731],
[ 938, 2729, 7092, ..., 5411, 6054, 5508]], device='cuda:0')
上のことを理解するために、簡易な例文を作りました。
a = torch.tensor(
[
[2.4522, 2.8841, 3.6130, 0.0000, 2.8224, 0.0000],
[4.5089, 3.8272, 2.5402, 3.1420, 2.7718, 3.0715],
[4.5089, 3.8272, 2.5402, 3.1506, 2.8058, 3.0757],
[3.2006, 3.5532, 4.1683, 0.0000, 2.8597, 0.0000],
[4.5089, 3.8272, 2.5402, 3.1491, 0.0000, 0.0000],
],
)
x, y = a.sort(1, descending=True)
print(x)
print(y)
m,n = y.sort(1)
print(n)
tensor([[3.6130, 2.8841, 2.8224, 2.4522, 0.0000, 0.0000],
[4.5089, 3.8272, 3.1420, 3.0715, 2.7718, 2.5402],
[4.5089, 3.8272, 3.1506, 3.0757, 2.8058, 2.5402],
[4.1683, 3.5532, 3.2006, 2.8597, 0.0000, 0.0000],
[4.5089, 3.8272, 3.1491, 2.5402, 0.0000, 0.0000]])
tensor([[2, 1, 4, 0, 3, 5],
[0, 1, 3, 5, 4, 2],
[0, 1, 3, 5, 4, 2],
[2, 1, 0, 4, 3, 5],
[0, 1, 3, 2, 4, 5]])
tensor([[3, 1, 0, 4, 2, 5],
[0, 1, 5, 2, 4, 3],
[0, 1, 5, 2, 4, 3],
[2, 1, 0, 4, 3, 5],
[0, 1, 3, 2, 4, 5]])
したのことはHard Negative Miningの最後に、ポジティブボックスと洗い出したネガティブボックスのIndexを取得して、もう一度信頼度を計算します。
num_neg = torch.clamp(num_pos*self.negpos_ratio, max=num_dbox)
neg_mask = idx_rank < (num_neg).expand_as(idx_rank)
pos_idx_mask = pos_mask.unsqueeze(2).expand_as(conf_data)
neg_idx_mask = neg_mask.unsqueeze(2).expand_as(conf_data)
conf_hnm = conf_data[(pos_idx_mask+neg_idx_mask).gt(0)].view(-1, num_classes)
conf_t_label_hnm = conf_t_label[(pos_mask+neg_mask).gt(0)]
loss_c = F.cross_entropy(conf_hnm, conf_t_label_hnm, reduction='sum')
まとめ###
信頼度の訓練方法を紹介しました
1.各デフォルトボックスが信頼度教師データを特定します
2.各デフォルトボックスごとに、SSDモデルの信頼度出力と教師データと損失を計算します
3.ポジティブボックスとネガティブボックスのバランスをとるために、Hard Negative Miningが行います
次の章は推論を紹介したいと思います。