RetinaNet 作為一個高效的物件偵測模型,其核心優勢在於結合了特徵金字塔網路(FPN)和錨點機制,以提升偵測效能。FPN 允許模型在不同尺度上偵測物件,而錨點則提供預測物件邊界框的參考。為了應對物件偵測中常見的正負樣本不平衡問題,RetinaNet 採用 Focal Loss 作為損失函式,降低易分類別樣本的權重,從而更專注於難以區分的樣本。此外,非極大值抑制(NMS)和 Soft-NMS 則用於去除冗餘的偵測框,提升偵測結果的精確度。
RetinaNet 架構與錨點(Anchor)機制詳解
RetinaNet 是一種高效的物件偵測模型,其核心優勢在於利用特徵金字塔網路(Feature Pyramid Network, FPN)與精心設計的錨點(Anchor)機制來提升偵測效能。本文將探討 RetinaNet 的架構、錨點生成方式及其在物件偵測中的關鍵作用。
錨點(Anchor)機制解析
在物件偵測任務中,錨點是用於預測物件邊界框(Bounding Box)的基礎。RetinaNet 採用多尺度的錨點策略,以適應不同大小的物件。其具體實作如下:
- 錨點生成:錨點在輸入影像的不同特徵金字塔層級上均勻分佈,其大小與比例根據層級進行調整。例如,在 RetinaNet 中,特徵金字塔包含五個層級(P3 至 P7),對應的錨點基本尺寸分別為 32x32、64x64、128x128、256x256 和 512x512 畫素。
- 錨點間距:在不同層級的特徵圖上,錨點間的間距分別為 8、16、32、64 和 128 畫素,這使得模型能夠偵測不同大小的物件。
- 自適應調整:錨點的組態需要根據特定的資料集進行微調,以確保其與真實偵測框的特性相符。通常,這是透過調整輸入影像的大小來實作,而不是直接修改錨點生成的引數。
內容解密:
- 錨點大小與層級對應:錨點大小與特徵金字塔的層級直接相關,越深的層級對應越大的錨點,能夠偵測更大尺寸的物件。
- 錨點間距的意義:適當的錨點間距確保了模型在不同尺度上均能有效覆寫輸入影像,從而提升偵測的全面性。
- 資料集適配:針對特定資料集調整錨點組態,可以顯著提升模型的偵測效能。
偵測損失計算
為了評估模型的偵測效能,需要將預測的偵測框與真實框(Ground Truth)進行匹配。匹配過程根據 IOU(Intersection over Union)指標進行計算:
- IOU 矩陣計算:計算所有真實框與錨點之間的 IOU 值,形成一個 N x M 的矩陣,其中 N 是真實框數量,M 是錨點數量。
- 錨點分配:
- 若某錨點與某真實框的 IOU 值最大且大於 0.5,則該錨點被分配給該真實框。
- 若某錨點的所有 IOU 值均小於 0.4,則該錨點被標記為背景(即無物件)。
- IOU 值介於 0.4 和 0.5 之間的錨點在訓練過程中被忽略。
程式碼範例
import numpy as np
def compute_iou(box1, box2):
# 省略具體實作
pass
def assign_anchors(ground_truth_boxes, anchor_boxes):
iou_matrix = np.zeros((len(ground_truth_boxes), len(anchor_boxes)))
for i, gt_box in enumerate(ground_truth_boxes):
for j, anchor_box in enumerate(anchor_boxes):
iou_matrix[i, j] = compute_iou(gt_box, anchor_box)
# 根據 IOU 矩陣進行錨點分配
for j in range(iou_matrix.shape[1]):
max_iou_idx = np.argmax(iou_matrix[:, j])
if iou_matrix[max_iou_idx, j] > 0.5:
# 將錨點分配給對應的真實框
pass
elif np.max(iou_matrix[:, j]) < 0.4:
# 將錨點標記為背景
pass
else:
# 忽略該錨點
pass
內容解密:
- IOU 矩陣的作用:IOU 矩陣用於量化真實框與錨點之間的重疊程度,是錨點分配的基礎。
- 錨點分配規則:透過 IOU 值確定錨點的類別(物件或背景),確保模型學習到有效的偵測特徵。
- 忽略中間 IOU 值錨點:避免模型對難以區分的樣本過度擬合,提升模型的泛化能力。
RetinaNet 架構概覽
RetinaNet 的整體架構結合了 ResNet(或其他骨幹網路)、FPN 以及分類別和邊界框迴歸頭,其主要特點包括:
- 特徵金字塔網路(FPN):利用多尺度特徵圖提升對不同大小物件的偵測能力。
- 分類別與迴歸頭:採用簡單的卷積層結構,分別用於預測類別機率和邊界框偏移量。
- 分享權重:分類別和迴歸頭在不同特徵金字塔層級之間分享權重,提升模型的效率和一致性。
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title RetinaNet 物件偵測架構與錨點機制
package "機器學習流程" {
package "資料處理" {
component [資料收集] as collect
component [資料清洗] as clean
component [特徵工程] as feature
}
package "模型訓練" {
component [模型選擇] as select
component [超參數調優] as tune
component [交叉驗證] as cv
}
package "評估部署" {
component [模型評估] as eval
component [模型部署] as deploy
component [監控維護] as monitor
}
}
collect --> clean : 原始資料
clean --> feature : 乾淨資料
feature --> select : 特徵向量
select --> tune : 基礎模型
tune --> cv : 最佳參數
cv --> eval : 訓練模型
eval --> deploy : 驗證模型
deploy --> monitor : 生產模型
note right of feature
特徵工程包含:
- 特徵選擇
- 特徵轉換
- 降維處理
end note
note right of eval
評估指標:
- 準確率/召回率
- F1 Score
- AUC-ROC
end note
@enduml此圖示說明瞭 RetinaNet 的整體架構,從輸入影像到最終的偵測結果輸出。
內容解密:
- FPN 的作用:FPN 提供多尺度的特徵表示,有助於提升對不同大小物件的偵測能力。
- 分類別與迴歸頭的設計:簡單而有效的卷積結構使得模型能夠高效地進行類別預測和邊界框調整。
- 權重分享機制:跨層級的權重分享減少了模型的引數數量,並增強了不同尺度特徵之間的一致性。
物件偵測中的錨框與損失函式
在物件偵測模型中,錨框(anchor box)的分類別和迴歸是兩個重要的任務。分類別任務旨在判斷錨框是否包含目標物件,而迴歸任務則是預測目標物件的邊界框(bounding box)。
分類別與迴歸的輸出層設計
RetinaNet 的分類別輸出層使用 sigmoid 啟用函式,以允許多標籤的預測。雖然看起來像是允許多個標籤,但實際上是允許輸出全零,代表「背景類別」,即沒有偵測到任何物件。
# sigmoid 啟用函式範例
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# 輸出範例
x = np.array([0.5, -0.5, 0])
print(sigmoid(x))
內容解密:
sigmoid函式將輸入值對映到 0 和 1 之間,代表機率值。- RetinaNet 使用 sigmoid 而不是 softmax,因為 softmax 無法輸出全零。
- 分類別輸出層的設計允許模型預測多個類別或背景。
邊界框迴歸輸出層則沒有使用啟用函式,直接計算錨框和偵測框之間的差異。為了使迴歸值落在 [-1, 1] 範圍內,使用了特定的公式進行轉換:
# 邊界框迴歸公式範例
def compute_box_coordinates(anchor_box, deltas, U, V):
XA, YA, WA, HA = anchor_box
X, Y, W, H = deltas
X_pixels = X * U * WA + XA
Y_pixels = Y * U * HA + YA
W_pixels = WA * np.exp(W * V)
H_pixels = HA * np.exp(H * V)
return X_pixels, Y_pixels, W_pixels, H_pixels
# 輸出範例
anchor_box = (10, 10, 5, 5) # XA, YA, WA, HA
deltas = (0.1, 0.1, 0.1, 0.1) # X, Y, W, H
U = 0.1
V = 0.2
print(compute_box_coordinates(anchor_box, deltas, U, V))
內容解密:
compute_box_coordinates函式根據錨框和預測的偏移量計算最終的邊界框座標。U和V是調節因子,分別對應座標和尺寸的預期變異數。- 公式的設計確保了預測值在合理範圍內。
焦點損失(Focal Loss)
在物件偵測任務中,大多數錨框對應的是背景,因此需要一種機制來平衡正負樣本。RetinaNet 提出了一種新的損失函式——焦點損失(Focal Loss),以減少易分類別樣本的權重。
# 焦點損失範例
def focal_loss(y_true, y_pred, gamma=2):
loss = -y_true * (1 - y_pred)**gamma * np.log(y_pred) - (1 - y_true) * y_pred**gamma * np.log(1 - y_pred)
return loss
# 輸出範例
y_true = np.array([0, 1])
y_pred = np.array([0.1, 0.9])
gamma = 2
print(focal_loss(y_true, y_pred, gamma))
內容解密:
focal_loss函式根據真實標籤和預測機率計算焦點損失。gamma引數控制著易分類別樣本的權重降低程度。- 焦點損失減少了背景類別對總損失的影響。
平滑 L1 損失(Smooth L1 Loss)
對於迴歸任務,常用的損失函式包括 L1 和 L2 損失。然而,L1 損失的梯度恆定,而 L2 損失對異常值敏感。平滑 L1 損失結合了兩者的優點:
# 平滑 L1 損失範例
def smooth_l1_loss(y_true, y_pred):
diff = np.abs(y_true - y_pred)
loss = np.where(diff < 1, 0.5 * diff**2, diff - 0.5)
return loss
# 輸出範例
y_true = np.array([10])
y_pred = np.array([9.5])
print(smooth_l1_loss(y_true, y_pred))
內容解密:
smooth_l1_loss函式根據真實值和預測值之間的差異計算平滑 L1 損失。- 當差異小於 1 時,使用 L2 損失;否則,使用 L1 損失。
- 這種設計減少了異常值的影響,同時保持了良好的梯度特性。
物件偵測中的損失函式與非極大值抑制
在物件偵測任務中,損失函式的選擇對於模型的效能至關重要。RetinaNet 使用了 Focal Loss 來解決類別不平衡問題,但在此之前,我們先來探討 Huber Loss 在迴歸任務中的應用。
Huber Loss
Huber Loss 結合了 L1 和 L2 損失的優點,對於小的誤差使用二次方損失(L2),而對於大的誤差使用線性損失(L1)。這樣的好處是既能保持對小誤差的敏感,又能避免被少數大誤差所主導。其公式如下:
當 $|a - a’| \leq \delta$ 時,$L_\delta(a - a’) = \frac{1}{2}(a - a’)^2$
當 $|a - a’| > \delta$ 時,$L_\delta(a - a’) = \delta|a - a’| - \frac{1}{2}\delta^2$
其中 $\delta$ 是一個可調整的引數,用於控制從二次方到線性損失的轉換點。
另外,也可以使用一個替代公式來避免分段定義:
$L_\delta(a - a’) = \delta^2(\sqrt{1 + (\frac{a - a’}{\delta})^2} - 1)$
這個公式雖然與標準的 Huber Loss 不完全相同,但具有相同的行為特性:在小誤差時表現為二次方,大誤差時表現為線性。在 RetinaNet 中,使用 $\delta = 1$ 可以取得良好的效果。
程式碼範例:Huber Loss 實作
import tensorflow as tf
def huber_loss(y_true, y_pred, delta=1.0):
error = y_true - y_pred
abs_error = tf.abs(error)
quadratic = tf.minimum(abs_error, delta)
linear = abs_error - quadratic
loss = 0.5 * tf.square(quadratic) + delta * linear
return tf.reduce_mean(loss)
# 使用範例
y_true = tf.constant([1.0, 2.0, 3.0])
y_pred = tf.constant([1.1, 1.9, 3.2])
loss = huber_loss(y_true, y_pred)
print(loss)
內容解密:
- 錯誤計算:首先計算預測值與真實值之間的誤差
error。 - 絕對誤差:計算誤差的絕對值
abs_error。 - 二次方與線性部分:根據 $\delta$ 的值,決定哪些誤差被視為小誤差(使用二次方損失),哪些被視為大誤差(使用線性損失)。
- 損失計算:結合二次方和線性部分的損失,計算最終的 Huber Loss。
- 平均損失:對所有樣本的損失取平均,得到最終輸出的損失值。
非極大值抑制(NMS)
在物件偵測中,模型通常會為每個物件產生多個候選框。非極大值抑制(NMS)是一種用於選擇最具代表性的偵測框的演算法。
NMS 演算法步驟
- 對每個類別,計算所有預測框之間的 IOU(交並比)。
- 如果兩個框的 IOU 超過某個閾值 $A$,則保留置信度較高的框。
- 重複此過程,直到所有框都被處理。
程式碼範例:NMS 實作
def NMS(boxes, class_confidence, threshold=0.5):
result_boxes = []
for b1 in boxes:
discard = False
for b2 in boxes:
if IOU(b1, b2) > threshold:
if class_confidence[b2] > class_confidence[b1]:
discard = True
break
if not discard:
result_boxes.append(b1)
return result_boxes
# IOU 計算函式
def IOU(box1, box2):
# 省略具體實作
pass
內容解密:
- 遍歷所有框:對每個預測框
b1,檢查是否應被丟棄。 - IOU 比較:與其他所有框
b2計算 IOU,如果 IOU 超過閾值且b2的置信度更高,則標記b1為丟棄。 - 保留框:未被標記丟棄的框被加入結果列表。
- 傳回結果:最終傳回經過 NMS 處理後的框列表。
Soft-NMS
Soft-NMS 是 NMS 的一種變體,它不是直接丟棄重疊的框,而是降低其置信度得分。
Soft-NMS 公式
對於每個框,其置信度被降低的因子為 $\exp(-\frac{IOU^2}{\sigma})$,其中 $\sigma$ 是調整因子。
程式碼範例:Soft-NMS 實作
def Soft_NMS(boxes, class_confidence, sigma=0.5):
result_boxes = []
while boxes:
max_idx = np.argmax(class_confidence)
max_box = boxes[max_idx]
result_boxes.append(max_box)
# 計算其他框的置信度降低因子
for i, box in enumerate(boxes):
if i != max_idx:
iou = IOU(max_box, box)
class_confidence[i] *= np.exp(-(iou**2)/sigma)
# 移除已處理的框
boxes = np.delete(boxes, max_idx, axis=0)
class_confidence = np.delete(class_confidence, max_idx)
return result_boxes
內容解密:
- 選擇最高置信度框:每次迭代中,選擇置信度最高的框作為
max_box。 - 降低其他框的置信度:根據 IOU 值,降低其他框的置信度。
- 移除已處理框:將
max_box從待處理列表中移除,並重複此過程直到所有框都被處理。 - 傳回結果:最終傳回經過 Soft-NMS 處理後的框列表。
其他考慮因素
使用預訓練骨幹網路
由於分類別資料集通常比物件偵測資料集更大,因此使用預訓練的骨幹網路可以顯著提升物件偵測模型的效能。常見的做法是在大型分類別資料集上預訓練骨幹網路,然後在目標偵測任務上進行微調。
資料增強
由於物件偵測資料集通常較小,資料增強技術對於提升模型效能至關重要。常見的資料增強方法包括隨機裁剪、縮放等,以增加訓練樣本的多樣性。
程式碼範例:資料增強實作
import tensorflow as tf
def random_crop_and_resize(image, boxes, size=(640, 640)):
# 省略具體實作
pass
# 使用範例
image = tf.io.read_file('image.jpg')
image = tf.image.decode_jpeg(image, channels=3)
boxes = tf.constant([[0.1, 0.1, 0.3, 0.3]]) # 示例 bounding box
image, boxes = random_crop_and_resize(image, boxes)
內容解密:
- 隨機裁剪與縮放:對輸入影像進行隨機裁剪和縮放,以產生新的訓練樣本。
- 調整 bounding box:相應地調整 bounding box 的座標,以適應新的影像尺寸。
- 傳回結果:最終傳回經過資料增強處理後的影像和 bounding box。