Ciao, sono Alex di DEV STORIES DOT EU. scikit-image: The AI Image Pipeline, episodio 1 di 5. Prima che un modello di deep learning possa riconoscere un volto, deve digerire una griglia di numeri. Se dai in pasto questa griglia nell'ordine sbagliato, il tuo modello non impara nulla e l'elaborazione rallenta drasticamente. The AI Image Pipeline:
NumPy at the Core è la soluzione per strutturare correttamente questa griglia.
Quando carichi un'immagine usando scikit-image, non ottieni un oggetto immagine proprietario. Ottieni un array N-dimensionale standard, noto come ndarray. Un'immagine è semplicemente una matrice di pixel. Dato che è un semplice array NumPy, puoi usare qualsiasi operazione standard per fare slice, mask o manipolare direttamente i dati dell'immagine.
Ecco il punto chiave. Il modo in cui scikit-image indicizza questi array mette in difficoltà molti sviluppatori. Nella geometria standard usi le coordinate x e y, dove x è orizzontale e y è verticale. scikit-image abbandona questo approccio per la notazione a matrice. Il primo indice è la riga, che corrisponde alla posizione verticale. Il secondo indice è la colonna, che corrisponde alla posizione orizzontale. Se hai un'immagine a colori, il terzo indice è il channel del colore. Un'immagine a colori bidimensionale standard è in realtà un array tridimensionale ordinato come riga, colonna, channel.
Supponi di preparare un batch di immagini a colori da dare in pasto a una Convolutional Neural Network. La tua rete si aspetta una shape specifica. Se la tua immagine è alta 256 pixel e larga 256 pixel con i channel rosso, verde e blu, la shape di una singola immagine è 256, 256, 3. Ma un batch di queste immagini aggiunge una quarta dimensione all'inizio, che rappresenta il numero di immagini.
Quando applichi le funzioni di scikit-image a questi dati, la funzione deve sapere quale dimensione contiene i valori dei colori, in modo da non elaborarli come dati spaziali. Questo viene gestito dall'argomento channel axis. Impostando l'argomento channel axis a meno uno, dici alla funzione che i channel del colore si trovano sempre nell'ultima dimensione dell'array. Questo assicura che la funzione punti ai dati di colore corretti, indipendentemente dal fatto che tu le passi una singola immagine o un grande batch con dimensioni spaziali extra.
Questo ci porta alla memoria. Questa è la parte che conta per la velocità di elaborazione. Gli array NumPy vengono salvati in blocchi di memoria contigui, usando di default un ordine C-like. Questo significa che l'ultima dimensione nella shape dell'array cambia più velocemente nella memoria fisica. Per il nostro array di immagini standard fatto di riga, colonna, channel, i singoli channel di colore per un singolo pixel si trovano uno accanto all'altro nella tua RAM hardware.
Se scrivi codice custom per fare un loop su questi pixel, devi rispettare questo memory layout. La regola è assoluta: la dimensione più a destra del tuo array dovrebbe essere elaborata nel loop più interno. Iteri sulle righe nel loop esterno, poi sulle colonne, e infine sui channel all'interno. Se inverti questo ordine e fai un loop sulle righe all'interno, costringi la CPU a saltare avanti e indietro tra indirizzi di memoria frammentati. Questo distrugge la cache locality e fa girare il tuo codice molto più lentamente.
La cosa più critica da ricordare è che scikit-image non usa coordinate orizzontali e verticali; usa una rigida indicizzazione a matrice di riga, colonna e channel, e far combaciare i tuoi loop con quell'esatto ordine di memoria è ciò che mantiene veloce la tua data pipeline.
Se vuoi aiutare a portare avanti questo podcast, puoi supportare lo show cercando DevStoriesEU su Patreon. Questo è tutto per questo episodio. Grazie per l'ascolto, e continua a sviluppare!