1. Przegląd
W tym module poznasz nowoczesną architekturę konwolucyjną i wykorzystasz tę wiedzę do wdrożenia prostej, ale skutecznej sieci konwolucyjnej o nazwie „squeezenet”.
Ten moduł zawiera niezbędne wyjaśnienia teoretyczne dotyczące konwolucyjnych sieci neuronowych i jest dobrym punktem wyjścia dla programistów, którzy chcą dowiedzieć się więcej o deep learning.
Ten moduł jest czwartą częścią serii „Keras on TPU”. Możesz wykonać je w podanej kolejności lub niezależnie od siebie.
- Potoki danych o szybkości TPU: tf.data.Dataset i TFRecords
- Pierwszy model Keras z uczeniem przez przenoszenie
- Splotowe sieci neuronowe z użyciem Keras i TPU
- [THIS LAB] Nowoczesne sieci konwolucyjne, SqueezeNet, Xception z użyciem Keras i TPU

Czego się nauczysz
- Aby opanować styl funkcjonalny Keras
- Aby utworzyć model przy użyciu architektury squeezenet
- korzystać z TPU, aby szybko trenować modele i iteracyjnie ulepszać architekturę;
- Implementowanie rozszerzania danych za pomocą tf.data.dataset
- Dostrajanie wstępnie wytrenowanego dużego modelu (Xception) na TPU
Prześlij opinię
Jeśli zauważysz w tym module coś nieprawidłowego, poinformuj nas o tym. Opinie można przesyłać za pomocą zgłoszeń w GitHub [link do opinii].
2. Krótkie wprowadzenie do Google Colaboratory
W tym module używamy Google Collaboratory, więc nie musisz niczego konfigurować. Colaboratory to platforma online z notatnikami przeznaczona do celów edukacyjnych. Oferuje bezpłatne szkolenia dotyczące procesorów, procesorów graficznych i TPU.

Możesz otworzyć ten przykładowy notatnik i uruchomić kilka komórek, aby zapoznać się z Colaboratory.
Wybieranie backendu TPU

W menu Colab kliknij Środowisko wykonawcze > Zmień typ środowiska wykonawczego, a następnie wybierz TPU. W tym module nauczysz się korzystać z wydajnej jednostki TPU (Tensor Processing Unit) do trenowania z akceleracją sprzętową. Połączenie z czasem działania nastąpi automatycznie przy pierwszym wykonaniu kodu. Możesz też użyć przycisku „Połącz” w prawym górnym rogu.
Wykonywanie notatnika

Uruchamiaj komórki pojedynczo, klikając je i naciskając Shift + Enter. Możesz też uruchomić cały notatnik, klikając Środowisko wykonawcze > Uruchom wszystko.
Spis treści

Wszystkie notatniki mają spis treści. Możesz go otworzyć, klikając czarną strzałkę po lewej stronie.
Ukryte komórki

Niektóre komórki będą wyświetlać tylko swój tytuł. Jest to funkcja notatnika Colab. Możesz kliknąć je dwukrotnie, aby zobaczyć kod w środku, ale zwykle nie jest on zbyt interesujący. Zwykle są to funkcje pomocnicze lub wizualizacyjne. Aby zdefiniować funkcje w komórkach, musisz je uruchomić.
Uwierzytelnianie

Colab może uzyskać dostęp do Twoich prywatnych zasobników Google Cloud Storage, jeśli uwierzytelnienie nastąpi przy użyciu autoryzowanego konta. Powyższy fragment kodu uruchomi proces uwierzytelniania.
3. [INFO] Czym są jednostki Tensor Processing Unit (TPU)?
W skrócie

Kod do trenowania modelu na TPU w Keras (z możliwością powrotu do GPU lub CPU, jeśli TPU nie jest dostępny):
try: # detect TPUs
tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect()
strategy = tf.distribute.TPUStrategy(tpu)
except ValueError: # detect GPUs
strategy = tf.distribute.MirroredStrategy() # for CPU/GPU or multi-GPU machines
# use TPUStrategy scope to define model
with strategy.scope():
model = tf.keras.Sequential( ... )
model.compile( ... )
# train model normally on a tf.data.Dataset
model.fit(training_dataset, epochs=EPOCHS, steps_per_epoch=...)
Dziś użyjemy TPU do zbudowania i zoptymalizowania klasyfikatora kwiatów w interaktywnym tempie (minuty na przebieg trenowania).

Dlaczego jednostki TPU?
Nowoczesne procesory graficzne są zorganizowane wokół programowalnych „rdzeni”, co stanowi bardzo elastyczną architekturę, która umożliwia im wykonywanie różnych zadań, takich jak renderowanie 3D, uczenie głębokie, symulacje fizyczne itp. TPU z kolei łączą klasyczny procesor wektorowy z dedykowaną jednostką mnożenia macierzy i doskonale sprawdzają się w każdym zadaniu, w którym dominują duże mnożenia macierzy, np. w sieciach neuronowych.

Ilustracja: gęsta warstwa sieci neuronowej jako mnożenie macierzy, w którym partia 8 obrazów jest przetwarzana przez sieć neuronową jednocześnie. Wykonaj mnożenie wiersza przez kolumnę, aby sprawdzić, czy rzeczywiście oblicza ważoną sumę wartości wszystkich pikseli obrazu. Warstwy konwolucyjne też można przedstawić jako mnożenie macierzy, choć jest to nieco bardziej skomplikowane ( wyjaśnienie znajdziesz tutaj, w sekcji 1).
Sprzęt
MXU i VPU
Rdzeń TPU v2 składa się z jednostki mnożnika macierzy (MXU), która wykonuje mnożenie macierzy, oraz jednostki przetwarzania wektorowego (VPU), która wykonuje wszystkie inne zadania, takie jak aktywacje, softmax itp. Jednostka VPU obsługuje obliczenia zmiennoprzecinkowe 32-bitowe i całkowite 32-bitowe. MXU działa natomiast w formacie zmiennoprzecinkowym o mieszanej precyzji 16–32 bitów.

