Tensorflow2.0 實現 YOLOv3(三):yolov3.py

文章說明

本系列文章旨在對 Github 上 malin9402 提供的代碼進行說明,在這篇文章中,我們會對 YOLOv3 項目中的 yolov3.py 文件進行說明。

如果只是想運行 Github 上的代碼,可以參考對 YOLOv3 代碼的說明一文。

傳入參數

import numpy as np
import tensorflow as tf
import core.utils as utils
import core.common as common
import core.backbone as backbone
from core.config import cfg

NUM_CLASS = len(utils.read_class_names(cfg.YOLO.CLASSES))
ANCHORS = utils.get_anchors(cfg.YOLO.ANCHORS)
STRIDES = np.array(cfg.YOLO.STRIDES)  # [8, 16, 32]
IOU_LOSS_THRESH = cfg.YOLO.IOU_LOSS_THRESH  # IoU閾值爲0.5

其中:

  • NUM_CLASS:檢測物體的類別數量;
  • ANCHORS:(三種)先驗框的尺寸;
  • STRIDES:三個 feature map 上單位長度所代表的原始圖像長度;
  • IOU_LOSS_THRESH:IoU閾值。

YOLOv3

在文章 YOLOv3 之網絡結構中已給出詳細說明,此處不再贅述。

decode

目的:解碼 YOLOv3 網絡的輸出。
輸入:YOLOv3 網絡的輸出(三個 feature map 中的一個)。
步驟:

  • 假設輸入的形狀爲(1, 52, 52, 255),這裏的 1 是指每次只訓練一張圖片;52 是指輸入的 feature map 的長和寬,則這個 feature map 由 52x52 個格子組成;255 是指每個格子中含有的通道數。
  • 將輸入 reshape 成(1, 52, 52, 3, 85),3 是因爲每個格子上有三個先驗框;85 是指 4 個預測框信息(兩個中心位置的偏移量和兩個預測框長寬的偏移量),1 個預測框的置信度(判斷預測框中有沒有檢測物體),80 個預測框的類別概率。
  • 將 reshape 後的輸入切片分到不同數組中;
  • 對每個先驗框生成相對座標(畫網格);
  • 計算預測框的絕對座標以及寬高度;
  • 計算預測框的置信值和分類值。

下面這幅圖將先驗框和預測框畫到原始圖像中去了,其中黑色虛線框代表先驗框,藍色框表示的是預測框。
在這裏插入圖片描述

  • bhb_hbwb_w 分別表示預測框的長寬,php_hpwp_w 分別表示先驗框的長寬;
  • txt_xtyt_y 表示的是物體中心距離網格左上角位置的偏移量,cxc_xcyc_y 則代表預測框左上角的座標。
def decode(conv_output, i=0):
    conv_shape = tf.shape(conv_output)
    batch_size = conv_shape[0]  # 樣本數
    output_size = conv_shape[1]  # 輸出矩陣大小

    conv_output = tf.reshape(conv_output, (batch_size, output_size, output_size, 3, 5 + NUM_CLASS))

    conv_raw_dxdy = conv_output[:, :, :, :, 0:2] # 中心位置的偏移量
    conv_raw_dwdh = conv_output[:, :, :, :, 2:4] # 預測框長寬的偏移量
    conv_raw_conf = conv_output[:, :, :, :, 4:5] # 預測框的置信度
    conv_raw_prob = conv_output[:, :, :, :, 5:] # 預測框的類別概率

# 1.對每個先驗框生成在 feature map 上的相對座標,以左上角爲基準,其座標單位爲格子,即數值表示第幾個格子
    y = tf.tile(tf.range(output_size, dtype=tf.int32)[:, tf.newaxis], [1, output_size])  # shape=(52, 52)
    x = tf.tile(tf.range(output_size, dtype=tf.int32)[tf.newaxis, :], [output_size, 1])  # shape=(52, 52)
    xy_grid = tf.concat([x[:, :, tf.newaxis], y[:, :, tf.newaxis]], axis=-1)  # shape=(52, 52, 2)
    xy_grid = tf.tile(xy_grid[tf.newaxis, :, :, tf.newaxis, :], [batch_size, 1, 1, 3, 1])  # shape=(1, 52, 52, 3, 2)
    xy_grid = tf.cast(xy_grid, tf.float32)

