Cześć, tu Alex z DEV STORIES DOT EU. scikit-image: The AI Image Pipeline, odcinek 1 z 5. Zanim model deep learningowy będzie w stanie rozpoznać twarz, musi przetworzyć siatkę liczb. Jeśli podasz tę siatkę w złej kolejności, twój model niczego się nie nauczy, a przetwarzanie zwolni do ślimaczego tempa. The AI Image Pipeline:
NumPy at the Core to rozwiązanie, które pozwala poprawnie ustrukturyzować tę siatkę.
Kiedy ładujesz obraz za pomocą scikit-image, nie dostajesz żadnego własnościowego obiektu obrazu. Dostajesz standardowy N-wymiarowy array, znany jako ndarray. Obraz to po prostu macierz pikseli. Ponieważ jest to zwykły array w NumPy, możesz użyć dowolnej standardowej operacji, żeby bezpośrednio robić slice'y, maskować lub manipulować danymi obrazu.
Oto kluczowa sprawa. Sposób, w jaki scikit-image indeksuje te arraye, sprawia problemy wielu developerom. W standardowej geometrii używasz współrzędnych x i y, gdzie x to poziom, a y to pion. scikit-image rezygnuje z tego na rzecz notacji macierzowej. Pierwszy indeks to wiersz, który odpowiada pozycji pionowej. Drugi indeks to kolumna, która odpowiada pozycji poziomej. Jeśli masz kolorowy obraz, trzeci indeks to kanał koloru. Standardowy dwuwymiarowy kolorowy obraz to tak naprawdę trójwymiarowy array uporządkowany jako wiersz, kolumna, kanał.
Załóżmy, że przygotowujesz batch kolorowych obrazów, żeby przekazać je do konwolucyjnej sieci neuronowej. Twoja sieć oczekuje określonego shape'u. Jeśli twój obraz ma 256 pikseli wysokości i 256 pikseli szerokości z kanałami czerwonym, zielonym i niebieskim, shape pojedynczego obrazu to 256, 256, 3. Ale batch takich obrazów dodaje na początku czwarty wymiar, reprezentujący liczbę obrazów.
Kiedy stosujesz funkcje scikit-image do tych danych, funkcja musi wiedzieć, który wymiar zawiera wartości kolorów, żeby nie przetwarzała ich jako danych przestrzennych. Odpowiada za to argument channel_axis. Ustawiając argument channel_axis na minus jeden, mówisz funkcji, że kanały kolorów zawsze znajdują się w ostatnim wymiarze arraya. To gwarantuje, że funkcja odniesie się do właściwych danych kolorów, niezależnie od tego, czy przekażesz jej pojedynczy obraz, czy duży batch z dodatkowymi wymiarami przestrzennymi.
To prowadzi nas do pamięci. To jest ta część, która ma znaczenie dla szybkości przetwarzania. Arraye w NumPy są przechowywane w ciągłych blokach pamięci, domyślnie używając kolejności w stylu C. Oznacza to, że ostatni wymiar w shape'ie arraya zmienia się najszybciej w pamięci fizycznej. W naszym standardowym arrayu obrazu składającym się z wiersza, kolumny i kanału, poszczególne kanały kolorów dla pojedynczego piksela siedzą tuż obok siebie w twoim sprzętowym RAM-ie.
Jeśli piszesz customowy kod, żeby loopować po tych pikselach, musisz respektować ten układ pamięci. Zasada jest bezwzględna: skrajny prawy wymiar twojego arraya powinien być przetwarzany w najbardziej wewnętrznej pętli. Iterujesz po wierszach w zewnętrznej pętli, potem po kolumnach, a wewnątrz po kanałach. Jeśli to odwrócisz i będziesz loopować po wierszach wewnątrz, zmusisz CPU do skakania tam i z powrotem po pofragmentowanych adresach pamięci. To niszczy cache locality i sprawia, że twój kod działa znacznie wolniej.
Najważniejszą rzeczą do zapamiętania jest to, że scikit-image nie używa współrzędnych poziomych i pionowych; używa ścisłego indeksowania macierzowego wierszy, kolumn i kanałów, a dopasowanie twoich pętli do tej dokładnej kolejności w pamięci to coś, co utrzymuje szybkość twojego data pipeline'u.
Jeśli chcesz pomóc w utrzymaniu tego podcastu, możesz wesprzeć program, wyszukując DevStoriesEU na Patreonie. To wszystko w tym odcinku. Dzięki za słuchanie i buduj dalej!