Zmiennoprzecinkowe liczby mieszanej precyzji i bfloat16
MXU oblicza mnożenie macierzy przy użyciu danych wejściowych bfloat16 i danych wyjściowych float32. Pośrednie akumulacje są wykonywane z precyzją float32.

Trenowanie sieci neuronowych jest zwykle odporne na szum wprowadzany przez zmniejszoną precyzję zmiennoprzecinkową. W niektórych przypadkach szum pomaga nawet optymalizatorowi osiągnąć zbieżność. 16-bitowa precyzja zmiennoprzecinkowa była tradycyjnie używana do przyspieszania obliczeń, ale formaty float16 i float32 mają bardzo różne zakresy. Zmniejszenie precyzji z float32 do float16 zwykle powoduje przekroczenie zakresu w górę i w dół. Istnieją rozwiązania, ale zwykle wymagają dodatkowej pracy, aby float16 działał prawidłowo.
Dlatego w TPU wprowadziliśmy format bfloat16. Jest to skrócony format float32, który ma dokładnie te same bity wykładnika i zakres co float32. W połączeniu z faktem, że TPU wykonują mnożenie macierzy w mieszanej precyzji z danymi wejściowymi bfloat16, ale danymi wyjściowymi float32, oznacza to, że zwykle nie są potrzebne żadne zmiany w kodzie, aby skorzystać ze wzrostu wydajności wynikającego z mniejszej precyzji.
Macierz systoliczna
MXU wykonuje mnożenie macierzy w sprzęcie za pomocą tzw. architektury „tablicy systolicznej”, w której elementy danych przepływają przez tablicę jednostek obliczeniowych. (W medycynie termin „skurczowy” odnosi się do skurczów serca i przepływu krwi, a w tym przypadku do przepływu danych).
Podstawowym elementem mnożenia macierzy jest iloczyn skalarny wiersza jednej macierzy i kolumny drugiej macierzy (ilustracja u góry tej sekcji). W przypadku mnożenia macierzy Y=X*W jeden element wyniku to:
Y[2,0] = X[2,0]*W[0,0] + X[2,1]*W[1,0] + X[2,2]*W[2,0] + ... + X[2,n]*W[n,0]
Na procesorze graficznym programuje się ten iloczyn skalarny w „rdzeniu” procesora graficznego, a następnie wykonuje się go równolegle na tylu „rdzeniach”, ile jest dostępnych, aby spróbować obliczyć wszystkie wartości wynikowej macierzy naraz. Jeśli wynikowa macierz ma rozmiar 128 x 128, wymagałoby to 128 x 128=16 tys. „rdzeni”, co zwykle nie jest możliwe. Największe procesory graficzne mają około 4000 rdzeni. TPU z kolei wykorzystuje w jednostkach obliczeniowych MXU absolutne minimum sprzętu: tylko bfloat16 x bfloat16 => float32 mnożarki-akumulatory. Są one tak małe, że jednostka TPU może zaimplementować 16 tys. takich jednostek w macierzy MXU o wymiarach 128 x 128 i przetworzyć to mnożenie macierzy za jednym razem.

Ilustracja: tablica skurczowa MXU. Elementami obliczeniowymi są mnożniki-akumulatory. Wartości jednej macierzy są wczytywane do tablicy (czerwone kropki). Wartości z drugiej macierzy przepływają przez tablicę (szare kropki). Linie pionowe przenoszą wartości w górę. Linie poziome propagują sumy częściowe. Sprawdzenie, czy w miarę przepływu danych przez tablicę po prawej stronie pojawia się wynik mnożenia macierzy, pozostawiamy użytkownikowi.
Dodatkowo podczas obliczania iloczynów skalarnych w MXU sumy pośrednie przepływają między sąsiednimi jednostkami obliczeniowymi. Nie trzeba ich przechowywać ani pobierać z pamięci ani nawet z pliku rejestru. W rezultacie architektura tablicy systolicznej TPU ma znaczną przewagę pod względem gęstości i mocy, a także niepomijalną przewagę pod względem szybkości nad GPU podczas obliczania mnożenia macierzy.
Cloud TPU
Gdy poprosisz o 1 „Cloud TPU v2” na Google Cloud Platform, otrzymasz maszynę wirtualną z płytą TPU podłączoną przez PCI. Płyta TPU ma 4 dwurdzeniowe układy TPU. Każdy rdzeń TPU ma jednostkę VPU (Vector Processing Unit) i jednostkę MXU (MatriX multiply Unit) o wymiarach 128 x 128. Ta „Cloud TPU” jest zwykle połączona przez sieć z maszyną wirtualną, która ją zażądała. Pełny obraz wygląda więc tak:

Ilustracja: maszyna wirtualna z akceleratorem „Cloud TPU” podłączonym do sieci. „Cloud TPU” to maszyna wirtualna z płytą TPU podłączoną przez PCI, na której znajdują się 4 dwurdzeniowe układy TPU.
Pody TPU
W centrach danych Google układy TPU są połączone z interfejsem komputera o dużej mocy obliczeniowej (HPC), dzięki czemu mogą działać jako jeden bardzo duży akcelerator. Google nazywa je podami. Mogą one obejmować do 512 rdzeni TPU v2 lub 2048 rdzeni TPU v3.

Ilustracja: pod TPU v3. Płyty i szafy TPU połączone za pomocą połączenia międzysieciowego HPC.
Podczas trenowania gradienty są wymieniane między rdzeniami TPU za pomocą algorytmu all-reduce ( dobre wyjaśnienie algorytmu all-reduce znajdziesz tutaj). Trenowany model może wykorzystywać sprzęt, trenując na dużych rozmiarach wsadu.