# 2,計算預測框的絕對座標以及寬高度
	# 根據上圖公式計算預測框的中心位置
    pred_xy = (tf.sigmoid(conv_raw_dxdy) + xy_grid) * STRIDES[i]  # xy_grid表示 feature map 中左上角的位置,即是第幾行第幾個格子;STRIDES表示格子的長度,即 feature map 中一個格子在原始圖像上的長度
    # 根據上圖公式計算預測框的長和寬大小
    pred_wh = (tf.exp(conv_raw_dwdh) * ANCHORS[i]) * STRIDES[i] # ANCHORS[i] * STRIDES[i]表示先驗框在原始圖像中的大小
    pred_xywh = tf.concat([pred_xy, pred_wh], axis=-1)

# 3. 計算預測框的置信值和分類值
    pred_conf = tf.sigmoid(conv_raw_conf)
    pred_prob = tf.sigmoid(conv_raw_prob)
    return tf.concat([pred_xywh, pred_conf, pred_prob], axis=-1)

bbox_iou

bbox_iou 函數被用來計算兩個檢測框之間的 IOU 值。在 utils.py 中也有函數 bboxes_iou 被用來計算 IOU 值,它們之間的區別是輸入的檢測框的信息:

  • bboxes_iou 的輸入是兩個(或一個對多個)檢測框左上角+右下角的座標信息。
  • bbox_iou 的輸入是兩個(或一個對多個)檢測框中心座標+寬高的座標信息。

IOU 值其實就是兩個框的交集面積比上它們的並集面積,這個值越大,代表這兩個框的位置越接近。用下面這個圖片表示:
在這裏插入圖片描述

def bbox_iou(boxes1, boxes2):

    boxes1_area = boxes1[..., 2] * boxes1[..., 3]  # 第一個檢測框的面積
    boxes2_area = boxes2[..., 2] * boxes2[..., 3]  # 第二個檢測框的面積

    boxes1 = tf.concat([boxes1[..., :2] - boxes1[..., 2:] * 0.5,
                        boxes1[..., :2] + boxes1[..., 2:] * 0.5], axis=-1)  # 第一個檢測框的左上角座標+右下角座標
    boxes2 = tf.concat([boxes2[..., :2] - boxes2[..., 2:] * 0.5,
                        boxes2[..., :2] + boxes2[..., 2:] * 0.5], axis=-1)  # 第二個檢測框的左上角座標+右下角座標

    left_up = tf.maximum(boxes1[..., :2], boxes2[..., :2])  # 對上圖來說,left_up=[xmin2, ymin2]
    right_down = tf.minimum(boxes1[..., 2:], boxes2[..., 2:])  # 對上圖來說,right_down=[xmax1, ymax1]

    inter_section = tf.maximum(right_down - left_up, 0.0)  # 交集區域
    inter_area = inter_section[..., 0] * inter_section[..., 1]  # 交集面積
    union_area = boxes1_area + boxes2_area - inter_area  # 並集面積

    return 1.0 * inter_area / union_area

bbox_giou

在代碼原作者的文章中,GIoU (Generalized IoU,廣義 IoU ) 這種優化邊界框的新方式被做了較爲詳細的介紹,這裏用作者文章中的內容總結一下。

