7.2. Co zmieniło ML¶
Moduł image niesie ze sobą garść przestarzałych metod wykrywania – find_features() do wykrywania twarzy metodą kaskad Haara, find_eye() do stałego wyszukiwania źrenic, find_hog() do podsumowań kierunku gradientu, a także ścieżki find_keypoints() i find_lbp() dla dowolnych punktów kluczowych. Wszystkie one nadal działają; wszystkie zostały wyparte przez potok uczenia maszynowego.
7.2.1. Klasyczny podział: ręcznie zaprojektowane podsumowania, wyuczone decyzje¶
Klasyczny potok wizyjny był rzeczą dwuetapową. Pierwszy krok zamieniał surowe piksele w zwarty zbiór liczb wybranych tak, aby podsumować to, co znajdowało się na obrazie – nie same wartości pikseli, lecz krótszy opis tego, które wzorce pojawiły się i gdzie. Drugi krok brał to podsumowanie i podejmował decyzję: twarz czy nie, ten obiekt czy tamten, ten sam cel czy inny.
Podział miał znaczenie, ponieważ oba kroki miały różnych autorów. Pierwszy krok był napisany przez człowieka. Ktoś usiadł i zdecydował, że różnica jasności między dwoma konkretnymi prostokątami jest dobrym podsumowaniem obszaru oka, że dominujący kierunek krawędzi w każdej komórce siatki jest dobrym podsumowaniem zarysu stojącej osoby, że wzorzec jasny-lub-ciemny wokół każdego piksela jest dobrym podsumowaniem lokalnej tekstury. Każdy z tych wyborów był ręcznie napisanym algorytmem – napisanym, zdebugowanym i opublikowanym. Wymienione wyżej przestarzałe metody były podsumowaniami tego rodzaju, które stały się standardowymi narzędziami:
find_features()podsumowuje okno obrazu, sumując jasność wewnątrz kilku prostokątów i porównując sumy. Układy prostokątów zostały dobrane, ponieważ ludzkie twarze wykazują niezawodne kontrasty jasne-na-ciemnym: brwi na tle policzków, oczodoły na tle czoła, nos na tle otaczającej skóry.find_hog()podsumowuje obraz, przechodząc po siatce małych komórek i zapisując, który kierunek krawędzi dominuje w każdej komórce. Siatkę dobrano, ponieważ zarys stojącej osoby tworzy rozpoznawalny wzorzec kierunków krawędzi niezależnie od ubioru czy oświetlenia.find_lbp()podsumowuje sąsiedztwo każdego piksela, kodując, które z otaczających go pikseli są jaśniejsze, a które ciemniejsze. Kodowanie dobrano, ponieważ te wzorce jaśniejszy-niż / ciemniejszy-niż ujmują teksturę powierzchni niezależnie od ogólnego oświetlenia.find_keypoints()znajduje punkty narożne na obrazie i opisuje obszar wokół każdego narożnika w sposób, który pozostaje taki sam, gdy narożnik zostanie obrócony. Schemat narożnik-i-obrót dobrano, ponieważ te same narożniki pojawiają się ponownie, gdy scena jest oglądana pod innym kątem.
Gdy podsumowanie zostało już ręcznie napisane, niewielki krok uczenia na jego szczycie mógł połączyć liczby w decyzję. Algorytm wykrywania twarzy dołączył krok uczenia do podsumowania opartego na różnicach prostokątów, trenując go na oznaczonych etykietami obrazach twarzy i nie-twarzy, aby nauczył się, które kombinacje różnic sygnalizują twarz. Podsumowanie kierunku krawędzi zasilało krok uczenia trenowany na oznaczonych etykietami obrazach osób i nie-osób. Deskryptory narożników zasilały krok dopasowywania, który uczył się, jaką wagę przypisać każdemu narożnikowi. Każdy z tych drugich kroków jest algorytmem uczącym się – niewielkim jak na dzisiejsze standardy, ale jednak algorytmem uczącym się.
Znaczenie miał podział wkładu. Człowiek wnosił podsumowanie. Maszyna uczyła się kombinacji. Dodanie nowego celu oznaczało napisanie nowego podsumowania.
7.2.2. Co zmieniły sieci neuronowe¶
Sieć neuronowa znosi ten podział. Pierwsze warstwy sieci wykonują pracę podsumowującą, którą dawniej robiły ręcznie napisane algorytmy – wykrywając krawędzie, narożniki, zorientowane paski, tekstury, dokładnie te rzeczy, do których wykrywania były strojone wymienione wyżej przestarzałe metody – ale nie są napisane ręcznie. Są wyuczone z tych samych danych treningowych, z których wyuczony jest krok decyzyjny, w jednym przebiegu treningu, który dostraja obie połowy sieci naraz. Głębsze warstwy wykonują łączenie, które dawniej robił niewielki krok uczenia na szczycie ręcznie napisanych podsumowań, również wyuczone, w tym samym przebiegu.
Zmiana w tym, kto co projektuje, jest całkowita:
Człowiek projektuje wejście – przechwycone ramki o danym rozmiarze i formacie.
Człowiek projektuje wyjście – układ tensora wyniku (jedna wartość na klasę dla klasyfikacji, lista ramek dla wykrywania, siatka punktów kluczowych dla punktów charakterystycznych).
Człowiek dostarcza oznaczone etykietami dane treningowe – wystarczająco wiele przykładów celu i wystarczająco wiele przykładów nie-celów, aby proces treningu miał z czego się uczyć.
Wszystko pomiędzy wejściem a wyjściem jest generowane przez proces treningu. Nie ma osobnego kroku pisania podsumowań. Wczesne warstwy ustalają się jako detektory krawędzi i tekstur nie dlatego, że ktoś napisał je w ten sposób, lecz dlatego, że wykrywanie krawędzi i tekstur jest tym, co sprawia, że predykcje sieci pasują do etykiet. Głębsze warstwy ustalają się jako detektory kształtów i obiektów z tego samego powodu. Obie połowy są trenowane razem, co pozwala, by podsumowania wytwarzane przez każdą warstwę były dokładnie tymi podsumowaniami, których potrzebuje następna warstwa – a nie ogólnymi, na które musiał się zgodzić ręcznie pisany potok.
7.2.3. Komponowanie z modułem image¶
Potoki oparte na sieciach neuronowych nadal przechwytują przez te same API sensora, rysują wyniki przez te same prymitywy draw_rectangle() i draw_circle() oraz zawężają pracę przez te same obszary ROI (x, y, w, h). Typowy potok przechwytuje ramkę, opcjonalnie znajduje zgrubny cel za pomocą klasycznego detektora takiego jak find_blobs() i przekazuje jego ramkę ograniczającą do wnioskowania jako ROI, uruchamia wnioskowanie i nanosi zwrócone wykrycia z powrotem na pierwotną ramkę. Klasyczne prymitywy są podłożem; sieć jest nowym krokiem pośrodku.