Ilustracja: synchronizacja gradientów podczas trenowania z użyciem algorytmu all-reduce w dwuwymiarowej sieci HPC o topologii toroidu na jednostkach TPU Google.
Oprogramowanie
Trenowanie z dużą wielkością wsadu
Idealna wielkość wsadu w przypadku TPU to 128 elementów danych na rdzeń TPU, ale sprzęt może już wykazywać dobre wykorzystanie od 8 elementów danych na rdzeń TPU. Pamiętaj, że jedna Cloud TPU ma 8 rdzeni.
W tym module użyjemy interfejsu Keras API. W Keras określona przez Ciebie wielkość wsadu jest globalną wielkością wsadu dla całego TPU. Partie zostaną automatycznie podzielone na 8 części i uruchomione na 8 rdzeniach TPU.

Dodatkowe wskazówki dotyczące wydajności znajdziesz w przewodniku po wydajności TPU. W przypadku bardzo dużych rozmiarów partii w niektórych modelach może być wymagana szczególna ostrożność. Więcej informacji znajdziesz w artykule LARSOptimizer.
Dla zaawansowanych: XLA
Programy TensorFlow definiują wykresy obliczeniowe. TPU nie uruchamia bezpośrednio kodu Pythona, ale wykonuje wykres obliczeniowy zdefiniowany przez program TensorFlow. W tle kompilator XLA (kompilator przyspieszonej algebry liniowej) przekształca wykres obliczeniowy TensorFlow w kod maszynowy TPU. Ten kompilator wykonuje też wiele zaawansowanych optymalizacji kodu i układu pamięci. Kompilacja odbywa się automatycznie w miarę przesyłania zadań do TPU. Nie musisz wyraźnie uwzględniać XLA w łańcuchu kompilacji.

Ilustracja: aby uruchomić program na TPU, graf obliczeniowy zdefiniowany przez program TensorFlow jest najpierw tłumaczony na reprezentację XLA (kompilator przyspieszonej algebry liniowej), a następnie kompilowany przez XLA do kodu maszynowego TPU.
Korzystanie z TPU w Keras
TPU są obsługiwane przez interfejs Keras API od wersji TensorFlow 2.1. Obsługa Keras działa na TPU i podach TPU. Oto przykład, który działa na TPU, GPU i CPU:
try: # detect TPUs
tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect()
strategy = tf.distribute.TPUStrategy(tpu)
except ValueError: # detect GPUs
strategy = tf.distribute.MirroredStrategy() # for CPU/GPU or multi-GPU machines
# use TPUStrategy scope to define model
with strategy.scope():
model = tf.keras.Sequential( ... )
model.compile( ... )
# train model normally on a tf.data.Dataset
model.fit(training_dataset, epochs=EPOCHS, steps_per_epoch=...)
W tym fragmencie kodu:
TPUClusterResolver().connect()znajduje TPU w sieci. Działa bez parametrów w większości systemów Google Cloud (zadania AI Platform, Colaboratory, Kubeflow, maszyny wirtualne do deep learningu utworzone za pomocą narzędzia „ctpu up”). Te systemy wiedzą, gdzie znajduje się ich TPU, dzięki zmiennej środowiskowej TPU_NAME. Jeśli utworzysz TPU ręcznie, ustaw zmienną środowiskową TPU_NAME na maszynie wirtualnej, z której korzystasz, lub wywołaj polecenieTPUClusterResolverz jawnymi parametrami:TPUClusterResolver(tp_uname, zone, project)TPUStrategyto część, która implementuje algorytm dystrybucji i synchronizacji gradientów „all-reduce”.- Strategia jest stosowana w ramach zakresu. Model musi być zdefiniowany w zakresie funkcji strategy().
- Funkcja
tpu_model.fitoczekuje obiektu tf.data.Dataset jako danych wejściowych do trenowania na TPU.
Typowe zadania związane z przenoszeniem kodu na TPU
- Dane do modelu TensorFlow można wczytywać na wiele sposobów, ale w przypadku TPU wymagane jest użycie interfejsu
tf.data.Dataset. - Procesory TPU są bardzo szybkie, więc podczas ich używania wąskim gardłem często staje się wczytywanie danych. W przewodniku po wydajności TPU znajdziesz narzędzia, które pomogą Ci wykryć wąskie gardła danych, oraz inne wskazówki dotyczące wydajności.
- Liczby int8 lub int16 są traktowane jako int32. TPU nie ma sprzętu do liczb całkowitych działającego na mniej niż 32 bitach.
- Niektóre operacje TensorFlow nie są obsługiwane. Lista jest dostępna tutaj. Dobra wiadomość jest taka, że to ograniczenie dotyczy tylko kodu trenującego, czyli przejścia w przód i w tył przez model. W potoku wprowadzania danych nadal możesz używać wszystkich operacji TensorFlow, ponieważ będą one wykonywane na procesorze.
tf.py_funcnie jest obsługiwany na TPU.
4. [INFO] Klasyfikator sieci neuronowej – podstawy
W skrócie
Jeśli znasz już wszystkie pogrubione terminy w następnym akapicie, możesz przejść do kolejnego ćwiczenia. Jeśli dopiero zaczynasz przygodę z uczeniem głębokim, witamy. Czytaj dalej.
W przypadku modeli zbudowanych jako sekwencja warstw Keras oferuje interfejs Sequential API. Na przykład klasyfikator obrazów korzystający z 3 warstw gęstych można zapisać w Kerasie w ten sposób:
model = tf.keras.Sequential([
tf.keras.layers.Flatten(input_shape=[192, 192, 3]),
tf.keras.layers.Dense(500, activation="relu"),
tf.keras.layers.Dense(50, activation="relu"),
tf.keras.layers.Dense(5, activation='softmax') # classifying into 5 classes
])
# this configures the training of the model. Keras calls it "compiling" the model.
model.compile(
optimizer='adam',
loss= 'categorical_crossentropy',
metrics=['accuracy']) # % of correct answers
# train the model
model.fit(dataset, ... )