邊界框一般由左上角和右下角座標所表示,即 (xmin, ymin, xmax, ymax)。所以這其實也是一個向量。向量的距離一般可以用 L1 範數或者 L2 範數來度量。但是在 L1 及 L2 範數取到相同的值時,實際上檢測效果卻是差異巨大的,直接表現就是預測和真實檢測框的 IoU 值變化較大,這說明 L1 和 L2 範數不能很好的反映檢測效果。
在這裏插入圖片描述
在這裏插入圖片描述當 L1 或 L2 範數都相同的時候,發現 IoU 和 GIoU 的值差別都很大,這表明使用 L 範數來度量邊界框的距離是不合適的。在這種情況下,學術界普遍使用 IoU 來衡量兩個邊界框之間的相似性。作者發現使用 IoU 會有兩個缺點,導致其不太適合做損失函數:

  • 預測框和真實框之間沒有重合時,IoU 值爲 0, 導致優化損失函數時梯度也爲 0,意味着無法優化。如下圖所示,場景 A 和場景 B 的 IoU 值都爲 0,但是顯然場景 B 的預測效果較 A 更佳,因爲兩個邊界框的距離更近( L 範數更小)。
    在這裏插入圖片描述
  • 即使預測框和真實框之間相重合且具有相同的 IoU 值時,檢測的效果也具有較大差異,如下圖所示。
    在這裏插入圖片描述上面三幅圖的 IoU = 0.33, 但是 GIoU 值分別是 0.33, 0.24 和 -0.1, 這表明如果兩個邊界框重疊和對齊得越好,那麼得到的 GIoU 值就會越高。

GIoU 的計算公式爲:
在這裏插入圖片描述the smallest enclosing convex object C 指的是最小閉合凸面 C,例如在上述場景 A 和 B 中,C 的形狀分別爲:
在這裏插入圖片描述圖中綠色包含的區域就是最小閉合凸面 C,the smallest enclosing convex object。

在代碼中,輸入是模型輸出的兩個檢測框位置(中心座標+寬高)。輸出的是這兩個檢測框的 GIOU 值。

同樣用下圖舉例:
在這裏插入圖片描述

def bbox_giou(boxes1, boxes2):

    boxes1 = tf.concat([boxes1[..., :2] - boxes1[..., 2:] * 0.5,
                        boxes1[..., :2] + boxes1[..., 2:] * 0.5], axis=-1)  # 第一個檢測框的左上角座標+右下角座標

    boxes2 = tf.concat([boxes2[..., :2] - boxes2[..., 2:] * 0.5,
                        boxes2[..., :2] + boxes2[..., 2:] * 0.5], axis=-1)  # 第二個檢測框的左上角座標+右下角座標

	# 這兩部分……我也不知道有什麼用,好像改變不了什麼。
    boxes1 = tf.concat([tf.minimum(boxes1[..., :2], boxes1[..., 2:]),
                        tf.maximum(boxes1[..., :2], boxes1[..., 2:])], axis=-1)
    boxes2 = tf.concat([tf.minimum(boxes2[..., :2], boxes2[..., 2:]),
                        tf.maximum(boxes2[..., :2], boxes2[..., 2:])], axis=-1)

    boxes1_area = (boxes1[..., 2] - boxes1[..., 0]) * (boxes1[..., 3] - boxes1[..., 1])  # 第一個檢測框的面積
    boxes2_area = (boxes2[..., 2] - boxes2[..., 0]) * (boxes2[..., 3] - boxes2[..., 1])  # 第二個檢測框的面積

    left_up = tf.maximum(boxes1[..., :2], boxes2[..., :2])  # 對上圖來說,left_up=[xmin2, ymin2]
    right_down = tf.minimum(boxes1[..., 2:], boxes2[..., 2:])  # 對上圖來說,right_down=[xmax1, ymax1]

    inter_section = tf.maximum(right_down - left_up, 0.0)  # 交集區域
    inter_area = inter_section[..., 0] * inter_section[..., 1]  # 交集面積
    union_area = boxes1_area + boxes2_area - inter_area  # 並集面積
    iou = inter_area / union_area

    enclose_left_up = tf.minimum(boxes1[..., :2], boxes2[..., :2])  # 對上圖來說,enclose_left_up =[xmin1, ymin1]
    enclose_right_down = tf.maximum(boxes1[..., 2:], boxes2[..., 2:])  # 對上圖來說,enclose_right_down =[xmax2, ymax2]
    enclose = tf.maximum(enclose_right_down - enclose_left_up, 0.0)
    enclose_area = enclose[..., 0] * enclose[..., 1]  # 最小閉合凸面面積
    giou = iou - 1.0 * (enclose_area - union_area) / enclose_area

    return giou

