7.2. Cosa ha cambiato il ML

Il modulo image include una manciata di metodi di rilevamento legacyfind_features() per il rilevamento di volti tramite cascate di Haar, find_eye() per il rilevatore fisso di pupille, find_hog() per i riepiloghi della direzione del gradiente, i percorsi find_keypoints() e find_lbp() per keypoint arbitrari. Funzionano tutti ancora; tutti sono stati superati dalla pipeline di machine learning.

7.2.1. La divisione classica: riepiloghi progettati a mano, decisioni apprese

Una pipeline di visione classica era un processo a due passi. Il primo passo trasformava i pixel grezzi in un insieme compatto di numeri scelti per riepilogare ciò che era presente nell’immagine – non i valori dei pixel stessi, ma una descrizione più breve di quali pattern comparivano e dove. Il secondo passo prendeva quel riepilogo e prendeva una decisione: volto o no, questo oggetto o quell’altro, stesso obiettivo o diverso.

La divisione contava perché i due passi avevano autori diversi. Il primo passo era scritto da un essere umano. Qualcuno si sedeva e decideva che la differenza di luminosità tra due rettangoli specifici era un buon riepilogo della regione di un occhio, che la direzione dominante dei bordi in ogni cella di una griglia era un buon riepilogo della sagoma di una persona in piedi, che il pattern chiaro-o-scuro intorno a ciascun pixel era un buon riepilogo della texture locale. Ognuna di queste scelte era un algoritmo scritto a mano – scritto, sottoposto a debug e pubblicato. I metodi legacy sopra elencati erano tutti riepiloghi di questo tipo che erano diventati strumenti standard:

  • find_features() riepiloga una finestra dell’immagine sommando la luminosità all’interno di diversi rettangoli e confrontando i totali. I layout dei rettangoli sono stati scelti perché i volti umani mostrano contrasti affidabili chiaro-su-scuro: sopracciglia contro guance, orbite oculari contro fronte, naso contro la pelle circostante.

  • find_hog() riepiloga un’immagine percorrendo una griglia di piccole celle e registrando quale direzione dei bordi predomina in ogni cella. La griglia è stata scelta perché la sagoma di una persona in piedi produce un pattern riconoscibile di direzioni dei bordi indipendentemente dall’abbigliamento o dall’illuminazione.

  • find_lbp() riepiloga il vicinato di ciascun pixel codificando quali dei pixel circostanti sono più chiari e quali più scuri. La codifica è stata scelta perché questi pattern di più-chiaro / più-scuro catturano la texture di una superficie indipendentemente dall’illuminazione complessiva.

  • find_keypoints() trova i punti d’angolo nell’immagine e descrive l’area attorno a ciascun angolo in un modo che rimane invariato quando l’angolo viene ruotato. Lo schema angolo-e-rotazione è stato scelto perché gli stessi angoli ricompaiono quando una scena viene osservata da un’angolazione diversa.

Una volta che un riepilogo era stato scritto a mano, un piccolo passo di apprendimento sopra di esso poteva combinare i numeri in una decisione. L’algoritmo di rilevamento dei volti agganciava un passo di apprendimento al riepilogo delle differenze tra rettangoli, addestrandolo su immagini etichettate di volti e non-volti per apprendere quali combinazioni di differenze segnalano un volto. Il riepilogo della direzione dei bordi alimentava un passo di apprendimento addestrato su immagini etichettate di persone e non-persone. I descrittori degli angoli alimentavano un passo di confronto che apprendeva quanto peso assegnare a ciascun angolo. Ognuno di questi secondi passi è un algoritmo di apprendimento – piccolo per gli standard moderni, ma pur sempre un algoritmo di apprendimento.

Ciò che contava era la divisione dei contributi. L’essere umano contribuiva con il riepilogo. La macchina apprendeva la combinazione. Aggiungere un nuovo obiettivo significava scrivere un nuovo riepilogo.

7.2.2. Cosa hanno cambiato le reti neurali

Una rete neurale cancella la divisione. I primi strati della rete svolgono il lavoro di riepilogo che gli algoritmi scritti a mano svolgevano in passato – rilevamento di bordi, angoli, barre orientate, texture, esattamente le cose che ciascuno dei metodi legacy elencati sopra era ottimizzato per rilevare – ma non sono scritti a mano. Sono appresi dagli stessi dati di addestramento da cui viene appreso il passo decisionale, in un’unica passata di addestramento che regola contemporaneamente entrambe le metà della rete. Gli strati più profondi svolgono la combinazione che il piccolo passo di apprendimento sopra i riepiloghi scritti a mano svolgeva in passato, anch’essa appresa, nella stessa passata.

Il cambiamento di chi progetta cosa è totale:

  • L’essere umano progetta l”input – frame catturati di una data dimensione e formato.

  • L’essere umano progetta l”output – il formato del tensore di risultato (un punteggio per classe per la classificazione, un elenco di box per il rilevamento, una griglia di keypoint per i landmark).

  • L’essere umano fornisce i dati di addestramento etichettati – abbastanza esempi dell’obiettivo e abbastanza esempi di non-obiettivi affinché il processo di addestramento abbia qualcosa da cui apprendere.

Tutto ciò che sta tra input e output viene generato dal processo di addestramento. Non esiste un passo separato di scrittura del riepilogo. Gli strati iniziali si assestano su rilevatori di bordi e texture non perché qualcuno li abbia scritti in quel modo, ma perché il rilevamento di bordi e texture è ciò che fa corrispondere le predizioni della rete alle etichette. Gli strati più profondi si assestano su rilevatori di forme e oggetti per la stessa ragione. Entrambe le metà vengono addestrate insieme, il che consente ai riepiloghi prodotti da ogni strato di essere esattamente i riepiloghi di cui ha bisogno lo strato successivo – non quelli generici a cui una pipeline scritta a mano doveva accontentarsi.

7.2.3. Comporre con il modulo image

Le pipeline di reti neurali catturano comunque attraverso le stesse API del sensore, disegnano i risultati attraverso le stesse primitive draw_rectangle() e draw_circle() e delimitano il lavoro attraverso le stesse ROI (x, y, w, h). Una pipeline tipica cattura un frame, opzionalmente individua un obiettivo grossolano con un rilevatore classico come find_blobs() e passa il suo bounding box all’inferenza come ROI, esegue l’inferenza e annota i rilevamenti restituiti di nuovo sul frame originale. Le primitive classiche sono il substrato; la rete è il nuovo passo che sta nel mezzo.