Gęsta sieć neuronowa
Jest to najprostsza sieć neuronowa do klasyfikowania obrazów. Składa się z „neuronów” ułożonych w warstwach. Pierwsza warstwa przetwarza dane wejściowe i przekazuje wyniki do innych warstw. Jest ona nazywana „gęstą”, ponieważ każdy neuron jest połączony ze wszystkimi neuronami w poprzedniej warstwie.

Obraz możesz przekazać do takiej sieci, spłaszczając wartości RGB wszystkich pikseli do długiego wektora i używając go jako danych wejściowych. Nie jest to najlepsza technika rozpoznawania obrazów, ale w przyszłości ją ulepszymy.
Neurony, aktywacje, RELU
„Neuron” oblicza ważoną sumę wszystkich danych wejściowych, dodaje wartość zwaną „obciążeniem” i przekazuje wynik przez tzw. „funkcję aktywacji”. Wagi i wartości progowe są początkowo nieznane. Są one inicjowane losowo i „uczone” przez trenowanie sieci neuronowej na wielu znanych danych.

Najpopularniejsza funkcja aktywacji to RELU (Rectified Linear Unit). Jest to bardzo prosta funkcja, co widać na wykresie powyżej.
Funkcja aktywacji softmax
Powyższa sieć kończy się warstwą 5 neuronów, ponieważ klasyfikujemy kwiaty w 5 kategoriach (róża, tulipan, mniszek lekarski, stokrotka, słonecznik). Neurony w warstwach pośrednich są aktywowane za pomocą klasycznej funkcji aktywacji RELU. W ostatniej warstwie chcemy jednak obliczyć liczby z zakresu od 0 do 1, które będą reprezentować prawdopodobieństwo, że dany kwiat jest różą, tulipanem itp. W tym celu użyjemy funkcji aktywacji o nazwie „softmax”.
Zastosowanie funkcji softmax do wektora polega na obliczeniu wartości wykładniczej każdego elementu, a następnie znormalizowaniu wektora, zwykle za pomocą normy L1 (sumy wartości bezwzględnych), tak aby wartości sumowały się do 1 i można je było interpretować jako prawdopodobieństwa.

Funkcja straty entropii krzyżowej
Gdy nasza sieć neuronowa generuje prognozy na podstawie obrazów wejściowych, musimy zmierzyć, jak dobre są te prognozy, czyli odległość między tym, co mówi nam sieć, a prawidłowymi odpowiedziami, często nazywanymi „etykietami”. Pamiętaj, że mamy prawidłowe etykiety dla wszystkich obrazów w zbiorze danych.
Sprawdzi się dowolna odległość, ale w przypadku problemów z klasyfikacją najskuteczniejsza jest tzw. odległość entropii krzyżowej. Nazwiemy ją funkcją błędu lub „straty”:

Metoda gradientu prostego
„Trenowanie” sieci neuronowej polega na używaniu obrazów i etykiet treningowych do dostosowywania wag i odchyleń w celu zminimalizowania funkcji straty entropii krzyżowej. Działa to w następujący sposób:
Entropia krzyżowa jest funkcją wag, odchyleń, pikseli obrazu treningowego i jego znanej klasy.
Jeśli obliczymy pochodne cząstkowe entropii krzyżowej względem wszystkich wag i wszystkich odchyleń, otrzymamy „gradient” obliczony dla danego obrazu, etykiety oraz bieżącej wartości wag i odchyleń. Pamiętaj, że możemy mieć miliony wag i odchyleń, więc obliczanie gradientu wydaje się bardzo pracochłonne. Na szczęście TensorFlow robi to za nas. Własnością matematyczną gradientu jest to, że wskazuje on „w górę”. Chcemy, aby entropia krzyżowa była jak najmniejsza, więc idziemy w przeciwnym kierunku. Wagi i odchylenia aktualizujemy o ułamek gradientu. Następnie powtarzamy ten proces w przypadku kolejnych partii obrazów i etykiet treningowych w pętli trenowania. Mamy nadzieję, że zbiegnie się to w miejscu, w którym entropia krzyżowa jest minimalna, chociaż nic nie gwarantuje, że to minimum jest unikalne.

Mini-batching i dynamika
Możesz obliczyć gradient na podstawie tylko jednego przykładowego obrazu i od razu zaktualizować wagi i odchylenia, ale zrobienie tego na podstawie np. 128 obrazów daje gradient, który lepiej odzwierciedla ograniczenia narzucone przez różne przykładowe obrazy, a tym samym prawdopodobnie szybciej zbiega się do rozwiązania. Rozmiar mini-partii jest parametrem, który można dostosować.
Ta technika, czasami nazywana „stochastycznym spadkiem gradientu”, ma jeszcze jedną, bardziej praktyczną zaletę: praca z partiami oznacza też pracę z większymi macierzami, które zwykle łatwiej jest optymalizować na GPU i TPU.
Proces zbieżności może być jednak nieco chaotyczny, a nawet zatrzymać się, jeśli wektor gradientu będzie składać się z samych zer. Czy to oznacza, że znaleźliśmy minimum? Nie zawsze. Składnik gradientu może wynosić zero w przypadku wartości minimalnej lub maksymalnej. W przypadku wektora gradientu z milionami elementów, jeśli wszystkie są zerami, prawdopodobieństwo, że każde zero odpowiada minimum, a żadne z nich nie odpowiada punktowi maksimum, jest dość małe. W przestrzeni wielowymiarowej punkty siodłowe są dość powszechne i nie chcemy się w nich zatrzymywać.

