7.15. 自行撰寫¶
當目錄並未涵蓋某個模型時 ── 例如輸出版面為量身打造的研究網路、對既有架構所做的調整、語意詮釋因應用而異的張量 ── 應用程式便要提供自己的後處理器。其協定很單純:一個接收 (model, inputs, outputs) 並回傳應用程式期望從 predict() 得到之任何結果的可呼叫物件。
帶有 __call__ 的類別是慣用的形式:
class MyPostprocessor:
def __init__(self, threshold=0.5):
self.threshold = threshold
def __call__(self, model, inputs, outputs):
...
return result
一個普通的函式也行得通 ── 引擎只檢查該物件是否可呼叫。
7.15.1. 將其掛接進來¶
有兩個掛接點。建構子上的 postprocess= 關鍵字引數會為該模型上每一次 predict() 呼叫綁定該可呼叫物件:
model = ml.Model("/rom/my_model.tflite",
postprocess=MyPostprocessor())
若要針對單次呼叫覆寫此綁定 ── 在不重新載入模型的情況下抽換解碼器 ── 可直接將 callback= 傳給 predict:
result = model.predict([img], callback=MyOtherPostprocessor())
無論哪一種情況,可呼叫物件的簽章都是相同的。
7.15.2. 可呼叫物件會收到什麼¶
model── 即Model實例,對於量化參數(output_scale、output_zero_point、output_dtype)與輸入維度(input_shape)很有用。inputs── 即應用程式傳給predict()的輸入清單。第一個元素通常是綁定的Normalization實例;其roi屬性正是NMS將框重新映射回原始影像時所需要的。outputs── 即原始輸出張量,為一份ndarray物件的清單,採其原生 dtype。浮點輸出會原樣送達;整數輸出則以量化形式送達。
7.15.3. 量化運算¶
隨附的解碼器全都取用 ml.utils 中相同的輔助函式,而自訂的解碼器通常也想要相同的模式:quantize() 將浮點閾值提升到模型的量化空間,threshold() 在不對整個張量去量化的情況下進行過濾,而 dequantize() 則只對存活者執行一次。sigmoid() 與 logit() 可供輸出通道為 sigmoid 前 logits 的網路使用(MediaPipe 偵測器是典型案例)。
對於具有浮點輸出的模型 ── 例如迴歸頭、內建了最終去量化層的模型 ── 這些量化輔助函式會原樣穿透,因此同一份後處理器程式碼無需特例處理便能對任一種 dtype 運作。
7.15.4. 回傳值¶
可呼叫物件回傳什麼,predict() 就回傳什麼。對於輸出框的解碼器,慣例是將候選框推送過一個 NMS 並回傳其各類別清單 ── 這正是 非極大值抑制 所記載、且 YOLOv8 逐步解說 在實際情境中所建立的呼叫結構。至於其他任何情況,則回傳應用程式覺得方便的任何形式:單一個 ndarray、一個標籤字串、一個 (class, score, embedding) 元組,或一個字典。