compute_loss

compute_loss 函數被用來計算損失。

損失分爲三類:框迴歸損失、置信度損失以及分類損失。

框迴歸損失

計算過程:

  • 獲得置信度 respond_bboxrespond\_bbox
  • bbox_loss_scale=2bbox\_loss\_scale = 2-\frac{框的面積}{原圖面積}
  • 損失 giou_loss=respond_bbox×bbox_loss_scale×(1giou)giou\_loss = respond\_bbox\times bbox\_loss\_scale\times (1 - giou)

置信度損失

計算過程:

  • 對某檢測框求出它和所有真實框間的 IOU 值;
  • 得到這些 IOU 值中最大的 IOU 值;
  • 如果這個 IOU 值小於閾值,那麼認爲該檢測框內不包含物體,爲背景框(負樣本),否則這個框是前景框(正樣本);
  • 正樣本誤差:直接計算交叉熵損失即可;
  • 負樣本誤差:同樣計算交叉熵損失,但對於如果最大 IOU 值大於閾值,但這個檢測框實際上是負樣本這種情況,我們不計誤差;
  • 置信度損失 = 正樣本誤差 + 負樣本誤差。

分類損失

對於分類損失,我們同樣只考慮正樣本損失,即對正樣本進行交叉熵損失計算。

輸入:

  • pred:模型輸出中經過解碼的檢測框,即原圖上的檢測框;
  • conv:模型輸出中沒有經過解碼的檢測框,即 feature map 上的檢測框;
  • label:標籤的格式爲 [batch_size, output_size, output_size, anchor_per_scale, 85=(2個位置xy+2個形狀wh+1個置信值+80個類別)];
  • i:表示是第幾個尺度上的 feature map。
def compute_loss(pred, conv, label, bboxes, i=0):

    conv_shape = tf.shape(conv)
    batch_size = conv_shape[0]
    output_size = conv_shape[1]
    input_size = STRIDES[i] * output_size  # 原始圖片尺寸
    input_size = tf.cast(input_size, tf.float32)
    conv = tf.reshape(conv, (batch_size, output_size, output_size, 3, 5 + NUM_CLASS))

    # 模型輸出的置信值與分類
    conv_raw_conf = conv[:, :, :, :, 4:5]
    conv_raw_prob = conv[:, :, :, :, 5:]
    pred_xywh = pred[:, :, :, :, 0:4]  # 模型輸出處理後預測框的位置
    pred_conf = pred[:, :, :, :, 4:5]  # 模型輸出處理後預測框的置信值
    label_xywh = label[:, :, :, :, 0:4]  # 標籤圖片的標註框位置
    respond_bbox = label[:, :, :, :, 4:5]  # 標籤圖片的置信值,有目標的爲1 沒有目標爲0
    label_prob = label[:, :, :, :, 5:]  # 標籤圖片的分類

# 1、框迴歸損失
	# 計算檢測框和真實框的 GIOU 值
    giou = tf.expand_dims(bbox_giou(pred_xywh, label_xywh), axis=-1)
    # bbox_loss_scale 制衡誤差 2-w*h
    bbox_loss_scale = 2.0 - 1.0 * label_xywh[:, :, :, :, 2:3] * label_xywh[:, :, :, :, 3:4] / (input_size ** 2)
    giou_loss = respond_bbox * bbox_loss_scale * (1 - giou)