Ilustracja: punkt siodłowy. Gradient wynosi 0, ale nie jest minimum we wszystkich kierunkach. (Atrybucja obrazu: Wikimedia: By Nicoguaro - Own work, CC BY 3.0)
Rozwiązaniem jest dodanie do algorytmu optymalizacji pewnego rozpędu, aby mógł on przechodzić przez punkty siodłowe bez zatrzymywania się.
Słownik
wsadowe lub miniwsadowe: trenowanie jest zawsze przeprowadzane na partiach danych treningowych i etykiet. Ułatwia to algorytmowi zbieżność. Wymiar „batch” jest zwykle pierwszym wymiarem tensorów danych. Na przykład tensor o kształcie [100, 192, 192, 3] zawiera 100 obrazów o wymiarach 192 x 192 piksele z 3 wartościami na piksel (RGB).
funkcja straty entropii krzyżowej: specjalna funkcja straty często używana w klasyfikatorach.
warstwa gęsta: warstwa neuronów, w której każdy neuron jest połączony ze wszystkimi neuronami w poprzedniej warstwie.
cechy: dane wejściowe sieci neuronowej są czasami nazywane „cechami”. Sztuka określania, które części zbioru danych (lub ich kombinacje) należy przekazać do sieci neuronowej, aby uzyskać dobre prognozy, jest nazywana „inżynierią cech”.
etykiety: inna nazwa „klas” lub prawidłowych odpowiedzi w problemie klasyfikacji nadzorowanej.
Tempo uczenia się: ułamek gradientu, o który wagi i odchylenia są aktualizowane w każdej iteracji pętli trenowania.
Logity: wyniki warstwy neuronów przed zastosowaniem funkcji aktywacji są nazywane „logitami”. Nazwa pochodzi od „funkcji logistycznej”, czyli „funkcji sigmoidalnej”, która była najpopularniejszą funkcją aktywacji. „Neuron outputs before logistic function” (Wyniki neuronu przed funkcją logistyczną) skrócono do „logits” (logity).
funkcja straty: funkcja błędu porównująca dane wyjściowe sieci neuronowej z prawidłowymi odpowiedziami;
Neuron: oblicza ważoną sumę danych wejściowych, dodaje do niej wartość progową i przekazuje wynik przez funkcję aktywacji.
Kodowanie 1 z n: klasa 3 z 5 jest kodowana jako wektor 5 elementów, z których wszystkie są zerami z wyjątkiem 3 elementu, który ma wartość 1.
relu: jednostka liniowa z prostownikiem. Popularna funkcja aktywacji neuronów.
sigmoid: kolejna funkcja aktywacji, która była kiedyś popularna i nadal jest przydatna w szczególnych przypadkach.
softmax: specjalna funkcja aktywacji, która działa na wektor, zwiększa różnicę między największym komponentem a wszystkimi pozostałymi, a także normalizuje wektor, aby jego suma wynosiła 1, dzięki czemu można go interpretować jako wektor prawdopodobieństw. Używana jako ostatni krok w klasyfikatorach.
tensor: „tensor” to macierz o dowolnej liczbie wymiarów. Tensor 1-wymiarowy to wektor. Tensor 2-wymiarowy to macierz. Możesz też mieć tensory o 3, 4, 5 wymiarach lub więcej.
5. [INFO] Splotowe sieci neuronowe
W skrócie
Jeśli znasz już wszystkie pogrubione terminy w następnym akapicie, możesz przejść do kolejnego ćwiczenia. Jeśli dopiero zaczynasz przygodę z konwolucyjnymi sieciami neuronowymi, czytaj dalej.

Ilustracja: filtrowanie obrazu za pomocą 2 kolejnych filtrów, z których każdy ma 48 wag do nauczenia (4 x 4 x 3=48).
Tak wygląda prosta splotowa sieć neuronowa w Kerasie:
model = tf.keras.Sequential([
# input: images of size 192x192x3 pixels (the three stands for RGB channels)
tf.keras.layers.Conv2D(kernel_size=3, filters=24, padding='same', activation='relu', input_shape=[192, 192, 3]),
tf.keras.layers.Conv2D(kernel_size=3, filters=24, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=12, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=6, padding='same', activation='relu'),
tf.keras.layers.Flatten(),
# classifying into 5 categories
tf.keras.layers.Dense(5, activation='softmax')
])
model.compile(
optimizer='adam',
loss= 'categorical_crossentropy',
metrics=['accuracy'])

Wprowadzenie do konwolucyjnych sieci neuronowych
W warstwie sieci konwolucyjnej jeden „neuron” oblicza ważoną sumę pikseli znajdujących się bezpośrednio nad nim, ale tylko w niewielkim obszarze obrazu. Dodaje on odchylenie i przekazuje sumę przez funkcję aktywacji, tak jak neuron w zwykłej warstwie gęstej. Następnie operacja jest powtarzana na całym obrazie z użyciem tych samych wag. Pamiętaj, że w warstwach gęstych każdy neuron miał własne wagi. W tym przypadku pojedynczy „fragment” wag przesuwa się po obrazie w obu kierunkach (jest to „splot”). Dane wyjściowe mają tyle wartości, ile pikseli na obrazie (chociaż na krawędziach konieczne jest pewne wypełnienie). Jest to operacja filtrowania z użyciem filtra o 48 wagach (4 x 4 x 3=48).
Jednak 48 wag nie wystarczy. Aby dodać więcej stopni swobody, powtarzamy tę samą operację z nowym zestawem wag. Spowoduje to wygenerowanie nowego zestawu danych wyjściowych filtra. Nazwijmy go „kanałem” wyników, analogicznie do kanałów R, G, B na obrazie wejściowym.

