7.2. 機器學習改變了什麼

image 模組帶有少數幾個傳統(legacy)偵測方法——find_features() 用於 Haar 級聯人臉偵測、find_eye() 用於固定的瞳孔尋找器、find_hog() 用於梯度方向的摘要、find_keypoints()find_lbp() 這兩條路徑則用於任意關鍵點。它們全都仍可運作;但全都已被機器學習流程所取代。

7.2.1. 傳統的分工:人工設計的摘要、學習而來的決策

傳統的視覺流程是一個兩步驟的東西。第一步把原始像素轉換成一組精簡的數字,這些數字經過挑選,用來摘要(summarise)畫面中的內容——不是像素值本身,而是一段較簡短的描述,說明哪些圖樣出現在哪些地方。第二步則拿這份摘要做出決策:是不是臉、是這個物件還是那個物件、是同一個目標還是不同目標。

這種分工之所以重要,是因為這兩個步驟有不同的作者。第一步是由人撰寫的。某個人坐下來,決定兩個特定矩形之間的亮度差異是眼部區域的良好摘要、決定網格中每個格子內的主導邊緣方向是站立人物輪廓的良好摘要、決定每個像素周圍的明暗圖樣是局部紋理的良好摘要。這每一個選擇都是一個人工撰寫的演算法——撰寫、除錯並發表。上述的傳統方法全都是這類已成為標準工具的摘要:

  • find_features() 透過把數個矩形內部的亮度加總起來並比較這些總和,來摘要影像中的一個視窗。這些矩形的版面之所以被選用,是因為人臉會呈現出可靠的明暗對比:眉毛對臉頰、眼窩對額頭、鼻子對周圍的皮膚。

  • find_hog() 透過走訪一個由小格子組成的網格,並記錄每個格子中佔主導的邊緣方向,來摘要一張影像。這個網格之所以被選用,是因為站立人物的輪廓無論衣著或光線如何,都會產生一種可辨識的邊緣方向圖樣。

  • find_lbp() 透過編碼每個像素周圍哪些像素較亮、哪些較暗,來摘要該像素的鄰域。這種編碼之所以被選用,是因為這些較亮/較暗的圖樣能捕捉一個表面的紋理,而不受整體光線影響。

  • find_keypoints() 在影像中找出轉角點,並以一種在轉角旋轉時仍保持不變的方式描述每個轉角周圍的區域。這套轉角與旋轉的方案之所以被選用,是因為當一個場景從不同角度觀看時,相同的轉角會重新出現。

一旦摘要被人工撰寫出來,便可以在其上加一個小型的學習(learning)步驟,把這些數字組合成一個決策。人臉偵測演算法在矩形差異摘要之上栓上了一個學習步驟,以已標記的臉部與非臉部影像訓練它,學習哪些差異組合代表一張臉。邊緣方向摘要則餵入一個以已標記的人物與非人物影像訓練的學習步驟。轉角描述子餵入一個比對步驟,學習要賦予每個轉角多少權重。這每一個第二步驟都是一個學習演算法——以現代標準而言算是小型的,但仍是學習演算法。

真正重要的是貢獻上的分工。人類貢獻了摘要,機器則學習了組合。要新增一個目標,就意味著要撰寫一份新的摘要。

7.2.2. 神經網路改變了什麼

神經網路抹除了這種分工。網路的前幾層做的正是過去人工撰寫的演算法所做的摘要工作——偵測邊緣、轉角、帶方向的條紋、紋理,也就是上面列出的那些傳統方法各自所調校去偵測的東西——但它們不是人工撰寫的。它們是從決策步驟所學習的同一批訓練資料中學習而來,在單一的訓練回合中同時調整網路的兩個部分。較深的層則做組合的工作,也就是過去那個加在人工摘要之上的小型學習步驟所做的事,同樣是學習而來,也在同一個回合中完成。

在「誰設計什麼」這件事上的改變是徹底的:

  • 由人類設計輸入——指定尺寸與格式的擷取影格。

  • 由人類設計輸出——結果張量的版面(分類為每個類別一個分數、偵測為一串邊界框、地標為一個關鍵點網格)。

  • 由人類提供已標記的訓練資料——足夠多的目標範例以及足夠多的非目標範例,好讓訓練流程有東西可以學習。

輸入與輸出之間的一切都由訓練流程產生。沒有獨立的摘要撰寫步驟。早期的層之所以安定成邊緣與紋理偵測器,並不是因為有人那樣寫它們,而是因為邊緣與紋理偵測正是讓網路的預測與標籤相符的關鍵。較深的層基於同樣的理由安定成形狀與物件偵測器。兩個部分一起訓練,這讓每一層所產生的摘要能恰好是下一層所需要的摘要——而不是人工撰寫的流程不得不將就採用的那種通用摘要。

7.2.3. 與 image 模組搭配運用

神經網路流程仍然透過相同的 sensor API 擷取影像、透過相同的 draw_rectangle()draw_circle() 基元繪製結果,並透過相同的 (x, y, w, h) ROI 來界定工作範圍。一個典型的流程會擷取一個影格,選擇性地以像 find_blobs() 這樣的傳統偵測器找出一個粗略的目標,並把它的邊界框當作 ROI 傳給推論,執行推論,再把回傳的偵測結果標註回原始影格中。傳統基元是基底;網路則是夾在中間的新步驟。