# 2、置信度損失
    # 生成負樣本
    iou = bbox_iou(pred_xywh[:, :, :, :, np.newaxis, :], bboxes[:, np.newaxis, np.newaxis, np.newaxis, :, :])
    max_iou = tf.expand_dims(tf.reduce_max(iou, axis=-1), axis=-1)  # [batch_size, output_size, output_size, 1, 1]

    # respond_bgd 形狀爲 [batch_size, output_size, output_size, anchor_per_scale, x],當無目標且小於閾值時x爲1,否則爲0
    respond_bgd = (1.0 - respond_bbox) * tf.cast(max_iou < IOU_LOSS_THRESH, tf.float32)
    conf_focal = tf.pow(respond_bbox - pred_conf, 2)

    conf_loss = conf_focal * (
            # 正樣本誤差
            respond_bbox * tf.nn.sigmoid_cross_entropy_with_logits(labels=respond_bbox, logits=conv_raw_conf)
            +
            # 負樣本誤差
            respond_bgd * tf.nn.sigmoid_cross_entropy_with_logits(labels=respond_bbox, logits=conv_raw_conf)
    )

# 3.分類損失
    prob_loss = respond_bbox * tf.nn.sigmoid_cross_entropy_with_logits(labels=label_prob, logits=conv_raw_prob)

    # 誤差平均
    giou_loss = tf.reduce_mean(tf.reduce_sum(giou_loss, axis=[1, 2, 3, 4]))
    conf_loss = tf.reduce_mean(tf.reduce_sum(conf_loss, axis=[1, 2, 3, 4]))
    prob_loss = tf.reduce_mean(tf.reduce_sum(prob_loss, axis=[1, 2, 3, 4]))

    return giou_loss, conf_loss, prob_loss

完整代碼

import numpy as np
import tensorflow as tf
import core.utils as utils
import core.common as common
import core.backbone as backbone
from core.config import cfg


NUM_CLASS       = len(utils.read_class_names(cfg.YOLO.CLASSES))
ANCHORS         = utils.get_anchors(cfg.YOLO.ANCHORS)
STRIDES         = np.array(cfg.YOLO.STRIDES)
IOU_LOSS_THRESH = cfg.YOLO.IOU_LOSS_THRESH

def YOLOv3(input_layer):
    route_1, route_2, conv = backbone.darknet53(input_layer)

    conv = common.convolutional(conv, (1, 1, 1024,  512))
    conv = common.convolutional(conv, (3, 3,  512, 1024))
    conv = common.convolutional(conv, (1, 1, 1024,  512))
    conv = common.convolutional(conv, (3, 3,  512, 1024))
    conv = common.convolutional(conv, (1, 1, 1024,  512))

    conv_lobj_branch = common.convolutional(conv, (3, 3, 512, 1024))
    conv_lbbox = common.convolutional(conv_lobj_branch, (1, 1, 1024, 3*(NUM_CLASS + 5)), activate=False, bn=False)

    conv = common.convolutional(conv, (1, 1,  512,  256))
    conv = common.upsample(conv)

    conv = tf.concat([conv, route_2], axis=-1)

    conv = common.convolutional(conv, (1, 1, 768, 256))
    conv = common.convolutional(conv, (3, 3, 256, 512))
    conv = common.convolutional(conv, (1, 1, 512, 256))
    conv = common.convolutional(conv, (3, 3, 256, 512))
    conv = common.convolutional(conv, (1, 1, 512, 256))

    conv_mobj_branch = common.convolutional(conv, (3, 3, 256, 512))
    conv_mbbox = common.convolutional(conv_mobj_branch, (1, 1, 512, 3*(NUM_CLASS + 5)), activate=False, bn=False)

    conv = common.convolutional(conv, (1, 1, 256, 128))
    conv = common.upsample(conv)

    conv = tf.concat([conv, route_1], axis=-1)

    conv = common.convolutional(conv, (1, 1, 384, 128))
    conv = common.convolutional(conv, (3, 3, 128, 256))
    conv = common.convolutional(conv, (1, 1, 256, 128))
    conv = common.convolutional(conv, (3, 3, 128, 256))
    conv = common.convolutional(conv, (1, 1, 256, 128))

    conv_sobj_branch = common.convolutional(conv, (3, 3, 128, 256))
    conv_sbbox = common.convolutional(conv_sobj_branch, (1, 1, 256, 3*(NUM_CLASS +5)), activate=False, bn=False)

    return [conv_sbbox, conv_mbbox, conv_lbbox]