Dwa (lub więcej) zestawy wag można zsumować jako jeden tensor, dodając nowy wymiar. Daje nam to ogólny kształt tensora wag dla warstwy konwolucyjnej. Liczba kanałów wejściowych i wyjściowych jest parametrem, więc możemy zacząć układać i łączyć warstwy konwolucyjne.

Ilustracja: konwolucyjna sieć neuronowa przekształca „kostki” danych w inne „kostki” danych.
Konwolucje z krokiem, maksymalne próbkowanie
Wykonując konwolucje z krokiem 2 lub 3, możemy też zmniejszyć wynikowy sześcian danych w wymiarach poziomych. Można to zrobić na 2 sposoby:
- Konwolucja z krokiem: przesuwany filtr jak powyżej, ale z krokiem > 1.
- Maksymalne uśrednianie: okno przesuwne stosujące operację MAX (zwykle na fragmentach 2x2, powtarzane co 2 piksele).

Ilustracja: przesunięcie okna obliczeniowego o 3 piksele powoduje zmniejszenie liczby wartości wyjściowych. Sploty z krokiem lub maksymalne próbkowanie (maksymalna wartość w oknie 2x2 przesuwającym się o krok 2) to sposób na zmniejszenie kostki danych w wymiarach poziomych.
Klasyfikator konwolucyjny
Na koniec dołączamy głowicę klasyfikacji, spłaszczając ostatnią kostkę danych i przekazując ją przez gęstą warstwę aktywowaną funkcją softmax. Typowy klasyfikator konwolucyjny może wyglądać tak:

Ilustracja: klasyfikator obrazów korzystający z warstw konwolucyjnych i softmax. Wykorzystuje filtry 3x3 i 1x1. Warstwy maxpool wybierają maksymalną wartość z grup punktów danych o rozmiarze 2x2. Głowica klasyfikacji jest zaimplementowana za pomocą warstwy gęstej z funkcją aktywacji softmax.
W Keras
Przedstawiony powyżej stos konwolucyjny można zapisać w Kerasie w ten sposób:
model = tf.keras.Sequential([
# input: images of size 192x192x3 pixels (the three stands for RGB channels)
tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu', input_shape=[192, 192, 3]),
tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
tf.keras.layers.MaxPooling2D(pool_size=2),
tf.keras.layers.Conv2D(kernel_size=3, filters=16, padding='same', activation='relu'),
tf.keras.layers.Conv2D(kernel_size=1, filters=8, padding='same', activation='relu'),
tf.keras.layers.Flatten(),
# classifying into 5 categories
tf.keras.layers.Dense(5, activation='softmax')
])
model.compile(
optimizer='adam',
loss= 'categorical_crossentropy',
metrics=['accuracy'])
6. [NEW INFO] Nowoczesne architektury konwolucyjne
W skrócie

Ilustracja: moduł konwolucyjny. Co w tej sytuacji będzie najlepsze? Warstwa max-pool, a po niej warstwa konwolucyjna 1x1 lub inna kombinacja warstw? Wypróbuj je wszystkie, połącz wyniki i pozwól sieci zdecydować. Po prawej stronie: architektura konwolucyjna „ inception” wykorzystująca takie moduły.
W Kerasie do tworzenia modeli, w których przepływ danych może się rozgałęziać, musisz używać stylu modelu „funkcjonalnego”. Oto przykład:
l = tf.keras.layers # syntax shortcut
y = l.Conv2D(filters=32, kernel_size=3, padding='same',
activation='relu', input_shape=[192, 192, 3])(x) # x=input image
# module start: branch out
y1 = l.Conv2D(filters=32, kernel_size=1, padding='same', activation='relu')(y)
y3 = l.Conv2D(filters=32, kernel_size=3, padding='same', activation='relu')(y)
y = l.concatenate([y1, y3]) # output now has 64 channels
# module end: concatenation
# many more layers ...
# Create the model by specifying the input and output tensors.
# Keras layers track their connections automatically so that's all that's needed.
z = l.Dense(5, activation='softmax')(y)
model = tf.keras.Model(x, z)

Inne tanie sztuczki
Małe filtry 3x3

Na tej ilustracji widać wynik zastosowania dwóch kolejnych filtrów 3x3. Spróbuj prześledzić, które punkty danych przyczyniły się do uzyskania wyniku: te 2 kolejne filtry 3x3 obliczają pewną kombinację regionu 5x5. Nie jest to dokładnie ta sama kombinacja, którą obliczyłby filtr 5x5, ale warto spróbować, ponieważ 2 kolejne filtry 3x3 są tańsze niż 1 filtr 5x5.
Konwolucje 1x1

W matematyce konwolucja „1x1” to mnożenie przez stałą, co nie jest zbyt przydatne. Pamiętaj jednak, że w przypadku konwolucyjnych sieci neuronowych filtr jest stosowany do kostki danych, a nie tylko do obrazu 2D. Filtr „1x1” oblicza ważoną sumę kolumny danych o rozmiarze 1x1 (patrz ilustracja), a przesuwając go po danych, uzyskasz kombinację liniową kanałów wejściowych. To jest przydatne. Jeśli potraktujesz kanały jako wyniki poszczególnych operacji filtrowania, np. filtr „spiczaste uszy”, „wąsy” i „szczelinowe oczy”, to warstwa konwolucyjna „1x1” będzie obliczać wiele możliwych kombinacji liniowych tych cech, co może być przydatne podczas wyszukiwania „kota”. Dodatkowo warstwy 1x1 używają mniejszej liczby wag.
7. Squeezenet
Prosty sposób na połączenie tych pomysłów został przedstawiony w publikacji „Squeezenet”. Autorzy proponują bardzo prostą architekturę modułu konwolucyjnego, w której używane są tylko warstwy konwolucyjne 1x1 i 3x3.

