7.12. 後處理器

偵測網路並不會直接輸出框。它輸出的是一個或多個張量,其版面取決於模型訓練時所採用的架構 ── 對 YOLO 家族的偵測器而言是一個二維的候選預測張量,對 MediaPipe 偵測器而言是一對 (boxes, scores) 張量,對姿態網路而言則是一份扁平的關鍵點座標清單。應用程式無法直接讀取其中任何一種;它真正想要的東西 ── 一份框清單、一份關鍵點清單、依類別劃分的結果 ── 必須從原始張量中解碼出來。

那個解碼器就是後處理器ml.postprocessing 模組依來源生態系統將它們分組。

7.12.1. Darknet

ml.postprocessing.darknet 解碼源自最初 YOLO 時代的模型。YOLO v2 引入了後來多數偵測器多少都以某種形式承襲的網格錨點概念,因此 v2 的版面是最清晰的起點。

YOLO v2 首先將輸入影像劃分成一個粗略的網格 ── 對於標準的 416 像素輸入而言是 13 乘 13 的版面,較小的模型則更小 ── 並訓練網路使每個網格儲存格負責偵測任何中心落在其內的物體。輸出張量的空間版面反映了輸入的版面:影像中每個儲存格在輸出中對應一個位置。

在每個網格儲存格上,網路並非憑空預測出一個框。它會從數個預先選定、稱為錨點的參考形狀中挑選 ── 這些是固定的 (width, height) 配對,是透過離線地對訓練集中的框尺寸進行分群所得,以涵蓋模型預期會看到的典型物體。網路在每個儲存格的工作,是針對每個錨點預測:框中心在儲存格內的微小偏移量、套用在錨點寬高上的縮放比例、一個物體性分數(表示該處存在任何物體的可能性),以及一個各類別機率向量。因此,一個 13 乘 13 的網格搭配預設的 5 個錨點與 20 個類別,每次推論便會輸出 13 * 13 * 5 * (4 + 1 + 20) = 21,125 個數值。

YoloV2 解碼該版面:它走訪各儲存格,套用每個錨點的偏移量與縮放比例以還原絕對框座標,將物體性與類別機率結合為各類別分數,進行閾值過濾,再把存活者推送給 NMS。當模型是針對自訂錨點表訓練時,該類別接受 anchors= 建構子引數,否則退回使用內建的預設值。針對特定類別集合調校的變體也隨附於同一個子模組中。

7.12.2. Ultralytics

ml.postprocessing.ultralytics 解碼較新的 YOLO 世代。YoloV8 讀取一個以欄為主的輸出,其中每一欄是一個錨點預測,內含框座標與一個各類別分數向量 ── 早期 YOLO 輸出所帶有的物體性通道在 v8 中已被移除,類別分數獨立存在。YOLOv8 逐步解說 會逐張量地走過解碼過程。較舊的 Ultralytics 時代版本也隨附於同一個子模組中,供針對其版面訓練的模型使用。

7.12.3. MediaPipe

ml.postprocessing.mediapipe 解碼 Google 的輕量化裝置端家族。BlazeFacehello-blazeface 中介紹的人臉偵測器:一個快速的、以錨點為基礎的偵測器,會為每張臉輸出框與六個地標座標,並以 (box, score, keypoints) 元組回傳,地標附加在各個框上,而非作為一份獨立的輸出清單。同家族的手部偵測、地標與姿態模型也一併隨附,並遵循相同的附加關鍵點回傳結構。

7.12.4. 挑選一個

正確的後處理器是由模型訓練時所採用的架構決定的,而不是由應用程式想要什麼決定的。一個 YOLOv8 的 .tflite 只能透過 YoloV8 正確解碼;一個 BlazeFace 的 .tflite 只能透過 BlazeFace。挑選後處理器是挑選模型的一部分。當某個模型的架構並未被任何隨附的後處理器所涵蓋時,自行撰寫一個 也很直接。

分類網路是例外。它們單一的輸出張量本身就是應用程式想要的東西 ── 一份各類別分數的清單 ── 不需要後處理器。不帶 postprocess= 載入模型,並將預測結果讀取為一個扁平的 ndarray,才是正確的途徑,這一點 張量 I/O 已介紹過。