def decode(conv_output, i=0):
    """
    return tensor of shape [batch_size, output_size, output_size, anchor_per_scale, 5 + num_classes]
            contains (x, y, w, h, score, probability)
    """

    conv_shape       = tf.shape(conv_output)
    batch_size       = conv_shape[0]
    output_size      = conv_shape[1]

    conv_output = tf.reshape(conv_output, (batch_size, output_size, output_size, 3, 5 + NUM_CLASS))

    conv_raw_dxdy = conv_output[:, :, :, :, 0:2]
    conv_raw_dwdh = conv_output[:, :, :, :, 2:4]
    conv_raw_conf = conv_output[:, :, :, :, 4:5]
    conv_raw_prob = conv_output[:, :, :, :, 5: ]

    y = tf.tile(tf.range(output_size, dtype=tf.int32)[:, tf.newaxis], [1, output_size])
    x = tf.tile(tf.range(output_size, dtype=tf.int32)[tf.newaxis, :], [output_size, 1])

    xy_grid = tf.concat([x[:, :, tf.newaxis], y[:, :, tf.newaxis]], axis=-1)
    xy_grid = tf.tile(xy_grid[tf.newaxis, :, :, tf.newaxis, :], [batch_size, 1, 1, 3, 1])
    xy_grid = tf.cast(xy_grid, tf.float32)

    pred_xy = (tf.sigmoid(conv_raw_dxdy) + xy_grid) * STRIDES[i]
    pred_wh = (tf.exp(conv_raw_dwdh) * ANCHORS[i]) * STRIDES[i]
    pred_xywh = tf.concat([pred_xy, pred_wh], axis=-1)

    pred_conf = tf.sigmoid(conv_raw_conf)
    pred_prob = tf.sigmoid(conv_raw_prob)

    return tf.concat([pred_xywh, pred_conf, pred_prob], axis=-1)

def bbox_iou(boxes1, boxes2):

    boxes1_area = boxes1[..., 2] * boxes1[..., 3]
    boxes2_area = boxes2[..., 2] * boxes2[..., 3]

    boxes1 = tf.concat([boxes1[..., :2] - boxes1[..., 2:] * 0.5,
                        boxes1[..., :2] + boxes1[..., 2:] * 0.5], axis=-1)
    boxes2 = tf.concat([boxes2[..., :2] - boxes2[..., 2:] * 0.5,
                        boxes2[..., :2] + boxes2[..., 2:] * 0.5], axis=-1)

    left_up = tf.maximum(boxes1[..., :2], boxes2[..., :2])
    right_down = tf.minimum(boxes1[..., 2:], boxes2[..., 2:])

    inter_section = tf.maximum(right_down - left_up, 0.0)
    inter_area = inter_section[..., 0] * inter_section[..., 1]
    union_area = boxes1_area + boxes2_area - inter_area

    return 1.0 * inter_area / union_area

