損失関数の役割は、ニューラルネットワークの予測情報と期待情報(ラベル)との距離を測定することであり、予測情報が期待情報に近ければ近いほど、損失関数の値は小さくなります。
ターゲット検出の分野では、一般的な損失は分類損失と回帰損失に分けられます。
L1ロス
L1 損失 (平均絶対誤差 (MAE) とも呼ばれます) は、モデルの予測値 f(x) と実際の値 y の間の絶対差の平均値を指します。式は次のとおりです。損失関数は一定で安定した勾配な
ので
、勾配爆発の問題は発生しません。
短所:
-1 と 1 の間の場合、その勾配は 1 または -1 のままであるため、つまり勾配には変換がなく、この区間では誤差が非常に小さいため、この部分の勾配が適切であることが期待されます。小さくなるはずです、ゆっくり近づいてください。
L2ロス
L2 損失は、平均二乗誤差 (MSE) とも呼ばれ、モデルの予測値 f(x) と実際の値 y の差の二乗の平均値を指します。式は次のとおりです。
利点:
関数曲線は連続的で、どこにでも誘導でき、誤差値が減少するにつれて勾配も減少し、最小値への収束に役立ちます。
短所:
誤差が大きい場合、その導関数は 2x であるため、この期間の勾配は大きく、外れ値に非常に敏感です。つまり、外れ値は十分に安定していません (ロバスト性が強くありません)。次の図のようになります。外れ値に対して敏感すぎる。
総合的に考えると、誤差が小さい場合、つまり勾配を小さくする必要がある場合には、損失関数が比較的滑らかでゆっくりと近づくことができ、誤差が大きい場合には、安定してロバストであることが期待されるため、 L1 損失と L2 損失の変形である Smooth L1 を提案します。
スムーズな L1 損失
簡単に言えば、Smooth L1 は L1 Loss の滑らかなバージョンであり、その式は次のとおりです。
この関数は実際には区分関数であり、0 での L1 の変曲点を解決する [-1,1] 間の L2 損失と、[-1, 1] 区間の外側の L1 損失であり、次の勾配を解決します。爆発の問題があるため、次の 2 つの側面から勾配を制限できます。
- 予測値と実際の値の間の誤差が大きすぎる場合、勾配値はあまり大きくなりません。
- 予測値と実際の値の間の誤差が小さい場合、勾配値は十分に小さいです。
次の図は、3 つの合成を示しています。
IOUの損失
IoU は交差率と呼ばれるもので、ターゲット検出で最も一般的に使用される指標です。アンカーベースの方法では、その役割は、ポジティブ サンプルとネガティブ サンプルを決定するために使用されるだけでなく、出力ボックス (予測ボックス ) とグラウンドトゥルース距離。
利点:
予測検出枠と実際の検出枠の検出効果を反映できると言える。
もう 1 つの優れた機能はスケール不変性、つまりスケール不変です。回帰タスクでは、予測ボックスと gt の間の距離を判断するための最も直接的な指標は IoU です。
欠点:
2 つのボックスが交差しない場合、定義によれば IoU=0 となり、2 つのボックス間の距離 (一致) を反映できません。同時に、損失=0であるため、勾配フィードバックはなく、学習およびトレーニングを実行できません。
IoU は、2 つの一致の度合いを正確に反映できません。以下の図に示すように、3 つのケースの IoU は等しいですが、一致度が異なることがわかります。回帰効果は左の図が最も良く、右の図が最も悪くなっています。 。
def box_iou_pairwise(boxes1, boxes2):
area1 = box_area(boxes1)
area2 = box_area(boxes2)
lt = torch.max(boxes1[:, :2], boxes2[:, :2]) # [N,2]
rb = torch.min(boxes1[:, 2:], boxes2[:, 2:]) # [N,2]
wh = (rb - lt).clamp(min=0) # [N,2]
inter = wh[:, 0] * wh[:, 1] # [N]
union = area1 + area2 - inter
iou = inter / union
return iou, union
ジオウ負け
まず 2 つのフレームの最小閉包領域面積 A c (一般的な理解: 予測フレームと実際のフレームの両方を含む最小フレームの面積) を計算し、次に IoU を計算し、次に の面積を計算します。 2 つのフレームに属さないクロージャ領域。クロージャ領域の割合。最後にこの割合を IoU から減算して GIoU を取得します。
長所:
短所:
いつ
def generalized_box_iou(boxes1, boxes2):
"""
Generalized IoU from https://giou.stanford.edu/
The boxes should be in [x0, y0, x1, y1] format
Returns a [N, M] pairwise matrix, where N = len(boxes1)
and M = len(boxes2)
"""
# degenerate boxes gives inf / nan results
# so do an early check
assert (boxes1[:, 2:] >= boxes1[:, :2]).all()
assert (boxes2[:, 2:] >= boxes2[:, :2]).all()
iou, union = box_iou(boxes1, boxes2)
lt = torch.min(boxes1[:, None, :2], boxes2[:, :2])
rb = torch.max(boxes1[:, None, 2:], boxes2[:, 2:])
wh = (rb - lt).clamp(min=0) # [N,M,2]
area = wh[:, :, 0] * wh[:, :, 1]
return iou - (area - union) / (area + 1e-6)
ディオウの喪失
問題は次のとおりです。このとき、イオウとジオウの損失は同じくらい大きいです。この時、GIOUもIOUに退化して
DIOUを提案する
上記の損失関数では、b と bgt はそれぞれアンカー フレームとターゲット フレームの中心点を表し、p は 2 つの中心点間のユークリッド距離の計算を表します。c は、アンカーとターゲット ボックスを同時にカバーできる最小の長方形の対角距離を表します。
def Diou(bboxes1, bboxes2):
rows = bboxes1.shape[0]
cols = bboxes2.shape[0]
dious = torch.zeros((rows, cols))
if rows * cols == 0: #
return dious
exchange = False
if bboxes1.shape[0] > bboxes2.shape[0]:
bboxes1, bboxes2 = bboxes2, bboxes1
dious = torch.zeros((cols, rows))
exchange = True
# #xmin,ymin,xmax,ymax->[:,0],[:,1],[:,2],[:,3]
w1 = bboxes1[:, 2] - bboxes1[:, 0]
h1 = bboxes1[:, 3] - bboxes1[:, 1]
w2 = bboxes2[:, 2] - bboxes2[:, 0]
h2 = bboxes2[:, 3] - bboxes2[:, 1]
area1 = w1 * h1
area2 = w2 * h2
center_x1 = (bboxes1[:, 2] + bboxes1[:, 0]) / 2
center_y1 = (bboxes1[:, 3] + bboxes1[:, 1]) / 2
center_x2 = (bboxes2[:, 2] + bboxes2[:, 0]) / 2
center_y2 = (bboxes2[:, 3] + bboxes2[:, 1]) / 2
inter_max_xy = torch.min(bboxes1[:, 2:], bboxes2[:, 2:])
inter_min_xy = torch.max(bboxes1[:, :2], bboxes2[:, :2])
out_max_xy = torch.max(bboxes1[:, 2:], bboxes2[:, 2:])
out_min_xy = torch.min(bboxes1[:, :2], bboxes2[:, :2])
inter = torch.clamp((inter_max_xy - inter_min_xy), min=0)
inter_area = inter[:, 0] * inter[:, 1]
inter_diag = (center_x2 - center_x1) ** 2 + (center_y2 - center_y1) ** 2
outer = torch.clamp((out_max_xy - out_min_xy), min=0)
outer_diag = (outer[:, 0] ** 2) + (outer[:, 1] ** 2)
union = area1 + area2 - inter_area
dious = inter_area / union - (inter_diag) / outer_diag
dious = torch.clamp(dious, min=-1.0, max=1.0)
if exchange:
dious = dious.T
return dious
CIOUの損失
問題は次のとおりです。アスペクト比を考慮していないため、損失値は同じです。
そこで CIOU を提案する. CIOU はアスペクト比 v を導入し, v の前に動的な重み値 a を追加する. アスペクト比が大きいほど, アスペクト比に注目する. より大きくしたい場合, iou はもっと大きくなる。
このうち、v の計算方法における前段の定数項目は経験値として使用されます。また、arctan の代わりに他の関数を使用することもでき、両者のアスペクト比の差が小さいほど損失値が小さくなるようにすることを目的としています。
下の右の図に示すように、iou が小さいため、アスペクト比は絶対に考慮しません。2 つの写真のアスペクト比が同じであるため、この時点では a が小さいほど焦点が合うはずです。幅と高さを大きくする方法について 左の図では、その iou が大きくなっています。つまり、すでに同じようなサイズになっています。その場合、アスペクト比と形状に注意を払う必要があります。
def box_ciou(b1, b2):
"""
输入为:
----------
b1: tensor, shape=(batch, feat_w, feat_h, anchor_num, 4), xywh
b2: tensor, shape=(batch, feat_w, feat_h, anchor_num, 4), xywh
返回为:
-------
ciou: tensor, shape=(batch, feat_w, feat_h, anchor_num, 1)
"""
# 求出预测框左上角右下角
b1_xy = b1[..., :2]
b1_wh = b1[..., 2:4]
b1_wh_half = b1_wh / 2.
b1_mins = b1_xy - b1_wh_half
b1_maxes = b1_xy + b1_wh_half
# 求出真实框左上角右下角
b2_xy = b2[..., :2]
b2_wh = b2[..., 2:4]
b2_wh_half = b2_wh / 2.
b2_mins = b2_xy - b2_wh_half
b2_maxes = b2_xy + b2_wh_half
# 求真实框和预测框所有的iou
intersect_mins = torch.max(b1_mins, b2_mins)
intersect_maxes = torch.min(b1_maxes, b2_maxes)
intersect_wh = torch.max(intersect_maxes - intersect_mins, torch.zeros_like(intersect_maxes))
intersect_area = intersect_wh[..., 0] * intersect_wh[..., 1]
b1_area = b1_wh[..., 0] * b1_wh[..., 1]
b2_area = b2_wh[..., 0] * b2_wh[..., 1]
union_area = b1_area + b2_area - intersect_area
iou = intersect_area / torch.clamp(union_area, min=1e-6)
# 计算中心的差距
center_distance = torch.sum(torch.pow((b1_xy - b2_xy), 2), axis=-1)
# 找到包裹两个框的最小框的左上角和右下角
enclose_mins = torch.min(b1_mins, b2_mins)
enclose_maxes = torch.max(b1_maxes, b2_maxes)
enclose_wh = torch.max(enclose_maxes - enclose_mins, torch.zeros_like(intersect_maxes))
# 计算对角线距离
enclose_diagonal = torch.sum(torch.pow(enclose_wh, 2), axis=-1)
ciou = iou - 1.0 * (center_distance) / torch.clamp(enclose_diagonal, min=1e-6)
v = (4 / (math.pi ** 2)) * torch.pow((torch.atan(b1_wh[..., 0] / torch.clamp(b1_wh[..., 1], min=1e-6)) - torch.atan(
b2_wh[..., 0] / torch.clamp(b2_wh[..., 1], min=1e-6))), 2)
alpha = v / torch.clamp((1.0 - iou + v), min=1e-6)
ciou = ciou - alpha * v
return ciou
アルファ IOU
Alpha IOU が役立つのはなぜですか?