Ilustracja: architektura SqueezeNet oparta na „modułach Fire”. Na przemian stosują warstwę 1x1, która „ściska” dane przychodzące w wymiarze pionowym, a następnie 2 równoległe warstwy konwolucyjne 1x1 i 3x3, które ponownie „rozszerzają” głębię danych.
Praktyczne
Kontynuuj pracę w poprzednim notatniku i zbuduj konwolucyjną sieć neuronową inspirowaną architekturą SqueezeNet. Musisz zmienić kod modelu na „styl funkcjonalny” Keras.
Keras_Flowers_TPU (playground).ipynb
Dodatkowe informacje
W tym ćwiczeniu przyda się zdefiniowanie funkcji pomocniczej dla modułu SqueezeNet:
def fire(x, squeeze, expand):
y = l.Conv2D(filters=squeeze, kernel_size=1, padding='same', activation='relu')(x)
y1 = l.Conv2D(filters=expand//2, kernel_size=1, padding='same', activation='relu')(y)
y3 = l.Conv2D(filters=expand//2, kernel_size=3, padding='same', activation='relu')(y)
return tf.keras.layers.concatenate([y1, y3])
# this is to make it behave similarly to other Keras layers
def fire_module(squeeze, expand):
return lambda x: fire(x, squeeze, expand)
# usage:
x = l.Input(shape=[192, 192, 3])
y = fire_module(squeeze=24, expand=48)(x) # typically, squeeze is less than expand
y = fire_module(squeeze=32, expand=64)(y)
...
model = tf.keras.Model(x, y)
Tym razem celem jest osiągnięcie 80% dokładności.
Warto spróbować
Zacznij od jednej warstwy splotowej, a potem dodaj „fire_modules”, na przemian z warstwami MaxPooling2D(pool_size=2). Możesz eksperymentować z 2–4 warstwami maksymalnego pulowania w sieci, a także z 1, 2 lub 3 kolejnymi modułami Fire między warstwami maksymalnego pulowania.
W modułach dotyczących pożarów parametr „squeeze” powinien być zwykle mniejszy niż parametr „expand”. Te parametry to w rzeczywistości liczby filtrów. Zwykle mieszczą się w zakresie od 8 do 196. Możesz eksperymentować z architekturami, w których liczba filtrów stopniowo rośnie w sieci, lub z prostymi architekturami, w których wszystkie moduły fire mają taką samą liczbę filtrów.
Oto przykład:
x = tf.keras.layers.Input(shape=[*IMAGE_SIZE, 3]) # input is 192x192 pixels RGB
y = tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu')(x)
y = fire_module(24, 48)(y)
y = tf.keras.layers.MaxPooling2D(pool_size=2)(y)
y = fire_module(24, 48)(y)
y = tf.keras.layers.MaxPooling2D(pool_size=2)(y)
y = fire_module(24, 48)(y)
y = tf.keras.layers.GlobalAveragePooling2D()(y)
y = tf.keras.layers.Dense(5, activation='softmax')(y)
model = tf.keras.Model(x, y)
W tym momencie możesz zauważyć, że eksperymenty nie przebiegają zbyt dobrze i że osiągnięcie 80-procentowej dokładności wydaje się odległe. Czas na kilka kolejnych tanich sztuczek.
Batch Normalization
Normalizacja wsadowa pomoże Ci rozwiązać problemy ze zbieżnością. Szczegółowe wyjaśnienia tej techniki znajdziesz w następnych warsztatach. Na razie używaj jej jako „magicznego” pomocnika, dodając ten wiersz po każdej warstwie konwolucyjnej w swojej sieci, w tym po warstwach w funkcji fire_module:
y = tf.keras.layers.BatchNormalization(momentum=0.9)(y)
# please adapt the input and output "y"s to whatever is appropriate in your context
Parametr momentum musi zostać zmniejszony z domyślnej wartości 0,99 do 0,9, ponieważ nasz zbiór danych jest mały. Na razie nie przejmuj się tym szczegółem.
Augmentacja danych
Możesz uzyskać kilka dodatkowych punktów procentowych, wzbogacając dane o proste przekształcenia, takie jak odwrócenie obrazu w pionie lub poziomie albo zmiany nasycenia:


W TensorFlow jest to bardzo proste dzięki interfejsowi tf.data.Dataset API. Zdefiniuj nową funkcję przekształcenia danych:
def data_augment(image, label):
image = tf.image.random_flip_left_right(image)
image = tf.image.random_saturation(image, lower=0, upper=2)
return image, label
Następnie użyj go w ostatecznym przekształceniu danych (komórka „training and validation datasets”, funkcja „get_batched_dataset”):
dataset = dataset.repeat() # existing line
# insert this
if augment_data:
dataset = dataset.map(data_augment, num_parallel_calls=AUTO)
dataset = dataset.shuffle(2048) # existing line
Nie zapomnij, aby rozszerzanie danych było opcjonalne, i dodaj niezbędny kod, aby mieć pewność, że rozszerzany jest tylko zbiór danych treningowych. Nie ma sensu powiększać zbioru danych weryfikacyjnych.
Osiągnięcie dokładności na poziomie 80% w 35 epokach powinno być teraz możliwe.
Rozwiązanie
Oto notatnik z rozwiązaniem. Możesz z niej skorzystać, jeśli utkniesz w martwym punkcie.
Keras_Flowers_TPU_squeezenet.ipynb
Omówione zagadnienia
- 🤔 Modele Keras w „stylu funkcyjnym”
- 🤓 Architektura Squeezenet
- 🤓 Rozszerzanie danych za pomocą tf.data.dataset
Poświęć chwilę na przejrzenie tej listy kontrolnej.
8. Dostrojony model Xception
Konwolucje rozdzielne
Ostatnio zyskuje popularność inny sposób implementacji warstw konwolucyjnych: konwolucje rozdzielne głębiowo. Wiem, że to skomplikowane, ale sama koncepcja jest dość prosta. Są one zaimplementowane w TensorFlow i Keras jako tf.keras.layers.SeparableConv2D.
Konwolucja separowalna również stosuje filtr do obrazu, ale używa innego zestawu wag dla każdego kanału obrazu wejściowego. Następnie następuje „konwolucja 1x1”, czyli seria iloczynów skalarnych, które dają ważoną sumę odfiltrowanych kanałów. Za każdym razem z nowymi wagami obliczana jest wymagana liczba ważonych rekombinacji kanałów.

Ilustracja: konwolucje separowalne. Faza 1. Konwolucje z osobnym filtrem dla każdego kanału. Faza 2: liniowe rekombinacje kanałów. Powtarzane z nowym zestawem wag, aż zostanie osiągnięta żądana liczba kanałów wyjściowych. Etap 1 można też powtórzyć, za każdym razem z nowymi wagami, ale w praktyce rzadko się to zdarza.
Konwolucje rozdzielne są używane w najnowszych architekturach sieci konwolucyjnych: MobileNetV2, Xception i EfficientNet. Nawiasem mówiąc, MobileNetV2 to model, którego używasz do uczenia przez przenoszenie.
Są one tańsze od zwykłych splotów i w praktyce okazują się równie skuteczne. Oto waga w przypadku przykładu przedstawionego powyżej:
Warstwa konwolucyjna: 4 x 4 x 3 x 5 = 240
Warstwa konwolucyjna z separacją: 4 x 4 x 3 + 3 x 5 = 48 + 15 = 63
Obliczenie liczby mnożeń wymaganych do zastosowania każdego stylu warstwy konwolucyjnej pozostawiamy czytelnikowi. Są one mniejsze i znacznie bardziej wydajne obliczeniowo.
Praktyczne
Zacznij od nowa w notatniku „transfer learning”, ale tym razem wybierz Xception jako wstępnie wytrenowany model. Xception używa tylko konwolucji rozdzielnych. Pozostaw wszystkie wagi z możliwością trenowania. Zamiast używać wstępnie wytrenowanych warstw, będziemy dostrajać wstępnie wytrenowane wagi na podstawie naszych danych.
Keras Flowers transfer learning (playground).ipynb
Cel: dokładność > 95% (tak, to możliwe!)
To ostatnie ćwiczenie wymaga nieco więcej pracy związanej z kodowaniem i nauką o danych.
Dodatkowe informacje o dostrajaniu
Model Xception jest dostępny w standardowych wytrenowanych modelach w tf.keras.application.* Tym razem nie zapomnij pozostawić wszystkich wag z możliwością trenowania.
pretrained_model = tf.keras.applications.Xception(input_shape=[*IMAGE_SIZE, 3],
include_top=False)
pretrained_model.trainable = True
Aby uzyskać dobre wyniki podczas dostrajania modelu, musisz zwrócić uwagę na tempo uczenia się i użyć harmonogramu tempa uczenia się z okresem optymalizacyjnym. W ten sposób:

Rozpoczęcie od standardowego tempa uczenia się zakłóciłoby wstępnie wytrenowane wagi modelu. Stopniowe rozpoczynanie zachowuje je, dopóki model nie zostanie dopasowany do Twoich danych i nie będzie w stanie ich odpowiednio modyfikować. Po okresie rozgrzewki możesz kontynuować ze stałym lub wykładniczo malejącym tempem uczenia się.
W Keras tempo uczenia się jest określane za pomocą wywołania zwrotnego, w którym możesz obliczyć odpowiednie tempo uczenia się dla każdej epoki. Keras przekaże do optymalizatora odpowiednie tempo uczenia się dla każdej epoki.
def lr_fn(epoch):
lr = ...
return lr
lr_callback = tf.keras.callbacks.LearningRateScheduler(lr_fn, verbose=True)
model.fit(..., callbacks=[lr_callback])
Rozwiązanie
Oto notatnik z rozwiązaniem. Możesz z niej skorzystać, jeśli utkniesz w martwym punkcie.
07_Keras_Flowers_TPU_xception_fine_tuned_best.ipynb
Omówione zagadnienia
- 🤔 Konwolucja z rozdzieleniem głębi
- 🤓 Harmonogramy tempa uczenia się
- 😈 Dostrajanie wstępnie wytrenowanego modelu.
Poświęć chwilę na przejrzenie tej listy kontrolnej.
9. Gratulacje!
Udało Ci się zbudować pierwszą nowoczesną splotową sieć neuronową i wytrenować ją do dokładności ponad 90%. Dzięki TPU kolejne iteracje szkolenia zajęły Ci tylko kilka minut. To koniec 4 samouczków „Keras na TPU”:
- Potoki danych o szybkości TPU: tf.data.Dataset i TFRecords
- Pierwszy model Keras z uczeniem przez przenoszenie
- Splotowe sieci neuronowe z użyciem Keras i TPU
- [THIS LAB] Nowoczesne sieci konwolucyjne, SqueezeNet, Xception z użyciem Keras i TPU
TPU w praktyce
TPU i GPU są dostępne na Cloud AI Platform:
- Na maszynach wirtualnych do deep learningu
- W Notatnikach w AI Platform
- W zadaniach trenowania w AI Platform
Na koniec dodamy, że chętnie poznamy Twoją opinię. Jeśli zauważysz w tym module coś nieprawidłowego lub uważasz, że można go ulepszyć, daj nam znać. Opinie można przesyłać za pomocą zgłoszeń w GitHub [link do opinii].

|