def bbox_giou(boxes1, boxes2):

    boxes1 = tf.concat([boxes1[..., :2] - boxes1[..., 2:] * 0.5,
                        boxes1[..., :2] + boxes1[..., 2:] * 0.5], axis=-1)
    boxes2 = tf.concat([boxes2[..., :2] - boxes2[..., 2:] * 0.5,
                        boxes2[..., :2] + boxes2[..., 2:] * 0.5], axis=-1)

    boxes1 = tf.concat([tf.minimum(boxes1[..., :2], boxes1[..., 2:]),
                        tf.maximum(boxes1[..., :2], boxes1[..., 2:])], axis=-1)
    boxes2 = tf.concat([tf.minimum(boxes2[..., :2], boxes2[..., 2:]),
                        tf.maximum(boxes2[..., :2], boxes2[..., 2:])], axis=-1)

    boxes1_area = (boxes1[..., 2] - boxes1[..., 0]) * (boxes1[..., 3] - boxes1[..., 1])
    boxes2_area = (boxes2[..., 2] - boxes2[..., 0]) * (boxes2[..., 3] - boxes2[..., 1])

    left_up = tf.maximum(boxes1[..., :2], boxes2[..., :2])
    right_down = tf.minimum(boxes1[..., 2:], boxes2[..., 2:])

    inter_section = tf.maximum(right_down - left_up, 0.0)
    inter_area = inter_section[..., 0] * inter_section[..., 1]
    union_area = boxes1_area + boxes2_area - inter_area
    iou = inter_area / union_area

    enclose_left_up = tf.minimum(boxes1[..., :2], boxes2[..., :2])
    enclose_right_down = tf.maximum(boxes1[..., 2:], boxes2[..., 2:])
    enclose = tf.maximum(enclose_right_down - enclose_left_up, 0.0)
    enclose_area = enclose[..., 0] * enclose[..., 1]
    giou = iou - 1.0 * (enclose_area - union_area) / enclose_area

    return giou


def compute_loss(pred, conv, label, bboxes, i=0):

    conv_shape  = tf.shape(conv)
    batch_size  = conv_shape[0]
    output_size = conv_shape[1]
    input_size  = STRIDES[i] * output_size
    conv = tf.reshape(conv, (batch_size, output_size, output_size, 3, 5 + NUM_CLASS))

    conv_raw_conf = conv[:, :, :, :, 4:5]
    conv_raw_prob = conv[:, :, :, :, 5:]

    pred_xywh     = pred[:, :, :, :, 0:4]
    pred_conf     = pred[:, :, :, :, 4:5]

    label_xywh    = label[:, :, :, :, 0:4]
    respond_bbox  = label[:, :, :, :, 4:5]
    label_prob    = label[:, :, :, :, 5:]

    giou = tf.expand_dims(bbox_giou(pred_xywh, label_xywh), axis=-1)
    input_size = tf.cast(input_size, tf.float32)

    bbox_loss_scale = 2.0 - 1.0 * label_xywh[:, :, :, :, 2:3] * label_xywh[:, :, :, :, 3:4] / (input_size ** 2)
    giou_loss = respond_bbox * bbox_loss_scale * (1- giou)

    iou = bbox_iou(pred_xywh[:, :, :, :, np.newaxis, :], bboxes[:, np.newaxis, np.newaxis, np.newaxis, :, :])
    max_iou = tf.expand_dims(tf.reduce_max(iou, axis=-1), axis=-1)

    respond_bgd = (1.0 - respond_bbox) * tf.cast( max_iou < IOU_LOSS_THRESH, tf.float32 )

    conf_focal = tf.pow(respond_bbox - pred_conf, 2)

    conf_loss = conf_focal * (
            respond_bbox * tf.nn.sigmoid_cross_entropy_with_logits(labels=respond_bbox, logits=conv_raw_conf)
            +
            respond_bgd * tf.nn.sigmoid_cross_entropy_with_logits(labels=respond_bbox, logits=conv_raw_conf)
    )

    prob_loss = respond_bbox * tf.nn.sigmoid_cross_entropy_with_logits(labels=label_prob, logits=conv_raw_prob)

    giou_loss = tf.reduce_mean(tf.reduce_sum(giou_loss, axis=[1,2,3,4]))
    conf_loss = tf.reduce_mean(tf.reduce_sum(conf_loss, axis=[1,2,3,4]))
    prob_loss = tf.reduce_mean(tf.reduce_sum(prob_loss, axis=[1,2,3,4]))

    return giou_loss, conf_loss, prob_loss
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章